diff --git a/VERSION.txt b/VERSION.txt index 3bcf233c891..70f1eab5ef3 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.0.beta1, Release Date: 2017-04-06 +SageMath version 8.0.beta2, Release Date: 2017-04-12 diff --git a/build/pkgs/brial/checksums.ini b/build/pkgs/brial/checksums.ini index d92959393df..52334d8d953 100644 --- a/build/pkgs/brial/checksums.ini +++ b/build/pkgs/brial/checksums.ini @@ -1,4 +1,4 @@ tarball=brial-VERSION.tar.bz2 -sha1=12ef021fc1236e25ff0b46680720918489fb4931 -md5=f332eaa0378e9b630f958e4dcd4ea6e8 -cksum=2509320148 +sha1=7e6b7a625773da78cefda241dc016f242da27d7f +md5=449858d7e7f918066eb9224d903cd8f6 +cksum=2006315550 diff --git a/build/pkgs/brial/package-version.txt b/build/pkgs/brial/package-version.txt index 7ada0d303f3..1e9b46b2298 100644 --- a/build/pkgs/brial/package-version.txt +++ b/build/pkgs/brial/package-version.txt @@ -1 +1 @@ -0.8.5 +0.8.7 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 63cc05dbc86..f63ecb19d54 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=461f156646483261f665ac9978a29c6224e2a20f -md5=45c477ceae69c8fbd7ea7e6de4a3d5aa -cksum=1746482096 +sha1=8bdfb4b87a803b90d6b9a937d961835955723a09 +md5=1f1aa69a7ac98e4443e82611a3bbea23 +cksum=4121821509 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index c34a8046fa3..a817176f4a6 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -215 +216 diff --git a/build/pkgs/functools32/spkg-install b/build/pkgs/functools32/spkg-install index 1e1ab83f3ea..98b9a95a6a0 100755 --- a/build/pkgs/functools32/spkg-install +++ b/build/pkgs/functools32/spkg-install @@ -6,7 +6,7 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi -if [ -n -x "$SAGE_LOCAL/bin/python2" ]; then +if [ "$SAGE_PYTHON3" = yes ]; then echo "Skipping functools32 since it is not necessary on Python 3" exit 0 fi diff --git a/build/pkgs/libgap/spkg-check b/build/pkgs/libgap/spkg-check index 11ea3ca10e2..802b96fddc8 100755 --- a/build/pkgs/libgap/spkg-check +++ b/build/pkgs/libgap/spkg-check @@ -1,6 +1,12 @@ #!/usr/bin/env bash +# location of the corresponding gap installation +# it is only used by the test suite +# it cannot be moved to spkg-install as it can interfere +# with proper detection of PIC flags, notably with clang +# see #22784 +CPPFLAGS="$CPPFLAGS"' -DSYS_DEFAULT_PATHS=\"'"$SAGE_LOCAL/gap/latest"'\"' cd src -make check +make check CPPFLAGS="$CPPFLAGS" diff --git a/build/pkgs/libgap/spkg-install b/build/pkgs/libgap/spkg-install index 34236cb8e6e..c502c30e7b6 100755 --- a/build/pkgs/libgap/spkg-install +++ b/build/pkgs/libgap/spkg-install @@ -24,10 +24,6 @@ fi source "$SAGE_LOCAL/gap/latest/sysinfo.gap" echo "GAP was configured with $GAParch_system" -# location of the corresponding gap installation -# only used for the testsuite -CPPFLAGS="$CPPFLAGS"' -DSYS_DEFAULT_PATHS=\"'"$SAGE_LOCAL/gap/latest"'\"' - cd src echo "Configuring libGAP..." diff --git a/build/pkgs/libgd/spkg-install b/build/pkgs/libgd/spkg-install index fd2badf229c..2aa82e9375e 100755 --- a/build/pkgs/libgd/spkg-install +++ b/build/pkgs/libgd/spkg-install @@ -13,11 +13,6 @@ rm "$SAGE_LOCAL"/lib/libgd.* cd src -# Needed to correctly pickup libiconv on FreeBSD -if [ "$UNAME" = "FreeBSD" ] ; then - LDFLAGS="-L/usr/local/lib $LDFLAGS" -fi - if [ "$SAGE64" = "yes" ]; then CFLAGS=" -m64 -g $CFLAGS" LDFLAGS="-m64 $LDFLAGS" diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt index 9df886c42a1..2a175dcdb19 100644 --- a/build/pkgs/linbox/package-version.txt +++ b/build/pkgs/linbox/package-version.txt @@ -1 +1 @@ -1.4.2 +1.4.2.p0 diff --git a/build/pkgs/linbox/patches/linbox-PR50.patch b/build/pkgs/linbox/patches/linbox-PR50.patch new file mode 100644 index 00000000000..386639695ef --- /dev/null +++ b/build/pkgs/linbox/patches/linbox-PR50.patch @@ -0,0 +1,52 @@ +From e6f686d73778bbc7850df74dd0e14f8707fa845f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= +Date: Thu, 6 Apr 2017 10:29:40 +1200 +Subject: [PATCH] Simplification in gf2.h headers. Be more standard conformant + +--- + linbox/field/gf2.h | 24 +----------------------- + 1 file changed, 1 insertion(+), 23 deletions(-) + +diff --git a/linbox/field/gf2.h b/linbox/field/gf2.h +index 08c14fd..8afc33c 100644 +--- a/linbox/field/gf2.h ++++ b/linbox/field/gf2.h +@@ -41,11 +41,7 @@ + #include "linbox/field/field-traits.h" + // #include "linbox/vector/vector-domain.h" + +-#if !defined(__PATHCC__) && !(defined(__APPLE__) && defined(__clang__)) +-#define stdBitReference std::_Bit_reference +-#else + #define stdBitReference std::vector::reference +-#endif + + // Namespace in which all LinBox code resides + namespace LinBox +@@ -1015,25 +1011,7 @@ namespace LinBox + // Specialization of homomorphism for basefield + #include "linbox/randiter/gf2.h" + +-#if __cplusplus >= 201103L +-#if defined( __APPLE__) && defined(__clang__) +-#include <__bit_reference> +-#else +-#include +-#endif /* __clang__ */ +-#else +-// #include +-{ +- //! @todo JGD 05.11.2009 : it should be in bits/stl_bvector.h ... +- inline void swap(stdBitReference __x, stdBitReference __y) +- { +- bool __tmp = __x; +- __x = __y; +- __y = __tmp; +- } +-} +-#endif +- ++#include + + #include "linbox/field/gf2.inl" + diff --git a/build/pkgs/linbox/disabled-spkg-check b/build/pkgs/linbox/spkg-check similarity index 100% rename from build/pkgs/linbox/disabled-spkg-check rename to build/pkgs/linbox/spkg-check diff --git a/build/pkgs/matplotlib/make-setup-config.py b/build/pkgs/matplotlib/make-setup-config.py index ad5a0c19ab5..9a86586f9b2 100644 --- a/build/pkgs/matplotlib/make-setup-config.py +++ b/build/pkgs/matplotlib/make-setup-config.py @@ -30,5 +30,5 @@ for backend in ('gtk', 'gtkagg', 'tkagg', 'wxagg', 'macosx', 'windowing'): config.set('gui_support', backend, graphical_backend) -with open('src/setup.cfg', 'wb') as configfile: +with open('src/setup.cfg', 'w') as configfile: config.write(configfile) diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install index 1082a08f03f..dde2382964b 100755 --- a/build/pkgs/numpy/spkg-install +++ b/build/pkgs/numpy/spkg-install @@ -1,7 +1,5 @@ #!/usr/bin/env bash -CUR=`pwd` - if [ -z "$SAGE_LOCAL" ]; then echo "SAGE_LOCAL undefined ... exiting" echo "Maybe run 'sage -sh'?" @@ -34,7 +32,7 @@ else export LDFLAGS="${LDFLAGS} -shared" fi -python "${CUR}"/lapack_conf.py +python ../lapack_conf.py # Make sure that the fortran objects are compiled with -fPIC export FFLAGS="$FFLAGS -fPIC" @@ -44,10 +42,7 @@ export NUMPY_FCONFIG="config_fc --noopt --noarch" ################################################ -rm -rf "$SAGE_LOCAL/lib/python/site-packages/numpy" - -# Program around a bug in SciPY's distutils. -unset CFLAGS +rm -rf "$SAGE_LOCAL/lib/python*/site-packages/numpy" python setup.py \ --no-user-cfg \ diff --git a/build/pkgs/openblas/patches/4998e19869636ceca28e158e1da28f0a4e1df5ee.patch b/build/pkgs/openblas/patches/4998e19869636ceca28e158e1da28f0a4e1df5ee.patch new file mode 100644 index 00000000000..53780998e64 --- /dev/null +++ b/build/pkgs/openblas/patches/4998e19869636ceca28e158e1da28f0a4e1df5ee.patch @@ -0,0 +1,832 @@ +Taken from https://github.com/xianyi/OpenBLAS/pull/982 + +From 4998e19869636ceca28e158e1da28f0a4e1df5ee Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Thu, 13 Oct 2016 16:51:08 +0200 +Subject: [PATCH] Change file comments to work around clang 3.9 assembler bug + +--- + kernel/x86_64/dgemm_kernel_4x8_sandy.S | 198 ++++++++++++++++----------------- + 1 file changed, 99 insertions(+), 99 deletions(-) + +diff --git a/kernel/x86_64/dgemm_kernel_4x8_sandy.S b/kernel/x86_64/dgemm_kernel_4x8_sandy.S +index a52bb07..926395c 100644 +--- a/kernel/x86_64/dgemm_kernel_4x8_sandy.S ++++ b/kernel/x86_64/dgemm_kernel_4x8_sandy.S +@@ -277,7 +277,7 @@ LEAQ (, %rax, SIZE), %rax; + LEAQ (ptrba, %rax, 8), ptrba; + LEAQ (ptrbb, %rax, 4), ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + PREFETCH2 0*SIZE(prebb); + XOR_DY yvec15, yvec15, yvec15; + PREFETCH2 8*SIZE(prebb); +@@ -317,7 +317,7 @@ ALIGN_5; + .L2_bodyB:; + # Computing kernel + +-#### Unroll times 1 #### ++//#### Unroll times 1 #### + LD_DY 4*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + SHUF_DY $0x03, yvec2, yvec2, yvec4; +@@ -345,7 +345,7 @@ MUL_DY yvec1, yvec5, yvec7; + ADD_DY yvec10, yvec6, yvec10; + ADD_DY yvec8, yvec7, yvec8; + +-#### Unroll times 2 #### ++//#### Unroll times 2 #### + LD_DY 12*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + SHUF_DY $0x03, yvec2, yvec2, yvec4; +@@ -373,7 +373,7 @@ MUL_DY yvec1, yvec5, yvec7; + ADD_DY yvec10, yvec6, yvec10; + ADD_DY yvec8, yvec7, yvec8; + +-#### Unroll times 3 #### ++//#### Unroll times 3 #### + LD_DY 20*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + SHUF_DY $0x03, yvec2, yvec2, yvec4; +@@ -402,7 +402,7 @@ MUL_DY yvec1, yvec5, yvec7; + ADD_DY yvec10, yvec6, yvec10; + ADD_DY yvec8, yvec7, yvec8; + +-#### Unroll times 4 #### ++//#### Unroll times 4 #### + LD_DY 28*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + SHUF_DY $0x03, yvec2, yvec2, yvec4; +@@ -446,7 +446,7 @@ TEST $2, %rax; + JLE .L3_loopE; + ALIGN_5 + .L3_bodyB: +-#### Unroll times 1 #### ++//#### Unroll times 1 #### + PREFETCH0 64*SIZE(ptrba) + LD_DY 4*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; +@@ -475,7 +475,7 @@ MUL_DY yvec1, yvec5, yvec7; + ADD_DY yvec10, yvec6, yvec10; + ADD_DY yvec8, yvec7, yvec8; + +-#### Unroll times 2 #### ++//#### Unroll times 2 #### + PREFETCH0 72*SIZE(ptrba) + LD_DY 12*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; +@@ -516,7 +516,7 @@ TEST $1, %rax; + JLE .L4_loopE; + ALIGN_5 + .L4_bodyB:; +-#### Unroll times 1 #### ++//#### Unroll times 1 #### + PREFETCH0 64*SIZE(ptrba) + LD_DY 4*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; +@@ -544,9 +544,9 @@ ADD_DY yvec10, yvec6, yvec10; + ADD_DY yvec8, yvec7, yvec8; + + .L4_loopE:; +-#### Load Alpha #### ++//#### Load Alpha #### + BROAD_DY MEMALPHA,yvec7; +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + MUL_DY yvec7,yvec15,yvec15; + MUL_DY yvec7,yvec14,yvec14; + MUL_DY yvec7,yvec13,yvec13; +@@ -555,7 +555,7 @@ MUL_DY yvec7,yvec11,yvec11; + MUL_DY yvec7,yvec10,yvec10; + MUL_DY yvec7,yvec9,yvec9; + MUL_DY yvec7,yvec8,yvec8; +-#### Reverse the Results #### ++//#### Reverse the Results #### + MOV_DY yvec15,yvec7; + REVS_DY $0x0a,yvec13,yvec15,yvec15; + REVS_DY $0x0a,yvec7,yvec13,yvec13; +@@ -568,13 +568,13 @@ REVS_DY $0x0a,yvec7,yvec9,yvec9; + MOV_DY yvec10,yvec7; + REVS_DY $0x0a,yvec8,yvec10,yvec10; + REVS_DY $0x0a,yvec7,yvec8,yvec8; +-#### Testing alignment #### ++//#### Testing alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L4_loopEx; # Unalign part write back + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1,yvec15,xvec7; + EXTRA_DY $1,yvec14,xvec6; + EXTRA_DY $1,yvec13,xvec5; +@@ -776,7 +776,7 @@ LEAQ (, %rax, SIZE), %rax; + LEAQ (ptrba, %rax, 4), ptrba; + LEAQ (ptrbb, %rax, 4), ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + XOR_DY yvec13, yvec13, yvec13; + LD_DY 0*SIZE(ptrbb), yvec2; +@@ -805,7 +805,7 @@ ALIGN_5; + .L6_bodyB:; + # Computing kernel + +-#### Untoll time 1 #### ++//#### Untoll time 1 #### + LD_DY 4*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -821,7 +821,7 @@ VPERMILP_DY $0x05, yvec2, yvec3; + MUL_DY yvec0, yvec5, yvec7; + ADD_DY yvec9, yvec7, yvec9; + +-#### Untoll time 2 #### ++//#### Untoll time 2 #### + LD_DY 8*SIZE(ptrba), yvec0; + MUL_DY yvec1, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -837,7 +837,7 @@ VPERMILP_DY $0x05, yvec2, yvec3; + MUL_DY yvec1, yvec5, yvec7; + ADD_DY yvec9, yvec7, yvec9; + +-#### Untoll time 3 #### ++//#### Untoll time 3 #### + LD_DY 12*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -855,7 +855,7 @@ ADDQ $16*SIZE, ptrbb; + MUL_DY yvec0, yvec5, yvec7; + ADD_DY yvec9, yvec7, yvec9; + +-#### Untoll time 4 #### ++//#### Untoll time 4 #### + LD_DY 0*SIZE(ptrba), yvec0; + MUL_DY yvec1, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -883,7 +883,7 @@ TEST $2, %rax; + JLE .L7_loopE; + ALIGN_5 + .L7_bodyB:; +-#### Untoll time 1 #### ++//#### Untoll time 1 #### + LD_DY 4*SIZE(ptrba), yvec1; + MUL_DY yvec0, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -901,7 +901,7 @@ ADDQ $8*SIZE, ptrbb; + MUL_DY yvec0, yvec5, yvec7; + ADD_DY yvec9, yvec7, yvec9; + +-#### Untoll time 2 #### ++//#### Untoll time 2 #### + LD_DY 0*SIZE(ptrba), yvec0; + MUL_DY yvec1, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; +@@ -927,7 +927,7 @@ TEST $1, %rax; + JLE .L8_loopE; + ALIGN_5 + .L8_bodyB:; +-#### Untoll time 1 #### ++//#### Untoll time 1 #### + MUL_DY yvec0, yvec2, yvec6; + ADD_DY yvec15, yvec6, yvec15; + SHUF_DY $0x03, yvec2, yvec2, yvec4; +@@ -943,27 +943,27 @@ MUL_DY yvec0, yvec5, yvec7; + ADD_DY yvec9, yvec7, yvec9; + + .L8_loopE:; +-#### Load Alpha #### ++//#### Load Alpha #### + BROAD_DY MEMALPHA, yvec7; +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + MUL_DY yvec7,yvec15,yvec15; + MUL_DY yvec7,yvec13,yvec13; + MUL_DY yvec7,yvec11,yvec11; + MUL_DY yvec7,yvec9,yvec9; +-#### Reverse the Results #### ++//#### Reverse the Results #### + MOV_DY yvec15, yvec7; + REVS_DY $0x0a,yvec13,yvec15,yvec15; + REVS_DY $0x0a,yvec7,yvec13,yvec13; + MOV_DY yvec11,yvec7; + REVS_DY $0x0a,yvec9,yvec11,yvec11; + REVS_DY $0x0a,yvec7,yvec9,yvec9; +-#### Testing alignment #### ++//#### Testing alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L8_loopEx; # Unalign part write back + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1,yvec15,xvec7; + EXTRA_DY $1,yvec13,xvec5; + EXTRA_DY $1,yvec11,xvec3; +@@ -1076,7 +1076,7 @@ LEAQ (, %rax, SIZE), %rax; + LEAQ (ptrba, %rax, 2), ptrba; + LEAQ (ptrbb, %rax, 4), ptrbb + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + LD_DX 0*SIZE(ptrbb), xvec2; + XOR_DY yvec15, yvec15, yvec15; + LD_DX 2*SIZE(ptrbb), xvec3; +@@ -1106,7 +1106,7 @@ ALIGN_5; + .L10_bodyB:; + # Computing kernel + +-##### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 4*SIZE(ptrbb), xvec6; + SHUF_DX $0x4e, xvec3, xvec5; + MUL_DX xvec0, xvec2, xvec2; +@@ -1123,7 +1123,7 @@ SHUF_DX $0x4e, xvec6, xvec4; + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec9, xvec9; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 8*SIZE(ptrbb), xvec2; + SHUF_DX $0x4e, xvec7, xvec5; + MUL_DX xvec1, xvec6, xvec6; +@@ -1140,7 +1140,7 @@ SHUF_DX $0x4e, xvec2, xvec4; + MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec9, xvec9; + +-##### Unroll time 3 #### ++//#### Unroll time 3 #### + LD_DX 12*SIZE(ptrbb), xvec6; + SHUF_DX $0x4e, xvec3, xvec5; + MUL_DX xvec0, xvec2, xvec2; +@@ -1159,7 +1159,7 @@ ADDQ $8*SIZE, ptrba; + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec9, xvec9; + +-#### Unroll time 4 #### ++//#### Unroll time 4 #### + LD_DX 0*SIZE(ptrbb), xvec2; + SHUF_DX $0x4e, xvec7, xvec5; + MUL_DX xvec1, xvec6, xvec6; +@@ -1188,7 +1188,7 @@ TEST $2, %rax; + JLE .L11_loopE; + ALIGN_5 + .L11_bodyB:; +-##### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 4*SIZE(ptrbb), xvec6; + SHUF_DX $0x4e, xvec3, xvec5; + MUL_DX xvec0, xvec2, xvec2; +@@ -1208,7 +1208,7 @@ ADDQ $4*SIZE, ptrba; + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec9, xvec9; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 0*SIZE(ptrbb), xvec2; + SHUF_DX $0x4e, xvec7, xvec5; + MUL_DX xvec1, xvec6, xvec6; +@@ -1251,27 +1251,27 @@ MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec9, xvec9; + + .L12_loopE:; +-#### Load Alpha #### ++//#### Load Alpha #### + BROAD_DX MEMALPHA, xvec7; +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + MUL_DX xvec7, xvec15, xvec15; + MUL_DX xvec7, xvec13, xvec13; + MUL_DX xvec7, xvec11, xvec11; + MUL_DX xvec7, xvec9, xvec9; +-#### Reverse the Results #### ++//#### Reverse the Results #### + MOV_DX xvec15, xvec6; + REVS_DX xvec13, xvec15, xvec15; + REVS_DX xvec6, xvec13, xvec13; + MOV_DX xvec11, xvec6; + REVS_DX xvec9, xvec11, xvec11; + REVS_DX xvec6, xvec9, xvec9; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L12_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + #ifndef TRMMKERNEL + ADD_DX 0*SIZE(C0), xvec13, xvec13; + ADD_DX 0*SIZE(C0, ldc, 1), xvec15, xvec15; +@@ -1345,7 +1345,7 @@ LEAQ (,%rax, SIZE), %rax; + ADDQ %rax, ptrba; + LEAQ (ptrbb, %rax, 4), ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + #ifndef TRMMKERNEL + MOVQ bk, k; +@@ -1429,11 +1429,11 @@ ADDQ $1*SIZE, ptrba; + ADDQ $4*SIZE, ptrbb; + + .L16_loopE: +-#### Load Alpha #### ++//#### Load Alpha #### + BROAD_DY MEMALPHA, yvec7; +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + MUL_DY yvec15, yvec7, yvec15; +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1, yvec15, xvec7; + #ifndef TRMMKERNEL + LDL_DX 0*SIZE(C0), xvec0, xvec0; +@@ -1497,7 +1497,7 @@ LEAQ (, %rax, SIZE), %rax; + LEAQ (ptrba, %rax, 8), ptrba; + LEAQ (ptrbb, %rax, 2), ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + XOR_DY yvec14, yvec14, yvec14; + XOR_DY yvec13, yvec13, yvec13; +@@ -1526,7 +1526,7 @@ JLE .L211_loopE; + ALIGN_5; + .L211_bodyB: + # Computing kernel +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1563,7 +1563,7 @@ ADD_DX xvec6, xvec9, xvec9; + MUL_DX xvec3, xvec7, xvec7; + ADD_DX xvec7, xvec8, xvec8; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 8*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1600,7 +1600,7 @@ ADD_DX xvec6, xvec9, xvec9; + MUL_DX xvec3, xvec7, xvec7; + ADD_DX xvec7, xvec8, xvec8; + +-#### Unroll time 3 #### ++//#### Unroll time 3 #### + LD_DX 16*SIZE(ptrba), xvec0; + LD_DX 4*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1637,7 +1637,7 @@ ADD_DX xvec6, xvec9, xvec9; + MUL_DX xvec3, xvec7, xvec7; + ADD_DX xvec7, xvec8, xvec8; + +-#### Unroll time 4 #### ++//#### Unroll time 4 #### + LD_DX 24*SIZE(ptrba), xvec0; + LD_DX 6*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1689,7 +1689,7 @@ JLE .L212_loopE; + ALIGN_5; + .L212_bodyB: + # Computing kernel +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1726,7 +1726,7 @@ ADD_DX xvec6, xvec9, xvec9; + MUL_DX xvec3, xvec7, xvec7; + ADD_DX xvec7, xvec8, xvec8; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 8*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1775,7 +1775,7 @@ TEST $1, %rax; + JLE .L213_loopE; + ALIGN_5 + .L213_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1815,7 +1815,7 @@ MUL_DX xvec3, xvec7, xvec7; + ADD_DX xvec7, xvec8, xvec8; + + .L213_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DX MEMALPHA, xvec7; + MUL_DX xvec7, xvec15, xvec15; + MUL_DX xvec7, xvec14, xvec14; +@@ -1825,7 +1825,7 @@ MUL_DX xvec7, xvec11, xvec11; + MUL_DX xvec7, xvec10, xvec10; + MUL_DX xvec7, xvec9, xvec9; + MUL_DX xvec7, xvec8, xvec8; +-#### Reverse ##### ++//#### Reverse #### + MOV_DX xvec15, xvec6; + REVS_DX xvec11, xvec15, xvec15; + REVS_DX xvec6, xvec11, xvec11; +@@ -1838,13 +1838,13 @@ REVS_DX xvec6, xvec9, xvec9; + MOV_DX xvec12, xvec6; + REVS_DX xvec8, xvec12, xvec12; + REVS_DX xvec6, xvec8, xvec8; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L213_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + #ifndef TRMMKERNEL + ADD_DX 0*SIZE(C0), xvec11, xvec11; + ADD_DX 2*SIZE(C0), xvec10, xvec10; +@@ -1952,7 +1952,7 @@ LEAQ (,%rax, SIZE), %rax; + LEAQ (ptrba, %rax, 4), ptrba; + LEAQ (ptrbb, %rax, 2), ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + XOR_DY yvec14, yvec14, yvec14; + XOR_DY yvec11, yvec11, yvec11; +@@ -1977,7 +1977,7 @@ JLE .L221_loopE; + ALIGN_5 + .L221_bodyB:; + # Computing kernel +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -1996,7 +1996,7 @@ ADD_DX xvec4, xvec11, xvec11; + MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec10, xvec10; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 4*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2015,7 +2015,7 @@ ADD_DX xvec4, xvec11, xvec11; + MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec10, xvec10; + +-#### Unroll time 3 #### ++//#### Unroll time 3 #### + LD_DX 8*SIZE(ptrba), xvec0; + LD_DX 4*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2034,7 +2034,7 @@ ADD_DX xvec4, xvec11, xvec11; + MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec10, xvec10; + +-#### Unroll time 4 #### ++//#### Unroll time 4 #### + LD_DX 12*SIZE(ptrba), xvec0; + LD_DX 6*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2067,7 +2067,7 @@ TEST $2, %rax; + JLE .L222_loopE; + ALIGN_5 + .L222_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2086,7 +2086,7 @@ ADD_DX xvec4, xvec11, xvec11; + MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec10, xvec10; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 4*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2116,7 +2116,7 @@ TEST $1, %rax; + JLE .L223_loopE; + ALIGN_5 + .L223_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + MOV_DX xvec4, xvec5; +@@ -2138,26 +2138,26 @@ MUL_DX xvec1, xvec5, xvec5; + ADD_DX xvec5, xvec10, xvec10; + + .L223_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DX MEMALPHA, xvec7; + MUL_DX xvec7, xvec15, xvec15; + MUL_DX xvec7, xvec14, xvec14; + MUL_DX xvec7, xvec11, xvec11; + MUL_DX xvec7, xvec10, xvec10; +-#### Reverse ##### ++//#### Reverse #### + MOV_DX xvec15, xvec6; + REVS_DX xvec11, xvec15, xvec15; + REVS_DX xvec6, xvec11, xvec11; + MOV_DX xvec14, xvec6; + REVS_DX xvec10, xvec14, xvec14; + REVS_DX xvec6, xvec10, xvec10; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L223_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + #ifndef TRMMKERNEL + ADD_DX 0*SIZE(C0), xvec11, xvec11; + ADD_DX 2*SIZE(C0), xvec10, xvec10; +@@ -2220,7 +2220,7 @@ ADDQ $4, kk + ADDQ $4*SIZE, C0; + ADDQ $4*SIZE, C1; + .L22_loopE:; +-TEST $2, bm; # Rm = 2 ++TEST $2, bm; // Rm = 2 + JLE .L23_loopE; + ALIGN_5; + .L23_bodyB: +@@ -2255,7 +2255,7 @@ JLE .L231_loopE; + ALIGN_5 + .L231_bodyB: + # Computing kernel +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2264,7 +2264,7 @@ ADD_DX xvec4, xvec15, xvec15; + + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec11, xvec11; +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 2*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2273,7 +2273,7 @@ ADD_DX xvec4, xvec15, xvec15; + + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec11, xvec11; +-#### Unroll time 3 #### ++//#### Unroll time 3 #### + LD_DX 4*SIZE(ptrba), xvec0; + LD_DX 4*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2282,7 +2282,7 @@ ADD_DX xvec4, xvec15, xvec15; + + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec11, xvec11; +-#### Unroll time 4 #### ++//#### Unroll time 4 #### + LD_DX 6*SIZE(ptrba), xvec0; + LD_DX 6*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2305,7 +2305,7 @@ TEST $2, %rax; + JLE .L232_loopE; + ALIGN_5 + .L232_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2314,7 +2314,7 @@ ADD_DX xvec4, xvec15, xvec15; + + MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec11, xvec11; +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DX 2*SIZE(ptrba), xvec0; + LD_DX 2*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2334,7 +2334,7 @@ TEST $1, %rax; + JLE .L233_loopE; + ALIGN_5 + .L233_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DX 0*SIZE(ptrba), xvec0; + LD_DX 0*SIZE(ptrbb), xvec4; + SHUF_DX $0x4e, xvec4, xvec5; +@@ -2345,21 +2345,21 @@ MUL_DX xvec0, xvec5, xvec5; + ADD_DX xvec5, xvec11, xvec11; + ADDQ $2*SIZE, ptrbb; + .L233_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DX MEMALPHA, xvec7; + MUL_DX xvec7, xvec15, xvec15; + MUL_DX xvec7, xvec11, xvec11; +-#### Reverse ##### ++//#### Reverse #### + MOV_DX xvec15, xvec6; + REVS_DX xvec11, xvec15, xvec15; + REVS_DX xvec6, xvec11, xvec11; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L233_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + #ifndef TRMMKERNEL + ADD_DX 0*SIZE(C0), xvec11, xvec11; + ADD_DX 0*SIZE(C1), xvec15, xvec15; +@@ -2408,7 +2408,7 @@ ADDQ $2, kk; + ADDQ $2*SIZE, C0; + ADDQ $2*SIZE, C1; + .L23_loopE: +-TEST $1, bm; # Rm = 1 ++TEST $1, bm; // Rm = 1 + JLE .L24_loopE; + ALIGN_5; + .L24_bodyB: +@@ -2534,7 +2534,7 @@ SALQ $4, k; + ADDQ k, bb; + LEAQ (C, ldc, 2), C; + .L20_loopE:; +-TEST $1, bn; # Rn = 1 ++TEST $1, bn; // Rn = 1 + JLE .L30_loopE; + ALIGN_5 + .L30_bodyB: +@@ -2558,7 +2558,7 @@ LEAQ (, %rax, SIZE), %rax; + LEAQ (ptrba, %rax, 8), ptrba; + ADDQ %rax, ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + XOR_DY yvec14, yvec14, yvec14; + #ifndef TRMMKERNEL +@@ -2580,7 +2580,7 @@ SARQ $2, k; + JLE .L311_loopE; + ALIGN_5 + .L311_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DY 0*SIZE(ptrba), yvec0; + LD_DY 4*SIZE(ptrba), yvec1; + BROAD_DY 0*SIZE(ptrbb), yvec2; +@@ -2589,7 +2589,7 @@ ADD_DY yvec0, yvec15, yvec15; + MUL_DY yvec2, yvec1, yvec1; + ADD_DY yvec1, yvec14, yvec14; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DY 8*SIZE(ptrba), yvec3; + LD_DY 12*SIZE(ptrba), yvec4; + BROAD_DY 1*SIZE(ptrbb), yvec5; +@@ -2598,7 +2598,7 @@ ADD_DY yvec3, yvec15, yvec15; + MUL_DY yvec5, yvec4, yvec4 + ADD_DY yvec4, yvec14, yvec14; + +-#### Unroll time 3 #### ++//#### Unroll time 3 #### + LD_DY 16*SIZE(ptrba), yvec0; + LD_DY 20*SIZE(ptrba), yvec1; + BROAD_DY 2*SIZE(ptrbb), yvec2; +@@ -2607,7 +2607,7 @@ ADD_DY yvec0, yvec15, yvec15; + MUL_DY yvec2, yvec1, yvec1; + ADD_DY yvec1, yvec14, yvec14; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DY 24*SIZE(ptrba), yvec3; + LD_DY 28*SIZE(ptrba), yvec4; + BROAD_DY 3*SIZE(ptrbb), yvec5; +@@ -2630,7 +2630,7 @@ TEST $2, %rax; + JLE .L312_loopE; + ALIGN_5 + .L312_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DY 0*SIZE(ptrba), yvec0; + LD_DY 4*SIZE(ptrba), yvec1; + BROAD_DY 0*SIZE(ptrbb), yvec2; +@@ -2639,7 +2639,7 @@ ADD_DY yvec0, yvec15, yvec15; + MUL_DY yvec2, yvec1, yvec1; + ADD_DY yvec1, yvec14, yvec14; + +-#### Unroll time 2 #### ++//#### Unroll time 2 #### + LD_DY 8*SIZE(ptrba), yvec3; + LD_DY 12*SIZE(ptrba), yvec4; + BROAD_DY 1*SIZE(ptrbb), yvec5; +@@ -2660,7 +2660,7 @@ TEST $1, %rax; + JLE .L313_loopE; + ALIGN_5 + .L313_bodyB: +-#### Unroll time 1 #### ++//#### Unroll time 1 #### + LD_DY 0*SIZE(ptrba), yvec0; + LD_DY 4*SIZE(ptrba), yvec1; + BROAD_DY 0*SIZE(ptrbb), yvec2; +@@ -2672,17 +2672,17 @@ ADD_DY yvec1, yvec14, yvec14; + ADDQ $1*SIZE, ptrbb; + + .L313_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DY MEMALPHA, yvec7; + MUL_DY yvec7, yvec15, yvec15; + MUL_DY yvec7, yvec14, yvec14; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L313_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1, yvec15, xvec13; + EXTRA_DY $1, yvec14, xvec12; + #ifndef TRMMKERNEL +@@ -2762,7 +2762,7 @@ LEAQ (,%rax, SIZE), %rax; + LEAQ (ptrba, %rax, 4), ptrba; + ADDQ %rax, ptrbb; + #endif +-#### Initial Results Register #### ++//#### Initial Results Register #### + XOR_DY yvec15, yvec15, yvec15; + #ifndef TRMMKERNEL + MOVQ bk, k; +@@ -2847,16 +2847,16 @@ ADDQ $4*SIZE, ptrba; + ADDQ $1*SIZE, ptrbb; + + .L323_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DY MEMALPHA, yvec7; + MUL_DY yvec7, yvec15, yvec15; +-#### Testing Alignment #### ++//#### Testing Alignment #### + MOVQ C0, %rax; + OR ldc, %rax; + TEST $15, %rax; + JNE .L323_loopEx; + ALIGN_5 +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1, yvec15, xvec14; + #ifndef TRMMKERNEL + ADD_DX 0*SIZE(C0), xvec15, xvec15; +@@ -2878,7 +2878,7 @@ ADDQ $4*SIZE, C0; + JMP .L32_loopE; + ALIGN_5 + .L323_loopEx: +-#### Writing Back #### ++//#### Writing Back #### + EXTRA_DY $1, yvec15, xvec14; + #ifndef TRMMKERNEL + LDL_DX 0*SIZE(C0), xvec13, xvec13; +@@ -2917,7 +2917,7 @@ LEAQ (, %rax, SIZE), %rax + LEAQ (ptrba, %rax, 2), ptrba + ADDQ %rax, ptrbb; + #endif +-#### Initial Result #### ++//#### Initial Result #### + XOR_DY yvec15, yvec15, yvec15; + #ifndef TRMMKERNEL + MOVQ bk, k; +@@ -3000,7 +3000,7 @@ ADD_DX xvec2, xvec15, xvec15; + ADDQ $2*SIZE, ptrba; + ADDQ $1*SIZE, ptrbb; + .L333_loopE: +-#### Multiply Alpha #### ++//#### Multiply Alpha #### + BROAD_DX MEMALPHA, xvec7; + MUL_DX xvec7, xvec15, xvec15; + #ifndef TRMMKERNEL +@@ -3119,7 +3119,7 @@ addq $1*SIZE, ptrba; + addq $1*SIZE, ptrbb; + + .L343_loopE: +-#### Writing Back #### ++//#### Writing Back #### + vmovsd MEMALPHA, xvec7; + vmulsd xvec7, xvec15, xvec15; + #ifndef TRMMKERNEL diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index c457e4ff002..fb3a2322a51 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.9.1.p2 +2.9.1.p3 diff --git a/build/pkgs/pari/patches/prot_none_4.patch b/build/pkgs/pari/patches/prot_none_4.patch new file mode 100644 index 00000000000..36e952855a2 --- /dev/null +++ b/build/pkgs/pari/patches/prot_none_4.patch @@ -0,0 +1,53 @@ +commit c3dc1546580eda3bff6243cf563801c8a26ec67f +Author: Jeroen Demeyer +Date: Mon Apr 3 16:11:54 2017 +0200 + + mmap the PARI stack with MAP_NORESERVE + +diff --git a/src/language/init.c b/src/language/init.c +index 34cce31..acebe2f 100644 +--- a/src/language/init.c ++++ b/src/language/init.c +@@ -597,12 +597,26 @@ pari_add_defaults_module(entree *ep) + #ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON + #endif ++#ifndef MAP_NORESERVE ++#define MAP_NORESERVE 0 ++#endif + static void * + pari_mainstack_malloc(size_t size) + { ++ /* Check that the system allows reserving "size" bytes. This is just ++ * a check, we immediately free the memory. */ + void *b = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +- return (b == MAP_FAILED) ? NULL: b; ++ if (b == MAP_FAILED) return NULL; ++ munmap(b, size); ++ ++ /* Map again, this time with MAP_NORESERVE. On some operating systems ++ * like Cygwin, this is needed because remapping with PROT_NONE and ++ * MAP_NORESERVE does not work as expected. */ ++ b = mmap(NULL, size, PROT_READ|PROT_WRITE, ++ MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); ++ if (b == MAP_FAILED) return NULL; ++ return b; + } + + static void +@@ -628,7 +642,13 @@ static void + pari_mainstack_mreset(pari_sp from, pari_sp to) + { + size_t s = to - from; +- mmap((void*)from, s, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); ++ void *addr, *res; ++ if (!s) return; ++ ++ addr = (void*)from; ++ res = mmap(addr, s, PROT_NONE, ++ MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); ++ if (res != addr) pari_err(e_MEM); + } + + /* Commit (make available) the virtual memory mapped between the diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 205ae8cd267..5dafd7ca0b6 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -6,6 +6,11 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi +if [ "$SAGE_PYTHON3" = yes ]; then + echo "Skipping SageNB since it is not Python3 compatible" + exit 0 +fi + cd src # Install a flat package (not an egg), which is the same as how pip diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 9b8a2aecd9b..fbbb323323d 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.0.beta1, Release Date: 2017-04-06 │ +│ SageMath version 8.0.beta2, Release Date: 2017-04-12 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index de67a9739e8..d7ea566a230 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.beta1' -SAGE_RELEASE_DATE='2017-04-06' +SAGE_VERSION='8.0.beta2' +SAGE_RELEASE_DATE='2017-04-12' diff --git a/src/doc/de/tutorial/tour_help.rst b/src/doc/de/tutorial/tour_help.rst index b5149c198a9..5e0884ad5e5 100644 --- a/src/doc/de/tutorial/tour_help.rst +++ b/src/doc/de/tutorial/tour_help.rst @@ -258,11 +258,13 @@ Quadrat- und Kubikzahlen. Die elementarste Datenstruktur in Sage ist die Liste. Sie ist -- wie der Name schon sagt -- nichts anderes als eine Liste beliebiger Objekte. Zum Beispiel erzeugt der ``range`` Befehl, den wir schon -verwendet haben, eine Liste: +verwendet haben, eine Liste (python 2): :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] Hier ist eine etwas kompliziertere Liste: diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 411fcac0f90..71ebb39b6f3 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -78,9 +78,11 @@ Non-associative algebras .. toctree:: :maxdepth: 2 + lie_algebras sage/algebras/jordan_algebra sage/combinat/free_prelie_algebra sage/algebras/shuffle_algebra sage/algebras/free_zinbiel_algebra .. include:: ../footer.txt + diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst new file mode 100644 index 00000000000..79bc38bc8a6 --- /dev/null +++ b/src/doc/en/reference/algebras/lie_algebras.rst @@ -0,0 +1,14 @@ +Lie Algebras +============ + +.. toctree:: + :maxdepth: 2 + + sage/algebras/lie_algebras/abelian + 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/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 fabfe4c465b..1d4ed57a230 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -79,6 +79,7 @@ Individual Categories sage/categories/finite_dimensional_bialgebras_with_basis sage/categories/finite_dimensional_coalgebras_with_basis sage/categories/finite_dimensional_hopf_algebras_with_basis + sage/categories/finite_dimensional_lie_algebras_with_basis sage/categories/finite_dimensional_modules_with_basis sage/categories/finite_dimensional_semisimple_algebras_with_basis sage/categories/finite_enumerated_sets @@ -121,6 +122,8 @@ Individual Categories sage/categories/j_trivial_semigroups sage/categories/lattice_posets sage/categories/left_modules + sage/categories/lie_algebras + sage/categories/lie_algebras_with_basis sage/categories/lie_groups sage/categories/l_trivial_semigroups sage/categories/magmas @@ -218,6 +221,7 @@ Examples of parents using categories sage/categories/examples/finite_coxeter_groups sage/categories/examples/finite_dimensional_algebras_with_basis sage/categories/examples/finite_enumerated_sets + sage/categories/examples/finite_dimensional_lie_algebras_with_basis sage/categories/examples/finite_monoids sage/categories/examples/finite_semigroups sage/categories/examples/finite_weyl_groups @@ -227,6 +231,8 @@ Examples of parents using categories sage/categories/examples/hopf_algebras_with_basis sage/categories/examples/infinite_enumerated_sets sage/categories/examples/manifolds + sage/categories/examples/lie_algebras + sage/categories/examples/lie_algebras_with_basis sage/categories/examples/monoids sage/categories/examples/posets sage/categories/examples/semigroups_cython diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 9529bafd281..672e7374041 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -498,6 +498,10 @@ REFERENCES: Leçons mathématiques de Bordeaux, vol. 4, pages 259-300, Cassini (2011). +.. [deG2000] Willem A. de Graaf. *Lie Algebras: Theory and Algorithms*. + North-Holland Mathematical Library. (2000). + Elsevier Science B.V. + .. [Deo1987a] \V. Deodhar, A splitting criterion for the Bruhat orderings on Coxeter groups. Comm. Algebra, 15:1889-1894, 1987. diff --git a/src/doc/en/thematic_tutorials/functional_programming.rst b/src/doc/en/thematic_tutorials/functional_programming.rst index ef24c37ba92..af6d160c061 100644 --- a/src/doc/en/thematic_tutorials/functional_programming.rst +++ b/src/doc/en/thematic_tutorials/functional_programming.rst @@ -191,7 +191,7 @@ matrices:: sage: rows = [randint(1, 10) for i in range(10)] sage: cols = [randint(1, 10) for i in range(10)] sage: rings = [ZZ]*10 - sage: M = map(random_matrix, rings, rows, cols) + sage: M = list(map(random_matrix, rings, rows, cols)) sage: M[0] # random [ -1 -3 -1 -37 1 -1 -4 5] @@ -213,8 +213,8 @@ together with ``map`` as follows:: [ 2 6 2] sage: rows = [randint(1, 10) for i in range(10)] sage: cols = [randint(1, 10) for i in range(10)] - sage: M = map(rand_mat, rows, cols) - sage: M = map(matrix, M) + sage: M = list(map(rand_mat, rows, cols)) + sage: M = list(map(matrix, M)) sage: M[0] # random [ 9 1 5 2 10 10 1] diff --git a/src/doc/en/thematic_tutorials/numtheory_rsa.rst b/src/doc/en/thematic_tutorials/numtheory_rsa.rst index 1ffee452ef3..789dd73d3f2 100644 --- a/src/doc/en/thematic_tutorials/numtheory_rsa.rst +++ b/src/doc/en/thematic_tutorials/numtheory_rsa.rst @@ -400,7 +400,7 @@ practice. In Sage, we can obtain an integer representation of our message as follows:: sage: m = "HELLOWORLD" - sage: m = map(ord, m); m + sage: m = [ord(x) for x in m]; m [72, 69, 76, 76, 79, 87, 79, 82, 76, 68] sage: m = ZZ(list(reversed(m)), 100) ; m 72697676798779827668 diff --git a/src/doc/en/tutorial/tour_help.rst b/src/doc/en/tutorial/tour_help.rst index 5a9f364dd12..b290d3e949d 100644 --- a/src/doc/en/tutorial/tour_help.rst +++ b/src/doc/en/tutorial/tour_help.rst @@ -255,11 +255,13 @@ and make a table of squares and cubes. The most basic data structure in Sage is the list, which is -- as the name suggests -- just a list of arbitrary objects. For example, -the ``range`` command that we used creates a list: +the ``range`` command that we used creates a list (in python 2): :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] Here is a more complicated list: diff --git a/src/doc/es/tutorial/tour_help.rst b/src/doc/es/tutorial/tour_help.rst index fde1d7656d3..c759d1b0fb6 100644 --- a/src/doc/es/tutorial/tour_help.rst +++ b/src/doc/es/tutorial/tour_help.rst @@ -254,11 +254,13 @@ y hacemos una tabla de cuadrados y cubos. La estructura de datos más básica en Sage es la lista, la cual es -- como sugiere su nombre -- solo una lista de objetos arbitrarios. -Por ejemplo, el comando ``range`` que hemos usado crea una lista: +Por ejemplo, el comando ``range`` que hemos usado crea una lista (python 2): :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] He aquí una lista más complicada: diff --git a/src/doc/fr/tutorial/tour_help.rst b/src/doc/fr/tutorial/tour_help.rst index 68e6078006d..9e853c11659 100644 --- a/src/doc/fr/tutorial/tour_help.rst +++ b/src/doc/fr/tutorial/tour_help.rst @@ -260,11 +260,13 @@ et des cubes en trois colonnes, chacune d'une largeur de six caractères. La structure de données de base de Sage est la liste, qui est — comme son nom l'indique — une liste d'objets arbitraires. Par exemple, la commande ``range`` que nous avons utilisée plus haut crée en fait une -liste : +liste (en python 2): :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] Voici un exemple plus compliqué de liste : diff --git a/src/doc/ja/tutorial/tour_help.rst b/src/doc/ja/tutorial/tour_help.rst index 5e3d095cfec..a319a189e06 100644 --- a/src/doc/ja/tutorial/tour_help.rst +++ b/src/doc/ja/tutorial/tour_help.rst @@ -254,7 +254,9 @@ Sageにおける最も基本的なデータ構造はリストで,名前の示 :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] もう少し複雑なリストの例として: diff --git a/src/doc/pt/tutorial/tour_help.rst b/src/doc/pt/tutorial/tour_help.rst index b19c1ffbb1f..e31190a4e8c 100644 --- a/src/doc/pt/tutorial/tour_help.rst +++ b/src/doc/pt/tutorial/tour_help.rst @@ -260,7 +260,9 @@ exemplo, o comando ``range`` que usamos acima cria uma lista: :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] Abaixo segue uma lista mais complicada: diff --git a/src/doc/ru/tutorial/tour_help.rst b/src/doc/ru/tutorial/tour_help.rst index 06e447f840d..5650c9d70f9 100644 --- a/src/doc/ru/tutorial/tour_help.rst +++ b/src/doc/ru/tutorial/tour_help.rst @@ -246,7 +246,9 @@ tanh, taylor``. Данная функция является хорошим сп :: - sage: range(2,10) + sage: range(2,10) # optional - python2 + [2, 3, 4, 5, 6, 7, 8, 9] + sage: list(range(2,10)) # optional - python3 [2, 3, 4, 5, 6, 7, 8, 9] Далее показан пример более сложного списка: diff --git a/src/module_list.py b/src/module_list.py index 1bf6a2ccd6c..385800fb2d6 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -195,6 +195,9 @@ def uname_specific(name, value, alternative): language='c++', libraries = ["gmp", "m", "ntl"]), + Extension('sage.algebras.lie_algebras.lie_algebra_element', + sources = ["sage/algebras/lie_algebras/lie_algebra_element.pyx"]), + ################################ ## ## sage.arith @@ -559,7 +562,7 @@ def uname_specific(name, value, alternative): sources = ["sage/libs/homfly.pyx"], libraries = ["homfly", "gc"], package="libhomfly"), - + OptionalExtension('sage.libs.sirocco', sources = ["sage/libs/sirocco.pyx"], libraries = ["sirocco", "mpfr", "gmp"], diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 6c388961947..37d008dff3e 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -29,6 +29,7 @@ from .free_algebra_quotient import FreeAlgebraQuotient from .steenrod.all import * +from .lie_algebras.all import * from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 304293e4f51..449268d5c54 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -31,6 +31,7 @@ - :class:`algebras.Moebius ` - :class:`algebras.Jordan ` +- :class:`algebras.Lie ` - :class:`algebras.NilCoxeter ` - :class:`algebras.OrlikSolomon @@ -60,6 +61,7 @@ from sage.algebras.clifford_algebra import CliffordAlgebra as Clifford from sage.algebras.clifford_algebra import ExteriorAlgebra as Exterior from sage.algebras.weyl_algebra import DifferentialWeylAlgebra as DifferentialWeyl +from sage.algebras.lie_algebras.lie_algebra import LieAlgebra as Lie from sage.misc.lazy_import import lazy_import lazy_import('sage.algebras.nil_coxeter_algebra', 'NilCoxeterAlgebra', 'NilCoxeter') diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index 7447ae5225c..7203bae3fc2 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -744,7 +744,7 @@ def algebra_generators(self): x = self.gen(i) ret[str(x)] = x from sage.sets.family import Family - return Family(ret) + return Family(self.variable_names(), lambda i: ret[i]) @cached_method def gens(self): diff --git a/src/sage/algebras/lie_algebras/__init__.py b/src/sage/algebras/lie_algebras/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/algebras/lie_algebras/abelian.py b/src/sage/algebras/lie_algebras/abelian.py new file mode 100644 index 00000000000..5145d853f09 --- /dev/null +++ b/src/sage/algebras/lie_algebras/abelian.py @@ -0,0 +1,191 @@ +""" +Abelian Lie Algebras + +AUTHORS: + +- Travis Scrimshaw (2016-06-07): 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.structure.indexed_generators import (IndexedGenerators, + standardize_names_index_set) +from sage.categories.lie_algebras import LieAlgebras +from sage.algebras.lie_algebras.lie_algebra_element import LieAlgebraElement +from sage.algebras.lie_algebras.lie_algebra import InfinitelyGeneratedLieAlgebra +from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.infinity import infinity +from sage.sets.family import Family + +class AbelianLieAlgebra(LieAlgebraWithStructureCoefficients): + r""" + An abelian Lie algebra. + + A Lie algebra `\mathfrak{g}` is abelian if `[x, y] = 0` for all + `x, y \in \mathfrak{g}`. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.bracket(x, y) + 0 + """ + @staticmethod + def __classcall_private__(cls, R, names=None, index_set=None, **kwds): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: L1 = LieAlgebra(QQ, 'x,y', {}) + sage: L2. = LieAlgebra(QQ, abelian=True) + sage: L1 is L2 + True + """ + names, index_set = standardize_names_index_set(names, index_set) + if index_set.cardinality() == infinity: + return InfiniteDimensionalAbelianLieAlgebra(R, index_set, **kwds) + return super(AbelianLieAlgebra, cls).__classcall__(cls, R, names, index_set, **kwds) + + def __init__(self, R, names, index_set, **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) + sage: TestSuite(L).run() + """ + LieAlgebraWithStructureCoefficients.__init__(self, R, Family({}), names, index_set, **kwds) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: LieAlgebra(QQ, 3, 'x', abelian=True) + Abelian Lie algebra on 3 generators (x0, x1, x2) over Rational Field + """ + gens = self.lie_algebra_generators() + if gens.cardinality() == 1: + return "Abelian Lie algebra on generator {} over {}".format(tuple(gens)[0], self.base_ring()) + return "Abelian Lie algebra on {} generators {} over {}".format( + gens.cardinality(), tuple(gens), self.base_ring()) + + def _construct_UEA(self): + """ + Construct the universal enveloping algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) + sage: L._construct_UEA() + Multivariate Polynomial Ring in x0, x1, x2 over Rational Field + """ + return PolynomialRing(self.base_ring(), self.variable_names()) + + def is_abelian(self): + """ + Return ``True`` since ``self`` is an abelian Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) + sage: L.is_abelian() + True + """ + return True + + # abelian => nilpotent => solvable + is_nilpotent = is_solvable = is_abelian + + class Element(LieAlgebraWithStructureCoefficients.Element): + def _bracket_(self, y): + """ + Return the Lie bracket ``[self, y]``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.bracket(x, y) + 0 + """ + return self.parent().zero() + +class InfiniteDimensionalAbelianLieAlgebra(InfinitelyGeneratedLieAlgebra, IndexedGenerators): + r""" + An infinite dimensional abelian Lie algebra. + + A Lie algebra `\mathfrak{g}` is abelian if `[x, y] = 0` for all + `x, y \in \mathfrak{g}`. + """ + def __init__(self, R, index_set, prefix='L', **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, index_set=ZZ, abelian=True) + sage: TestSuite(L).run() + """ + cat = LieAlgebras(R).WithBasis() + InfinitelyGeneratedLieAlgebra.__init__(self, R, category=cat) + IndexedGenerators.__init__(self, index_set, prefix=prefix, **kwds) + + def dimension(self): + r""" + Return the dimension of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: L = lie_algebras.abelian(QQ, index_set=ZZ) + sage: L.dimension() + +Infinity + """ + return infinity + + def is_abelian(self): + """ + Return ``True`` since ``self`` is an abelian Lie algebra. + + EXAMPLES:: + + sage: L = lie_algebras.abelian(QQ, index_set=ZZ) + sage: L.is_abelian() + True + """ + return True + + # abelian => nilpotent => solvable + is_nilpotent = is_solvable = is_abelian + + # For compatibility with CombinatorialFreeModuleElement + _repr_term = IndexedGenerators._repr_generator + _latex_term = IndexedGenerators._latex_generator + + class Element(LieAlgebraElement): + def _bracket_(self, other): + """ + Return the Lie bracket ``[self, y]``. + + EXAMPLES:: + + sage: L = lie_algebras.abelian(QQ, index_set=ZZ) + sage: B = L.basis() + sage: l1 = B[1] + sage: l5 = B[5] + sage: l1.bracket(l5) + 0 + """ + return self.parent().zero() + diff --git a/src/sage/algebras/lie_algebras/all.py b/src/sage/algebras/lie_algebras/all.py new file mode 100644 index 00000000000..490fcea0a8e --- /dev/null +++ b/src/sage/algebras/lie_algebras/all.py @@ -0,0 +1,23 @@ +""" +Lie Algebras +""" + +#***************************************************************************** +# Copyright (C) 2013 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 lie_algebra import LieAlgebra +#from kac_moody import KacMoodyAlgebra +import examples as lie_algebras + diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py new file mode 100644 index 00000000000..a5891dd70cc --- /dev/null +++ b/src/sage/algebras/lie_algebras/examples.py @@ -0,0 +1,428 @@ +""" +Examples of Lie Algebras + +There are the following examples of Lie algebras: + +- A rather comprehensive family of 3-dimensional Lie + algebras +- The Lie algebra of affine transformations of the line +- All abelian Lie algebras on free modules +- The Lie algebra of upper triangular matrices +- The Lie algebra of strictly upper triangular matrices + +See also +:class:`sage.algebras.lie_algebras.virasoro.LieAlgebraRegularVectorFields` +and +:class:`sage.algebras.lie_algebras.virasoro.VirasoroAlgebra` for +other examples. + +AUTHORS: + +- Travis Scrimshaw (07-15-2013): Initial implementation +""" +#***************************************************************************** +# 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.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 + +def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): + r""" + The 3-dimensional Lie algebra over a given commutative ring `R` + with basis `\{X, Y, Z\}` subject to the relations: + + .. MATH:: + + [X, Y] = aZ + dY, \quad [Y, Z] = bX, \quad [Z, X] = cY + dZ + + where `a,b,c,d \in R`. + + This is always a well-defined 3-dimensional Lie algebra, as can + be easily proven by computation. + + EXAMPLES:: + + sage: L = lie_algebras.three_dimensional(QQ, 4, 1, -1, 2) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): 2*Y + 4*Z, ('X', 'Z'): Y - 2*Z, ('Y', 'Z'): X} + sage: TestSuite(L).run() + sage: L = lie_algebras.three_dimensional(QQ, 1, 0, 0, 0) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): Z} + sage: L = lie_algebras.three_dimensional(QQ, 0, 0, -1, -1) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): -Y, ('X', 'Z'): Y + Z} + sage: L = lie_algebras.three_dimensional(QQ, 0, 1, 0, 0) + sage: L.structure_coefficients() + Finite family {('Y', 'Z'): X} + sage: lie_algebras.three_dimensional(QQ, 0, 0, 0, 0) + Abelian Lie algebra on 3 generators (X, Y, Z) over Rational Field + sage: Q. = PolynomialRing(QQ) + sage: L = lie_algebras.three_dimensional(Q, a, b, c, d) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): d*Y + a*Z, ('X', 'Z'): (-c)*Y + (-d)*Z, ('Y', 'Z'): b*X} + sage: TestSuite(L).run() + """ + if isinstance(names, str): + names = names.split(',') + X = names[0] + Y = names[1] + Z = names[2] + from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + s_coeff = {(X,Y): {Z:a, Y:d}, (Y,Z): {X:b}, (Z,X): {Y:c, Z:d}} + return LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + +def cross_product(R, names=['X', 'Y', 'Z']): + r""" + The Lie algebra of `\RR^3` defined by the usual cross product + `\times`. + + EXAMPLES:: + + sage: L = lie_algebras.cross_product(QQ) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): Z, ('X', 'Z'): -Y, ('Y', 'Z'): X} + sage: TestSuite(L).run() + """ + L = three_dimensional(R, 1, 1, 1, 0, names=names) + L.rename("Lie algebra of RR^3 under cross product over {}".format(R)) + return L + +def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): + """ + Return a 3-dimensional Lie algebra of rank ``n``, where `0 \leq n \leq 3`. + + Here, the *rank* of a Lie algebra `L` is defined as the dimension + of its derived subalgebra `[L, L]`. (We are assuming that `R` is + a field of characteristic `0`; otherwise the Lie algebras + constructed by this function are still well-defined but no longer + might have the correct ranks.) This is not to be confused with + the other standard definition of a rank (namely, as the + dimension of a Cartan subalgebra, when `L` is semisimple). + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the rank + - ``a`` -- the deformation parameter (used for `n = 2`); this should + be a nonzero element of `R` in order for the resulting Lie + algebra to actually have the right rank(?) + - ``names`` -- (optional) the generator names + + EXAMPLES:: + + sage: lie_algebras.three_dimensional_by_rank(QQ, 0) + Abelian Lie algebra on 3 generators (X, Y, Z) over Rational Field + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 1) + sage: L.structure_coefficients() + Finite family {('Y', 'Z'): X} + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 2, 4) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): Y, ('X', 'Z'): Y + Z} + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 2, 0) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): Y} + sage: lie_algebras.three_dimensional_by_rank(QQ, 3) + sl2 over Rational Field + """ + if isinstance(names, str): + names = names.split(',') + names = tuple(names) + + if n == 0: + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names=names) + + if n == 1: + L = three_dimensional(R, 0, 1, 0, 0, names=names) # Strictly upper triangular matrices + L.rename("Lie algebra of 3x3 strictly upper triangular matrices over {}".format(R)) + return L + + if n == 2: + if a is None: + raise ValueError("The parameter 'a' must be specified") + X = names[0] + Y = names[1] + Z = names[2] + from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + if a == 0: + s_coeff = {(X,Y): {Y:R.one()}, (X,Z): {Y:R(a)}} + # Why use R(a) here if R == 0 ? Also this has rank 1. + L = LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + L.rename("Degenerate Lie algebra of dimension 3 and rank 2 over {}".format(R)) + else: + s_coeff = {(X,Y): {Y:R.one()}, (X,Z): {Y:R.one(), Z:R.one()}} + # a doesn't appear here :/ + L = LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + L.rename("Lie algebra of dimension 3 and rank 2 with parameter {} over {}".format(a, R)) + return L + + if n == 3: + #return sl(R, 2) + from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + E = names[0] + F = names[1] + H = names[2] + s_coeff = { (E,F): {H:R.one()}, (H,E): {E:R(2)}, (H,F): {F:R(-2)} } + L = LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + L.rename("sl2 over {}".format(R)) + return L + + raise ValueError("Invalid rank") + +# This can probably be replaced (removed) by sl once the classical Lie +# algebras are implemented +def sl(R, n, representation='bracket'): + r""" + Return the Lie algebra `\mathfrak{sl}_n`. + + EXAMPLES:: + + sage: sl2 = lie_algebras.sl(QQ, 2); sl2 + sl2 over Rational Field + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + + TESTS:: + + sage: sl2 = lie_algebras.sl(QQ, 2, representation='matrix') + sage: E,F,H = sl2.gens() + sage: E.bracket(F) == H + True + sage: H.bracket(E) == 2*E + True + sage: H.bracket(F) == -2*F + True + """ + if n != 2: + raise NotImplementedError("only n=2 is implemented") + + if representation == 'matrix': + from sage.matrix.matrix_space import MatrixSpace + from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative + MS = MatrixSpace(R, 2) + E = MS([[0,1],[0,0]]) + F = MS([[0,0],[1,0]]) + H = MS([[1,0],[0,-1]]) + L = LieAlgebraFromAssociative(MS, [E, F, H], ['E', 'F', 'H']) + L.rename("sl2 as a matrix Lie algebra over {}".format(R)) + elif representation == 'bracket': + L = three_dimensional_by_rank(R, 3, names=['E', 'F', 'H']) + else: + raise ValueError("invalid representation") + + return L + +def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): + """ + The Lie algebra of affine transformations of the line. + + EXAMPLES:: + + sage: L = lie_algebras.affine_transformations_line(QQ) + sage: L.structure_coefficients() + Finite family {('X', 'Y'): Y} + sage: X, Y = L.lie_algebra_generators() + sage: L[X, Y] == Y + True + sage: TestSuite(L).run() + sage: L = lie_algebras.affine_transformations_line(QQ, representation="matrix") + sage: X, Y = L.lie_algebra_generators() + sage: L[X, Y] == Y + True + sage: TestSuite(L).run() + """ + if isinstance(names, str): + names = names.split(',') + names = tuple(names) + if representation == 'matrix': + from sage.matrix.matrix_space import MatrixSpace + MS = MatrixSpace(R, 2, sparse=True) + one = R.one() + gens = tuple(MS({(0,i):one}) for i in range(2)) + from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative + return LieAlgebraFromAssociative(MS, gens, names=names) + X = names[0] + Y = names[1] + from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + s_coeff = {(X,Y): {Y:R.one()}} + L = LieAlgebraWithStructureCoefficients(R, s_coeff, names=names) + L.rename("Lie algebra of affine transformations of a line over {}".format(R)) + return L + +def abelian(R, names=None, index_set=None): + """ + Return the abelian Lie algebra generated by ``names``. + + EXAMPLES:: + + sage: lie_algebras.abelian(QQ, 'x, y, z') + Abelian Lie algebra on 3 generators (x, y, z) over Rational Field + """ + if isinstance(names, str): + names = names.split(',') + elif isinstance(names, (list, tuple)): + names = tuple(names) + elif names is not None: + if index_set is not None: + raise ValueError("invalid generator names") + index_set = names + names = None + from sage.rings.infinity import infinity + if (index_set is not None + and not isinstance(index_set, (list, tuple)) + and index_set.cardinality() == infinity): + from sage.algebras.lie_algebras.abelian import InfiniteDimensionalAbelianLieAlgebra + return InfiniteDimensionalAbelianLieAlgebra(R, index_set=index_set) + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names=names, index_set=index_set) + +def Heisenberg(R, n, representation="structure"): + """ + Return the rank ``n`` Heisenberg algebra in the given representation. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the rank (a nonnegative integer or infinity) + - ``representation`` -- (default: "structure") can be one of the following: + + - ``"structure"`` -- using structure coefficients + - ``"matrix"`` -- using matrices + + EXAMPLES:: + + sage: lie_algebras.Heisenberg(QQ, 3) + Heisenberg algebra of rank 3 over Rational Field + """ + from sage.rings.infinity import infinity + if n == infinity: + from sage.algebras.lie_algebras.heisenberg import InfiniteHeisenbergAlgebra + return InfiniteHeisenbergAlgebra(R) + if representation == "matrix": + from sage.algebras.lie_algebras.heisenberg import HeisenbergAlgebra_matrix + return HeisenbergAlgebra_matrix(R, n) + from sage.algebras.lie_algebras.heisenberg import HeisenbergAlgebra + return HeisenbergAlgebra(R, n) + +def regular_vector_fields(R): + r""" + Return the Lie algebra of regular vector fields on `\CC^{\times}`. + + .. SEEALSO:: + + :class:`~sage.algebras.lie_algebras.virasoro.LieAlgebraRegularVectorFields` + + EXAMPLES:: + + sage: lie_algebras.regular_vector_fields(QQ) + The Lie algebra of regular vector fields over Rational Field + """ + from sage.algebras.lie_algebras.virasoro import LieAlgebraRegularVectorFields + return LieAlgebraRegularVectorFields(R) + +def pwitt(R, p): + r""" + Return the `p`-Witt Lie algebra over `R`. + + EXAMPLES:: + + sage: lie_algebras.pwitt(GF(5), 5) + The 5-Witt Lie algebra over Finite Field of size 5 + """ + from sage.algebras.lie_algebras.virasoro import WittLieAlgebra_charp + return WittLieAlgebra_charp(R, p) + +def upper_triangular_matrices(R, n): + r""" + Return the Lie algebra `\mathfrak{b}_k` of `k \times k` upper + triangular matrices. + + .. TODO:: + + This implementation does not know it is finite-dimensional and + does not know its basis. + + EXAMPLES:: + + sage: L = lie_algebras.upper_triangular_matrices(QQ, 4); L + Lie algebra of 4-dimensional upper triangular matrices over Rational Field + sage: TestSuite(L).run() + sage: n0, n1, n2, t0, t1, t2, t3 = L.lie_algebra_generators() + sage: L[n2, t2] == -n2 + True + + TESTS:: + + sage: L = lie_algebras.upper_triangular_matrices(QQ, 1); L + Lie algebra of 1-dimensional upper triangular matrices over Rational Field + sage: TestSuite(L).run() + sage: L = lie_algebras.upper_triangular_matrices(QQ, 0); L + Lie algebra of 0-dimensional upper triangular matrices over Rational Field + sage: TestSuite(L).run() + """ + from sage.matrix.matrix_space import MatrixSpace + from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + names = tuple('n{}'.format(i) for i in range(n-1)) + names += tuple('t{}'.format(i) for i in range(n)) + gens = [MS({(i,i+1):one}) for i in range(n-1)] + gens += [MS({(i,i):one}) for i in range(n)] + L = LieAlgebraFromAssociative(MS, gens, names=names) + L.rename("Lie algebra of {}-dimensional upper triangular matrices over {}".format(n, L.base_ring())) + return L + +def strictly_upper_triangular_matrices(R, n): + r""" + Return the Lie algebra `\mathfrak{n}_k` of strictly `k \times k` upper + triangular matrices. + + .. TODO:: + + This implementation does not know it is finite-dimensional and + does not know its basis. + + EXAMPLES:: + + sage: L = lie_algebras.strictly_upper_triangular_matrices(QQ, 4); L + Lie algebra of 4-dimensional strictly upper triangular matrices over Rational Field + sage: TestSuite(L).run() + sage: n0, n1, n2 = L.lie_algebra_generators() + sage: L[n2, n1] + [ 0 0 0 0] + [ 0 0 0 -1] + [ 0 0 0 0] + [ 0 0 0 0] + + TESTS:: + + sage: L = lie_algebras.strictly_upper_triangular_matrices(QQ, 1); L + Lie algebra of 1-dimensional strictly upper triangular matrices over Rational Field + sage: TestSuite(L).run() + sage: L = lie_algebras.strictly_upper_triangular_matrices(QQ, 0); L + Lie algebra of 0-dimensional strictly upper triangular matrices over Rational Field + sage: TestSuite(L).run() + """ + from sage.matrix.matrix_space import MatrixSpace + from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative + MS = MatrixSpace(R, n, sparse=True) + one = R.one() + names = tuple('n{}'.format(i) for i in range(n-1)) + gens = tuple(MS({(i,i+1):one}) for i in range(n-1)) + L = LieAlgebraFromAssociative(MS, gens, names=names) + L.rename("Lie algebra of {}-dimensional strictly upper triangular matrices over {}".format(n, L.base_ring())) + return L + diff --git a/src/sage/algebras/lie_algebras/heisenberg.py b/src/sage/algebras/lie_algebras/heisenberg.py new file mode 100644 index 00000000000..2fe0b74080e --- /dev/null +++ b/src/sage/algebras/lie_algebras/heisenberg.py @@ -0,0 +1,749 @@ +""" +Heisenberg Algebras + +AUTHORS: + +- Travis Scrimshaw (2013-08-13): 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.structure.indexed_generators import IndexedGenerators + +from sage.algebras.lie_algebras.lie_algebra import (LieAlgebraFromAssociative, + LieAlgebraWithGenerators) +from sage.algebras.lie_algebras.lie_algebra_element import (LieAlgebraElement, + LieAlgebraMatrixWrapper) +from sage.categories.lie_algebras import LieAlgebras +from sage.categories.cartesian_product import cartesian_product +from sage.matrix.matrix_space import MatrixSpace +from sage.rings.integer import Integer +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family +from sage.sets.positive_integers import PositiveIntegers +from sage.sets.set import Set + +class HeisenbergAlgebra_abstract(IndexedGenerators): + """ + The common methods for the (non-matrix) Heisenberg algebras. + """ + def __init__(self, I): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) # indirect doctest + """ + IndexedGenerators.__init__(self, I, prefix='', bracket=False, + latex_bracket=False, string_quotes=False) + + def p(self, i): + """ + The generator `p_i` of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.p(2) + p2 + """ + return self.element_class(self, {'p%i'%i: self.base_ring().one()}) + + def q(self, i): + """ + The generator `q_i` of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.q(2) + q2 + """ + return self.element_class(self, {'q%i'%i: self.base_ring().one()}) + + def z(self): + """ + Return the basis element `z` of the Heisenberg algebra. + + The element `z` spans the center of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.z() + z + """ + return self.element_class(self, {'z': self.base_ring().one()}) + + def bracket_on_basis(self, x, y): + """ + Return the bracket of basis elements indexed by ``x`` and ``y`` + where ``x < y``. + + The basis of a Heisenberg algebra is ordered in such a way that + the `p_i` come first, the `q_i` come next, and the `z` comes last. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 3) + sage: p1 = ('p', 1) + sage: q1 = ('q', 1) + sage: H.bracket_on_basis(p1, q1) + z + """ + if y == 'z': # No need to test for x == 'z' since x < y is assumed. + return self.zero() + if x[0] == 'p' and y[0] == 'q' and x[1] == y[1]: + return self.z() + return self.zero() + + def _repr_term(self, m): + r""" + Return a string representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 3) + sage: H._repr_term('p1') + 'p1' + sage: H._repr_term('z') + 'z' + """ + return m + + def _latex_term(self, m): + r""" + Return a string representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 10) + sage: H._latex_term('p1') + 'p_{1}' + sage: H._latex_term('z') + 'z' + sage: latex(H.p(10)) + p_{10} + """ + if len(m) == 1: + return m + return "%s_{%s}"%(m[0], m[1:]) # else it is of length at least 2 + + Element = LieAlgebraElement + +class HeisenbergAlgebra_fd(object): + """ + Common methods for finite-dimensional Heisenberg algebras. + """ + def __init__(self, n): + """ + Initialize ``self``. + + INPUT: + + - ``n`` -- the rank + + TESTS:: + + sage: H = lie_algebras.Heisenberg(QQ, 3) # indirect doctest + """ + self._n = n + + def n(self): + """ + Return the rank of the Heisenberg algebra ``self``. + + This is the ``n`` such that ``self`` is the `n`-th Heisenberg + algebra. The dimension of this Heisenberg algebra is then + `2n + 1`. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 3) + sage: H.n() + 3 + sage: H = lie_algebras.Heisenberg(QQ, 3, representation="matrix") + sage: H.n() + 3 + """ + return self._n + + @cached_method + def gens(self): + """ + Return the Lie algebra generators of ``self``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 2) + sage: H.gens() + (p1, p2, q1, q2) + sage: H = lie_algebras.Heisenberg(QQ, 0) + sage: H.gens() + (z,) + """ + return tuple(self.lie_algebra_generators()) + + def gen(self, i): + """ + Return the ``i``-th generator of ``self``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 2) + sage: H.gen(0) + p1 + sage: H.gen(3) + q2 + """ + return self.gens()[i] + + @cached_method + def lie_algebra_generators(self): + """ + Return the Lie algebra generators of ``self``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 1) + sage: H.lie_algebra_generators() + Finite family {'q1': q1, 'p1': p1} + sage: H = lie_algebras.Heisenberg(QQ, 0) + sage: H.lie_algebra_generators() + Finite family {'z': z} + """ + if self._n == 0: + return Family(['z'], lambda i: self.z()) + k = ['p%s'%i for i in range(1, self._n+1)] + k += ['q%s'%i for i in range(1, self._n+1)] + d = {} + for i in range(1, self._n+1): + d['p%s'%i] = self.p(i) + d['q%s'%i] = self.q(i) + return Family(k, lambda i: d[i]) + + @cached_method + def basis(self): + """ + Return the basis of ``self``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, 1) + sage: H.basis() + Finite family {'q1': q1, 'p1': p1, 'z': z} + """ + d = {} + for i in range(1, self._n+1): + d['p%s'%i] = self.p(i) + d['q%s'%i] = self.q(i) + d['z'] = self.z() + return Family(self._indices, lambda i: d[i]) + + def _coerce_map_from_(self, H): + """ + Return the coercion map from ``H`` to ``self`` if one exists, + otherwise return ``None``. + + EXAMPLES:: + + sage: HB = lie_algebras.Heisenberg(QQ, 3) + sage: HM = lie_algebras.Heisenberg(QQ, 3, representation="matrix") + sage: HB.has_coerce_map_from(HM) + True + sage: HM.has_coerce_map_from(HB) + True + sage: HB(HM.p(2)) + p2 + sage: HM(-HB.q(3)) == -HM.q(3) + True + sage: HB(HM.z()) + z + sage: HM(HB.z()) == HM.z() + True + sage: HQ = lie_algebras.Heisenberg(QQ, 2) + sage: HB.has_coerce_map_from(HQ) + True + sage: HB(HQ.p(2)) + p2 + sage: HZ = lie_algebras.Heisenberg(ZZ, 2) + sage: HB.has_coerce_map_from(HZ) + True + sage: HB(HZ.p(2)) + p2 + sage: HZ = lie_algebras.Heisenberg(ZZ, 2, representation="matrix") + sage: HB.has_coerce_map_from(HZ) + True + sage: HB(HZ.p(2)) + p2 + """ + if isinstance(H, HeisenbergAlgebra_fd): + if H._n <= self._n and self.base_ring().has_coerce_map_from(H.base_ring()): + return H.module_morphism(lambda i: self.basis()[i], codomain=self) + return None # Otherwise no coercion + return super(HeisenbergAlgebra_fd, self)._coerce_map_from_(H) + +class HeisenbergAlgebra(HeisenbergAlgebra_fd, HeisenbergAlgebra_abstract, + LieAlgebraWithGenerators): + """ + A Heisenberg algebra defined using structure coefficients. + + The `n`-th Heisenberg algebra (where `n` is a nonnegative + integer or infinity) is the Lie algebra with basis + `\{p_i\}_{1 \leq i \leq n} \cup \{q_i\}_{1 \leq i \leq n} \cup \{z\}` + with the following relations: + + .. MATH:: + + [p_i, q_j] = \delta_{ij} z, \quad [p_i, z] = [q_i, z] = [p_i, p_j] + = [q_i, q_j] = 0. + + This Lie algebra is also known as the Heisenberg algebra of rank `n`. + + .. NOTE:: + + The relations `[p_i, q_j] = \delta_{ij} z`, `[p_i, z] = 0`, and + `[q_i, z] = 0` are known as canonical commutation relations. See + :wikipedia:`Canonical_commutation_relations`. + + .. WARNING:: + + The `n` in the above definition is called the "rank" of the + Heisenberg algebra; it is not, however, a rank in any of the usual + meanings that this word has in the theory of Lie algebras. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the rank of the Heisenberg algebra + + REFERENCES: + + - :wikipedia:`Heisenberg_algebra` + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 2) + """ + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 2) + sage: TestSuite(L).run() + sage: L = lie_algebras.Heisenberg(QQ, 0) # not tested -- :trac:`18224` + sage: TestSuite(L).run() + """ + HeisenbergAlgebra_fd.__init__(self, n) + names = tuple(['p%s'%i for i in range(1,n+1)] + + ['q%s'%i for i in range(1,n+1)] + + ['z']) + LieAlgebraWithGenerators.__init__(self, R, names=names, index_set=names, + category=LieAlgebras(R).FiniteDimensional().WithBasis()) + HeisenbergAlgebra_abstract.__init__(self, names) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.Heisenberg(QQ, 3) + Heisenberg algebra of rank 3 over Rational Field + """ + return "Heisenberg algebra of rank {0} over {1}".format(self._n, self.base_ring()) + +class InfiniteHeisenbergAlgebra(HeisenbergAlgebra_abstract, LieAlgebraWithGenerators): + r""" + The infinite Heisenberg algebra. + + This is the Heisenberg algebra on an infinite number of generators. In + other words, this is the Heisenberg algebra of rank `\infty`. See + :class:`HeisenbergAlgebra` for more information. + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: TestSuite(L).run() + sage: L.p(1).bracket(L.q(1)) == L.z() + True + sage: L.q(1).bracket(L.p(1)) == -L.z() + True + """ + S = cartesian_product([PositiveIntegers(), ['p','q']]) + cat = LieAlgebras(R).WithBasis() + LieAlgebraWithGenerators.__init__(self, R, index_set=S, category=cat) + HeisenbergAlgebra_abstract.__init__(self, S) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.Heisenberg(QQ, oo) + Infinite Heisenberg algebra over Rational Field + """ + return "Infinite Heisenberg algebra over {}".format(self.base_ring()) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L._an_element_() + p2 + q2 - 1/2*q3 + z + """ + c = self.base_ring().an_element() + return self.p(2) + self.q(2) - c * self.q(3) + self.z() + + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.lie_algebra_generators() + Lazy family (generator map(i))_{i in The Cartesian product of + (Positive integers, {'p', 'q'})} + + """ + return Family(self._indices, lambda x: self.monomial(x[1] + str(x[0])), + name='generator map') + + def basis(self): + """ + Return the basis of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.basis() + Lazy family (basis map(i))_{i in Disjoint union of Family ({'z'}, + The Cartesian product of (Positive integers, {'p', 'q'}))} + sage: L.basis()['z'] + z + sage: L.basis()[(12, 'p')] + p12 + """ + S = cartesian_product([PositiveIntegers(), ['p','q']]) + I = DisjointUnionEnumeratedSets([Set(['z']), S]) + def basis_elt(x): + if isinstance(x, str): + return self.monomial(x) + return self.monomial(x[1] + str(x[0])) + return Family(I, basis_elt, name="basis map") + + def _from_fd_on_basis(self, i): + """ + Return the monomial in ``self`` corresponding to the + basis element indexed by ``i``, where ``i`` is a basis index for + a *finite-dimensional* Heisenberg algebra. + + This is used for coercion. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, oo) + sage: H._from_fd_on_basis('p2') + p2 + sage: H._from_fd_on_basis('q3') + q3 + sage: H._from_fd_on_basis('z') + z + """ + if i == 'z': + return self.z() + if i[0] == 'p': + return self.p(Integer(i[1:])) + return self.q(Integer(i[1:])) + + def _coerce_map_from_(self, H): + """ + Return the coercion map from ``H`` to ``self`` if one exists, + otherwise return ``None``. + + EXAMPLES:: + + sage: H = lie_algebras.Heisenberg(QQ, oo) + sage: HZ = lie_algebras.Heisenberg(ZZ, oo) + sage: phi = H.coerce_map_from(HZ) + sage: phi(HZ.p(3)) == H.p(3) + True + sage: phi(HZ.p(3)).leading_coefficient().parent() + Rational Field + sage: HF = lie_algebras.Heisenberg(QQ, 3, representation="matrix") + sage: H.has_coerce_map_from(HF) + True + sage: H(HF.p(2)) + p2 + sage: H(HF.z()) + z + sage: HF = lie_algebras.Heisenberg(QQ, 3) + sage: H.has_coerce_map_from(HF) + True + sage: H(HF.p(2)) + p2 + sage: H(HF.z()) + z + """ + if isinstance(H, HeisenbergAlgebra_fd): + if self.base_ring().has_coerce_map_from(H.base_ring()): + return H.module_morphism(self._from_fd_on_basis, codomain=self) + return None # Otherwise no coercion + if isinstance(H, InfiniteHeisenbergAlgebra): + if self.base_ring().has_coerce_map_from(H.base_ring()): + return lambda C,x: self._from_dict(x._monomial_coefficients, coerce=True) + return None # Otherwise no coercion + return super(InfiniteHeisenbergAlgebra, self)._coerce_map_from_(H) + +####################################################### +## Finite rank Heisenberg algebra using matrices + +class HeisenbergAlgebra_matrix(HeisenbergAlgebra_fd, LieAlgebraFromAssociative): + r""" + A Heisenberg algebra represented using matrices. + + The `n`-th Heisenberg algebra over `R` is a Lie algebra which is + defined as the Lie algebra of the `(n+2) \times (n+2)`-matrices: + + .. MATH:: + + \begin{bmatrix} + 0 & p^T & k \\ + 0 & 0_n & q \\ + 0 & 0 & 0 + \end{bmatrix} + + where `p, q \in R^n` and `0_n` in the `n \times n` zero matrix. It has + a basis consisting of + + .. MATH:: + + \begin{aligned} + p_i & = \begin{bmatrix} + 0 & e_i^T & 0 \\ + 0 & 0_n & 0 \\ + 0 & 0 & 0 + \end{bmatrix} \qquad \text{for } 1 \leq i \leq n , + \\ q_i & = \begin{bmatrix} + 0 & 0 & 0 \\ + 0 & 0_n & e_i \\ + 0 & 0 & 0 + \end{bmatrix} \qquad \text{for } 1 \leq i \leq n , + \\ z & = \begin{bmatrix} + 0 & 0 & 1 \\ + 0 & 0_n & 0 \\ + 0 & 0 & 0 + \end{bmatrix}, + \end{aligned} + + where `\{e_i\}` is the standard basis of `R^n`. In other words, it has + the basis `(p_1, p_2, \ldots, p_n, q_1, q_2, \ldots, q_n, z)`, where + `p_i = E_{1, i+1}`, `q_i = E_{i+1, n+2}` and `z = E_{1, n+2}` are + elementary matrices. + + This Lie algebra is isomorphic to the `n`-th Heisenberg algebra + constructed in :class:`HeisenbergAlgebra`; the bases correspond to + each other. + + INPUT: + + - ``R`` -- the base ring + - ``n`` -- the nonnegative integer `n` + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 1, representation="matrix") + sage: p = L.p(1) + sage: q = L.q(1) + sage: z = L.bracket(p, q); z + [0 0 1] + [0 0 0] + [0 0 0] + sage: z == L.z() + True + sage: L.dimension() + 3 + + sage: L = lie_algebras.Heisenberg(QQ, 2, representation="matrix") + sage: sorted(dict(L.basis()).items()) + [( + [0 1 0 0] + [0 0 0 0] + [0 0 0 0] + 'p1', [0 0 0 0] + ), + ( + [0 0 1 0] + [0 0 0 0] + [0 0 0 0] + 'p2', [0 0 0 0] + ), + ( + [0 0 0 0] + [0 0 0 1] + [0 0 0 0] + 'q1', [0 0 0 0] + ), + ( + [0 0 0 0] + [0 0 0 0] + [0 0 0 1] + 'q2', [0 0 0 0] + ), + ( + [0 0 0 1] + [0 0 0 0] + [0 0 0 0] + 'z', [0 0 0 0] + )] + + sage: L = lie_algebras.Heisenberg(QQ, 0, representation="matrix") + sage: sorted(dict(L.basis()).items()) + [( + [0 1] + 'z', [0 0] + )] + sage: L.gens() + ( + [0 1] + [0 0] + ) + sage: L.lie_algebra_generators() + Finite family {'z': [0 1] + [0 0]} + """ + def __init__(self, R, n): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 2, representation="matrix") + sage: TestSuite(L).run() + """ + HeisenbergAlgebra_fd.__init__(self, n) + MS = MatrixSpace(R, n+2, sparse=True) + one = R.one() + p = tuple(MS({(0,i): one}) for i in range(1, n+1)) + q = tuple(MS({(i,n+1): one}) for i in range(1, n+1)) + z = (MS({(0,n+1): one}),) + names = tuple('p%s'%i for i in range(1,n+1)) + names = names + tuple('q%s'%i for i in range(1,n+1)) + ('z',) + cat = LieAlgebras(R).FiniteDimensional().WithBasis() + LieAlgebraFromAssociative.__init__(self, MS, p + q + z, names=names, + index_set=names, category=cat) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.Heisenberg(QQ, 3, representation="matrix") + Heisenberg algebra of rank 3 over Rational Field + """ + return "Heisenberg algebra of rank {} over {}".format(self._n, self.base_ring()) + + def p(self, i): + r""" + Return the generator `p_i` of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 1, representation="matrix") + sage: L.p(1) + [0 1 0] + [0 0 0] + [0 0 0] + """ + return self._gens['p%s'%i] + + def q(self, i): + r""" + Return the generator `q_i` of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 1, representation="matrix") + sage: L.q(1) + [0 0 0] + [0 0 1] + [0 0 0] + """ + return self._gens['q%s'%i] + + def z(self): + """ + Return the basis element `z` of the Heisenberg algebra. + + The element `z` spans the center of the Heisenberg algebra. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 1, representation="matrix") + sage: L.z() + [0 0 1] + [0 0 0] + [0 0 0] + """ + return self._gens['z'] + + class Element(LieAlgebraMatrixWrapper, LieAlgebraFromAssociative.Element): + def monomial_coefficients(self, copy=True): + """ + Return a dictionary whose keys are indices of basis elements in + the support of ``self`` and whose values are the corresponding + coefficients. + + INPUT: + + - ``copy`` -- ignored + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 3, representation="matrix") + sage: elt = L(Matrix(QQ, [[0, 1, 3, 0, 3], [0, 0, 0, 0, 0], [0, 0, 0, 0, -3], + ....: [0, 0, 0, 0, 7], [0, 0, 0, 0, 0]])) + sage: elt + [ 0 1 3 0 3] + [ 0 0 0 0 0] + [ 0 0 0 0 -3] + [ 0 0 0 0 7] + [ 0 0 0 0 0] + sage: sorted(elt.monomial_coefficients().items()) + [('p1', 1), ('p2', 3), ('q2', -3), ('q3', 7), ('z', 3)] + """ + d = {} + n = self.parent()._n + for i, mon in enumerate(self.parent().basis().keys()): + if i < n: + entry = self[0, i+1] + elif i < 2 * n: + entry = self[i-n+1, n+1] + else: + entry = self[0, n+1] + if entry: + d[mon] = entry + return d + diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py new file mode 100644 index 00000000000..488dffc1eca --- /dev/null +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -0,0 +1,1282 @@ +""" +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 six.moves import range +from six import iteritems + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.indexed_generators import standardize_names_index_set +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation + +from sage.categories.algebras import Algebras +from sage.categories.lie_algebras import LieAlgebras, LiftMorphism +from sage.categories.rings import Rings +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 +from sage.rings.ring import Ring +from sage.matrix.matrix_space import MatrixSpace +from sage.sets.family import Family, AbstractFamily + +class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators): + r""" + A Lie algebra `L` over a base ring `R`. + + A Lie algebra is an `R`-module `L` with a bilinear operation called + Lie bracket `[\cdot, \cdot] : L \times L \to L` such that + `[x, x] = 0` and the following relation holds: + + .. MATH:: + + \bigl[ x, [y, z] \bigr] + \bigl[ y, [z, x] \bigr] + + \bigl[ z, [x, y] \bigr] = 0. + + This relation is known as the *Jacobi identity* (or sometimes the Jacobi + relation). We note that from `[x, x] = 0`, we have `[x + y, x + y] = 0`. + Next from bilinearity, we see that + + .. MATH:: + + 0 = [x + y, x + y] = [x, x] + [x, y] + [y, x] + [y, y] + = [x, y] + [y, x], + + thus `[x, y] = -[y, x]` and the Lie bracket is antisymmetric. + + Lie algebras are closely related to Lie groups. Let `G` be a Lie group + and fix some `g \in G`. We can construct the Lie algebra `L` of `G` by + considering the tangent space at `g`. We can also (partially) recover `G` + from `L` by using what is known as the exponential map. + + Given any associative algebra `A`, we can construct a Lie algebra `L` + on the `R`-module `A` by defining the Lie bracket to be the commutator + `[a, b] = ab - ba`. We call an associative algebra `A` which contains + `L` in this fashion an *enveloping algebra* of `L`. The embedding + `L \to A` which sends the Lie bracket to the commutator will be called + a Lie embedding. Now if we are given a Lie algebra `L`, we + can construct an enveloping algebra `U_L` with Lie embedding `h : L \to + U_L` which has the following universal property: for any enveloping + algebra `A` with Lie embedding `f : L \to A`, there exists a unique unital + algebra homomorphism `g : U_L \to A` such that `f = g \circ h`. The + algebra `U_L` is known as the *universal enveloping algebra* of `L`. + + INPUT: + + See examples below for various input options. + + EXAMPLES: + + **1.** The simplest examples of Lie algebras are *abelian Lie + algebras*. These are Lie algebras whose Lie bracket is (identically) + zero. We can create them using the ``abelian`` keyword:: + + sage: L. = LieAlgebra(QQ, abelian=True); L + Abelian Lie algebra on 3 generators (x, y, z) over Rational Field + + **2.** A Lie algebra can be built from any associative algebra by + defining the Lie bracket to be the commutator. For example, we can + start with the descent algebra:: + + sage: D = DescentAlgebra(QQ, 4).D() + sage: L = LieAlgebra(associative=D); L + Lie algebra of Descent algebra of 4 over Rational Field + in the standard basis + sage: L(D[2]).bracket(L(D[3])) + D{1, 2} - D{1, 3} + D{2} - D{3} + + Next we use a free algebra and do some simple computations:: + + sage: R. = FreeAlgebra(QQ, 3) + sage: L. = LieAlgebra(associative=R.gens()) + sage: x-y+z + a - b + c + sage: L.bracket(x-y, x-z) + a*b - a*c - b*a + b*c + c*a - c*b + sage: L.bracket(x-y, L.bracket(x,y)) + a^2*b - 2*a*b*a + a*b^2 + b*a^2 - 2*b*a*b + b^2*a + + We can also use a subset of the elements as a generating set + of the Lie algebra:: + + sage: R. = FreeAlgebra(QQ, 3) + sage: L. = LieAlgebra(associative=[a,b+c]) + sage: L.bracket(x, y) + a*b + a*c - b*a - c*a + + Now for a more complicated example using the group ring of `S_3` as our + base algebra:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L. = LieAlgebra(associative=S.gens()) + sage: L.bracket(x, y) + (2,3) - (1,3) + sage: L.bracket(x, y-x) + (2,3) - (1,3) + sage: L.bracket(L.bracket(x, y), y) + 2*(1,2,3) - 2*(1,3,2) + sage: L.bracket(x, L.bracket(x, y)) + (2,3) - 2*(1,2) + (1,3) + sage: L.bracket(x, L.bracket(L.bracket(x, y), y)) + 0 + + Here is an example using matrices:: + + sage: MS = MatrixSpace(QQ,2) + sage: m1 = MS([[0, -1], [1, 0]]) + sage: m2 = MS([[-1, 4], [3, 2]]) + sage: L. = LieAlgebra(associative=[m1, m2]) + sage: x + [ 0 -1] + [ 1 0] + sage: y + [-1 4] + [ 3 2] + sage: L.bracket(x,y) + [-7 -3] + [-3 7] + sage: L.bracket(y,y) + [0 0] + [0 0] + sage: L.bracket(y,x) + [ 7 3] + [ 3 -7] + sage: L.bracket(x, L.bracket(y,x)) + [-6 14] + [14 6] + + (See :class:`LieAlgebraFromAssociative` for other examples.) + + **3.** We can also creating a Lie algebra by inputting a set of + structure coefficients. For example, we can create the Lie algebra + of `\QQ^3` under the Lie bracket `\times` (cross-product):: + + sage: d = {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}} + sage: L. = LieAlgebra(QQ, d) + sage: L + Lie algebra on 3 generators (x, y, z) over Rational Field + + To compute the Lie bracket of two elements, you cannot use the ``*`` + operator. Indeed, this automatically lifts up to the universal + enveloping algebra and takes the (associative) product there. + To get elements in the Lie algebra, you must use :meth:`bracket`:: + + sage: L = LieAlgebra(QQ, {('e','h'): {'e':-2}, ('f','h'): {'f':2}, + ....: ('e','f'): {'h':1}}, names='e,f,h') + sage: e,f,h = L.lie_algebra_generators() + sage: L.bracket(h, e) + 2*e + sage: elt = h*e; elt + e*h + 2*e + sage: P = elt.parent(); P + Noncommutative Multivariate Polynomial Ring in e, f, h over Rational Field, + nc-relations: {...} + sage: R = P.relations() + sage: for rhs in sorted(R, key=str): print("{} = {}".format(rhs, R[rhs])) + f*e = e*f - h + h*e = e*h + 2*e + h*f = f*h - 2*f + + For convienence, there are two shorthand notations for computing + Lie brackets:: + + sage: L([h,e]) + 2*e + sage: L([h,[e,f]]) + 0 + sage: L([[h,e],[e,f]]) + -4*e + sage: L[h, e] + 2*e + sage: L[h, L[e, f]] + 0 + + .. WARNING:: + + Because this is a modified (abused) version of python syntax, it + does **NOT** work with addition. For example ``L([e + [h, f], h])`` + and ``L[e + [h, f], h]`` will both raise errors. Instead you must + use ``L[e + L[h, f], h]``. + + Now we construct a free Lie algebra in a few different ways. There are + two primary representations, as brackets and as polynomials:: + + sage: L = LieAlgebra(QQ, 'x,y,z'); L # not tested #16823 + Free Lie algebra generated by (x, y, z) over Rational Field + sage: P. = LieAlgebra(QQ, representation="polynomial"); P + Lie algebra generated by (a, b, c) in + Free Algebra on 3 generators (a, b, c) over Rational Field + + We currently (:trac:`16823`) have the free Lie algebra given in the + polynomial representation, which is the Lie subalgebra of the Free + algebra generated by the degree-`1` component. + So the generators of the free Lie algebra are the generators of the + free algebra and the Lie bracket is the commutator:: + + sage: P.bracket(a, b) + P.bracket(a - c, b + 3*c) + 2*a*b + 3*a*c - 2*b*a + b*c - 3*c*a - c*b + + REFERENCES: + + - [deG2000]_ Willem A. de Graaf. *Lie Algebras: Theory and Algorithms*. + - [Ka1990]_ Victor Kac, *Infinite dimensional Lie algebras*. + - :wikipedia:`Lie_algebra` + """ + # This works because it is an abstract base class and this + # __classcall_private__ will only be called when calling LieAlgebra + @staticmethod + def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, + index_set=None, abelian=False, **kwds): + """ + Select the correct parent based upon input. + + TESTS:: + + sage: LieAlgebra(QQ, abelian=True, names='x,y,z') + Abelian Lie algebra on 3 generators (x, y, z) over Rational Field + sage: LieAlgebra(QQ, {('e','h'): {'e':-2}, ('f','h'): {'f':2}, + ....: ('e','f'): {'h':1}}, names='e,f,h') + Lie algebra on 3 generators (e, f, h) over Rational Field + """ + # Parse associative algebra input + # ----- + + assoc = kwds.get("associative", None) + if assoc is not None: + return LieAlgebraFromAssociative(assoc, names=names, index_set=index_set) + + # Parse the remaining arguments + # ----- + + if R is None: + raise ValueError("invalid arguments") + + check_assoc = lambda A: (isinstance(A, (Ring, MatrixSpace)) + or A in Rings() + or A in Algebras(R).Associative()) + if arg0 in ZZ or check_assoc(arg1): + # Check if we need to swap the arguments + arg0, arg1 = arg1, arg0 + + # Parse the first argument + # ----- + + if isinstance(arg0, dict): + if not arg0: + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names, index_set) + elif isinstance(arg0.keys()[0], (list,tuple)): + # We assume it is some structure coefficients + arg1, arg0 = arg0, arg1 + + if isinstance(arg0, (list, tuple)): + if all(isinstance(x, str) for x in arg0): + # If they are all strings, then it is a list of variables + names = tuple(arg0) + + if isinstance(arg0, str): + names = tuple(arg0.split(',')) + elif isinstance(names, str): + names = tuple(names.split(',')) + + # Parse the second argument + + if isinstance(arg1, dict): + # Assume it is some structure coefficients + from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + return LieAlgebraWithStructureCoefficients(R, arg1, names, index_set, **kwds) + + # Otherwise it must be either a free or abelian Lie algebra + + if arg1 in ZZ: + if isinstance(arg0, str): + names = arg0 + if names is None: + index_set = list(range(arg1)) + else: + if isinstance(names, str): + names = tuple(names.split(',')) + if arg1 != 1 and len(names) == 1: + names = tuple('{}{}'.format(names[0], i) + for i in range(arg1)) + if arg1 != len(names): + raise ValueError("the number of names must equal the" + " number of generators") + + if abelian: + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names, index_set) + + # Otherwise it is the free Lie algebra + rep = kwds.get("representation", "bracket") + if rep == "polynomial": + # 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 + F = FreeAlgebra(R, names) + if index_set is None: + index_set = F.variable_names() + # TODO: As part of #16823, this should instead construct a + # subclass with specialized methods for the free Lie algebra + return LieAlgebraFromAssociative(F, F.gens(), names=names, index_set=index_set) + + raise NotImplementedError("the free Lie algebra has only been" + " implemented using polynomials in the" + " free algebra, see trac ticket #16823") + + def __init__(self, R, names=None, category=None): + """ + The Lie algebra. + + INPUT: + + - ``R`` -- the base ring + + - ``names`` -- (optional) the names of the generators + + - ``category`` -- the category of the Lie algebra; the default is the + category of Lie algebras over ``R`` + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.category() + Category of finite dimensional lie algebras with basis over Rational Field + """ + category = LieAlgebras(R).or_subcategory(category) + Parent.__init__(self, base=R, names=names, category=category) + + def _element_constructor_(self, x): + """ + Convert ``x`` into ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: elt = L([x, y]); elt + x*y - y*x + sage: elt.parent() is L + True + """ + if isinstance(x, list) and len(x) == 2: + return self(x[0])._bracket_(self(x[1])) + + try: + if x in self.module(): + return self.from_vector(x) + except AttributeError: + pass + + if x in self.base_ring(): + if x != 0: + raise ValueError("can only convert the scalar 0 into a Lie algebra element") + return self.zero() + + return self.element_class(self, x) + + def __getitem__(self, x): + """ + If `x` is a pair `(a, b)`, return the Lie bracket `[a, b]`. + Otherwise try to return the `x`-th element of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: L[x, [y, x]] + -x^2*y + 2*x*y*x - y*x^2 + """ + if isinstance(x, tuple) and len(x) == 2: + return self(x[0])._bracket_(self(x[1])) + return super(LieAlgebra, self).__getitem__(x) + + def _coerce_map_from_(self, R): + """ + Return ``True`` if there is a coercion from ``R`` into ``self`` and + ``False`` otherwise. + + The things that coerce into ``self`` are: + + - Lie algebras in the same variables over a base with a coercion + map into ``self.base_ring()``. + + - A module which coerces into the base vector space of ``self``. + + TESTS:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L._coerce_map_from_(L.module()) + True + sage: L._coerce_map_from_(FreeModule(ZZ, 2)) + True + """ + if not isinstance(R, LieAlgebra): + # Should be moved to LieAlgebrasWithBasis somehow since it is a generic coercion + if self.module is not NotImplemented: + return self.module().has_coerce_map_from(R) + return False + + # We check if it is a subalgebra of something that can coerce into ``self`` + #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra + #if isinstance(R, LieSubalgebra) and self.has_coerce_map_from(R._ambient): + # return R.ambient_lift + + # Lie algebras in the same indices over any base that coerces in + if R._indices != self._indices: + return False + + return self.base_ring().has_coerce_map_from(R.base_ring()) + + @cached_method + def zero(self): + """ + Return the element `0`. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: L.zero() + 0 + """ + return self.element_class(self, {}) + + # The following methods should belong to ModulesWithBasis? + def _from_dict(self, d, coerce=False, remove_zeros=True): + """ + Construct an element of ``self`` from an ``{index: coefficient}`` + dictionary. + + INPUT: + + - ``d`` -- a dictionary ``{index: coeff}`` where each ``index`` is the + index of a basis element and each ``coeff`` belongs to the + coefficient ring ``self.base_ring()`` + + - ``coerce`` -- a boolean (default: ``False``), whether to coerce the + ``coeff``s to the coefficient ring + + - ``remove_zeros`` -- a boolean (default: ``True``), if some + ``coeff``s may be zero and should therefore be removed + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: d = {'p1': 4, 'q3': 1/2, 'z': -2} + sage: L._from_dict(d) + 4*p1 + 1/2*q3 - 2*z + """ + assert isinstance(d, dict) + if coerce: + R = self.base_ring() + d = {key: R(coeff) for key,coeff in iteritems(d)} + if remove_zeros: + d = {key: coeff for key, coeff in iteritems(d) if coeff} + return self.element_class(self, d) + + def monomial(self, i): + """ + Return the monomial indexed by ``i``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.monomial('p1') + p1 + """ + return self.element_class(self, {i: self.base_ring().one()}) + + def term(self, i, c=None): + """ + Return the term indexed by ``i`` with coefficient ``c``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L.term('p1', 4) + 4*p1 + """ + if c is None: + c = self.base_ring().one() + else: + c = self.base_ring()(c) + return self.element_class(self, {i: c}) + + def get_order(self): + """ + Return an ordering of the basis indices. + + .. TODO:: + + Remove this method and in :class:`CombinatorialFreeModule` + in favor of a method in the category of (finite dimensional) + modules with basis. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {}) + sage: L.get_order() + ('x', 'y') + """ + try: + return self._basis_ordering + except AttributeError: + raise ValueError("the Lie algebra is not finite dimensional with a basis") + + #Element = LieAlgebraElement # Default for all Lie algebras + +class LieAlgebraWithGenerators(LieAlgebra): + """ + A Lie algebra with distinguished generators. + """ + def __init__(self, R, names=None, index_set=None, category=None, prefix='L', **kwds): + """ + The Lie algebra. + + INPUT: + + - ``R`` -- the base ring + - ``names`` -- (optional) the names of the generators + - ``index_set`` -- (optional) the indexing set + - ``category`` -- the category of the Lie algebra; the default is the + category of Lie algebras over ``R`` + - ``prefix`` -- (optional) the prefix for the generator representation + - any keyword accepted by + :class:`~sage.structure.indexed_generators.IndexedGenerators` + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.category() + Category of finite dimensional lie algebras with basis over Rational Field + """ + self._indices = index_set + LieAlgebra.__init__(self, R, names, category) + + @cached_method + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: L.lie_algebra_generators() + Finite family {'y': y, 'x': x} + """ + return Family(self._indices, self.monomial, name="monomial map") + + @cached_method + def gens(self): + """ + Return a tuple whose entries are the generators for this + object, in some order. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.gens() + (x, y) + """ + G = self.lie_algebra_generators() + try: + return tuple(G[i] for i in self.variable_names()) + except (KeyError, IndexError): + return tuple(G[i] for i in self.indices()) + except (KeyError, ValueError): + return tuple(G) + + def gen(self, i): + """ + Return the ``i``-th generator of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.gen(0) + x + """ + return self.gens()[i] + + def indices(self): + """ + Return the indices of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: L.indices() + {'x', 'y'} + """ + return self._indices + +class FinitelyGeneratedLieAlgebra(LieAlgebraWithGenerators): + r""" + A finitely generated Lie algebra. + """ + def __init__(self, R, names=None, index_set=None, category=None): + """ + Initialize ``self``. + + INPUT: + + - ``R`` -- the base ring + + - ``names`` -- the names of the generators + + - ``index_set`` -- the index set of the generators + + - ``category`` -- the category of the Lie algebra + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.category() + Category of finite dimensional lie algebras with basis over Rational Field + """ + LieAlgebraWithGenerators.__init__(self, R, names, index_set, category) + self.__ngens = len(self._indices) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: F. = LieAlgebra(QQ, {('x','y'): {'x': 1}}) + sage: F + Lie algebra on 2 generators (x, y) over Rational Field + """ + if self.__ngens == 1: + return "Lie algebra on the generator {0} over {1}".format( + self.gen(0), self.base_ring()) + return "Lie algebra on {0} generators {1} over {2}".format( + self.__ngens, self.gens(), self.base_ring()) + + @lazy_attribute + def _ordered_indices(self): + """ + Return the index set of the basis of ``self`` in (some) order. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L._ordered_indices + ('x', 'y') + """ + return tuple(self.basis().keys()) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: L.an_element() + x + y + """ + return self.sum(self.lie_algebra_generators()) + +class InfinitelyGeneratedLieAlgebra(LieAlgebraWithGenerators): + r""" + An infinitely generated Lie algebra. + """ + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, oo) + sage: L._an_element_() + p2 + q2 - 1/2*q3 + z + """ + return self.lie_algebra_generators()[self._indices.an_element()] + +# Do we want this to return lie_algebra_generators()? Perhaps in the category? +# def gens(self): +# """ +# Return a tuple whose entries are the generators for this +# object, in some order. +# +# EXAMPLES:: +# +# sage: L. = LieAlgebra(QQ, abelian=True) +# sage: L.gens() +# (x, y) +# """ +# return self.lie_algebra_generators() + +class LieAlgebraFromAssociative(LieAlgebraWithGenerators): + """ + A Lie algebra whose elements are from an associative algebra and whose + bracket is the commutator. + + .. TODO:: + + Split this class into 2 classes, the base class for the Lie + algebra corresponding to the full associative algebra and a + subclass for the Lie subalgebra (of the full algebra) + generated by a generating set? + + .. TODO:: + + Return the subalgebra generated by the basis + elements of ``self`` for the universal enveloping algebra. + + EXAMPLES: + + For the first example, we start with a commutative algebra. + Note that the bracket of everything will be 0:: + + sage: R = SymmetricGroupAlgebra(QQ, 2) + sage: L = LieAlgebra(associative=R) + sage: x, y = L.basis() + sage: L.bracket(x, y) + 0 + + Next we use a free algebra and do some simple computations:: + + sage: R. = FreeAlgebra(QQ, 2) + sage: L = LieAlgebra(associative=R) + sage: x,y = L(a), L(b) + sage: x-y + a - b + sage: L.bracket(x-y, x) + a*b - b*a + sage: L.bracket(x-y, L.bracket(x,y)) + a^2*b - 2*a*b*a + a*b^2 + b*a^2 - 2*b*a*b + b^2*a + + We can also use a subset of the generators as a generating set + of the Lie algebra:: + + sage: R. = FreeAlgebra(QQ, 3) + sage: L. = LieAlgebra(associative=[a,b]) + + Now for a more complicated example using the group ring of `S_3` + as our base algebra:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L. = LieAlgebra(associative=S.gens()) + sage: L.bracket(x, y) + (2,3) - (1,3) + sage: L.bracket(x, y-x) + (2,3) - (1,3) + sage: L.bracket(L.bracket(x, y), y) + 2*(1,2,3) - 2*(1,3,2) + sage: L.bracket(x, L.bracket(x, y)) + (2,3) - 2*(1,2) + (1,3) + sage: L.bracket(x, L.bracket(L.bracket(x, y), y)) + 0 + + Here is an example using matrices:: + + sage: MS = MatrixSpace(QQ,2) + sage: m1 = MS([[0, -1], [1, 0]]) + sage: m2 = MS([[-1, 4], [3, 2]]) + sage: L. = LieAlgebra(associative=[m1, m2]) + sage: x + [ 0 -1] + [ 1 0] + sage: y + [-1 4] + [ 3 2] + sage: L.bracket(x,y) + [-7 -3] + [-3 7] + sage: L.bracket(y,y) + [0 0] + [0 0] + sage: L.bracket(y,x) + [ 7 3] + [ 3 -7] + sage: L.bracket(x, L.bracket(y,x)) + [-6 14] + [14 6] + """ + @staticmethod + def __classcall_private__(cls, A, gens=None, names=None, index_set=None, + free_lie_algebra=False): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L1 = LieAlgebra(associative=tuple(S.gens()), names=['x','y']) + sage: L2 = LieAlgebra(associative=[ S(G((1,2,3))), S(G((1,2))) ], names='x,y') + sage: L1 is L2 + True + + sage: F. = FreeAlgebra(QQ) + sage: L1 = LieAlgebra(associative=F.algebra_generators(), names='x,y,z') + sage: L2. = LieAlgebra(associative=F.gens()) + sage: L1 is L2 + True + """ + # If A is not a ring, then we treat it as a set of generators + if isinstance(A, Parent) and A.category().is_subcategory(Rings()): + if gens is None and index_set is None: + # Use the indexing set of the basis + try: + index_set = A.basis().keys() + except (AttributeError, NotImplementedError): + pass + else: + gens = A + A = None + + if index_set is None: + # See if we can get an index set from the generators + try: + index_set = gens.keys() + except (AttributeError, ValueError): + pass + + ngens = None + if isinstance(gens, AbstractFamily): + if index_set is None and names is None: + index_set = gens.keys() + if gens.cardinality() < float('inf'): + # TODO: This makes the generators of a finitely generated + # Lie algebra into an ordered list for uniqueness and then + # reconstructs the family. Instead create a key for the + # cache this way and then pass the family. + try: + gens = tuple([gens[i] for i in index_set]) + except KeyError: + gens = tuple(gens) + ngens = len(gens) + + elif isinstance(gens, dict): + if index_set is None and names is None: + index_set = gens.keys() + gens = gens.values() + ngens = len(gens) + elif gens is not None: # Assume it is list-like + gens = tuple(gens) + ngens = len(gens) + if index_set is None and names is None: + index_set = list(range(ngens)) + + if ngens is not None: + if A is None: + A = gens[0].parent() + # Make sure all the generators have the same parent of A + gens = tuple([A(g) for g in gens]) + + names, index_set = standardize_names_index_set(names, index_set, ngens) + + if isinstance(A, MatrixSpace): + if gens is not None: + for g in gens: + g.set_immutable() + return MatrixLieAlgebraFromAssociative(A, gens, names=names, + index_set=index_set) + + return super(LieAlgebraFromAssociative, cls).__classcall__(cls, + A, gens, names=names, index_set=index_set) + + def __init__(self, A, gens=None, names=None, index_set=None, category=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: TestSuite(L).run() + + TESTS:: + + sage: from sage.algebras.lie_algebras.lie_algebra import LieAlgebraFromAssociative as LAFA + sage: LAFA(MatrixSpace(QQ, 0, sparse=True), [], names=()) + Lie algebra generated by () in Full MatrixSpace of 0 by 0 sparse matrices over Rational Field + """ + self._assoc = A + R = self._assoc.base_ring() + + # We strip the following axioms from the category of the assoc. algebra: + # FiniteDimensional and WithBasis + category = LieAlgebras(R).or_subcategory(category) + if 'FiniteDimensional' in self._assoc.category().axioms(): + category = category.FiniteDimensional() + if 'WithBasis' in self._assoc.category().axioms() and gens is None: + category = category.WithBasis() + + LieAlgebraWithGenerators.__init__(self, R, names, index_set, category) + + if isinstance(gens, tuple): + # This guarantees that the generators have a specified ordering + d = {self._indices[i]: self.element_class(self, v) + for i,v in enumerate(gens)} + gens = Family(list(self._indices), lambda i: d[i]) + elif gens is not None: # It is a family + gens = Family(self._indices, + lambda i: self.element_class(self, gens[i]), + name="generator map") + self._gens = gens + + # We don't need to store the original generators because we can + # get them from lifting this object's generators + + # We construct the lift map to the ambient associative algebra + LiftMorphismToAssociative(self, self._assoc).register_as_coercion() + + def _repr_option(self, key): + """ + Metadata about the :meth:`_repr_` output. + + See :meth:`sage.structure.parent._repr_option` for details. + + EXAMPLES:: + + sage: MS = MatrixSpace(QQ,2) + sage: L. = LieAlgebra(associative=[MS.one()]) + sage: L._repr_option('element_ascii_art') + True + """ + return self._assoc._repr_option(key) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: LieAlgebra(associative=S) + Lie algebra of Group algebra of group + "Symmetric group of order 3! as a permutation group" + over base ring Rational Field + sage: LieAlgebra(associative=S.gens()) + Lie algebra generated by ((1,2,3), (1,2)) + in Group algebra of group + "Symmetric group of order 3! as a permutation group" + over base ring Rational Field + """ + if self._gens is not None: + return "Lie algebra generated by {} in {}".format(tuple(self._gens), self._assoc) + return "Lie algebra of {}".format(self._assoc) + + def _element_constructor_(self, x): + """ + Convert ``x`` into ``self``. + + EXAMPLES:: + + sage: S = SymmetricGroupAlgebra(QQ, 3) + sage: L = LieAlgebra(associative=S) + sage: x,y = S.algebra_generators() + sage: elt = L(x - y); elt + [2, 1, 3] - [2, 3, 1] + sage: elt.parent() is L + True + sage: elt == L(x) - L(y) + True + sage: L([x, y]) + -[1, 3, 2] + [3, 2, 1] + sage: L(2) + 2*[1, 2, 3] + """ + if isinstance(x, list) and len(x) == 2: + return self(x[0])._bracket_(self(x[1])) + return self.element_class(self, self._assoc(x)) + + def associative_algebra(self): + """ + Return the associative algebra used to construct ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: L.associative_algebra() is S + True + """ + return self._assoc + + def lie_algebra_generators(self): + """ + Return the Lie algebra generators of ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: L.lie_algebra_generators() + Finite family {(2,3): (2,3), (1,2): (1,2), (1,3): (1,3), + (1,2,3): (1,2,3), (1,3,2): (1,3,2), (): ()} + """ + if self._gens is not None: + return self._gens + try: + ngens = self._indices.cardinality() + except AttributeError: + ngens = len(self._indices) + if ngens < float('inf'): + return Family(list(self._indices), self.monomial) + return Family(self._indices, self.monomial, name="generator map") + + def monomial(self, i): + """ + Return the monomial indexed by ``i``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(QQ) + sage: L = LieAlgebra(associative=F) + sage: L.monomial(x.leading_support()) + x + """ + if i not in self.basis().keys(): + #return self(self._assoc.monomial(i)) + raise ValueError("not an index") + return self.element_class(self, self._assoc.monomial(i)) + + def term(self, i, c=None): + """ + Return the term indexed by ``i`` with coefficient ``c``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(QQ) + sage: L = LieAlgebra(associative=F) + sage: L.term(x.leading_support(), 4) + 4*x + """ + if i not in self.basis().keys(): + #return self(self._assoc.term(i, c)) + raise ValueError("not an index") + return self.element_class(self, self._assoc.term(i, c)) + + @cached_method + def zero(self): + """ + Return the element `0` in ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: L.zero() + 0 + """ + return self.element_class(self, self._assoc.zero()) + + def is_abelian(self): + """ + Return ``True`` if ``self`` is abelian. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 2, 'x,y') + sage: L = LieAlgebra(associative=R.gens()) + sage: L.is_abelian() + False + + sage: R = PolynomialRing(QQ, 'x,y') + sage: L = LieAlgebra(associative=R.gens()) + sage: L.is_abelian() + True + + An example with a Lie algebra from the group algebra:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: L.is_abelian() + False + + Now we construct a Lie algebra from commuting elements in the group + algebra:: + + sage: G = SymmetricGroup(5) + sage: S = GroupAlgebra(G, QQ) + sage: gens = map(S, [G((1, 2)), G((3, 4))]) + sage: L. = LieAlgebra(associative=gens) + sage: L.is_abelian() + True + """ + if self._assoc.is_commutative(): + return True + return super(LieAlgebraFromAssociative, self).is_abelian() + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(QQ) + + An infinitely generated example:: + + sage: L = LieAlgebra(associative=F) + sage: L.an_element() + 1 + + A finitely generated example:: + + sage: L = LieAlgebra(associative=F.gens()) + sage: L.an_element() + x + y + """ + G = self.lie_algebra_generators() + if G.cardinality() < float('inf'): + return self.sum(G) + return G[self._indices.an_element()] + + class Element(LieAlgebraElementWrapper): + def _bracket_(self, rhs): + """ + Return the bracket ``[self, rhs]``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, representation="polynomial") + sage: L.bracket(x, y) + x*y - y*x + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L. = LieAlgebra(associative=S.gens()) + sage: L.bracket(x, y) + (2,3) - (1,3) + + sage: L = lie_algebras.sl(QQ, 2, representation='matrix') + sage: L.bracket(L.gen(0), L.gen(1)) + [ 1 0] + [ 0 -1] + """ + ret = self.value * rhs.value - rhs.value * self.value + return self.__class__(self.parent(), ret) + + def lift_associative(self): + """ + Lift ``self`` to the ambient associative algebra (which + might be smaller than the universal enveloping algebra). + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: x.lift_associative() + x + sage: x.lift_associative().parent() + Free Algebra on 3 generators (x, y, z) over Rational Field + """ + return self.value + + def monomial_coefficients(self, copy=True): + """ + Return the monomial coefficients of ``self`` (if this + notion makes sense for ``self.parent()``). + + EXAMPLES:: + + sage: R. = FreeAlgebra(QQ) + sage: L = LieAlgebra(associative=R) + sage: elt = L(x) + 2*L(y) - L(z) + sage: sorted(elt.monomial_coefficients().items()) + [(x, 1), (y, 2), (z, -1)] + + sage: L = LieAlgebra(associative=[x,y]) + sage: elt = L(x) + 2*L(y) + sage: elt.monomial_coefficients() + Traceback (most recent call last): + ... + NotImplementedError: the basis is not defined + """ + if self.parent()._gens is not None: + raise NotImplementedError("the basis is not defined") + return self.value.monomial_coefficients(copy) + +class LiftMorphismToAssociative(LiftMorphism): + """ + The natural lifting morphism from a Lie algebra constructed from + an associative algebra `A` to `A`. + """ + def preimage(self, x): + """ + Return the preimage of ``x`` under ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'a,b,c') + sage: L = LieAlgebra(associative=R) + sage: x,y,z = R.gens() + sage: f = R.coerce_map_from(L) + sage: p = f.preimage(x*y - z); p + -c + a*b + sage: p.parent() is L + True + """ + return self.domain().element_class(self.domain(), x) + + def _call_(self, x): + """ + Return the image of ``x`` under ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: f = R.coerce_map_from(L) + sage: a = f(L([x,y]) + z); a + z + x*y - y*x + sage: a.parent() is R + True + """ + return x.value + + def section(self): + """ + Return the section map of ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: f = R.coerce_map_from(L) + sage: f.section() + Generic morphism: + From: Free Algebra on 3 generators (x, y, z) over Rational Field + To: Lie algebra generated by (x, y, z) in Free Algebra on 3 generators (x, y, z) over Rational Field + """ + return SetMorphism(Hom(self.codomain(), self.domain()), + self.preimage) + +class MatrixLieAlgebraFromAssociative(LieAlgebraFromAssociative): + """ + A Lie algebra constructed from a matrix algebra. + """ + class Element(LieAlgebraMatrixWrapper, LieAlgebraFromAssociative.Element): + pass + diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pxd b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd new file mode 100644 index 00000000000..47c46336c51 --- /dev/null +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd @@ -0,0 +1,16 @@ +from sage.structure.element_wrapper cimport ElementWrapper + +cdef class LieAlgebraElementWrapper(ElementWrapper): + cpdef _add_(self, right) + cpdef _sub_(self, right) + +cdef class LieAlgebraMatrixWrapper(LieAlgebraElementWrapper): + pass + +cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): + cpdef bracket(self, right) + cpdef _bracket_(self, right) + cpdef to_vector(self) + cpdef dict monomial_coefficients(self, bint copy=*) + #cpdef lift(self) + diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx new file mode 100644 index 00000000000..c5b47732616 --- /dev/null +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -0,0 +1,557 @@ +# -*- coding: utf-8 -*- +""" +Lie Algebra Elements + +AUTHORS: + +- Travis Scrimshaw (2013-05-04): Initial implementation +""" + +#***************************************************************************** +# 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 copy import copy + +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_wrapper cimport ElementWrapper + +# TODO: Inherit from IndexedFreeModuleElement and make cdef once #22632 is merged +# TODO: Do we want a dense version? +class LieAlgebraElement(CombinatorialFreeModule.Element): + """ + A Lie algebra element. + """ + # Need to bypass the coercion model + def __mul__(self, y): + """ + If we are multiplying two non-zero elements, automatically + lift up to the universal enveloping algebra. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: y*x + x*y - z + """ + if self.is_zero() or y.is_zero(): + return self.parent().zero() + if y in self.base_ring(): + return y * self + # Otherwise we lift to the UEA + return self.lift() * y + + #def _im_gens_(self, codomain, im_gens): + # """ + # Return the image of ``self`` in ``codomain`` under the map that sends + # the images of the generators of the parent of ``self`` to the + # tuple of elements of ``im_gens``. + # + # EXAMPLES:: + # """ + # s = codomain.zero() + # if not self: # If we are 0 + # return s + # names = self.parent().variable_names() + # return codomain.sum(c * t._im_gens_(codomain, im_gens, names) + # for t, c in self._monomial_coefficients.iteritems()) + + def lift(self): + """ + Lift ``self`` to the universal enveloping algebra. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'z':1}}) + sage: x.lift().parent() == L.universal_enveloping_algebra() + True + """ + UEA = self.parent().universal_enveloping_algebra() + gen_dict = UEA.gens_dict() + s = UEA.zero() + if not self: + return s + for t, c in self._monomial_coefficients.iteritems(): + s += c * gen_dict[t] + return s + +cdef class LieAlgebraElementWrapper(ElementWrapper): + """ + Wrap an element as a Lie algebra element. + + TESTS: + + We check comparisons:: + + sage: L = lie_algebras.sl(QQ, 2, representation='matrix') + sage: L.bracket(L.gen(0), L.gen(1)) == -L.bracket(L.gen(1), L.gen(0)) + True + + The next doctests show similar behavior, although on elements of + other classes:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3) + sage: L.bracket(L.gen(0), L.gen(1)) == -L.bracket(L.gen(1), L.gen(0)) + True + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 1) + sage: L.bracket(L.gen(0), L.gen(1)) == -L.bracket(L.gen(1), L.gen(0)) + True + + Check inequality:: + + sage: L = lie_algebras.sl(QQ, 2, representation='matrix') + sage: L.bracket(L.gen(0), L.gen(1)) != -L.bracket(L.gen(1), L.gen(0)) + False + sage: L.zero() == 0 + True + sage: L.zero() != 0 + False + + The next doctests show similar behavior, although on elements of + other classes:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3) + sage: L.bracket(L.gen(0), L.gen(1)) != -L.bracket(L.gen(1), L.gen(0)) + False + sage: L.an_element() + X + Y + Z + sage: L.an_element() == 0 + False + sage: L.an_element() != 0 + True + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 1) + sage: L.bracket(L.gen(0), L.gen(1)) != -L.bracket(L.gen(1), L.gen(0)) + False + sage: L.zero() == 0 + True + sage: L.zero() != 0 + False + sage: L.zero() >= 0 + True + sage: L.zero() < 0 + False + """ + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: x + y + x + y + """ + return repr(self.value) + + def _latex_(self): + r""" + Return a `\LaTeX` representation of ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x') + sage: L. = LieAlgebra(associative=R.gens()) + sage: latex(x0 + x1) + x_{0} + x_{1} + """ + from sage.misc.latex import latex + return latex(self.value) + + def _ascii_art_(self): + """ + Return an ascii art representation of ``self``. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LieAlgebra(associative=s) + sage: P = Partition([4,2,2,1]) + sage: x = L.basis()[P] + sage: ascii_art(x) + s + **** + ** + ** + * + """ + from sage.typeset.ascii_art import ascii_art + return ascii_art(self.value) + + def _unicode_art_(self): + """ + Return a unicode art representation of ``self``. + + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).s() + sage: L = LieAlgebra(associative=s) + sage: P = Partition([4,2,2,1]) + sage: x = L.basis()[P] + sage: unicode_art(x) + s + ┌┬┬┬┐ + ├┼┼┴┘ + ├┼┤ + ├┼┘ + └┘ + """ + from sage.typeset.unicode_art import unicode_art + return unicode_art(self.value) + + def __nonzero__(self): + """ + Return if ``self`` is non-zero. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: bool(L.zero()) + False + sage: bool(x + y) + True + """ + return bool(self.value) + + cpdef _add_(self, right): + """ + Add ``self`` and ``rhs``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: x + y + x + y + """ + return type(self)(self._parent, self.value + right.value) + + cpdef _sub_(self, right): + """ + Subtract ``self`` and ``rhs``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: x - y + x - y + """ + return type(self)(self._parent, self.value - right.value) + + # We need to bypass the coercion framework + # We let the universal enveloping algebra handle the rest if both + # arguments are non-zero + def __mul__(self, x): + """ + If we are multiplying two non-zero elements, automatically + lift up to the universal enveloping algebra. + + .. TODO:: + + Write tests for this method once :trac:`16822` is + implemented. + + EXAMPLES:: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L. = LieAlgebra(associative=S.gens()) + sage: u = x*3; u + 3*(1,2,3) + sage: parent(u) == L + True + sage: u = x*(3/2); u + 3/2*(1,2,3) + sage: parent(u) == L + True + sage: elt = x*y - y*x; elt # not tested: needs #16822 + 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(): + return self._acted_upon_(x, True) + # Otherwise we lift to the UEA + return self.lift() * x + + def __div__(self, x): + """ + Division by coefficients. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 3) + sage: x = L.an_element(); x + p1 + sage: x / 2 + 1/2*p1 + """ + return self * (~x) + + cpdef _acted_upon_(self, scalar, bint self_on_left): + """ + Return the action of a scalar on ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: 3*x + 3*x + sage: parent(3*x) == parent(x) + True + sage: x / 2 + 1/2*x + sage: y * (1/2) + 1/2*y + sage: y * 1/2 + 1/2*y + sage: 1/2 * y + 1/2*y + sage: QQ(1/2) * y + 1/2*y + """ + # This was copied and IDK if it still applies (TCS): + # With the current design, the coercion model does not have + # 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(): + # 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 + if self_on_left: + return type(self)(self._parent, self.value * scalar) + return type(self)(self._parent, scalar * self.value) + + def __neg__(self): + """ + Return the negation of ``self``. + + EXAMPLES:: + + sage: R = FreeAlgebra(QQ, 3, 'x,y,z') + sage: L. = LieAlgebra(associative=R.gens()) + sage: -x + -x + """ + return type(self)(self._parent, -self.value) + + def __getitem__(self, i): + """ + Redirect the ``__getitem__()`` to the wrapped element. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2, representation='matrix') + sage: m = L.gen(0) + sage: m[0,0] + 0 + sage: m[0][1] + 1 + """ + return self.value.__getitem__(i) + +# TODO: Also used for vectors, find a better name +cdef class LieAlgebraMatrixWrapper(LieAlgebraElementWrapper): + """ + Lie algebra element wrapper around a matrix. + """ + def __init__(self, parent, value): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Heisenberg(QQ, 1, representation="matrix") + sage: z = L.z() + sage: z.value.is_immutable() + True + """ + value.set_immutable() # Make the matrix immutable for hashing + LieAlgebraElementWrapper.__init__(self, parent, value) + +cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): + """ + An element of a Lie algebra given by structure coefficients. + """ + def _repr_(self): + """ + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: x - 3/2 * y + x - 3/2*y + """ + return repr_lincomb(self._sorted_items_for_printing(), + scalar_mult=self._parent._print_options['scalar_mult'], + repr_monomial=self._parent._repr_generator, + strip_one=True) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: elt = x - 3/2 * y + sage: latex(elt) + x - \frac{3}{2}y + """ + 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 bracket(self, right): + """ + Return the Lie bracket ``[self, right]``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}) + sage: x.bracket(y) + z + sage: y.bracket(x) + -z + sage: (x + y - z).bracket(x - y + z) + -2*y - 2*z + """ + if not have_same_parent(self, right): + self, right = coercion_model.canonical_coercion(self, right) + return self._bracket_(right) + + # We need this method because the LieAlgebra.bracket method (from the + # category) calls this, where we are guaranteed to have the same parent. + cpdef _bracket_(self, right): + """ + Return the Lie bracket ``[self, right]``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}) + sage: x._bracket_(y) + z + sage: y._bracket_(x) + -z + """ + P = self._parent + cdef dict s_coeff = P._s_coeff + d = P.dimension() + cdef list ret = [P.base_ring().zero()]*d + cdef int i1, i2, i3 + cdef StructureCoefficientsElement rt = right + for i1 in range(d): + c1 = self.value[i1] + if not c1: + continue + for i2 in range(d): + c2 = rt.value[i2] + if not c2: + continue + prod_c1_c2 = c1 * c2 + if (i1, i2) in s_coeff: + v = s_coeff[i1, i2] + for i3 in range(d): + ret[i3] += prod_c1_c2 * v[i3] + elif (i2, i1) in s_coeff: + v = s_coeff[i2, i1] + for i3 in range(d): + ret[i3] -= prod_c1_c2 * v[i3] + return type(self)(P, P._M(ret)) + + def __iter__(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: elt = x - 3/2 * y + sage: list(elt) + [('x', 1), ('y', -3/2)] + """ + zero = self.base_ring().zero() + I = self.parent()._indices + cdef int i + for i,v in enumerate(self.value): + if v != zero: + yield (I[i], v) + + cpdef to_vector(self): + """ + Return ``self`` as a vector. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: a = x + 3*y - z/2 + sage: a.to_vector() + (1, 3, -1/2) + """ + return self.value + + def lift(self): + """ + Return the lift of ``self`` to the universal enveloping algebra. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: elt = x - 3/2 * y + sage: l = elt.lift(); l + x - 3/2*y + sage: l.parent() + Noncommutative Multivariate Polynomial Ring in x, y + over Rational Field, nc-relations: {y*x: x*y - x} + """ + UEA = self.parent().universal_enveloping_algebra() + gens = UEA.gens() + return UEA.sum(c * gens[i] for i, c in self.value.iteritems()) + + cpdef dict monomial_coefficients(self, bint copy=True): + """ + Return the monomial coefficients of ``self`` as a dictionary. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: a = 2*x - 3/2*y + z + sage: a.monomial_coefficients() + {'x': 2, 'y': -3/2, 'z': 1} + sage: a = 2*x - 3/2*z + sage: a.monomial_coefficients() + {'x': 2, 'z': -3/2} + """ + I = self._parent._indices + return {I[i]: v for i,v in self.value.iteritems()} + + def __getitem__(self, i): + """ + Return the coefficient of the basis element indexed by ``i``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: elt = x - 3/2 * y + sage: elt['y'] + -3/2 + """ + return self.value[self._parent._indices.index(i)] + diff --git a/src/sage/algebras/lie_algebras/structure_coefficients.py b/src/sage/algebras/lie_algebras/structure_coefficients.py new file mode 100644 index 00000000000..b78405e2739 --- /dev/null +++ b/src/sage/algebras/lie_algebras/structure_coefficients.py @@ -0,0 +1,380 @@ +""" +Lie Algebras Given By Structure Coefficients + +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 six import iteritems + +from sage.misc.cachefunc import cached_method +#from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.indexed_generators import (IndexedGenerators, + standardize_names_index_set) + +from sage.categories.lie_algebras import LieAlgebras + +from sage.algebras.lie_algebras.lie_algebra_element import StructureCoefficientsElement +from sage.algebras.lie_algebras.lie_algebra import FinitelyGeneratedLieAlgebra +#from sage.algebras.lie_algebras.subalgebra import LieSubalgebra +#from sage.algebras.lie_algebras.ideal import LieAlgebraIdeal +#from sage.algebras.lie_algebras.quotient import QuotientLieAlgebra +from sage.modules.free_module import FreeModule +from sage.sets.family import Family + +class LieAlgebraWithStructureCoefficients(FinitelyGeneratedLieAlgebra, IndexedGenerators): + r""" + A Lie algebra with a set of specified structure coefficients. + + The structure coefficients are specified as a dictionary `d` whose + keys are pairs of basis indices, and whose values are + dictionaries which in turn are indexed by basis indices. The value + of `d` at a pair `(u, v)` of basis indices is the dictionary whose + `w`-th entry (for `w` a basis index) is the coefficient of `b_w` + in the Lie bracket `[b_u, b_v]` (where `b_x` means the basis + element with index `x`). + + INPUT: + + - ``R`` -- a ring, to be used as the base ring + + - ``s_coeff`` -- a dictionary, indexed by pairs of basis indices + (see below), and whose values are dictionaries which are + indexed by (single) basis indices and whose values are elements + of `R` + + - ``names`` -- list or tuple of strings + + - ``index_set`` -- (default: ``names``) list or tuple of hashable + and comparable elements + + OUTPUT: + + A Lie algebra over ``R`` which (as an `R`-module) is free with + a basis indexed by the elements of ``index_set``. The `i`-th + basis element is displayed using the name ``names[i]``. + If we let `b_i` denote this `i`-th basis element, then the Lie + bracket is given by the requirement that the `b_k`-coefficient + of `[b_i, b_j]` is ``s_coeff[(i, j)][k]`` if + ``s_coeff[(i, j)]`` exists, otherwise ``-s_coeff[(j, i)][k]`` + if ``s_coeff[(j, i)]`` exists, otherwise `0`. + + EXAMPLES: + + We create the Lie algebra of `\QQ^3` under the Lie bracket defined + by `\times` (cross-product):: + + sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}) + sage: (x,y,z) = L.gens() + sage: L.bracket(x, y) + z + sage: L.bracket(y, x) + -z + + TESTS: + + We can input structure coefficients that fail the Jacobi + identity, but the test suite will call us out on it:: + + sage: Fake = LieAlgebra(QQ, 'x,y,z', {('x','y'):{'z':3}, ('y','z'):{'z':1}, ('z','x'):{'y':1}}) + sage: TestSuite(Fake).run() + Failure in _test_jacobi_identity: + ... + + Old tests !!!!!placeholder for now!!!!!:: + + sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) + sage: L.basis() + Finite family {'y': y, 'x': x} + """ + @staticmethod + def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, **kwds): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: L1 = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) + sage: L2 = LieAlgebra(QQ, 'x,y', {('y','x'): {'x':-1}}) + sage: L1 is L2 + True + + Check that we convert names to the indexing set:: + + sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'z':1}, ('y','z'): {'x':1}, ('z','x'): {'y':1}}, index_set=range(3)) + sage: (x,y,z) = L.gens() + sage: L[x,y] + L[2] + """ + names, index_set = standardize_names_index_set(names, index_set) + + # Make sure the structure coefficients are given by the index set + if names is not None and names != tuple(index_set): + d = {x: index_set[i] for i,x in enumerate(names)} + get_pairs = lambda X: X.items() if isinstance(X, dict) else X + try: + s_coeff = {(d[k[0]], d[k[1]]): [(d[x], y) for x,y in get_pairs(s_coeff[k])] + for k in s_coeff} + except KeyError: + # At this point we assume they are given by the index set + pass + + s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(s_coeff, index_set) + if s_coeff.cardinality() == 0: + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names, index_set, **kwds) + + if (names is None and len(index_set) <= 1) or len(names) <= 1: + from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra + return AbelianLieAlgebra(R, names, index_set, **kwds) + + return super(LieAlgebraWithStructureCoefficients, cls).__classcall__( + cls, R, s_coeff, names, index_set, **kwds) + + @staticmethod + def _standardize_s_coeff(s_coeff, index_set): + """ + Helper function to standardize ``s_coeff`` into the appropriate form + (dictionary indexed by pairs, whose values are dictionaries). + Strips items with coefficients of 0 and duplicate entries. + This does not check the Jacobi relation (nor antisymmetry if the + cardinality is infinite). + + EXAMPLES:: + + sage: from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients + sage: d = {('y','x'): {'x':-1}} + sage: LieAlgebraWithStructureCoefficients._standardize_s_coeff(d, ('x', 'y')) + Finite family {('x', 'y'): (('x', 1),)} + """ + # Try to handle infinite basis (once/if supported) + #if isinstance(s_coeff, AbstractFamily) and s_coeff.cardinality() == infinity: + # return s_coeff + + index_to_pos = {k: i for i,k in enumerate(index_set)} + + sc = {} + # Make sure the first gen is smaller than the second in each key + for k in s_coeff.keys(): + v = s_coeff[k] + if isinstance(v, dict): + v = v.items() + + if index_to_pos[k[0]] > index_to_pos[k[1]]: + key = (k[1], k[0]) + vals = tuple((g, -val) for g, val in v if val != 0) + else: + if not index_to_pos[k[0]] < index_to_pos[k[1]]: + if k[0] == k[1]: + if not all(val == 0 for g, val in v): + raise ValueError("elements {} are equal but their bracket is not set to 0".format(k)) + continue + key = tuple(k) + vals = tuple((g, val) for g, val in v if val != 0) + + if key in sc.keys() and sorted(sc[key]) != sorted(vals): + raise ValueError("two distinct values given for one and the same bracket") + + if vals: + sc[key] = vals + return Family(sc) + + def __init__(self, R, s_coeff, names, index_set, category=None, prefix=None, + bracket=None, latex_bracket=None, string_quotes=None, **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) + sage: TestSuite(L).run() + """ + default = (names != tuple(index_set)) + if prefix is None: + if default: + prefix = 'L' + else: + prefix = '' + if bracket is None: + bracket = default + if latex_bracket is None: + latex_bracket = default + if string_quotes is None: + string_quotes = default + + #self._pos_to_index = dict(enumerate(index_set)) + self._index_to_pos = {k: i for i,k in enumerate(index_set)} + if "sorting_key" not in kwds: + kwds["sorting_key"] = self._index_to_pos.__getitem__ + + cat = LieAlgebras(R).WithBasis().FiniteDimensional().or_subcategory(category) + FinitelyGeneratedLieAlgebra.__init__(self, R, names, index_set, cat) + IndexedGenerators.__init__(self, self._indices, prefix=prefix, + bracket=bracket, latex_bracket=latex_bracket, + string_quotes=string_quotes, **kwds) + + self._M = FreeModule(R, len(index_set)) + + # Transform the values in the structure coefficients to elements + def to_vector(tuples): + vec = [R.zero()]*len(index_set) + for k,c in tuples: + vec[self._index_to_pos[k]] = c + vec = self._M(vec) + vec.set_immutable() + return vec + self._s_coeff = {(self._index_to_pos[k[0]], self._index_to_pos[k[1]]): + to_vector(s_coeff[k]) + for k in s_coeff.keys()} + + # For compatibility with CombinatorialFreeModuleElement + _repr_term = IndexedGenerators._repr_generator + _latex_term = IndexedGenerators._latex_generator + + def structure_coefficients(self, include_zeros=False): + """ + Return the dictonary of structure coefficients of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 'x,y,z', {('x','y'): {'x':1}}) + sage: L.structure_coefficients() + Finite family {('x', 'y'): x} + sage: S = L.structure_coefficients(True); S + Finite family {('x', 'y'): x, ('x', 'z'): 0, ('y', 'z'): 0} + sage: S['x','z'].parent() is L + True + """ + if not include_zeros: + pos_to_index = dict(enumerate(self._indices)) + return Family({(pos_to_index[k[0]], pos_to_index[k[1]]): + self.element_class(self, self._s_coeff[k]) + for k in self._s_coeff}) + ret = {} + zero = self._M.zero() + for i,x in enumerate(self._indices): + for j, y in enumerate(self._indices[i+1:]): + elt = self._s_coeff.get((i, j+i+1), zero) + ret[x,y] = self.element_class(self, elt) # +i+1 for offset + return Family(ret) + + def dimension(self): + """ + Return the dimension of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, 'x,y', {('x','y'):{'x':1}}) + sage: L.dimension() + 2 + """ + return self.basis().cardinality() + + def module(self, sparse=True): + """ + Return ``self`` as a free module. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'z':1}}) + sage: L.module() + Sparse vector space of dimension 3 over Rational Field + """ + return FreeModule(self.base_ring(), self.dimension(), sparse=sparse) + + @cached_method + def zero(self): + """ + Return the element `0` in ``self``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: L.zero() + 0 + """ + return self.element_class(self, self._M.zero()) + + def monomial(self, k): + """ + Return the monomial indexed by ``k``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: L.monomial('x') + x + """ + return self.element_class(self, self._M.basis()[self._index_to_pos[k]]) + + def from_vector(self, v): + """ + Return an element of ``self`` from the vector ``v``. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: L.from_vector([1, 2, -2]) + x + 2*y - 2*z + """ + return self.element_class(self, self._M(v)) + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.three_dimensional(QQ, 4, 1, -1, 2) + sage: L.some_elements() + [X, Y, Z, X + Y + Z] + """ + return list(self.basis()) + [self.sum(self.basis())] + + class Element(StructureCoefficientsElement): + def _sorted_items_for_printing(self): + """ + Return a list of pairs ``(k, c)`` used in printing. + + .. WARNING:: + + The internal representation order is fixed, whereas this + depends on ``"sorting_key"`` print option as it is used + only for printing. + + EXAMPLES:: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'z':1}}) + sage: elt = x + y/2 - z; elt + x + 1/2*y - z + sage: elt._sorted_items_for_printing() + [('x', 1), ('y', 1/2), ('z', -1)] + sage: key = {'x': 2, 'y': 1, 'z': 0} + sage: L.print_options(sorting_key=key.__getitem__) + sage: elt._sorted_items_for_printing() + [('z', -1), ('y', 1/2), ('x', 1)] + sage: elt + -z + 1/2*y + x + """ + print_options = self.parent().print_options() + pos_to_index = dict(enumerate(self.parent()._indices)) + v = [(pos_to_index[k], c) for k, c in iteritems(self.value)] + 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 + diff --git a/src/sage/algebras/lie_algebras/virasoro.py b/src/sage/algebras/lie_algebras/virasoro.py new file mode 100644 index 00000000000..24e2e4090df --- /dev/null +++ b/src/sage/algebras/lie_algebras/virasoro.py @@ -0,0 +1,474 @@ +""" +Virasoro Algebra and Related 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.categories.lie_algebras import LieAlgebras +from sage.rings.all import ZZ +from sage.sets.family import Family +from sage.sets.set import Set +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.structure.indexed_generators import IndexedGenerators +from sage.algebras.lie_algebras.lie_algebra_element import LieAlgebraElement +from sage.algebras.lie_algebras.lie_algebra import (InfinitelyGeneratedLieAlgebra, + FinitelyGeneratedLieAlgebra) + +class LieAlgebraRegularVectorFields(InfinitelyGeneratedLieAlgebra, IndexedGenerators): + r""" + The Lie algebra of regular vector fields on `\CC^{\times}`. + + This is the Lie algebra with basis `\{d_i\}_{i \in \ZZ}` and subject + to the relations + + .. MATH:: + + [d_i, d_j] = (j - i) d_{i+j}. + + This is also known as the Witt (Lie) algebra. + + REFERENCES: + + - :wikipedia:`Witt_algebra` + + .. SEEALSO:: + + :class:`WittLieAlgebra_charp` + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: TestSuite(L).run() + """ + cat = LieAlgebras(R).WithBasis() + InfinitelyGeneratedLieAlgebra.__init__(self, R, index_set=ZZ, category=cat) + IndexedGenerators.__init__(self, ZZ, prefix='d', bracket='[') + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.regular_vector_fields(QQ) + The Lie algebra of regular vector fields over Rational Field + """ + return "The Lie algebra of regular vector fields over {}".format(self.base_ring()) + + # For compatibility with CombinatorialFreeModuleElement + _repr_term = IndexedGenerators._repr_generator + _latex_term = IndexedGenerators._latex_generator + + @cached_method + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: L.lie_algebra_generators() + Lazy family (generator map(i))_{i in Integer Ring} + """ + return Family(self._indices, self.monomial, name='generator map') + + def bracket_on_basis(self, i, j): + """ + Return the bracket of basis elements indexed by ``x`` and ``y`` + where ``x < y``. + + (This particular implementation actually does not require + ``x < y``.) + + EXAMPLES:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: L.bracket_on_basis(2, -2) + -4*d[0] + sage: L.bracket_on_basis(2, 4) + 2*d[6] + sage: L.bracket_on_basis(4, 4) + 0 + """ + return self.term(i + j, j - i) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: L.an_element() + d[-1] + d[0] - 3*d[1] + """ + return self.monomial(0) - 3*self.monomial(1) + self.monomial(-1) + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: L.some_elements() + [d[0], d[2], d[-2], d[-1] + d[0] - 3*d[1]] + """ + return [self.monomial(0), self.monomial(2), self.monomial(-2), self.an_element()] + + Element = LieAlgebraElement + +class WittLieAlgebra_charp(FinitelyGeneratedLieAlgebra, IndexedGenerators): + r""" + The `p`-Witt Lie algebra over a ring `R` in which + `p \cdot 1_R = 0`. + + Let `R` be a ring and `p` be a positive integer such that + `p \cdot 1_R = 0`. The `p`-Witt Lie algebra over `R` is + the Lie algebra with basis `\{d_0, d_1, \ldots, d_{p-1}\}` + and subject to the relations + + .. MATH:: + + [d_i, d_j] = (j - i) d_{i+j}, + + where the `i+j` on the right hand side is identified with its + remainder modulo `p`. + + .. SEEALSO:: + + :class:`LieAlgebraRegularVectorFields` + """ + def __init__(self, R, p): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.pwitt(GF(5), 5); L + The 5-Witt Lie algebra over Finite Field of size 5 + sage: TestSuite(L).run() + sage: L = lie_algebras.pwitt(Zmod(6), 6) + sage: TestSuite(L).run() # not tested -- universal envelope doesn't work + sage: L._test_jacobi_identity() + """ + cat = LieAlgebras(R).FiniteDimensional().WithBasis() + FinitelyGeneratedLieAlgebra.__init__(self, R, index_set=range(p), category=cat) + IndexedGenerators.__init__(self, range(p), prefix='d', bracket='[') + self._p = p + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.pwitt(Zmod(5), 5) + The 5-Witt Lie algebra over Ring of integers modulo 5 + sage: lie_algebras.pwitt(Zmod(5), 15) + The 15-Witt Lie algebra over Ring of integers modulo 5 + """ + return "The {}-Witt Lie algebra over {}".format(self._p, self.base_ring()) + + # For compatibility with CombinatorialFreeModuleElement + _repr_term = IndexedGenerators._repr_generator + _latex_term = IndexedGenerators._latex_generator + + @cached_method + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L = lie_algebras.pwitt(Zmod(5), 5) + sage: L.lie_algebra_generators() + Finite family {0: d[0], 1: d[1], 2: d[2], 3: d[3], 4: d[4]} + """ + return Family(self._indices, self.monomial, name='generator map') + + def bracket_on_basis(self, i, j): + """ + Return the bracket of basis elements indexed by ``x`` and ``y`` + where ``x < y``. + + (This particular implementation actually does not require + ``x < y``.) + + EXAMPLES:: + + sage: L = lie_algebras.pwitt(Zmod(5), 5) + sage: L.bracket_on_basis(2, 3) + d[0] + sage: L.bracket_on_basis(3, 2) + 4*d[0] + sage: L.bracket_on_basis(2, 2) + 0 + sage: L.bracket_on_basis(1, 3) + 2*d[4] + """ + return self.term((i + j) % self._p, j - i) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.pwitt(Zmod(5), 5) + sage: L.an_element() + d[0] + 2*d[1] + d[4] + """ + return self.monomial(0) - 3*self.monomial(1 % self._p) + self.monomial((-1) % self._p) + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.pwitt(Zmod(5), 5) + sage: L.some_elements() + [d[0], d[2], d[3], d[0] + 2*d[1] + d[4]] + """ + return [self.monomial(0), self.monomial(2 % self._p), + self.monomial((-2) % self._p), + self.an_element()] + + Element = LieAlgebraElement + +def _basis_key(x): + """ + Helper function that generates a key for the basis elements + of the Virasoro algebra. + + EXAMPLES:: + + sage: from sage.algebras.lie_algebras.virasoro import _basis_key + sage: _basis_key('c') + +Infinity + sage: _basis_key(2) + 2 + """ + if x == 'c': + from sage.rings.infinity import infinity + return infinity + return x + +class VirasoroAlgebra(InfinitelyGeneratedLieAlgebra, IndexedGenerators): + r""" + The Virasoro algebra. + + This is the Lie algebra with basis `\{d_i\}_{i \in \ZZ} \cup \{c\}` + and subject to the relations + + .. MATH:: + + [d_i, d_j] = (j - i) d_{i+j} + \frac{1}{12}(j^3 - j) \delta_{i,-j} c + + and + + .. MATH:: + + [d_i, c] = 0. + + (Here, it is assumed that the base ring `R` has `2` invertible.) + + This is the universal central extension `\widetilde{\mathfrak{d}}` of + the Lie algebra `\mathfrak{d}` of + :class:`regular vector fields ` + on `\CC^{\times}`. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + + REFERENCES: + + - :wikipedia:`Virasoro_algebra` + """ + def __init__(self, R): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: TestSuite(d).run() + """ + cat = LieAlgebras(R).WithBasis() + InfinitelyGeneratedLieAlgebra.__init__(self, R, index_set=ZZ, category=cat) + IndexedGenerators.__init__(self, ZZ, prefix='d', bracket='[', + sorting_key=_basis_key) + + def _repr_term(self, m): + """ + Return a string representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d._repr_term('c') + 'c' + sage: d._repr_term(2) + 'd[2]' + """ + if isinstance(m, str): + return m + return IndexedGenerators._repr_generator(self, m) + + def _latex_term(self, m): + r""" + Return a `\LaTeX` representation of the term indexed by ``m``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d._latex_term('c') + 'c' + sage: d._latex_term(2) + 'd_{2}' + sage: d._latex_term(-13) + 'd_{-13}' + """ + if isinstance(m, str): + return m + return IndexedGenerators._latex_generator(self, m) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: lie_algebras.VirasoroAlgebra(QQ) + The Virasoro algebra over Rational Field + """ + return "The Virasoro algebra over {}".format(self.base_ring()) + + @cached_method + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d.lie_algebra_generators() + Lazy family (generator map(i))_{i in Integer Ring} + """ + return Family(self._indices, self.monomial, name='generator map') + + @cached_method + def basis(self): + """ + Return a basis of ``self``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: B = d.basis(); B + Lazy family (basis map(i))_{i in Disjoint union of + Family ({'c'}, Integer Ring)} + sage: B['c'] + c + sage: B[3] + d[3] + sage: B[-15] + d[-15] + """ + I = DisjointUnionEnumeratedSets([Set(['c']), ZZ]) + return Family(I, self.monomial, name='basis map') + + def d(self, i): + """ + Return the element `d_i` in ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.VirasoroAlgebra(QQ) + sage: L.d(2) + d[2] + """ + return self.monomial(i) + + def c(self): + """ + The central element `c` in ``self``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d.c() + c + """ + return self.monomial('c') + + def bracket_on_basis(self, i, j): + """ + Return the bracket of basis elements indexed by ``x`` and ``y`` + where ``x < y``. + + (This particular implementation actually does not require + ``x < y``.) + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d.bracket_on_basis('c', 2) + 0 + sage: d.bracket_on_basis(2, -2) + -4*d[0] - 1/2*c + """ + if i == 'c' or j == 'c': + return self.zero() + ret = self._from_dict({i + j: j-i}) + if i == -j: + ret += (j ** 3 - j) / 12 * self.c() + return ret + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d.an_element() + d[-1] + d[0] - 1/2*d[1] + c + """ + d = self.monomial + return d(0) - self.base_ring().an_element()*d(1) + d(-1) + d('c') + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: d = lie_algebras.VirasoroAlgebra(QQ) + sage: d.some_elements() + [d[0], d[2], d[-2], c, d[-1] + d[0] - 1/2*d[1] + c] + """ + d = self.monomial + return [d(0), d(2), d(-2), d('c'), self.an_element()] + + Element = LieAlgebraElement + diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 262409e1a15..1f47c9247ac 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -37,6 +37,11 @@ import sage.rings.fast_arith as fast_arith prime_range = fast_arith.prime_range +from sage.misc.lazy_import import lazy_import +lazy_import('sage.arith.all', 'lcm', deprecation=22630) +lazy_import('sage.arith.all', 'lcm', '__LCM_sequence', deprecation=22630) +lazy_import('sage.arith.all', 'lcm', 'LCM', deprecation=22630) + ################################################################## # Elementary Arithmetic @@ -44,11 +49,8 @@ def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, use_digits=None, height_bound=None, proof=False): """ - Returns a polynomial of degree at most `degree` which is - approximately satisfied by the number `z`. Note that the returned - polynomial need not be irreducible, and indeed usually won't be if - `z` is a good approximation to an algebraic number of degree less - than `degree`. + Returns an irreducible polynomial of degree at most `degree` which + is approximately satisfied by the number `z`. You can specify the number of known bits or digits of `z` with ``known_bits=k`` or ``known_digits=k``. PARI is then told to @@ -96,12 +98,8 @@ def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, use_dig sage: z = (1/2)*(1 + RDF(sqrt(3)) *CC.0); z 0.500000000000000 + 0.866025403784439*I - sage: p = algdep(z, 6); p - x^3 + 1 - sage: p.factor() - (x + 1) * (x^2 - x + 1) - sage: z^2 - z + 1 # abs tol 2e-16 - 0.000000000000000 + sage: algdep(z, 6) + x^2 - x + 1 This example involves a `p`-adic number:: @@ -168,18 +166,28 @@ def algdep(z, degree, known_bits=None, use_bits=None, known_digits=None, use_dig sage: algdep(complex("1+2j"), 4) x^2 - 2*x + 5 + + We get an irreducible polynomial even if PARI returns a reducible + one:: + + sage: z = CDF(1, RR(3).sqrt())/2 + sage: pari(z).algdep(5) + x^5 + x^2 + sage: algdep(z, 5) + x^2 - x + 1 """ if proof and not height_bound: raise ValueError("height_bound must be given for proof=True") - x = ZZ['x'].gen() + R = ZZ['x'] + x = R.gen() z = py_scalar_to_element(z) if isinstance(z, Integer): if height_bound and abs(z) >= height_bound: return None - return x - ZZ(z) + return x - z degree = ZZ(degree) @@ -244,7 +252,9 @@ def norm(v): y = pari(z) f = y.algdep(degree) - return x.parent()(f) + # f might be reducible. Find the best fitting irreducible factor + factors = [p for p, e in R(f).factor()] + return min(factors, key=lambda f: abs(f(z))) algebraic_dependency = algdep @@ -1624,11 +1634,6 @@ def __GCD_sequence(v, **kwargs): return g return g -from sage.misc.lazy_import import lazy_import -lazy_import('sage.arith.functions', 'lcm', deprecation=22630) -lazy_import('sage.arith.functions', 'lcm', '__LCM_sequence', deprecation=22630) -lazy_import('sage.arith.functions', 'lcm', 'LCM', deprecation=22630) - def xlcm(m, n): r""" Extended lcm function: given two positive integers `m,n`, returns diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 5fedf2e850b..e0ae5df1739 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1259,26 +1259,48 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): ################################################################### # Laplace transform ################################################################### -def laplace(ex, t, s): +def laplace(ex, t, s, algorithm='maxima'): r""" - Attempts to compute and return the Laplace transform of - ``self`` with respect to the variable `t` and - transform parameter `s`. If this function cannot find a - solution, a formal function is returned. - - The function that is returned may be be viewed as a function of - `s`. + Return the Laplace transform with respect to the variable `t` and + transform parameter `s`, if possible. + + If this function cannot find a solution, a formal function is returned. + The function that is returned may be viewed as a function of `s`. DEFINITION: - The Laplace transform of a function `f(t)`, - defined for all real numbers `t \geq 0`, is the function - `F(s)` defined by + The Laplace transform of a function `f(t)`, defined for all real numbers + `t \geq 0`, is the function `F(s)` defined by .. MATH:: F(s) = \int_{0}^{\infty} e^{-st} f(t) dt. + INPUT: + + - ``ex`` - a symbolic expression + + - ``t`` - independent variable + + - ``s`` - transform parameter + + - ``algorithm`` - (default: ``'maxima'``) one of + + - ``'maxima'`` - use Maxima (the default) + + - ``'sympy'`` - use SymPy + + - ``'giac'`` - use Giac + + NOTES: + + - The ``'sympy'`` algorithm returns the tuple (`F`, `a`, ``cond``) where `F` is the Laplace + transform of `f(t)`, `Re(s)>a` is the half-plane of convergence, and cond + are auxiliary convergence conditions. + + .. SEEALSO:: + :func:`inverse_laplace` + EXAMPLES: We compute a few Laplace transforms:: @@ -1300,8 +1322,6 @@ def laplace(ex, t, s): sage: g.laplace(x, s) s*laplace(f(x), x, s) - f(0) - EXAMPLES: - A BATTLE BETWEEN the X-women and the Y-men (by David Joyner): Solve @@ -1354,27 +1374,103 @@ def laplace(ex, t, s): sage: inverse_laplace(L, s, t) t*e^(a + 2*t)*sin(t) - Unable to compute solution:: + Unable to compute solution with Maxima:: + + sage: laplace(heaviside(t-1), t, s) + laplace(heaviside(t - 1), t, s) + + Heaviside step function can be handled with different interfaces. + Try with giac:: + + sage: laplace(heaviside(t-1), t, s, algorithm='giac') + e^(-s)/s + + Try with SymPy:: + + sage: laplace(heaviside(t-1), t, s, algorithm='sympy') + (e^(-s)/s, 0, True) + + TESTS:: + + Testing Giac:: + + sage: var('t, s') + (t, s) + 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:: + + 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) + + Testing SymPy:: + + sage: laplace(t^n, t, s, algorithm='sympy') + (s^(-n)*gamma(n + 1)/s, 0, -re(n) < 1) + + Testing Maxima:: + + sage: laplace(t^n, t, s, algorithm='maxima') + s^(-n - 1)*gamma(n + 1) + + Testing expression that is not parsed from SymPy to Sage:: - sage: laplace(1/s, s, t) - laplace(1/s, s, t) + sage: laplace(cos(t^2), t, s, algorithm='sympy') + Traceback (most recent call last): + ... + AttributeError: Unable to convert SymPy result (=sqrt(pi)*(sqrt(2)*sin(s**2/4)*fresnelc(sqrt(2)*s/(2*sqrt(pi))) - + sqrt(2)*cos(s**2/4)*fresnels(sqrt(2)*s/(2*sqrt(pi))) + cos(s**2/4 + pi/4))/2) into Sage """ if not isinstance(ex, (Expression, Function)): ex = SR(ex) - return ex.parent()(ex._maxima_().laplace(var(t), var(s))) -def inverse_laplace(ex, t, s): - r""" - Attempts to compute the inverse Laplace transform of - ``self`` with respect to the variable `t` and - transform parameter `s`. If this function cannot find a - solution, a formal function is returned. + if algorithm == 'maxima': + return ex.parent()(ex._maxima_().laplace(var(t), var(s))) - The function that is returned may be be viewed as a function of - `s`. + elif algorithm == 'sympy': + ex_sy, t, s = [expr._sympy_() for expr in (ex, t, s)] + from sympy import laplace_transform + result = laplace_transform(ex_sy, t, s) + if isinstance(result, tuple): + try: + (result, a, cond) = result + return result._sage_(), a, cond + except AttributeError: + raise AttributeError("Unable to convert SymPy result (={}) into" + " Sage".format(result)) + elif 'LaplaceTransform' in format(result): + return dummy_laplace(ex, t, s) + else: + return result - DEFINITION: The inverse Laplace transform of a function - `F(s)`, is the function `f(t)` defined by + elif algorithm == 'giac': + from sage.interfaces.giac import giac + try: + result = giac.laplace(ex, t, s) + except TypeError: + raise ValueError("Giac cannot make sense of: %s" % ex_gi) + return result.sage() + + else: + raise ValueError("Unknown algorithm: %s" % algorithm) + +def inverse_laplace(ex, s, t, algorithm='maxima'): + r""" + Return the inverse Laplace transform with respect to the variable `t` and + transform parameter `s`, if possible. + + If this function cannot find a solution, a formal function is returned. + The function that is returned may be viewed as a function of `t`. + + DEFINITION: + + The inverse Laplace transform of a function `F(s)` is the function + `f(t)`, defined by .. MATH:: @@ -1383,6 +1479,25 @@ def inverse_laplace(ex, t, s): where `\gamma` is chosen so that the contour path of integration is in the region of convergence of `F(s)`. + INPUT: + + - ``ex`` - a symbolic expression + + - ``s`` - transform parameter + + - ``t`` - independent variable + + - ``algorithm`` - (default: ``'maxima'``) one of + + - ``'maxima'`` - use Maxima (the default) + + - ``'sympy'`` - use SymPy + + - ``'giac'`` - use Giac + + .. SEEALSO:: + :func:`laplace` + EXAMPLES:: sage: var('w, m') @@ -1401,15 +1516,111 @@ def inverse_laplace(ex, t, s): sage: inverse_laplace(1/(s^3+1), s, t) 1/3*(sqrt(3)*sin(1/2*sqrt(3)*t) - cos(1/2*sqrt(3)*t))*e^(1/2*t) + 1/3*e^(-t) - No explicit inverse Laplace transform, so one is returned formally - as a function ``ilt``:: + No explicit inverse Laplace transform, so one is returned formally a + function ``ilt``:: sage: inverse_laplace(cos(s), s, t) ilt(cos(s), s, t) + + Transform an expression involving a time-shift, via SymPy:: + + sage: inverse_laplace(1/s^2*exp(-s), s, t, algorithm='sympy') + -(log(e^(-t)) + 1)*heaviside(t - 1) + + The same instance with Giac:: + + sage: inverse_laplace(1/s^2*exp(-s), s, t, algorithm='giac') + (t - 1)*heaviside(t - 1) + + Transform a rational expression:: + + sage: inverse_laplace((2*s^2*exp(-2*s) - exp(-s))/(s^3+1), s, t, algorithm='giac') + -1/3*(sqrt(3)*e^(1/2*t - 1/2)*sin(1/2*sqrt(3)*(t - 1)) - cos(1/2*sqrt(3)*(t - 1))*e^(1/2*t - 1/2) + + e^(-t + 1))*heaviside(t - 1) + 2/3*(2*cos(1/2*sqrt(3)*(t - 2))*e^(1/2*t - 1) + e^(-t + 2))*heaviside(t - 2) + + Dirac delta function can also be handled:: + + sage: inverse_laplace(1, s, t, algorithm='giac') + dirac_delta(t) + + TESTS:: + + Testing unevaluated expression from Maxima:: + + sage: var('t, s') + (t, s) + sage: inverse_laplace(exp(-s)/s, s, t) + ilt(e^(-s)/s, s, t) + + Testing Giac:: + + sage: inverse_laplace(exp(-s)/s, s, t, algorithm='giac') + heaviside(t - 1) + + Testing SymPy:: + + sage: inverse_laplace(exp(-s)/s, s, t, algorithm='sympy') + heaviside(t - 1) + + Testing unevaluated expression from Giac:: + + 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]) + + Try with Maxima:: + + sage: inverse_laplace(1/s^n, s, t, algorithm='maxima') + ilt(1/(s^n), s, t) + + Try with SymPy:: + + sage: inverse_laplace(1/s^n, s, t, algorithm='sympy') + t^(n - 1)*heaviside(t)/gamma(n) + + Testing unevaluated expression from SymPy:: + + sage: inverse_laplace(cos(s), s, t, algorithm='sympy') + ilt(cos(s), t, s) + + Testing unevaluated expression from 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]) """ if not isinstance(ex, Expression): ex = SR(ex) - return ex.parent()(ex._maxima_().ilt(var(t), var(s))) + + if algorithm == 'maxima': + return ex.parent()(ex._maxima_().ilt(var(s), var(t))) + + elif algorithm == 'sympy': + ex_sy, s, t = [expr._sympy_() for expr in (ex, s, t)] + from sympy import inverse_laplace_transform + result = inverse_laplace_transform(ex_sy, s, t) + try: + return result._sage_() + except AttributeError: + if 'InverseLaplaceTransform' in format(result): + return dummy_inverse_laplace(ex, t, s) + else: + raise AttributeError("Unable to convert SymPy result (={}) into" + " Sage".format(result)) + + elif algorithm == 'giac': + from sage.interfaces.giac import giac + try: + result = giac.invlaplace(ex, s, t) + except TypeError: + raise ValueError("Giac cannot make sense of: %s" % ex) + return result.sage() + + else: + raise ValueError("Unknown algorithm: %s" % algorithm) ################################################################### # symbolic evaluation "at" a point @@ -1776,15 +1987,15 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): 2 sage: var('my_new_var').full_simplify() my_new_var - + ODE solution constants are treated differently (:trac:`16007`):: - + sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms sage: sefms('%k1*x + %k2*y + %c') _K1*x + _K2*y + _C Check that some hypothetical variables don't end up as special constants (:trac:`6882`):: - + sage: from sage.calculus.calculus import symbolic_expression_from_maxima_string as sefms sage: sefms('%i')^2 -1 @@ -1848,7 +2059,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): #we apply the square-bracket replacing patterns repeatedly #to ensure that nested brackets get handled (from inside to out) while True: - olds = s + olds = s s = polylog_ex.sub('polylog(\\1,', s) s = maxima_polygamma.sub('psi(\g<1>,', s) # this replaces psi[n](foo) with psi(n,foo), ensuring that derivatives of the digamma function are parsed properly below if s == olds: break @@ -2108,4 +2319,3 @@ def _find_Mvar(name): make_float = lambda x: SR(RealDoubleElement(x)), make_var = _find_Mvar, make_function = _find_func) - diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index 81bf572bb99..bc306d29050 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -72,6 +72,7 @@ from .coalgebras import Coalgebras from .bialgebras import Bialgebras from .hopf_algebras import HopfAlgebras +from .lie_algebras import LieAlgebras # specific algebras from .monoid_algebras import MonoidAlgebras diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 2c1b583fdf6..b231350c8da 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2589,13 +2589,14 @@ def category_sample(): sage: sorted(category_sample(), key=str) [Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, + Category of Lie algebras over Rational Field, Category of additive magmas, ..., Category of fields, ..., Category of graded hopf algebras with basis over Rational Field, ..., Category of modular abelian varieties over Rational Field, ..., Category of simplicial complexes, ..., Category of vector spaces over Rational Field, ..., - Category of weyl groups,... + Category of weyl groups, ... """ import sage.categories.all abstract_classes_for_categories = [Category] diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py new file mode 100644 index 00000000000..b815aeb9112 --- /dev/null +++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py @@ -0,0 +1,406 @@ +r""" +Examples of a finite dimensional Lie algebra with basis +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** +from six import iteritems + +from sage.misc.cachefunc import cached_method +from sage.sets.family import Family +from sage.categories.all import LieAlgebras +from sage.modules.free_module import FreeModule +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.examples.lie_algebras import LieAlgebraFromAssociative as BaseExample + +class AbelianLieAlgebra(Parent, UniqueRepresentation): + r""" + An example of a finite dimensional Lie algebra with basis: + the abelian Lie algebra. + + Let `R` be a commutative ring, and `M` an `R`-module. The + *abelian Lie algebra* on `M` is the `R`-Lie algebra + obtained by endowing `M` with the trivial Lie bracket + (`[a, b] = 0` for all `a, b \in M`). + + This class illustrates a minimal implementation of a finite dimensional + Lie algebra with basis. + + INPUT: + + - ``R`` -- base ring + + - ``n`` -- (optional) a nonnegative integer (default: ``None``) + + - ``M`` -- an `R`-module (default: the free `R`-module of + rank ``n``) to serve as the ground space for the Lie algebra + + - ``ambient`` -- (optional) a Lie algebra; if this is set, + then the resulting Lie algebra is declared a Lie subalgebra + of ``ambient`` + + OUTPUT: + + The abelian Lie algebra on `M`. + """ + @staticmethod + def __classcall_private__(cls, R, n=None, M=None, ambient=None): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: from sage.categories.examples.finite_dimensional_lie_algebras_with_basis import AbelianLieAlgebra + sage: A1 = AbelianLieAlgebra(QQ, n=3) + sage: A2 = AbelianLieAlgebra(QQ, M=FreeModule(QQ, 3)) + sage: A3 = AbelianLieAlgebra(QQ, 3, FreeModule(QQ, 3)) + sage: A1 is A2 and A2 is A3 + True + + sage: A1 = AbelianLieAlgebra(QQ, 2) + sage: A2 = AbelianLieAlgebra(ZZ, 2) + sage: A1 is A2 + False + + sage: A1 = AbelianLieAlgebra(QQ, 0) + sage: A2 = AbelianLieAlgebra(QQ, 1) + sage: A1 is A2 + False + """ + if M is None: + M = FreeModule(R, n) + else: + M = M.change_ring(R) + n = M.dimension() + return super(AbelianLieAlgebra, cls).__classcall__(cls, R, n=n, M=M, + ambient=ambient) + + def __init__(self, R, n=None, M=None, ambient=None): + """ + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: TestSuite(L).run() + """ + self._M = M + cat = LieAlgebras(R).FiniteDimensional().WithBasis() + if ambient is None: + ambient = self + else: + cat = cat.Subobjects() + self._ambient = ambient + Parent.__init__(self, base=R, category=cat) + + def _repr_(self): + """ + EXAMPLES:: + + sage: LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field + """ + ret = "An example of a finite dimensional Lie algebra with basis:" \ + " the {}-dimensional abelian Lie algebra over {}".format( + self.dimension(), self.base_ring()) + B = self._M.basis_matrix() + if not B.is_one(): + ret += " with basis matrix:\n{!r}".format(B) + return ret + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L(0) + (0, 0, 0) + sage: M = FreeModule(ZZ, 3) + sage: L(M([1, -2, 2])) + (1, -2, 2) + sage: a,b,c = L.lie_algebra_generators() + sage: X = L.subalgebra([a+b, 2*a+c]) + sage: x,y = X.basis() + sage: L(x) + (1, 0, 1/2) + sage: L(x+y) + (1, 1, 0) + """ + if isinstance(x, AbelianLieAlgebra.Element): + x = x.value + return self.element_class(self, self._M(x)) + + @cached_method + def zero(self): + """ + Return the zero element. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.zero() + (0, 0, 0) + """ + return self.element_class(self, self._M.zero()) + + def basis_matrix(self): + """ + Return the basis matrix of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.basis_matrix() + [1 0 0] + [0 1 0] + [0 0 1] + """ + return self._M.basis_matrix() + + def ambient(self): + """ + Return the ambient Lie algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: S = L.subalgebra([2*a+b, b + c]) + sage: S.ambient() == L + True + """ + return self._ambient + + def subalgebra(self, gens): + """ + Return the Lie subalgebra of ``self`` generated by the + elements of the iterable ``gens``. + + This currently requires the ground ring `R` to be a field. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.subalgebra([2*a+b, b + c]) + An example of a finite dimensional Lie algebra with basis: + the 2-dimensional abelian Lie algebra over Rational Field with + basis matrix: + [ 1 0 -1/2] + [ 0 1 1] + """ + N = self._M.subspace([g.value for g in gens]) + return AbelianLieAlgebra(self.base_ring(), M=N, ambient=self._ambient) + + ideal = subalgebra + + def is_ideal(self, A): + """ + Return if ``self`` is an ideal of the ambient space ``A``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.is_ideal(L) + True + sage: S1 = L.subalgebra([2*a+b, b + c]) + sage: S1.is_ideal(L) + True + sage: S2 = L.subalgebra([2*a+b]) + sage: S2.is_ideal(S1) + True + sage: S1.is_ideal(S2) + False + """ + if not isinstance(A, AbelianLieAlgebra): + return super(AbelianLieAlgebra, self).is_ideal(A) + if A == self or A == self._ambient: + return True + if self._ambient != A._ambient: + return False + return self._M.is_submodule(A._M) + + def basis(self): + """ + Return the basis of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.basis() + Finite family {0: (1, 0, 0), 1: (0, 1, 0), 2: (0, 0, 1)} + """ + d = {i: self.element_class(self, b) + for i,b in enumerate(self._M.basis())} + return Family(d) + + lie_algebra_generators = basis + + def gens(self): + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.gens() + ((1, 0, 0), (0, 1, 0), (0, 0, 1)) + """ + return tuple(self._M.basis()) + + def module(self): + """ + Return an `R`-module which is isomorphic to the + underlying `R`-module of ``self``. + + See + :meth:`sage.categories.lie_algebras.LieAlgebras.module` for + an explanation. + + In this particular example, this returns the module `M` + that was used to construct ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.module() + Vector space of dimension 3 over Rational Field + + sage: a, b, c = L.lie_algebra_generators() + sage: S = L.subalgebra([2*a+b, b + c]) + sage: S.module() + Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [ 1 0 -1/2] + [ 0 1 1] + """ + return self._M + + def from_vector(self, v): + """ + Return the element of ``self`` corresponding to the + vector ``v`` in ``self.module()``. + + Implement this if you implement :meth:`module`; see the + documentation of + :meth:`sage.categories.lie_algebras.LieAlgebras.module` + for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u + (1, 0, 0) + sage: parent(u) is L + True + """ + return self.element_class(self, self._M(v)) + + class Element(BaseExample.Element): + def __iter__(self): + """ + Iterate over ``self`` by returning pairs ``(i, c)`` where ``i`` + is the index of the basis element and ``c`` is the corresponding + coefficient. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 2*a - c + sage: list(elt) + [(0, 2), (2, -1)] + """ + zero = self.parent().base_ring().zero() + for i, c in iteritems(self.value): + if c != zero: + yield (i, c) + + def __getitem__(self, i): + """ + Redirect the ``__getitem__()`` to the wrapped element unless + ``i`` is a basis index. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 2*a + b - c + sage: elt[0] + 2 + sage: elt[2] + -1 + """ + return self.value.__getitem__(i) + + def _bracket_(self, y): + """ + Return the Lie bracket ``[self, y]``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: a.bracket(c) + (0, 0, 0) + sage: a.bracket(b).bracket(c) + (0, 0, 0) + """ + return self.parent().zero() + + def lift(self): + """ + Return the lift of ``self`` to the universal enveloping algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 2*a + 2*b + 3*c + sage: elt.lift() + 2*b0 + 2*b1 + 3*b2 + """ + UEA = self.parent().universal_enveloping_algebra() + gens = UEA.gens() + return UEA.sum(c * gens[i] for i, c in iteritems(self.value)) + + def to_vector(self): + """ + Return ``self`` as a vector in + ``self.parent().module()``. + + See the docstring of the latter method for the meaning + of this. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 2*a + 2*b + 3*c + sage: elt.to_vector() + (2, 2, 3) + """ + return self.value + + def monomial_coefficients(self, copy=True): + """ + Return the monomial coefficients of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 2*a + 2*b + 3*c + sage: elt.monomial_coefficients() + {0: 2, 1: 2, 2: 3} + """ + return self.value.monomial_coefficients(copy) + +Example = AbelianLieAlgebra + diff --git a/src/sage/categories/examples/lie_algebras.py b/src/sage/categories/examples/lie_algebras.py new file mode 100644 index 00000000000..695bd7c1b1e --- /dev/null +++ b/src/sage/categories/examples/lie_algebras.py @@ -0,0 +1,332 @@ +r""" +Examples of a Lie algebra +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +#from sage.misc.cachefunc import cached_method +from sage.sets.family import Family +from sage.categories.all import LieAlgebras +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper + +class LieAlgebraFromAssociative(Parent, UniqueRepresentation): + r""" + An example of a Lie algebra: a Lie algebra generated by + a set of elements of an associative algebra. + + This class illustrates a minimal implementation of a Lie algebra. + + Let `R` be a commutative ring, and `A` an associative + `R`-algebra. The Lie algebra `A` (sometimes denoted `A^-`) + is defined to be the `R`-module `A` with Lie bracket given by + the commutator in `A`: that is, `[a, b] := ab - ba` for all + `a, b \in A`. + + What this class implements is not precisely `A^-`, however; + it is the Lie subalgebra of `A^-` generated by the elements + of the iterable ``gens``. This specific implementation does not + provide a reasonable containment test (i.e., it does not allow + you to check if a given element `a` of `A^-` belongs to this + Lie subalgebra); it, however, allows computing inside it. + + INPUT: + + - ``gens`` -- a nonempty iterable consisting of elements of an + associative algebra `A` + + OUTPUT: + + The Lie subalgebra of `A^-` generated by the elements of + ``gens`` + + EXAMPLES: + + We create a model of `\mathfrak{sl}_2` using matrices:: + + sage: gens = [matrix([[0,1],[0,0]]), matrix([[0,0],[1,0]]), matrix([[1,0],[0,-1]])] + sage: for g in gens: + ....: g.set_immutable() + sage: L = LieAlgebras(QQ).example(gens) + sage: e,f,h = L.lie_algebra_generators() + sage: e.bracket(f) == h + True + sage: h.bracket(e) == 2*e + True + sage: h.bracket(f) == -2*f + True + """ + @staticmethod + def __classcall_private__(cls, gens): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: S3 = SymmetricGroupAlgebra(QQ, 3) + sage: L1 = LieAlgebras(QQ).example() + sage: gens = list(S3.algebra_generators()) + sage: L2 = LieAlgebras(QQ).example(gens) + sage: L1 is L2 + True + """ + return super(LieAlgebraFromAssociative, cls).__classcall__(cls, tuple(gens)) + + def __init__(self, gens): + """ + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: TestSuite(L).run() + """ + if not gens: + raise ValueError("need at least one generator") + self._gens = gens + self._A = gens[0].parent() + R = self._A.base_ring() + Parent.__init__(self, base=R, category=LieAlgebras(R)) + + def _repr_(self): + """ + EXAMPLES:: + + sage: LieAlgebras(QQ).example() + An example of a Lie algebra: the Lie algebra from the associative algebra + Symmetric group algebra of order 3 over Rational Field + generated by ([2, 1, 3], [2, 3, 1]) + """ + return "An example of a Lie algebra: the Lie algebra from the" \ + " associative algebra {} generated by {}".format( + self._A, self._gens) + + def _element_constructor_(self, value): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: S3 = SymmetricGroupAlgebra(ZZ, 3) + sage: gens = S3.algebra_generators() + sage: L = LieAlgebras(QQ).example() + sage: L(3*gens[0] + gens[1]) + 3*[2, 1, 3] + [2, 3, 1] + """ + return self.element_class(self, self._A(value)) + + def zero(self): + """ + Return the element 0. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: L.zero() + 0 + """ + return self.element_class(self, self._A.zero()) + + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: L.lie_algebra_generators() + Family ([2, 1, 3], [2, 3, 1]) + """ + return Family([self.element_class(self, g) for g in self._gens]) + + # TODO: refactor to use LieAlgebraElementWrapper once more of #14901 is added in + class Element(ElementWrapper): + """ + Wrap an element as a Lie algebra element. + """ + def __eq__(self, rhs): + """ + Check equality. + + This check is rather restrictive: ``self`` and ``rhs`` are only + revealed as equal if they are equal *and* have the same parent + (or both are zero). + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x == x + True + sage: x.bracket(y) == -y.bracket(x) + True + sage: x == y + False + sage: x.bracket(x) == L.zero() + True + sage: x.bracket(x) == 0 + True + """ + if not isinstance(rhs, LieAlgebraFromAssociative.Element): + return self.value == 0 and rhs == 0 + return self.parent() == rhs.parent() and self.value == rhs.value + + def __ne__(self, rhs): + """ + Check not-equals. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x != y + True + sage: x != 0 + True + sage: x != x + False + sage: x.bracket(y) != -y.bracket(x) + False + """ + return not self.__eq__(rhs) + + def __bool__(self): + """ + Check non-zero. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: bool(sum(L.lie_algebra_generators())) + True + sage: bool(L.zero()) + False + """ + return bool(self.value) + + __nonzero__ = __bool__ + + def _add_(self, rhs): + """ + Add ``self`` and ``rhs``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x + y + [2, 1, 3] + [2, 3, 1] + """ + return self.__class__(self.parent(), self.value + rhs.value) + + def _sub_(self, rhs): + """ + Subtract ``self`` and ``rhs``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x - y + [2, 1, 3] - [2, 3, 1] + """ + return self.__class__(self.parent(), self.value - rhs.value) + + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of a scalar on ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: 3 * x + 3*[2, 1, 3] + """ + # This was copied, but IDK if it still applies: + # With the current design, the coercion model does not have + # 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(): + # 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 + if self_on_left: + return self.__class__(self.parent(), self.value * scalar) + return self.__class__(self.parent(), scalar * self.value) + + def __div__(self, x, self_on_left=False): + """ + Division by coefficients. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: y / 4 + 1/4*[2, 3, 1] + """ + if self_on_left: + return self * (~x) + return (~x) * self + + def __neg__(self): + """ + Return the negation of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: -x + -[2, 1, 3] + """ + return self.__class__(self.parent(), -self.value) + + def __getitem__(self, i): + """ + Redirect the ``__getitem__()`` to the wrapped element. + + EXAMPLES:: + + sage: gens = [matrix([[0,1],[0,0]]), matrix([[0,0],[1,0]]), matrix([[1,0],[0,-1]])] + sage: for g in gens: + ....: g.set_immutable() + sage: L = LieAlgebras(QQ).example(gens) + sage: e,f,h = L.lie_algebra_generators() + sage: h[0,0] + 1 + sage: h[1,1] + -1 + sage: h[0,1] + 0 + """ + return self.value.__getitem__(i) + + def _bracket_(self, rhs): + """ + Return the Lie bracket ``[self, rhs]``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: elt = 2*x - y + sage: elt.bracket(elt) + 0 + sage: elt.bracket(x) + -[1, 3, 2] + [3, 2, 1] + sage: elt2 = x.bracket(y) + x + sage: elt.bracket(elt2) + -2*[2, 1, 3] + 4*[2, 3, 1] - 4*[3, 1, 2] + 2*[3, 2, 1] + """ + return self.__class__(self.parent(), self.value * rhs.value - rhs.value * self.value) + +Example = LieAlgebraFromAssociative + diff --git a/src/sage/categories/examples/lie_algebras_with_basis.py b/src/sage/categories/examples/lie_algebras_with_basis.py new file mode 100644 index 00000000000..6c122daa56e --- /dev/null +++ b/src/sage/categories/examples/lie_algebras_with_basis.py @@ -0,0 +1,192 @@ +r""" +Examples of a Lie algebra with basis +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +#from sage.misc.cachefunc import cached_method +from sage.sets.family import Family +from sage.categories.lie_algebras import LieAlgebras +from sage.categories.algebras import Algebras +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid +from sage.combinat.free_module import CombinatorialFreeModule + +class AbelianLieAlgebra(CombinatorialFreeModule): + r""" + An example of a Lie algebra: the abelian Lie algebra. + + This class illustrates a minimal implementation of a Lie algebra with + a distinguished basis. + """ + def __init__(self, R, gens): + """ + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: TestSuite(L).run() + """ + cat = LieAlgebras(R).WithBasis() + CombinatorialFreeModule.__init__(self, R, gens, category=cat) + + def _construct_UEA(self): + """ + Construct the universal enveloping algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L._construct_UEA() + Polynomial algebra with generators indexed by Partitions over Rational Field + """ + return IndexedPolynomialRing(self.base_ring(), self._indices) + + def _repr_(self): + """ + EXAMPLES:: + + sage: LieAlgebras(QQ).WithBasis().example() + An example of a Lie algebra: the abelian Lie algebra on the + generators indexed by Partitions over Rational Field + """ + return "An example of a Lie algebra: the abelian Lie algebra on the" \ + " generators indexed by {} over {}".format( + self.basis().keys(), self.base_ring()) + + def lie_algebra_generators(self): + """ + Return the generators of ``self`` as a Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L.lie_algebra_generators() + Lazy family (Term map from Partitions to + An example of a Lie algebra: the abelian Lie algebra on the + generators indexed by Partitions over Rational + Field(i))_{i in Partitions} + """ + return self.basis() + + def bracket_on_basis(self, x, y): + """ + Return the Lie bracket on basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L.bracket_on_basis(Partition([4,1]), Partition([2,2,1])) + 0 + """ + return self.zero() + + class Element(CombinatorialFreeModule.Element): + def lift(self): + """ + Return the lift of ``self`` to the universal enveloping algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: elt = L.an_element() + sage: elt.lift() + 3*P[F[2]] + 2*P[F[1]] + 2*P[F[]] + """ + UEA = self.parent().universal_enveloping_algebra() + I = UEA._indices + return UEA.sum_of_terms((I.gen(t), c) for t, c in self) + +Example = AbelianLieAlgebra + +############## + +class IndexedPolynomialRing(CombinatorialFreeModule): + """ + Polynomial ring whose generators are indexed by an arbitrary set. + + .. TODO:: + + Currently this is just used as the universal enveloping algebra + for the example of the abelian Lie algebra. This should be + factored out into a more complete class. + """ + def __init__(self, R, indices, **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: UEA = L.universal_enveloping_algebra() + sage: TestSuite(UEA).run() + """ + if 'category' not in kwds: + kwds['category'] = Algebras(R).WithBasis() + if 'prefix' not in kwds: + kwds['prefix'] = 'P' + # This is a workaround until IndexedFree(Abelian)Monoid elements compare properly + kwds['sorting_key'] = lambda x: x.to_word_list() + kwds['sorting_reverse'] = True + M = IndexedFreeAbelianMonoid(indices, bracket='') + CombinatorialFreeModule.__init__(self, R, M, **kwds) + + def _repr_(self): + """ + Return a string represenation of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L.universal_enveloping_algebra() + Polynomial algebra with generators indexed by Partitions over Rational Field + """ + return "Polynomial algebra with generators indexed by {} over {}".format( + self._indices._indices, self.base_ring()) + + def one_basis(self): + """ + Return the index of element `1`. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: UEA = L.universal_enveloping_algebra() + sage: UEA.one_basis() + 1 + sage: UEA.one_basis().parent() + Free abelian monoid indexed by Partitions + """ + return self._indices.one() + + def product_on_basis(self, x, y): + """ + Return the product of the monomials indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: UEA = L.universal_enveloping_algebra() + sage: I = UEA._indices + sage: UEA.product_on_basis(I.an_element(), I.an_element()) + P[F[]^4*F[1]^4*F[2]^6] + """ + return self.monomial(x*y) + + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: UEA = L.universal_enveloping_algebra() + sage: UEA.algebra_generators() + Lazy family (algebra generator map(i))_{i in Partitions} + """ + I = self._indices + return Family(I._indices, lambda x: self.monomial(I.gen(x)), + name="algebra generator map") + diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py new file mode 100644 index 00000000000..0b821bca1aa --- /dev/null +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -0,0 +1,808 @@ +r""" +Finite Dimensional Lie Algebras With Basis + +AUTHORS: + +- Travis Scrimshaw (07-15-2013): Initial implementation +""" + +#***************************************************************************** +# 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 __future__ import print_function + +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.lie_algebras import LieAlgebras +from sage.categories.subobjects import SubobjectsCategory +from sage.algebras.free_algebra import FreeAlgebra +from sage.sets.family import Family +from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector + +class FiniteDimensionalLieAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): + """ + Category of finite dimensional Lie algebras with a basis. + + .. TODO:: + + Many of these tests should use non-abelian Lie algebras and need to + be added after :trac:`16820`. + """ + _base_category_class_and_axiom = [LieAlgebras.FiniteDimensional, "WithBasis"] + + def example(self, n=3): + """ + Return an example of a finite dimensional Lie algebra with basis as per + :meth:`Category.example `. + + EXAMPLES:: + + sage: C = LieAlgebras(QQ).FiniteDimensional().WithBasis() + sage: C.example() + An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field + + Other dimensions can be specified as an optional argument:: + + sage: C.example(5) + An example of a finite dimensional Lie algebra with basis: + the 5-dimensional abelian Lie algebra over Rational Field + """ + from sage.categories.examples.finite_dimensional_lie_algebras_with_basis import Example + return Example(self.base_ring(), n) + + class ParentMethods: + @cached_method + def _construct_UEA(self): + """ + Construct the universal enveloping algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: UEA = L._construct_UEA(); UEA + Noncommutative Multivariate Polynomial Ring in b0, b1, b2 + over Rational Field, nc-relations: {} + sage: UEA.relations(add_commutative=True) + {b1*b0: b0*b1, b2*b0: b0*b2, b2*b1: b1*b2} + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'z':1}, ('y','z'):{'x':1}, ('z','x'):{'y':1}}) + sage: UEA = L._construct_UEA(); UEA + Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, + nc-relations: {...} + sage: sorted(UEA.relations().items(), key=str) + [(y*x, x*y - z), (z*x, x*z + y), (z*y, y*z - x)] + """ + # Create the UEA relations + # We need to get names for the basis elements, not just the generators + I = self._basis_ordering + try: + names = [str(x) for x in I] + F = FreeAlgebra(self.base_ring(), names) + except ValueError: + names = ['b{}'.format(i) for i in range(self.dimension())] + F = FreeAlgebra(self.base_ring(), names) + # ``F`` is the free algebra over the basis of ``self``. The + # universal enveloping algebra of ``self`` will be constructed + # as a quotient of ``F``. + d = F.gens_dict() + rels = {} + S = self.structure_coefficients(True) + get_var = lambda g: d[names[I.index(g)]] + # The function ``get_var`` sends an element of the basis of + # ``self`` to the corresponding element of ``F``. + for k in S.keys(): + g0 = get_var(k[0]) + g1 = get_var(k[1]) + if g0 < g1: + rels[g1*g0] = g0*g1 - F.sum(val*get_var(g) for g, val in S[k]) + else: + rels[g0*g1] = g1*g0 + F.sum(val*get_var(g) for g, val in S[k]) + return F.g_algebra(rels) + + @lazy_attribute + def _basis_ordering(self): + """ + Return the indices of the basis of ``self`` as a tuple in + a fixed order. + + Override this attribute to get a specific ordering of the basis. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L._basis_ordering + (0, 1, 2) + """ + return tuple(self.basis().keys()) + + def _dense_free_module(self, R=None): + """ + Return a dense free module associated to ``self`` over ``R``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L._dense_free_module() + Vector space of dimension 3 over Rational Field + """ + if R is None: + R = self.base_ring() + from sage.modules.free_module import FreeModule + return FreeModule(R, self.dimension()) + + module = _dense_free_module + + def from_vector(self, v): + """ + Return the element of ``self`` corresponding to the + vector ``v`` in ``self.module()``. + + Implement this if you implement :meth:`module`; see the + documentation of + :meth:`sage.categories.lie_algebras.LieAlgebras.module` + for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u + (1, 0, 0) + sage: parent(u) is L + True + """ + B = self.basis() + return self.sum(v[i] * B[k] for i,k in enumerate(self._basis_ordering) + if v[i] != 0) + + def killing_matrix(self, x, y): + r""" + Return the Killing matrix of ``x`` and ``y``, where ``x`` + and ``y`` are two elements of ``self``. + + The Killing matrix is defined as the matrix corresponding + to the action of + `\operatorname{ad}_x \circ \operatorname{ad}_y` in the + basis of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.lie_algebra_generators() + sage: L.killing_matrix(a, b) + [0 0 0] + [0 0 0] + [0 0 0] + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: L.killing_matrix(x, y) + [ 0 0] + [-1 0] + """ + return x.adjoint_matrix() * y.adjoint_matrix() + + def killing_form(self, x, y): + r""" + Return the Killing form on ``x`` and ``y``, where ``x`` + and ``y`` are two elements of ``self``. + + The Killing form is defined as + + .. MATH:: + + \langle x \mid y \rangle + = \operatorname{tr}\left( \operatorname{ad}_x + \circ \operatorname{ad}_y \right). + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.lie_algebra_generators() + sage: L.killing_form(a, b) + 0 + """ + return self.killing_matrix(x, y).trace() + + @cached_method + def killing_form_matrix(self): + """ + Return the matrix of the Killing form of ``self``. + + The rows and the columns of this matrix are indexed by the + elements of the basis of ``self`` (in the order provided by + :meth:`basis`). + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.killing_form_matrix() + [0 0 0] + [0 0 0] + [0 0 0] + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example(0) + sage: m = L.killing_form_matrix(); m + [] + sage: parent(m) + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + """ + B = self.basis() + m = matrix(self.base_ring(), + [[self.killing_form(x, y) for x in B] for y in B]) + m.set_immutable() + return m + + @cached_method + def structure_coefficients(self, include_zeros=False): + """ + Return the structure coefficients of ``self``. + + INPUT: + + - ``include_zeros`` -- (default: ``False``) if ``True``, then + include the `[x, y] = 0` pairs in the output + + OUTPUT: + + A dictionary whose keys are pairs of basis indices `(i, j)` + with `i < j`, and whose values are the corresponding + *elements* `[b_i, b_j]` in the Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.structure_coefficients() + Finite family {} + sage: L.structure_coefficients(True) + Finite family {(0, 1): (0, 0, 0), (1, 2): (0, 0, 0), (0, 2): (0, 0, 0)} + + :: + + sage: G = SymmetricGroup(3) + sage: S = GroupAlgebra(G, QQ) + sage: L = LieAlgebra(associative=S) + sage: L.structure_coefficients() + Finite family {((1,3,2), (1,3)): (2,3) - (1,2), + ((1,2), (1,2,3)): -(2,3) + (1,3), + ((1,2,3), (1,3)): -(2,3) + (1,2), + ((2,3), (1,3,2)): -(1,2) + (1,3), + ((2,3), (1,3)): -(1,2,3) + (1,3,2), + ((2,3), (1,2)): (1,2,3) - (1,3,2), + ((2,3), (1,2,3)): (1,2) - (1,3), + ((1,2), (1,3,2)): (2,3) - (1,3), + ((1,2), (1,3)): (1,2,3) - (1,3,2)} + """ + d = {} + B = self.basis() + K = list(B.keys()) + zero = self.zero() + for i, x in enumerate(K): + for y in K[i + 1:]: + bx = B[x] + by = B[y] + val = self.bracket(bx, by) + if not include_zeros and val == zero: + continue + if self._basis_key(x) > self._basis_key(y): + d[y,x] = -val + else: + d[x,y] = val + return Family(d) + + def centralizer_basis(self, S): + """ + Return a basis of the centralizer of ``S`` in ``self``. + + INPUT: + + - ``S`` -- a subalgebra of ``self`` or a list of elements that + represent generators for a subalgebra + + .. SEEALSO:: + + :meth:`centralizer` + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.lie_algebra_generators() + sage: L.centralizer_basis([a + b, 2*a + c]) + [(1, 0, 0), (0, 1, 0), (0, 0, 1)] + + sage: H = lie_algebras.Heisenberg(QQ, 2) + sage: H.centralizer_basis(H) + [z] + + + sage: D = DescentAlgebra(QQ, 4).D() + sage: L = LieAlgebra(associative=D) + sage: L.centralizer_basis(L) + [D{}, + D{1} + D{1, 2} + D{2, 3} + D{3}, + D{1, 2, 3} + D{1, 3} + D{2}] + sage: D.center_basis() + (D{}, + D{1} + D{1, 2} + D{2, 3} + D{3}, + D{1, 2, 3} + D{1, 3} + D{2}) + """ + #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra + #if isinstance(S, LieSubalgebra) or S is self: + if S is self: + from sage.matrix.special import identity_matrix + m = identity_matrix(self.base_ring(), self.dimension()) + elif isinstance(S, (list, tuple)): + m = matrix([v.to_vector() for v in self.echelon_form(S)]) + else: + m = self.subalgebra(S).basis_matrix() + + S = self.structure_coefficients() + sc = {} + for k in S.keys(): + v = S[k].to_vector() + sc[k] = v + sc[k[1],k[0]] = -v + X = self.basis().keys() + d = len(X) + c_mat = matrix(self.base_ring(), + [[sum(m[i,j] * sc[x,xp][k] for j,xp in enumerate(X) + if (x, xp) in sc) + for x in X] + for i in range(d) for k in range(d)]) + C = c_mat.right_kernel().basis_matrix() + return [self.from_vector(v) for v in C] + + def centralizer(self, S): + """ + Return the centralizer of ``S`` in ``self``. + + INPUT: + + - ``S`` -- a subalgebra of ``self`` or a list of elements that + represent generators for a subalgebra + + .. SEEALSO:: + + :meth:`centralizer_basis` + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.lie_algebra_generators() + sage: S = L.centralizer([a + b, 2*a + c]); S + An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field + sage: S.basis_matrix() + [1 0 0] + [0 1 0] + [0 0 1] + """ + return self.subalgebra(self.centralizer_basis(S)) + + def center(self): + """ + Return the center of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: Z = L.center(); Z + An example of a finite dimensional Lie algebra with basis: the + 3-dimensional abelian Lie algebra over Rational Field + sage: Z.basis_matrix() + [1 0 0] + [0 1 0] + [0 0 1] + """ + return self.centralizer(self) + + @cached_method + def is_ideal(self, A): + """ + Return if ``self`` is an ideal of ``A``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: I = L.ideal([2*a - c, b + c]) + sage: I.is_ideal(L) + True + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: L.is_ideal(L) + True + + sage: F = LieAlgebra(QQ, 'F', representation='polynomial') + sage: L.is_ideal(F) + Traceback (most recent call last): + ... + NotImplementedError: A must be a finite dimensional Lie algebra + with basis + """ + if A == self: + return True + if A not in LieAlgebras(self.base_ring()).FiniteDimensional().WithBasis(): + raise NotImplementedError("A must be a finite dimensional" + " Lie algebra with basis") + B = self.basis() + AB = A.basis() + try: + b_mat = matrix(A.base_ring(), [A.bracket(b, ab).to_vector() + for b in B for ab in AB]) + except (ValueError, TypeError): + return False + return b_mat.row_space().is_submodule(self.module()) + + def product_space(self, L, submodule=False): + r""" + Return the product space ``[self, L]``. + + INPUT: + + - ``L`` -- a Lie subalgebra of ``self`` + - ``submodule`` -- (default: ``False``) if ``True``, then the + result is forced to be a submodule of ``self`` + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a,b,c = L.lie_algebra_generators() + sage: X = L.subalgebra([a, b+c]) + sage: L.product_space(X) + An example of a finite dimensional Lie algebra with basis: + the 0-dimensional abelian Lie algebra over Rational Field + with basis matrix: + [] + sage: Y = L.subalgebra([a, 2*b-c]) + sage: X.product_space(Y) + An example of a finite dimensional Lie algebra with basis: + the 0-dimensional abelian Lie algebra over Rational + Field with basis matrix: + [] + + :: + + sage: H = lie_algebras.Heisenberg(ZZ, 4) + sage: Hp = H.product_space(H, submodule=True).basis() + sage: [H.from_vector(v) for v in Hp] + [z] + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: Lp = L.product_space(L) # todo: not implemented - #17416 + sage: Lp # todo: not implemented - #17416 + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + (x,) + sage: Lp.product_space(L) # todo: not implemented - #17416 + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + (x,) + sage: L.product_space(Lp) # todo: not implemented - #17416 + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + (x,) + sage: Lp.product_space(Lp) # todo: not implemented - #17416 + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + () + """ + # Make sure we lift everything to the ambient space + try: + A = self._ambient + except AttributeError: + try: + A = L._ambient + except AttributeError: + A = self + + B = self.basis() + LB = L.basis() + b_mat = matrix(A.base_ring(), [A.bracket(b, lb).to_vector() + for b in B for lb in LB]) + if submodule is True or not (self.is_ideal(A) and L.is_ideal(A)): + return b_mat.row_space() + # We echelonize the matrix here + # TODO: Do we want to? + b_mat.echelonize() + r = b_mat.rank() + gens = [A.from_vector(row) for row in b_mat.rows()[:r]] + return A.ideal(gens) + + @cached_method + def derived_subalgebra(self): + """ + Return the derived subalgebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.derived_subalgebra() + An example of a finite dimensional Lie algebra with basis: + the 0-dimensional abelian Lie algebra over Rational Field + with basis matrix: + [] + """ + return self.product_space(self) + + @cached_method + def derived_series(self): + r""" + Return the derived series `(\mathfrak{g}^{(i)})_i` of ``self`` + where the rightmost + `\mathfrak{g}^{(k)} = \mathfrak{g}^{(k+1)} = \cdots`. + + We define the derived series of a Lie algebra `\mathfrak{g}` + recursively by `\mathfrak{g}^{(0)} := \mathfrak{g}` and + + .. MATH:: + + \mathfrak{g}^{(k+1)} = + [\mathfrak{g}^{(k)}, \mathfrak{g}^{(k)}] + + and recall that + `\mathfrak{g}^{(k)} \supseteq \mathfrak{g}^{(k+1)}`. + Alternatively we can express this as + + .. MATH:: + + \mathfrak{g} \supseteq [\mathfrak{g}, \mathfrak{g}] \supseteq + \bigl[ [\mathfrak{g}, \mathfrak{g}], [\mathfrak{g}, + \mathfrak{g}] \bigr] \supseteq + \biggl[ \bigl[ [\mathfrak{g}, \mathfrak{g}], [\mathfrak{g}, + \mathfrak{g}] \bigr], \bigl[ [\mathfrak{g}, \mathfrak{g}], + [\mathfrak{g}, \mathfrak{g}] \bigr] \biggr] \supseteq \cdots. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.derived_series() + (An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field, + An example of a finite dimensional Lie algebra with basis: + the 0-dimensional abelian Lie algebra over Rational Field + with basis matrix: + []) + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: L.derived_series() # todo: not implemented - #17416 + (Lie algebra on 2 generators (x, y) over Rational Field, + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + (x,), + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + ()) + """ + L = [self] + while L[-1].dimension() > 0: + p = L[-1].derived_subalgebra() + if L[-1].dimension() == p.dimension(): + break + L.append(p) + return tuple(L) + + @cached_method + def lower_central_series(self): + r""" + Return the lower central series `(\mathfrak{g}_{i})_i` + of ``self`` where the rightmost + `\mathfrak{g}_k = \mathfrak{g}_{k+1} = \cdots`. + + We define the lower central series of a Lie algebra `\mathfrak{g}` + recursively by `\mathfrak{g}_0 := \mathfrak{g}` and + + .. MATH:: + + \mathfrak{g}_{k+1} = [\mathfrak{g}, \mathfrak{g}_{k}] + + and recall that `\mathfrak{g}_{k} \supseteq \mathfrak{g}_{k+1}`. + Alternatively we can express this as + + .. MATH:: + + \mathfrak{g} \supseteq [\mathfrak{g}, \mathfrak{g}] \supseteq + \bigl[ [\mathfrak{g}, \mathfrak{g}], \mathfrak{g} \bigr] + \supseteq\biggl[\bigl[ [\mathfrak{g}, \mathfrak{g}], + \mathfrak{g} \bigr], \mathfrak{g}\biggr] \supseteq \cdots. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.derived_series() + (An example of a finite dimensional Lie algebra with basis: + the 3-dimensional abelian Lie algebra over Rational Field, + An example of a finite dimensional Lie algebra with basis: + the 0-dimensional abelian Lie algebra over Rational Field + with basis matrix: + []) + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: L.lower_central_series() # todo: not implemented - #17416 + (Lie algebra on 2 generators (x, y) over Rational Field, + Subalgebra generated of Lie algebra on 2 generators (x, y) over Rational Field with basis: + (x,)) + """ + L = [self] + while L[-1].dimension() > 0: + s = self.product_space(L[-1]) + if L[-1].dimension() == s.dimension(): + break + L.append(s) + return tuple(L) + + def is_abelian(self): + """ + Return if ``self`` is an abelian Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_abelian() + True + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}}) + sage: L.is_abelian() + False + """ + return len(self.structure_coefficients()) == 0 + # TODO: boolean handling of empty family + #return not self.structure_coefficients() + + def is_solvable(self): + r""" + Return if ``self`` is a solvable Lie algebra. + + A Lie algebra is solvable if the derived series eventually + becomes `0`. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_solvable() + True + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: L.is_solvable() # todo: not implemented - #17416 + False + """ + return not self.derived_series()[-1].dimension() + + def is_nilpotent(self): + r""" + Return if ``self`` is a nilpotent Lie algebra. + + A Lie algebra is nilpotent if the lower central series eventually + becomes `0`. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_nilpotent() + True + """ + return not self.lower_central_series()[-1].dimension() + + def is_semisimple(self): + """ + Return if ``self`` if a semisimple Lie algebra. + + A Lie algebra is semisimple if the solvable radical is zero. In + characteristic 0, this is equivalent to saying the Killing form + is non-degenerate. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_semisimple() + False + """ + return not self.killing_form_matrix().is_singular() + + class ElementMethods: + def adjoint_matrix(self): # In #11111 (more or less) by using matrix of a mophism + """ + Return the matrix of the adjoint action of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.an_element().adjoint_matrix() + [0 0 0] + [0 0 0] + [0 0 0] + + :: + + sage: L. = LieAlgebra(QQ, {('x','y'):{'x':1}}) + sage: x.adjoint_matrix() + [0 0] + [1 0] + sage: y.adjoint_matrix() + [-1 0] + [ 0 0] + """ + P = self.parent() + basis = P.basis() + return matrix(self.base_ring(), + [P.bracket(self, b).to_vector() for b in basis]) + + def to_vector(self): + """ + Return the vector in ``g.module()`` corresponding to the + element ``self`` of ``g`` (where ``g`` is the parent of + ``self``). + + Implement this if you implement ``g.module()``. + See :meth:`sage.categories.lie_algebras.LieAlgebras.module` + for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.an_element().to_vector() + (0, 0, 0) + + sage: D = DescentAlgebra(QQ, 4).D() + sage: L = LieAlgebra(associative=D) + sage: L.an_element().to_vector() + (1, 1, 1, 1, 1, 1, 1, 1) + """ + M = self.parent().module() + B = M.basis() + return M.sum(self[k] * B[i] for i,k in enumerate(self.parent()._basis_ordering)) + + _vector_ = to_vector + + class Subobjects(SubobjectsCategory): + """ + A category for subalgebras of a finite dimensional Lie algebra + with basis. + """ + class ParentMethods: + @abstract_method + def ambient(self): + """ + Return the ambient Lie algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: S = L.subalgebra([2*a+b, b + c]) + sage: S.ambient() == L + True + """ + + @abstract_method + def basis_matrix(self): + """ + Return the basis matrix of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: S = L.subalgebra([2*a+b, b + c]) + sage: S.basis_matrix() + [ 1 0 -1/2] + [ 0 1 1] + """ + diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index 19ab1928af4..097db3f2d0f 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -93,9 +93,9 @@ class ParentMethods: def group_generators(self): """ - Returns group generators for self. + Return group generators for ``self``. - This default implementation calls :meth:`.gens`, for + This default implementation calls :meth:`gens`, for backward compatibility. EXAMPLES:: diff --git a/src/sage/categories/lie_algebras.py b/src/sage/categories/lie_algebras.py new file mode 100644 index 00000000000..6ba9c68b369 --- /dev/null +++ b/src/sage/categories/lie_algebras.py @@ -0,0 +1,759 @@ +r""" +Lie Algebras + +AUTHORS: + +- Travis Scrimshaw (07-15-2013): Initial implementation +""" + +#***************************************************************************** +# Copyright (C) 2013 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.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.lazy_import import LazyImport +from sage.categories.category import JoinCategory, Category +from sage.categories.category_types import Category_over_base_ring +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.modules import Modules +from sage.categories.sets_cat import Sets +from sage.categories.homset import Hom +from sage.categories.morphism import Morphism +from sage.structure.element import coerce_binop + +class LieAlgebras(Category_over_base_ring): + """ + The category of Lie algebras. + + EXAMPLES:: + + sage: C = LieAlgebras(QQ); C + Category of Lie algebras over Rational Field + sage: sorted(C.super_categories(), key=str) + [Category of vector spaces over Rational Field] + + We construct a typical parent in this category, and do some + computations with it:: + + sage: A = C.example(); A + An example of a Lie algebra: the Lie algebra from the associative + algebra Symmetric group algebra of order 3 over Rational Field + generated by ([2, 1, 3], [2, 3, 1]) + + sage: A.category() + Category of Lie algebras over Rational Field + + sage: A.base_ring() + Rational Field + + sage: a,b = A.lie_algebra_generators() + sage: a.bracket(b) + -[1, 3, 2] + [3, 2, 1] + sage: b.bracket(2*a + b) + 2*[1, 3, 2] - 2*[3, 2, 1] + + sage: A.bracket(a, b) + -[1, 3, 2] + [3, 2, 1] + + Please see the source code of `A` (with ``A??``) for how to + implement other Lie algebras. + + TESTS:: + + sage: C = LieAlgebras(QQ) + sage: TestSuite(C).run() + sage: TestSuite(C.example()).run() + + .. TODO:: + + Many of these tests should use Lie algebras that are not the minimal + example and need to be added after :trac:`16820` (and :trac:`16823`). + """ + @cached_method + def super_categories(self): + """ + EXAMPLES:: + + sage: LieAlgebras(QQ).super_categories() + [Category of vector spaces over Rational Field] + """ + # We do not also derive from (Magmatic) algebras since we don't want * + # to be our Lie bracket + # Also this doesn't inherit the ability to add axioms like Associative + # and Unital, both of which do not make sense for Lie algebras + return [Modules(self.base_ring())] + + # TODO: Find some way to do this without copying most of the logic. + def _repr_object_names(self): + r""" + Return the name of the objects of this category. + + .. SEEALSO:: :meth:`Category._repr_object_names` + + EXAMPLES:: + + sage: LieAlgebras(QQ)._repr_object_names() + 'Lie algebras over Rational Field' + sage: LieAlgebras(Fields())._repr_object_names() + 'Lie algebras over fields' + sage: from sage.categories.category import JoinCategory + sage: from sage.categories.category_with_axiom import Blahs + sage: LieAlgebras(JoinCategory((Blahs().Flying(), Fields()))) + Category of Lie algebras over (flying unital blahs and fields) + """ + base = self.base() + if isinstance(base, Category): + if isinstance(base, JoinCategory): + name = '('+' and '.join(C._repr_object_names() for C in base.super_categories())+')' + else: + name = base._repr_object_names() + else: + name = base + return "Lie algebras over {}".format(name) + + def example(self, gens=None): + """ + Return an example of a Lie algebra as per + :meth:`Category.example `. + + EXAMPLES:: + + sage: LieAlgebras(QQ).example() + An example of a Lie algebra: the Lie algebra from the associative algebra + Symmetric group algebra of order 3 over Rational Field + generated by ([2, 1, 3], [2, 3, 1]) + + Another set of generators can be specified as an optional argument:: + + sage: F. = FreeAlgebra(QQ) + sage: LieAlgebras(QQ).example(F.gens()) + An example of a Lie algebra: the Lie algebra from the associative algebra + Free Algebra on 3 generators (x, y, z) over Rational Field + generated by (x, y, z) + """ + if gens is None: + from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra + from sage.rings.all import QQ + gens = SymmetricGroupAlgebra(QQ, 3).algebra_generators() + from sage.categories.examples.lie_algebras import Example + return Example(gens) + + WithBasis = LazyImport('sage.categories.lie_algebras_with_basis', + 'LieAlgebrasWithBasis', as_name='WithBasis') + + class FiniteDimensional(CategoryWithAxiom_over_base_ring): + WithBasis = LazyImport('sage.categories.finite_dimensional_lie_algebras_with_basis', + 'FiniteDimensionalLieAlgebrasWithBasis', as_name='WithBasis') + + def extra_super_categories(self): + """ + Implements the fact that a finite dimensional Lie algebra over + a finite ring is finite. + + EXAMPLES:: + + sage: LieAlgebras(IntegerModRing(4)).FiniteDimensional().extra_super_categories() + [Category of finite sets] + sage: LieAlgebras(ZZ).FiniteDimensional().extra_super_categories() + [] + sage: LieAlgebras(GF(5)).FiniteDimensional().is_subcategory(Sets().Finite()) + True + sage: LieAlgebras(ZZ).FiniteDimensional().is_subcategory(Sets().Finite()) + False + sage: LieAlgebras(GF(5)).WithBasis().FiniteDimensional().is_subcategory(Sets().Finite()) + True + """ + if self.base_ring() in Sets().Finite(): + return [Sets().Finite()] + return [] + + class ParentMethods: + #@abstract_method + #def lie_algebra_generators(self): + # """ + # Return the generators of ``self`` as a Lie algebra. + # """ + + # TODO: Move this to LieAlgebraElement, cythonize, and use more standard + # coercion framework test (i.e., have_same_parent) + def bracket(self, lhs, rhs): + """ + Return the Lie bracket ``[lhs, rhs]`` after coercing ``lhs`` and + ``rhs`` into elements of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: L.bracket(x, x + y) + -[1, 3, 2] + [3, 2, 1] + sage: L.bracket(x, 0) + 0 + sage: L.bracket(0, x) + 0 + """ + return self(lhs)._bracket_(self(rhs)) + + # Do not override this. Instead implement :meth:`_construct_UEA`; + # then, :meth:`lift` and :meth:`universal_enveloping_algebra` + # will automatically setup the coercion. + def universal_enveloping_algebra(self): + """ + Return the universal enveloping algebra of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.universal_enveloping_algebra() + Noncommutative Multivariate Polynomial Ring in b0, b1, b2 + over Rational Field, nc-relations: {} + + :: + + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) + sage: L.universal_enveloping_algebra() + Multivariate Polynomial Ring in x0, x1, x2 over Rational Field + + .. SEEALSO:: + + :meth:`lift` + """ + return self.lift.codomain() + + @abstract_method(optional=True) + def _construct_UEA(self): + """ + Return the universal enveloping algebra of ``self``. + + Unlike :meth:`universal_enveloping_algebra`, this method does not + (usually) construct the canonical lift morphism from ``self`` + to the universal enveloping algebra (let alone register it + as a coercion). + + One should implement this method and the ``lift`` method for + the element class to construct the morphism the universal + enveloping algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L._construct_UEA() + Noncommutative Multivariate Polynomial Ring in b0, b1, b2 + over Rational Field, nc-relations: {} + + :: + + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) + sage: L.universal_enveloping_algebra() # indirect doctest + Multivariate Polynomial Ring in x0, x1, x2 over Rational Field + """ + + @abstract_method(optional=True) + def module(self): + r""" + Return an `R`-module which is isomorphic to the + underlying `R`-module of ``self``. + + The rationale behind this method is to enable linear + algebraic functionality on ``self`` (such as + computing the span of a list of vectors in ``self``) + via an isomorphism from ``self`` to an `R`-module + (typically, although not always, an `R`-module of + the form `R^n` for an `n \in \NN`) on which such + functionality already exists. For this method to be + of any use, it should return an `R`-module which has + linear algebraic functionality that ``self`` does + not have. + + For instance, if ``self`` has ordered basis + `(e, f, h)`, then ``self.module()`` will be the + `R`-module `R^3`, and the elements `e`, `f` and + `h` of ``self`` will correspond to the basis + vectors `(1, 0, 0)`, `(0, 1, 0)` and `(0, 0, 1)` + of ``self.module()``. + + This method :meth:`module` needs to be set whenever + a finite-dimensional Lie algebra with basis is + intended to support linear algebra (which is, e.g., + used in the computation of centralizers and lower + central series). One then needs to also implement + the `R`-module isomorphism from ``self`` to + ``self.module()`` in both directions; that is, + implement: + + * a ``to_vector`` ElementMethod which sends every + element of ``self`` to the corresponding element of + ``self.module()``; + + * a ``from_vector`` ParentMethod which sends every + element of ``self.module()`` to an element + of ``self``. + + The ``from_vector`` method will automatically serve + as an element constructor of ``self`` (that is, + ``self(v)`` for any ``v`` in ``self.module()`` will + return ``self.from_vector(v)``). + + .. TODO:: + + Ensure that this is actually so. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.module() + Vector space of dimension 3 over Rational Field + """ + + @abstract_method(optional=True) + def from_vector(self, v): + """ + Return the element of ``self`` corresponding to the + vector ``v`` in ``self.module()``. + + Implement this if you implement :meth:`module`; see the + documentation of the latter for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u + (1, 0, 0) + sage: parent(u) is L + True + """ + + @lazy_attribute + def lift(self): + """ + Construct the lift morphism from ``self`` to the universal + enveloping algebra of ``self`` (the latter is implemented + as :meth:`universal_enveloping_algebra`). + + This is a Lie algebra homomorphism. It is injective if + ``self`` is a free module over its base ring, or if the + base ring is a `\QQ`-algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: lifted = L.lift(2*a + b - c); lifted + 2*b0 + b1 - b2 + sage: lifted.parent() is L.universal_enveloping_algebra() + True + """ + M = LiftMorphism(self, self._construct_UEA()) + M.register_as_coercion() + return M + + def subalgebra(self, gens, names=None, index_set=None, category=None): + r""" + Return the subalgebra of ``self`` generated by ``gens``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.subalgebra([2*a - c, b + c]) + An example of a finite dimensional Lie algebra with basis: + the 2-dimensional abelian Lie algebra over Rational Field + with basis matrix: + [ 1 0 -1/2] + [ 0 1 1] + + :: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: L.subalgebra([x + y]) + Traceback (most recent call last): + ... + NotImplementedError: subalgebras not yet implemented: see #17416 + """ + raise NotImplementedError("subalgebras not yet implemented: see #17416") + #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra + #return LieSubalgebra(gens, names, index_set, category) + + def ideal(self, gens, names=None, index_set=None, category=None): + r""" + Return the ideal of ``self`` generated by ``gens``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.ideal([2*a - c, b + c]) + An example of a finite dimensional Lie algebra with basis: + the 2-dimensional abelian Lie algebra over Rational Field + with basis matrix: + [ 1 0 -1/2] + [ 0 1 1] + + :: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: L.ideal([x + y]) + Traceback (most recent call last): + ... + NotImplementedError: ideals not yet implemented: see #16824 + """ + raise NotImplementedError("ideals not yet implemented: see #16824") + #from sage.algebras.lie_algebras.ideal import LieIdeal + #return LieIdeal(gens, names, index_set, category) + + def is_ideal(self, A): + """ + Return if ``self`` is an ideal of ``A``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: L.is_ideal(L) + True + """ + if A == self: + return True + raise NotImplementedError("ideals not yet implemented: see #16824") + #from sage.algebras.lie_algebras.ideal import LieIdeal + #return isinstance(self, LieIdeal) and self._ambient is A + + @abstract_method(optional=True) + def killing_form(self, x, y): + """ + Return the Killing form of ``x`` and ``y``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.killing_form(a, b+c) + 0 + """ + + def is_abelian(self): + r""" + Return ``True`` if this Lie algebra is abelian. + + A Lie algebra `\mathfrak{g}` is abelian if `[x, y] = 0` for all + `x, y \in \mathfrak{g}`. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: L.is_abelian() + False + sage: R = QQ['x,y'] + sage: L = LieAlgebras(QQ).example(R.gens()) + sage: L.is_abelian() + True + + :: + + sage: L. = LieAlgebra(QQ,1) # todo: not implemented - #16823 + sage: L.is_abelian() # todo: not implemented - #16823 + True + sage: L. = LieAlgebra(QQ,2) # todo: not implemented - #16823 + sage: L.is_abelian() # todo: not implemented - #16823 + False + """ + G = self.lie_algebra_generators() + if G not in FiniteEnumeratedSets(): + raise NotImplementedError("infinite number of generators") + zero = self.zero() + return all(x._bracket_(y) == zero for x in G for y in G) + + def is_commutative(self): + """ + Return if ``self`` is commutative. This is equivalent to ``self`` + being abelian. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: L.is_commutative() + False + + :: + + sage: L. = LieAlgebra(QQ, 1) # todo: not implemented - #16823 + sage: L.is_commutative() # todo: not implemented - #16823 + True + """ + return self.is_abelian() + + @abstract_method(optional=True) + def is_solvable(self): + """ + Return if ``self`` is a solvable Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_solvable() + True + """ + + @abstract_method(optional=True) + def is_nilpotent(self): + """ + Return if ``self`` is a nilpotent Lie algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.is_nilpotent() + True + """ + + def _test_jacobi_identity(self, **options): + """ + Test that the Jacobi identity is satisfied on (not + necessarily all) elements of this set. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + + EXAMPLES: + + By default, this method runs the tests only on the + elements returned by ``self.some_elements()``:: + + sage: L = LieAlgebras(QQ).example() + sage: L._test_jacobi_identity() + + However, the elements tested can be customized with the + ``elements`` keyword argument:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: L._test_jacobi_identity(elements=[x+y, x, 2*y, x.bracket(y)]) + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + elts = tester.some_elements() + jacobi = lambda x, y, z: self.bracket(x, self.bracket(y, z)) + \ + self.bracket(y, self.bracket(z, x)) + \ + self.bracket(z, self.bracket(x, y)) + zero = self.zero() + for x in elts: + for y in elts: + if x == y: + continue + for z in elts: + tester.assert_(jacobi(x, y, z) == zero) + + def _test_antisymmetry(self, **options): + """ + Test that the antisymmetry axiom is satisfied on (not + necessarily all) elements of this set. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + + EXAMPLES: + + By default, this method runs the tests only on the + elements returned by ``self.some_elements()``:: + + sage: L = LieAlgebras(QQ).example() + sage: L._test_antisymmetry() + + However, the elements tested can be customized with the + ``elements`` keyword argument:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: L._test_antisymmetry(elements=[x+y, x, 2*y, x.bracket(y)]) + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + elts = tester.some_elements() + zero = self.zero() + for x in elts: + tester.assert_(self.bracket(x, x) == zero) + + def _test_distributivity(self, **options): + r""" + Test the distributivity of the Lie bracket `[,]` on `+` on (not + necessarily all) elements of this set. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + + TESTS:: + + sage: L = LieAlgebras(QQ).example() + sage: L._test_distributivity() + + EXAMPLES: + + By default, this method runs the tests only on the + elements returned by ``self.some_elements()``:: + + sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") + sage: L.some_elements() + [x + y + z] + sage: L._test_distributivity() + + However, the elements tested can be customized with the + ``elements`` keyword argument:: + + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) # todo: not implemented - #16821 + sage: h1 = L.gen(0) # todo: not implemented - #16821 + sage: h2 = L.gen(1) # todo: not implemented - #16821 + sage: e2 = L.gen(3) # todo: not implemented - #16821 + sage: L._test_distributivity(elements=[h1, h2, e2]) # todo: not implemented - #16821 + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + S = tester.some_elements() + from sage.misc.misc import some_tuples + for x,y,z in some_tuples(S, 3, tester._max_runs): + # left distributivity + tester.assert_(self.bracket(x, (y + z)) + == self.bracket(x, y) + self.bracket(x, z)) + # right distributivity + tester.assert_(self.bracket((x + y), z) + == self.bracket(x, z) + self.bracket(y, z)) + + class ElementMethods: + @coerce_binop + def bracket(self, rhs): + """ + Return the Lie bracket ``[self, rhs]``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x.bracket(y) + -[1, 3, 2] + [3, 2, 1] + sage: x.bracket(0) + 0 + """ + return self._bracket_(rhs) + + # Implement this method to define the Lie bracket. You do not + # need to deal with the coercions here. + @abstract_method + def _bracket_(self, y): + """ + Return the Lie bracket ``[self, y]``, where ``y`` is an + element of the same Lie algebra as ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x._bracket_(y) + -[1, 3, 2] + [3, 2, 1] + sage: y._bracket_(x) + [1, 3, 2] - [3, 2, 1] + sage: x._bracket_(x) + 0 + """ + + @abstract_method(optional=True) + def to_vector(self): + """ + Return the vector in ``g.module()`` corresponding to the + element ``self`` of ``g`` (where ``g`` is the parent of + ``self``). + + Implement this if you implement ``g.module()``. + See :meth:`LieAlgebras.module` for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: u = L((1, 0, 0)).to_vector(); u + (1, 0, 0) + sage: parent(u) + Vector space of dimension 3 over Rational Field + """ + + @abstract_method(optional=True) + def lift(self): + """ + Return the image of ``self`` under the canonical lift from the Lie + algebra to its universal enveloping algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 3*a + b - c + sage: elt.lift() + 3*b0 + b1 - b2 + + :: + + sage: L. = LieAlgebra(QQ, abelian=True) + sage: x.lift() + x + """ + + def killing_form(self, x): + """ + Return the Killing form of ``self`` and ``x``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: a.killing_form(b) + 0 + """ + return self.parent().killing_form(self, x) + +class LiftMorphism(Morphism): + """ + The natural lifting morphism from a Lie algebra to its + enveloping algebra. + """ + def __init__(self, domain, codomain): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: f = L.lift + + We skip the category test since this is currently not an element of + a homspace:: + + sage: TestSuite(f).run(skip="_test_category") + """ + Morphism.__init__(self, Hom(domain, codomain)) + + def _call_(self, x): + """ + Lift ``x`` to the universal enveloping algebra. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: L.lift(3*a + b - c) + 3*b0 + b1 - b2 + """ + return x.lift() + diff --git a/src/sage/categories/lie_algebras_with_basis.py b/src/sage/categories/lie_algebras_with_basis.py new file mode 100644 index 00000000000..573e4de307f --- /dev/null +++ b/src/sage/categories/lie_algebras_with_basis.py @@ -0,0 +1,198 @@ +r""" +Lie Algebras With Basis + +AUTHORS: + +- Travis Scrimshaw (07-15-2013): Initial implementation +""" + +#***************************************************************************** +# 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.abstract_method import abstract_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 + +class LieAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): + """ + Category of Lie algebras with a basis. + """ + _base_category_class_and_axiom = [LieAlgebras, "WithBasis"] + + def example(self, gens=None): + """ + Return an example of a Lie algebra as per + :meth:`Category.example `. + + EXAMPLES:: + + sage: LieAlgebras(QQ).WithBasis().example() + An example of a Lie algebra: the abelian Lie algebra on the + generators indexed by Partitions over Rational Field + + Another set of generators can be specified as an optional argument:: + + sage: LieAlgebras(QQ).WithBasis().example(Compositions()) + An example of a Lie algebra: the abelian Lie algebra on the + generators indexed by Compositions of non-negative integers + over Rational Field + """ + if gens is None: + from sage.combinat.partition import Partitions + gens = Partitions() + from sage.categories.examples.lie_algebras_with_basis import Example + return Example(self.base_ring(), gens) + + class ParentMethods: + def _basis_key(self, x): + """ + Return the key used to compare two basis element indices. + + The default is to call the element itself. + + TESTS:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L._basis_key(Partition([3,1])) + [3, 1] + """ + return x + + @abstract_method(optional=True) + def bracket_on_basis(self, x, y): + """ + Return the bracket of basis elements indexed by ``x`` and ``y`` + where ``x < y``. If this is not implemented, then the method + ``_bracket_()`` for the elements must be overwritten. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L.bracket_on_basis(Partition([3,1]), Partition([2,2,1,1])) + 0 + """ + + def module(self): + """ + Return an `R`-module which is isomorphic to the + underlying `R`-module of ``self``. + + See + :meth:`sage.categories.lie_algebras.LieAlgebras.module` for + an explanation. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: L.module() + Free module generated by Partitions over Rational Field + """ + from sage.combinat.free_module import CombinatorialFreeModule + try: + # Try to see if it has an indexing set + return CombinatorialFreeModule(self.base_ring(), self.basis().keys()) + except AttributeError: + # Otherwise just index by the basis of ``self`` as a fallback + return CombinatorialFreeModule(self.base_ring(), self.basis()) + + def from_vector(self, v): + """ + Return the element of ``self`` corresponding to the + vector ``v`` in ``self.module()``. + + Implement this if you implement :meth:`module`; see the + documentation of + :meth:`sage.categories.lie_algebras.LieAlgebras.module` + for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u + (1, 0, 0) + sage: parent(u) is L + True + """ + B = self.basis() + return self.sum(v[i] * B[i] for i in v.support()) + + # Remove once #22629 is merged + def dimension(self): + """ + Return the dimension of ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.dimension() + 3 + + :: + + sage: L = LieAlgebra(QQ, 'x,y', {('x','y'): {'x':1}}) + sage: L.dimension() + 2 + """ + return self.basis().cardinality() + + class ElementMethods: + def _bracket_(self, y): + """ + Return the Lie bracket ``[self, y]``, where ``y`` is an + element of the same Lie algebra as ``self``. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).WithBasis().example() + sage: G = L.lie_algebra_generators() + sage: x = G[Partition([4,3,3,1])] + sage: y = G[Partition([6,1])] + sage: x.bracket(y) + 0 + """ + P = self.parent() + + def term(ml, mr): + key_ml = P._basis_key(ml) + key_mr = P._basis_key(mr) + if key_ml == key_mr: + return P.zero() + if key_ml < key_mr: + return P.bracket_on_basis(ml, mr) + return -P.bracket_on_basis(mr, ml) + + return P.sum(cl * cr * term(ml, mr) + for ml, cl in self for mr, cr in y) + + def to_vector(self): + """ + Return the vector in ``g.module()`` corresponding to the + element ``self`` of ``g`` (where ``g`` is the parent of + ``self``). + + Implement this if you implement ``g.module()``. + See :meth:`sage.categories.lie_algebras.LieAlgebras.module` + for how this is to be done. + + EXAMPLES:: + + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: L.an_element().to_vector() + (0, 0, 0) + + .. TODO:: + + Doctest this implementation on an example not overshadowed. + """ + M = self.parent().module() + B = M.basis() + return M.sum(self[i] * B[i] for i in self.support()) + diff --git a/src/sage/combinat/crystals/virtual_crystal.py b/src/sage/combinat/crystals/virtual_crystal.py index 9f72c2f4291..812360fb7ba 100644 --- a/src/sage/combinat/crystals/virtual_crystal.py +++ b/src/sage/combinat/crystals/virtual_crystal.py @@ -24,6 +24,8 @@ # http://www.gnu.org/licenses/ #**************************************************************************** +from __future__ import division + from sage.categories.crystals import Crystals from sage.categories.finite_crystals import FiniteCrystals from sage.combinat.root_system.cartan_type import CartanType @@ -364,7 +366,7 @@ def epsilon(self, i): 1 """ P = self.parent() - return self.value.epsilon(P._virtualization[i][0]) / P._scaling_factors[i] + return self.value.epsilon(P._virtualization[i][0]) // P._scaling_factors[i] def phi(self, i): r""" @@ -383,7 +385,7 @@ def phi(self, i): 0 """ P = self.parent() - return self.value.phi(P._virtualization[i][0]) / P._scaling_factors[i] + return self.value.phi(P._virtualization[i][0]) // P._scaling_factors[i] def weight(self): """ @@ -410,7 +412,7 @@ def weight(self): La = WLR.fundamental_weights() v = P._virtualization sf = P._scaling_factors - return WLR.sum(wt.scalar(ac[v[i][0]]) / sf[i] * La[i] + return WLR.sum(wt.scalar(ac[v[i][0]]) // sf[i] * La[i] for i in self.index_set()) # TODO: implement a devirtualization map diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index efb0682077a..6e27bc73fa3 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -353,7 +353,7 @@ cdef class dancing_linksWrapper: :: sage: S = Subsets(range(5)) - sage: rows = map(list, S) + sage: rows = [list(x) for x in S] sage: d = dlx_solver(rows) sage: d.number_of_solutions() 52 diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index f45fc9286f3..a288afeac1c 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -74,9 +74,9 @@ :widths: 30, 70 :delim: | - :meth:`~FiniteLatticePoset.atoms` | Return the list of elements covering the bottom element. - :meth:`~FiniteLatticePoset.coatoms` | Return the list of elements covered by the top element. - :meth:`~FiniteLatticePoset.double_irreducibles` | Return the list of double irreducible elements. + :meth:`~FiniteMeetSemilattice.atoms()` | Return elements covering the bottom element. + :meth:`~FiniteJoinSemilattice.coatoms()` | Return elements covered by the top element. + :meth:`~FiniteLatticePoset.double_irreducibles` | Return double irreducible elements. :meth:`~FiniteLatticePoset.join_primes` | Return the join prime elements. :meth:`~FiniteLatticePoset.meet_primes` | Return the meet prime elements. :meth:`~FiniteLatticePoset.complements` | Return the list of complements of an element, or the dictionary of complements for all elements. @@ -135,6 +135,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +from six.moves import range +from six import iteritems from sage.categories.finite_lattice_posets import FiniteLatticePosets from sage.combinat.posets.posets import Poset, FinitePoset @@ -145,7 +147,7 @@ from sage.misc.decorators import rename_keyword -import six + #################################################################################### @@ -312,6 +314,33 @@ def meet(self, x, y=None): m = self._hasse_diagram._meet[i, m] return self._vertex_to_element(m) + def atoms(self): + """ + Return the list atoms of this (semi)lattice. + + An *atom* of a lattice is an element covering the bottom element. + + .. SEEALSO:: + + :meth:`~FiniteJoinSemilattice.coatoms()`. + + EXAMPLES:: + + sage: L = Posets.DivisorLattice(60) + sage: sorted(L.atoms()) + [2, 3, 5] + + TESTS:: + + sage: LatticePoset().atoms() + [] + sage: LatticePoset({0: []}).atoms() + [] + """ + if self.cardinality() == 0: + return [] + return self.upper_covers(self.bottom()) + def pseudocomplement(self, element): """ Return the pseudocomplement of ``element``, if it exists. @@ -537,8 +566,34 @@ def join(self, x, y=None): j = self._hasse_diagram._join[i, j] return self._vertex_to_element(j) + def coatoms(self): + """ + Return the list of co-atoms of this (semi)lattice. -#################################################################################### + A *co-atom* of a lattice is an element covered by the top element. + + .. SEEALSO:: + + :meth:`~FiniteMeetSemilattice.atoms()`. + + EXAMPLES:: + + sage: L = Posets.DivisorLattice(60) + sage: sorted(L.coatoms()) + [12, 20, 30] + + TESTS:: + + sage: LatticePoset().coatoms() + [] + sage: LatticePoset({0: []}).coatoms() + [] + """ + if self.cardinality() == 0: + return [] + return self.lower_covers(self.top()) + +############################################################################### def LatticePoset(data=None, *args, **options): r""" @@ -650,60 +705,6 @@ def _repr_(self): s += " with distinguished linear extension" return s - def atoms(self): - """ - Return the atoms of this lattice. - - An *atom* of a lattice is an element covering the bottom element. - - .. SEEALSO:: - - :meth:`coatoms` - - EXAMPLES:: - - sage: L = Posets.DivisorLattice(60) - sage: sorted(L.atoms()) - [2, 3, 5] - - TESTS:: - - sage: LatticePoset().atoms() - [] - sage: LatticePoset({0: []}).atoms() - [] - """ - if self.cardinality() == 0: - return [] - return self.upper_covers(self.bottom()) - - def coatoms(self): - """ - Return the co-atoms of this lattice. - - A *co-atom* of a lattice is an element covered by the top element. - - .. SEEALSO:: - - :meth:`atoms` - - EXAMPLES:: - - sage: L = Posets.DivisorLattice(60) - sage: sorted(L.coatoms()) - [12, 20, 30] - - TESTS:: - - sage: LatticePoset().coatoms() - [] - sage: LatticePoset({0: []}).coatoms() - [] - """ - if self.cardinality() == 0: - return [] - return self.lower_covers(self.top()) - def double_irreducibles(self): """ Return the list of double irreducible elements of this lattice. @@ -1477,7 +1478,7 @@ def is_relatively_complemented(self, certificate=False): for e1 in range(n-1): C = Counter(flatten([H.neighbors_out(e2) for e2 in H.neighbors_out(e1)])) - for e3, c in six.iteritems(C): + for e3, c in iteritems(C): if c == 1 and len(H.closed_interval(e1, e3)) == 3: if not certificate: return False @@ -4013,7 +4014,7 @@ def quotient(self, congruence, labels='tuple'): minimal_vertices = [part[0] for part in parts_H] H = self._hasse_diagram.transitive_closure().subgraph(minimal_vertices).transitive_reduction() if labels == 'integer': - H.relabel(range(len(minimal_vertices))) + H.relabel(list(range(len(minimal_vertices)))) return LatticePoset(H) part_dict = {m[0]:[self._vertex_to_element(x) for x in m] for m in parts_H} diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index a3b6831af46..04f76940afe 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5699,10 +5699,10 @@ def order_polytope(self): sage: P = posets.AntichainPoset(3) sage: Q = P.order_polytope();Q - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices sage: P = posets.PentagonPoset() sage: Q = P.order_polytope();Q - A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 8 vertices + A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 8 vertices sage: P = Poset([[1,2,3],[[1,2],[1,3]]]) sage: Q = P.order_polytope() @@ -5717,13 +5717,13 @@ def order_polytope(self): Discrete Comput. Geom. (1986), :doi:`10.1007/BF02187680` """ from sage.geometry.polyhedron.constructor import Polyhedron - ineqs = [[0] + [ZZ(j==v)-ZZ(j==u) for j in self] - for u,v,w in self.hasse_diagram().edges()] + ineqs = [[0] + [ZZ(j==v) - ZZ(j==u) for j in self] + for u, v, w in self.hasse_diagram().edges()] for i in self.maximal_elements(): ineqs += [[1] + [-ZZ(j==i) for j in self]] for i in self.minimal_elements(): ineqs += [[0] + [ZZ(j==i) for j in self]] - return Polyhedron(ieqs=ineqs) + return Polyhedron(ieqs=ineqs, base_ring=ZZ) def chain_polytope(self): r""" @@ -5750,16 +5750,17 @@ def chain_polytope(self): sage: P = posets.AntichainPoset(3) sage: Q = P.chain_polytope();Q - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices sage: P = posets.PentagonPoset() sage: Q = P.chain_polytope();Q - A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 8 vertices + A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 8 vertices """ from sage.geometry.polyhedron.constructor import Polyhedron - ineqs=[[1]+[-ZZ(j in chain) for j in self] for chain in self.maximal_chains()] + ineqs = [[1] + [-ZZ(j in chain) for j in self] + for chain in self.maximal_chains()] for i in self: - ineqs+=[[0]+[ZZ(j==i) for j in self]] - return Polyhedron(ieqs=ineqs) + ineqs += [[0] + [ZZ(j==i) for j in self]] + return Polyhedron(ieqs=ineqs, base_ring=ZZ) def zeta_polynomial(self): r""" diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 2d9462e240d..2e4101a357b 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -25,7 +25,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function +from __future__ import print_function, division from sage.misc.cachefunc import cached_method from sage.structure.list_clone import ClonableArray @@ -2225,12 +2225,12 @@ def cocharge(self): """ #return self.to_virtual_configuration().cocharge() / self.parent()._folded_ct.gamma[0] vct = self.parent()._folded_ct - cc = 0 - rigging_sum = 0 + cc = ZZ.zero() + rigging_sum = ZZ.zero() sigma = vct.folding_orbit() gamma = vct.scaling_factors() for a, p in enumerate(self): - t_check = len(sigma[a+1]) * gamma[a+1] / gamma[0] + t_check = len(sigma[a + 1]) * gamma[a+1] // gamma[0] for pos, i in enumerate(p._list): # Add the rigging rigging_sum += t_check * p.rigging[pos] diff --git a/src/sage/combinat/root_system/branching_rules.py b/src/sage/combinat/root_system/branching_rules.py index 5041af55557..30b28ed7150 100644 --- a/src/sage/combinat/root_system/branching_rules.py +++ b/src/sage/combinat/root_system/branching_rules.py @@ -16,9 +16,10 @@ from sage.structure.sage_object import SageObject from sage.combinat.root_system.cartan_type import CartanType from sage.modules.free_module_element import vector -from sage.rings.all import ZZ, QQ +from sage.rings.all import QQ from sage.misc.functional import is_even, is_odd + def branch_weyl_character(chi, R, S, rule="default"): r""" A branching rule describes the restriction of representations from @@ -268,7 +269,7 @@ def branch_weyl_character(chi, R, S, rule="default"): sage: E7=WeylCharacterRing("E7",style="coroots") sage: A3xA3xA1=WeylCharacterRing("A3xA3xA1",style="coroots") sage: E7(1,0,0,0,0,0,0).branch(A3xA3xA1,rule="extended") # long time (0.7s) - A3xA3xA1(0,0,1,0,0,1,1) + A3xA3xA1(0,1,0,0,1,0,0) + A3xA3xA1(1,0,0,1,0,0,1) + + A3xA3xA1(0,0,1,0,0,1,1) + A3xA3xA1(0,1,0,0,1,0,0) + A3xA3xA1(1,0,0,1,0,0,1) + A3xA3xA1(1,0,1,0,0,0,0) + A3xA3xA1(0,0,0,1,0,1,0) + A3xA3xA1(0,0,0,0,0,0,2) sage: fw = E7.fundamental_weights() sage: [E7(fw[i]).branch(D6,rule="levi") for i in [1,2,7]] # long time (0.3s) @@ -882,7 +883,7 @@ def branch_weyl_character(chi, R, S, rule="default"): 3*G2(1,0) sage: A2xA2xG2=WeylCharacterRing("A2xA2xG2",style="coroots") sage: A2xA2xG2(0,1,1,1,0,1).branch(A2xG2,rule="proj13") - 8*A2xG2(0,1,0,1) + 8*A2xG2(0,1,0,1) A more general way of specifying a branching rule from a reducible type is to supply a *list* of rules, one *component rule* for each @@ -967,13 +968,13 @@ def rule(x): or simply:: - rule = lambda x : [x[0]-x[3],x[1]-x[2]] + rule = lambda x: [x[0]-x[3],x[1]-x[2]] We may now make and use the branching rule as follows. EXAMPLES:: - sage: br = BranchingRule("A3", "C2", lambda x : [x[0]-x[3],x[1]-x[2]], "homemade"); br + sage: br = BranchingRule("A3", "C2", lambda x: [x[0]-x[3],x[1]-x[2]], "homemade"); br homemade branching rule A3 => C2 sage: [A3,C2]=[WeylCharacterRing(x,style="coroots") for x in ["A3","C2"]] sage: A3(0,1,0).branch(C2,rule=br) @@ -981,9 +982,9 @@ def rule(x): """ if isinstance(rule, str) or isinstance(rule, list): rule = branching_rule(R._cartan_type, S._cartan_type, rule) - if hasattr(rule,"_S"): + if hasattr(rule, "_S"): if rule._S != S.cartan_type(): - raise ValueError("rule has wrong target Cartan type") + raise ValueError("rule has wrong target Cartan type") mdict = {} for k in chi.weight_multiplicities(): # TODO: Could this use the new from_vector of ambient_space ? @@ -1004,11 +1005,13 @@ def rule(x): mdict[h] = chi_mdict[k] return S.char_from_weights(mdict) + class BranchingRule(SageObject): """ A class for branching rules. """ - def __init__(self, R, S, f, name="default", intermediate_types=[], intermediate_names=[]): + def __init__(self, R, S, f, name="default", intermediate_types=[], + intermediate_names=[]): """ INPUT: @@ -1019,7 +1022,7 @@ def __init__(self, R, S, f, name="default", intermediate_types=[], intermediate_ self._S = CartanType(S) self._f = f self._intermediate_types = intermediate_types - if len(intermediate_names) > 0: + if intermediate_names: self._intermediate_names = intermediate_names else: self._intermediate_names = [name] @@ -1034,16 +1037,18 @@ def _repr_(self): sage: b=branching_rule("F4","B3",rule="levi")*branching_rule("B3","G2",rule="miscellaneous"); b composite branching rule F4 => (levi) B3 => (miscellaneous) G2 """ + R_repr = self._R._repr_(compact=True) + S_repr = self._S._repr_(compact=True) if self._name == "composite": - ret = "composite branching rule %s => "%self._R._repr_(compact=True) + ret = "composite branching rule %s => " % R_repr for i in range(len(self._intermediate_types)): intt = self._intermediate_types[i] intn = self._intermediate_names[i] - ret += "(%s) %s => "%(intn, intt._repr_(compact=True)) - ret += "(%s) %s"%(self._intermediate_names[-1], self._S._repr_(compact=True)) + ret += "(%s) %s => " % (intn, intt._repr_(compact=True)) + ret += "(%s) %s" % (self._intermediate_names[-1], S_repr) return ret - else: - return "%s branching rule %s => %s"%(self._name, self._R._repr_(compact=True), self._S._repr_(compact=True)) + + return "%s branching rule %s => %s" % (self._name, R_repr, S_repr) def __call__(self, x): """ @@ -1058,7 +1063,7 @@ def __call__(self, x): except Exception: return self._f(x.to_vector()) - def __cmp__(self, other): + def __eq__(self, other): """ Two branching rules with the same source and target Cartan types are considered equal if they are the same as mappings from the weight @@ -1081,50 +1086,68 @@ def __cmp__(self, other): composite branching rule A3 => (isomorphic) D3 => (symmetric) B2 => (levi) A1 sage: d == e False - sage: b1 = BranchingRule("A2","A2",lambda x : [x[2], x[1], x[0]], "long Weyl element conjugation") - sage: b2 = BranchingRule("A2","A2",lambda x : x, "identity map") + sage: b1 = BranchingRule("A2","A2",lambda x: [x[2], x[1], x[0]], "long Weyl element conjugation") + sage: b2 = BranchingRule("A2","A2",lambda x: x, "identity map") sage: b1 == b2 False sage: A2 = WeylCharacterRing("A2",style="coroots") sage: [A2(f).branch(A2,rule=b1) == A2(f).branch(A2,rule=b2) for f in A2.fundamental_weights()] [True, True] """ - c = cmp(type(self), type(other)) - if c: - return c + if not isinstance(other, BranchingRule): + return False + Rspace = RootSystem(self._R).ambient_space() Rspace_other = RootSystem(other._R).ambient_space() - c = cmp(Rspace, Rspace_other) - if c: - return c + if Rspace != Rspace_other: + return False + Sspace = RootSystem(self._S).ambient_space() Sspace_other = RootSystem(other._S).ambient_space() - c = cmp(Sspace, Sspace_other) - if c: - return c + if Sspace != Sspace_other: + return False + for v in Rspace.fundamental_weights(): w = list(v.to_vector()) - c = cmp(Sspace(self(w)), Sspace(other(w))) - if c: - return c - return 0 + if Sspace(self(w)) != Sspace(other(w)): + return False + return True + + def __ne__(self, other): + """ + Test inequality + + EXAMPLES:: + + sage: b1 = BranchingRule("A2","A2",lambda x: [x[2], x[1], x[0]], "long Weyl element conjugation") + sage: b2 = BranchingRule("A2","A2",lambda x: x, "identity map") + sage: b1 != b2 + True + """ + return not(self == other) def __mul__(self, other): """ EXAMPLES:: - sage: E6=WeylCharacterRing("E6",style="coroots") - sage: A5=WeylCharacterRing("A5",style="coroots") + sage: E6 = WeylCharacterRing("E6",style="coroots") + sage: A5 = WeylCharacterRing("A5",style="coroots") sage: br = branching_rule("E6","A5xA1",rule="extended")*branching_rule("A5xA1","A5",rule="proj1"); br composite branching rule E6 => (extended) A5xA1 => (proj1) A5 sage: E6(1,0,0,0,0,0).branch(A5,rule=br) A5(0,0,0,1,0) + 2*A5(1,0,0,0,0) """ if self._S == other._R: - intermediates = flatten([self._intermediate_types, self._S, other._intermediate_types]) - internames = flatten([self._intermediate_names, other._intermediate_names]) - f = lambda x : other._f(self._f(x)) - return BranchingRule(self._R, other._S, f, "composite", intermediate_types=intermediates, intermediate_names=internames) + intermediates = flatten([self._intermediate_types, self._S, + other._intermediate_types]) + internames = flatten([self._intermediate_names, + other._intermediate_names]) + + def f(x): + return other._f(self._f(x)) + return BranchingRule(self._R, other._S, f, "composite", + intermediate_types=intermediates, + intermediate_names=internames) else: raise ValueError("unable to define composite: source and target don't agree") @@ -1157,7 +1180,7 @@ def describe(self, verbose=False, debug=False, no_r=False): EXAMPLES:: sage: branching_rule("G2","A2","extended").describe() - + 3 O=<=O---O 1 2 0 @@ -1173,7 +1196,7 @@ def describe(self, verbose=False, debug=False, no_r=False): 2 => 1 For more detailed information use verbose=True - + In this example, `0` is the affine root, that is, the negative of the highest root, for `"G2"`. If `i => j` is printed, this means that the i-th simple (or affine) root of the ambient @@ -1192,10 +1215,11 @@ def describe(self, verbose=False, debug=False, no_r=False): if self._S.is_compound(): for j in range(len(self._S.component_types())): ctype = self._S.component_types()[j] - component_rule = self*branching_rule(self._S, ctype,"proj%s"%(j+1)) - print("\nprojection %d on %s " % (j+1, + component_rule = self*branching_rule(self._S, ctype, + "proj%s" % (j + 1)) + print("\nprojection %d on %s " % (j + 1, ctype._repr_(compact=True)), - component_rule.describe(verbose=verbose, no_r=True)) + component_rule.describe(verbose=verbose, no_r=True)) if not verbose: print("\nfor more detailed information use verbose=True") else: @@ -1263,11 +1287,13 @@ def branch(self, chi, style=None): A2(0,1) + A2(1,0) + A2(0,2) + 2*A2(1,1) + A2(2,0) + A2(1,2) + A2(2,1) """ + from sage.combinat.root_system.weyl_characters import WeylCharacterRing if style is None: style = chi.parent()._style - S = sage.combinat.root_system.weyl_characters.WeylCharacterRing(self.Stype(), style=style) + S = WeylCharacterRing(self.Stype(), style=style) return chi.branch(S, rule=self) + def branching_rule(Rtype, Stype, rule="default"): """ Creates a branching rule. @@ -1292,14 +1318,14 @@ def branching_rule(Rtype, Stype, rule="default"): """ if rule == "plethysm": try: - S = sage.combinat.root_system.weyl_characters.WeylCharacterRing(Stype.split("(")[0],style="coroots") + S = sage.combinat.root_system.weyl_characters.WeylCharacterRing(Stype.split("(")[0], style="coroots") chi = S(eval("("+Stype.split("(")[1])) except Exception: - S = sage.combinat.root_system.weyl_characters.WeylCharacterRing(Stype.split(".")[0],style="coroots") - chi= eval("S."+Stype.split(".")[1]) + S = sage.combinat.root_system.weyl_characters.WeylCharacterRing(Stype.split(".")[0], style="coroots") + chi = eval("S." + Stype.split(".")[1]) return branching_rule_from_plethysm(chi, Rtype) - Rtype=CartanType(Rtype) - Stype=CartanType(Stype) + Rtype = CartanType(Rtype) + Stype = CartanType(Stype) r = Rtype.rank() s = Stype.rank() rdim = Rtype.root_system().ambient_space().dimension() @@ -1320,7 +1346,7 @@ def branching_rule(Rtype, Stype, rule="default"): if not Stype.is_compound(): k = len(Rtypes) n = RootSystem(Stype).ambient_space().dimension() - return BranchingRule(Rtype, Stype, lambda x : [sum(x[i+n*j] for j in range(k)) for i in range(n)], "diagonal") + return BranchingRule(Rtype, Stype, lambda x: [sum(x[i+n*j] for j in range(k)) for i in range(n)], "diagonal") raise ValueError("invalid Cartan types for diagonal branching rule") else: raise ValueError("Rule not found") @@ -1332,12 +1358,12 @@ def branching_rule(Rtype, Stype, rule="default"): l = rule[i] if l != "omit": if l == "identity": - rules.append(BranchingRule(Rtypes[i], Rtypes[i], lambda x : x, "identity")) + rules.append(BranchingRule(Rtypes[i], Rtypes[i], lambda x: x, "identity")) else: rules.append(l) stor.append(i) shifts = Rtype._shifts - Stypes = [CartanType(l._S) for l in rules] + Stypes = [CartanType(ru._S) for ru in rules] ntypes = len(Stypes) if Stype.is_compound(): def br(x): @@ -1348,6 +1374,7 @@ def br(x): else: j = stor[0] rulej = rules[0] + def br(x): return rulej(x[shifts[j]:shifts[j+1]]) return BranchingRule(Rtype, Stype, br, name) @@ -1381,52 +1408,52 @@ def br(x): elif rule == "identity": if Rtype is not Stype: raise ValueError("Cartan types must match for identity rule") - return BranchingRule(Rtype, Stype, lambda x : x, "identity") + return BranchingRule(Rtype, Stype, lambda x: x, "identity") elif rule == "levi": if not s == r-1: raise ValueError("Incompatible ranks") if Rtype[0] == 'A': if Stype.is_compound(): - if all(ct[0]=='A' for ct in stypes) and rdim == sdim: - return BranchingRule(Rtype, Stype, lambda x : x, "levi") + if all(ct[0] == 'A' for ct in stypes) and rdim == sdim: + return BranchingRule(Rtype, Stype, lambda x: x, "levi") else: raise ValueError("Rule not found") elif Stype[0] == 'A': - return BranchingRule(Rtype, Stype, lambda x : list(x)[:r], "levi") + return BranchingRule(Rtype, Stype, lambda x: list(x)[:r], "levi") else: raise ValueError("Rule not found") elif Rtype[0] in ['B', 'C', 'D']: if Stype.is_atomic(): if Stype[0] == 'A': - return BranchingRule(Rtype, Stype, lambda x : x, "levi") + return BranchingRule(Rtype, Stype, lambda x: x, "levi") elif Stype[0] == Rtype[0]: - return BranchingRule(Rtype, Stype, lambda x : list(x)[1:], "levi") + return BranchingRule(Rtype, Stype, lambda x: list(x)[1:], "levi") elif stypes[-1][0] == Rtype[0] and all(t[0] == 'A' for t in stypes[:-1]): - return BranchingRule(Rtype, Stype, lambda x : x, "levi") + return BranchingRule(Rtype, Stype, lambda x: x, "levi") else: raise ValueError("Rule not found") elif Rtype == CartanType("E6"): if Stype == CartanType("D5"): - return BranchingRule(Rtype, Stype, lambda x : [-x[4],-x[3],-x[2],-x[1],-x[0]], "levi") - elif Stype == CartanType("A5"): # non-maximal levi + return BranchingRule(Rtype, Stype, lambda x: [-x[4],-x[3],-x[2],-x[1],-x[0]], "levi") + elif Stype == CartanType("A5"): # non-maximal levi return branching_rule("E6","A5xA1","extended")*branching_rule("A5xA1","A5","proj1") elif Stype.is_compound(): - if Stype[0] == CartanType("A4") and Stype[1] == CartanType("A1"): # non-maximal levi + if Stype[0] == CartanType("A4") and Stype[1] == CartanType("A1"): # non-maximal levi return branching_rule("E6","A5xA1","extended")*branching_rule("A5xA1","A4xA1",[branching_rule("A5","A4","levi"),"identity"]) - if Stype[0] == CartanType("A1") and Stype[1] == CartanType("A4"): # non-maximal levi + if Stype[0] == CartanType("A1") and Stype[1] == CartanType("A4"): # non-maximal levi return branching_rule("E6","A1xA5","extended")*branching_rule("A1xA5","A1xA4",["identity",branching_rule("A5","A4","levi")]) - elif Stype[0] == CartanType("A2") and Stype[1] == CartanType("A2") and Stype[2]== CartanType("A1"): # non-maximal levi + elif Stype[0] == CartanType("A2") and Stype[1] == CartanType("A2") and Stype[2] == CartanType("A1"): # non-maximal levi return branching_rule("E6","A2xA2xA2","extended")*branching_rule("A2xA2xA2","A2xA2xA2",["identity","identity",branching_rule("A2","A2","automorphic")*branching_rule("A2","A1","levi")]) - elif Stype[0] == CartanType("A2") and Stype[1] == CartanType("A1") and Stype[2]== CartanType("A2"): # non-maximal levi + elif Stype[0] == CartanType("A2") and Stype[1] == CartanType("A1") and Stype[2] == CartanType("A2"): # non-maximal levi raise ValueError("Not implemented: use A2xA2xA1 levi or A2xA2xA2 extended rule. (Non-maximal Levi.)") - elif Stype[0] == CartanType("A1") and Stype[1] == CartanType("A2") and Stype[2]== CartanType("A2"): # non-maximal levi + elif Stype[0] == CartanType("A1") and Stype[1] == CartanType("A2") and Stype[2] == CartanType("A2"): # non-maximal levi raise ValueError("Not implemented: use A2xA2xA1 levi or A2xA2xA2 extended rule. (Non-maximal Levi.)") elif Rtype == CartanType("E7"): if Stype == CartanType("D6"): - return branching_rule("E7","D6xA1","extended")*branching_rule("D6xA1","D6","proj1") # non-maximal levi + return branching_rule("E7","D6xA1","extended")*branching_rule("D6xA1","D6","proj1") # non-maximal levi if Stype == CartanType("E6"): - return BranchingRule(Rtype, Stype, lambda x : [x[0], x[1], x[2], x[3], x[4], (x[5]+x[6]-x[7])/3, (2*x[5]+5*x[6]+x[7])/6, (-2*x[5]+x[6]+5*x[7])/6], "levi") - elif Stype == CartanType("A6"): # non-maximal levi + return BranchingRule(Rtype, Stype, lambda x: [x[0], x[1], x[2], x[3], x[4], (x[5]+x[6]-x[7])/3, (2*x[5]+5*x[6]+x[7])/6, (-2*x[5]+x[6]+5*x[7])/6], "levi") + elif Stype == CartanType("A6"): # non-maximal levi return branching_rule("E7","A7","extended")*branching_rule("A7","A7","automorphic")*branching_rule("A7","A6","levi") if Stype.is_compound(): if Stype[0] == CartanType("A5") and Stype[1] == CartanType("A1"): @@ -1435,42 +1462,47 @@ def br(x): raise NotImplementedError("Not implemented: use A5xA1") elif Rtype == CartanType("E8"): if Stype == CartanType("D7"): - return BranchingRule(Rtype, Stype, lambda x : [-x[6],-x[5],-x[4],-x[3],-x[2],-x[1],-x[0]], "levi") + return BranchingRule(Rtype, Stype, lambda x: [-x[6],-x[5],-x[4],-x[3],-x[2],-x[1],-x[0]], "levi") elif Stype == CartanType("E7"): - return BranchingRule(Rtype, Stype, lambda x : [x[0],x[1],x[2],x[3],x[4],x[5],(x[6]-x[7])/2,(x[7]-x[6])/2], "levi") + return BranchingRule(Rtype, Stype, lambda x: [x[0],x[1],x[2],x[3],x[4],x[5],(x[6]-x[7])/2,(x[7]-x[6])/2], "levi") elif Stype == CartanType("A7"): return branching_rule("E8","A8","extended")*branching_rule("A8","A7","levi") raise NotImplementedError("Not implemented yet: branch first using extended rule to get non-maximal levis") elif Rtype == CartanType("F4"): if Stype == CartanType("B3"): - return BranchingRule(Rtype, Stype, lambda x : x[1:], "levi") + return BranchingRule(Rtype, Stype, lambda x: x[1:], "levi") elif Stype == CartanType("C3"): - return BranchingRule(Rtype, Stype, lambda x : [x[1]-x[0],x[2]+x[3],x[2]-x[3]], "levi") + return BranchingRule(Rtype, Stype, lambda x: [x[1]-x[0],x[2]+x[3],x[2]-x[3]], "levi") else: raise NotImplementedError("Not implemented yet") elif Rtype == CartanType("G2") and Stype == CartanType("A1"): - return BranchingRule(Rtype, Stype, lambda x : list(x)[1:][:2], "levi") + return BranchingRule(Rtype, Stype, lambda x: list(x)[1:][:2], "levi") else: raise ValueError("Rule not found") elif rule == "automorphic": if not Rtype == Stype: raise ValueError("Cartan types must agree for automorphic branching rule") elif Rtype[0] == 'A': - def rule(x) : y = [-i for i in x]; y.reverse(); return y + def rule(x): + y = [-i for i in x] + y.reverse() + return y return BranchingRule(Rtype, Stype, rule, "automorphic") elif Rtype[0] == 'D': - def rule(x) : x[len(x)-1] = -x[len(x)-1]; return x + def rule(x): + x[len(x) - 1] = -x[len(x) - 1] + return x return BranchingRule(Rtype, Stype, rule, "automorphic") elif Rtype[0] == 'E' and r == 6: - M = matrix(QQ,[(3, 3, 3, -3, 0, 0, 0, 0), \ - (3, 3, -3, 3, 0, 0, 0, 0), \ - (3, -3, 3, 3, 0, 0, 0, 0), \ - (-3, 3, 3, 3, 0, 0, 0, 0), \ - (0, 0, 0, 0, -3, -3, -3, 3), \ - (0, 0, 0, 0, -3, 5, -1, 1), \ - (0, 0, 0, 0, -3, -1, 5, 1), \ + M = matrix(QQ,[(3, 3, 3, -3, 0, 0, 0, 0), + (3, 3, -3, 3, 0, 0, 0, 0), + (3, -3, 3, 3, 0, 0, 0, 0), + (-3, 3, 3, 3, 0, 0, 0, 0), + (0, 0, 0, 0, -3, -3, -3, 3), + (0, 0, 0, 0, -3, 5, -1, 1), + (0, 0, 0, 0, -3, -1, 5, 1), (0, 0, 0, 0, 3, 1, 1, 5)])/6 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "automorphic") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "automorphic") else: raise ValueError("No automorphism found") elif rule == "triality": @@ -1479,23 +1511,25 @@ def rule(x) : x[len(x)-1] = -x[len(x)-1]; return x elif not Rtype[0] == 'D' and r == 4: raise ValueError("Triality is for D4 only") else: - return BranchingRule(Rtype, Stype, lambda x : [(x[0]+x[1]+x[2]+x[3])/2,(x[0]+x[1]-x[2]-x[3])/2,(x[0]-x[1]+x[2]-x[3])/2,(-x[0]+x[1]+x[2]-x[3])/2], "triality") + return BranchingRule(Rtype, Stype, lambda x: [(x[0]+x[1]+x[2]+x[3])/2,(x[0]+x[1]-x[2]-x[3])/2,(x[0]-x[1]+x[2]-x[3])/2,(-x[0]+x[1]+x[2]-x[3])/2], "triality") elif rule == "symmetric": if Rtype[0] == 'A': if (Stype[0] == 'C' or Stype[0] == 'D' and r == 2*s-1) or (Stype[0] == 'B' and r == 2*s): - return BranchingRule(Rtype, Stype, lambda x : [x[i]-x[r-i] for i in range(s)], "symmetric") + return BranchingRule(Rtype, Stype, lambda x: [x[i]-x[r-i] for i in range(s)], "symmetric") else: raise ValueError("Rule not found") elif Rtype[0] == 'D' and Stype[0] == 'B' and s == r-1: - return BranchingRule(Rtype, Stype, lambda x : x[:s], "symmetric") + return BranchingRule(Rtype, Stype, lambda x: x[:s], "symmetric") elif Rtype == CartanType("D4") and Stype == CartanType("G2"): - return BranchingRule(Rtype, Stype, lambda x : [x[0]+x[1], -x[1]+x[2], -x[0]-x[2]], "symmetric") + return BranchingRule(Rtype, Stype, lambda x: [x[0]+x[1], -x[1]+x[2], -x[0]-x[2]], "symmetric") elif Rtype == CartanType("E6") and Stype == CartanType("F4"): - return BranchingRule(Rtype, Stype, lambda x : [(x[4]-3*x[5])/2,(x[0]+x[1]+x[2]+x[3])/2,(-x[0]-x[1]+x[2]+x[3])/2,(-x[0]+x[1]-x[2]+x[3])/2], "symmetric") + return BranchingRule(Rtype, Stype, lambda x: [(x[4]-3*x[5])/2,(x[0]+x[1]+x[2]+x[3])/2,(-x[0]-x[1]+x[2]+x[3])/2,(-x[0]+x[1]-x[2]+x[3])/2], "symmetric") elif Rtype == CartanType("E6") and Stype == CartanType("C4"): def f(x): - [x0,x1,x2,x3,x4,x5]=x[:6] - return [(x0+x1+x2+x3+x4-3*x5)/2, (-x0-x1-x2-x3+x4-3*x5)/2, -x0+x3, -x1+x2] + [x0, x1, x2, x3, x4, x5] = x[:6] + return [(x0+x1+x2+x3+x4-3*x5)/2, + (-x0-x1-x2-x3+x4-3*x5)/2, + -x0 + x3, -x1 + x2] return BranchingRule(Rtype, Stype, f, "symmetric") else: raise ValueError("Rule not found") @@ -1515,109 +1549,116 @@ def f(x): else: sdeg += 2*t[1]+1 if rdeg == sdeg: - return BranchingRule(Rtype, Stype, lambda x : x[:s], "orthogonal_sum") + return BranchingRule(Rtype, Stype, lambda x: x[:s], "orthogonal_sum") else: raise ValueError("Rule not found") elif Rtype[0] == 'C': if all(t[0] == Rtype[0] for t in stypes): - return BranchingRule(Rtype, Stype, lambda x : x, "orthogonal_sum") + return BranchingRule(Rtype, Stype, lambda x: x, "orthogonal_sum") if rule == "orthogonal_sum": raise ValueError("Rule not found") elif Rtype[0] == 'E': if r == 6: if stypes == [CartanType("A5"),CartanType("A1")]: - M = matrix(QQ,[(-3, -3, -3, -3, -3, -5, -5, 5), \ - (-9, 3, 3, 3, 3, 1, 1, -1), \ - (3, -9, 3, 3, 3, 1, 1, -1), \ - (3, 3, -9, 3, 3, 1, 1, -1), \ - (3, 3, 3, -9, 3, 1, 1, -1), \ - (3, 3, 3, 3, -9, 9, -3, 3), \ - (-3, -3, -3, -3, -3, -1, 11, 1), \ + M = matrix(QQ,[(-3, -3, -3, -3, -3, -5, -5, 5), + (-9, 3, 3, 3, 3, 1, 1, -1), + (3, -9, 3, 3, 3, 1, 1, -1), + (3, 3, -9, 3, 3, 1, 1, -1), + (3, 3, 3, -9, 3, 1, 1, -1), + (3, 3, 3, 3, -9, 9, -3, 3), + (-3, -3, -3, -3, -3, -1, 11, 1), (3, 3, 3, 3, 3, 1, 1, 11)])/12 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") if stypes == [CartanType("A1"),CartanType("A5")]: - M = matrix(QQ,[(-3, -3, -3, -3, -3, -1, 11, 1), \ - (3, 3, 3, 3, 3, 1, 1, 11), \ - (-3, -3, -3, -3, -3, -5, -5, 5), \ - (-9, 3, 3, 3, 3, 1, 1, -1), \ - (3, -9, 3, 3, 3, 1, 1, -1), \ - (3, 3, -9, 3, 3, 1, 1, -1), \ - (3, 3, 3, -9, 3, 1, 1, -1), \ + M = matrix(QQ,[(-3, -3, -3, -3, -3, -1, 11, 1), + (3, 3, 3, 3, 3, 1, 1, 11), + (-3, -3, -3, -3, -3, -5, -5, 5), + (-9, 3, 3, 3, 3, 1, 1, -1), + (3, -9, 3, 3, 3, 1, 1, -1), + (3, 3, -9, 3, 3, 1, 1, -1), + (3, 3, 3, -9, 3, 1, 1, -1), (3, 3, 3, 3, -9, 9, -3, 3)])/12 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") if stypes == [CartanType("A2"),CartanType("A2"),CartanType("A2")]: - M = matrix(QQ,[(0, 0, -2, -2, -2, -2, -2, 2), \ - (-3, 3, 1, 1, 1, 1, 1, -1), \ - (3, -3, 1, 1, 1, 1, 1, -1), \ - (0, 0, -2, -2, 4, 0, 0, 0), \ - (0, 0, -2, 4, -2, 0, 0, 0), \ - (0, 0, 4, -2, -2, 0, 0, 0), \ - (0, 0, -2, -2, -2, 2, 2, -2), \ - (3, 3, 1, 1, 1, -1, -1, 1), \ + M = matrix(QQ,[(0, 0, -2, -2, -2, -2, -2, 2), + (-3, 3, 1, 1, 1, 1, 1, -1), + (3, -3, 1, 1, 1, 1, 1, -1), + (0, 0, -2, -2, 4, 0, 0, 0), + (0, 0, -2, 4, -2, 0, 0, 0), + (0, 0, 4, -2, -2, 0, 0, 0), + (0, 0, -2, -2, -2, 2, 2, -2), + (3, 3, 1, 1, 1, -1, -1, 1), (-3, -3, 1, 1, 1, -1, -1, 1)])/6 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif r == 7: if stypes == [CartanType("D6"),CartanType("A1")]: - return BranchingRule(Rtype, Stype, lambda x : [x[5],x[4],x[3],x[2],x[1],x[0],x[6],x[7]], "extended") + return BranchingRule(Rtype, Stype, lambda x: [x[5],x[4],x[3],x[2],x[1],x[0],x[6],x[7]], "extended") elif stypes == [CartanType("A1"),CartanType("D6")]: - return BranchingRule(Rtype, Stype, lambda x : [x[6],x[7],x[5],x[4],x[3],x[2],x[1],x[0]], "extended") + return BranchingRule(Rtype, Stype, lambda x: [x[6],x[7],x[5],x[4],x[3],x[2],x[1],x[0]], "extended") elif stypes == [CartanType("A5"),CartanType("A2")]: - M = matrix(QQ,[(5, 1, 1, 1, 1, 1, 0, 0), \ - (-1, -5, 1, 1, 1, 1, 0, 0), \ - (-1, 1, -5, 1, 1, 1, 0, 0), \ - (-1, 1, 1, -5, 1, 1, 0, 0), \ - (-1, 1, 1, 1, -5, 1, 0, 0), \ - (-1, 1, 1, 1, 1, -5, 0, 0), \ - (1, -1, -1, -1, -1, -1, 0, -6), \ - (1, -1, -1, -1, -1, -1, -6, 0), \ + M = matrix(QQ,[(5, 1, 1, 1, 1, 1, 0, 0), + (-1, -5, 1, 1, 1, 1, 0, 0), + (-1, 1, -5, 1, 1, 1, 0, 0), + (-1, 1, 1, -5, 1, 1, 0, 0), + (-1, 1, 1, 1, -5, 1, 0, 0), + (-1, 1, 1, 1, 1, -5, 0, 0), + (1, -1, -1, -1, -1, -1, 0, -6), + (1, -1, -1, -1, -1, -1, -6, 0), (-2, 2, 2, 2, 2, 2, -3, -3)])/6 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif stypes == [CartanType("A3"),CartanType("A3"),CartanType("A1")]: - M = matrix(QQ, [(0, 0, -1, -1, -1, -1, 2, -2), \ - (0, 0, -1, -1, -1, -1, -2, 2), \ - (-2, 2, 1, 1, 1, 1, 0, 0), \ - (2, -2, 1, 1, 1, 1, 0, 0), \ - (0, 0, -1, -1, -1, 3, 0, 0), \ - (0, 0, -1, -1, 3, -1, 0, 0), \ - (0, 0, -1, 3, -1, -1, 0, 0), \ - (0, 0, 3, -1, -1, -1, 0, 0), \ - (2, 2, 0, 0, 0, 0, -2, -2), \ + M = matrix(QQ, [(0, 0, -1, -1, -1, -1, 2, -2), + (0, 0, -1, -1, -1, -1, -2, 2), + (-2, 2, 1, 1, 1, 1, 0, 0), + (2, -2, 1, 1, 1, 1, 0, 0), + (0, 0, -1, -1, -1, 3, 0, 0), + (0, 0, -1, -1, 3, -1, 0, 0), + (0, 0, -1, 3, -1, -1, 0, 0), + (0, 0, 3, -1, -1, -1, 0, 0), + (2, 2, 0, 0, 0, 0, -2, -2), (-2, -2, 0, 0, 0, 0, -2, -2)])/4 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif r == 8: if stypes == [CartanType("A4"),CartanType("A4")]: - M = matrix(QQ,[(0, 0, 0, -4, -4, -4, -4, 4), \ - (-5, 5, 5, 1, 1, 1, 1, -1), \ - (5, -5, 5, 1, 1, 1, 1, -1), \ - (5, 5, -5, 1, 1, 1, 1, -1), \ - (-5, -5, -5, 1, 1, 1, 1, -1), \ - (0, 0, 0, -8, 2, 2, 2, -2), \ - (0, 0, 0, 2, -8, 2, 2, -2), \ - (0, 0, 0, 2, 2, -8, 2, -2), \ - (0, 0, 0, 2, 2, 2, -8, -2), \ + M = matrix(QQ,[(0, 0, 0, -4, -4, -4, -4, 4), + (-5, 5, 5, 1, 1, 1, 1, -1), + (5, -5, 5, 1, 1, 1, 1, -1), + (5, 5, -5, 1, 1, 1, 1, -1), + (-5, -5, -5, 1, 1, 1, 1, -1), + (0, 0, 0, -8, 2, 2, 2, -2), + (0, 0, 0, 2, -8, 2, 2, -2), + (0, 0, 0, 2, 2, -8, 2, -2), + (0, 0, 0, 2, 2, 2, -8, -2), (0, 0, 0, 2, 2, 2, 2, 8)])/10 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") - elif len(stypes)==3: - if 5 in stypes[0][i]: # S is A5xA2xA1 + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") + elif len(stypes) == 3: + if 5 in stypes[0][i]: # S is A5xA2xA1 raise NotImplementedError("Not maximal: first branch to A7xA1") elif stypes == [CartanType("D5"), CartanType("A3")]: - raise NotImplementedError("Not maximal: first branch to D8 then D5xD3=D5xA3") + raise NotImplementedError("Not maximal: first branch to D8 then D5xD3=D5xA3") elif stypes == [CartanType("A3"), CartanType("D5")]: raise NotImplementedError("Not maximal: first branch to D8 then D5xD3=D5xA3") elif stypes == [CartanType("E6"), CartanType("A2")]: - br = lambda x : [x[0],x[1],x[2],x[3],x[4], \ - (x[5]+x[6]-x[7])/3,(x[5]+x[6]-x[7])/3,(-x[5]-x[6]+x[7])/3, \ - (-x[5]-x[6]-2*x[7])/3,(-x[5]+2*x[6]+x[7])/3,(2*x[5]-x[6]+x[7])/3] + def br(x): + return [x[0], x[1], x[2], x[3], x[4], + (x[5]+x[6]-x[7])/3,(x[5]+x[6]-x[7])/3, + (-x[5]-x[6]+x[7])/3, + (-x[5]-x[6]-2*x[7])/3, + (-x[5]+2*x[6]+x[7])/3, + (2*x[5]-x[6]+x[7])/3] return BranchingRule(Rtype, Stype, br, "extended") elif stypes == [CartanType("E7"), CartanType("A1")]: - br = lambda x : [x[0],x[1],x[2],x[3],x[4],x[5],(x[6]-x[7])/2,(-x[6]+x[7])/2,(-x[6]-x[7])/2,(x[6]+x[7])/2] + def br(x): + return [x[0], x[1], x[2], x[3], x[4], x[5], + (x[6]-x[7])/2, (-x[6]+x[7])/2, + (-x[6]-x[7])/2, (x[6]+x[7])/2] return BranchingRule(Rtype, Stype, br, "extended") raise ValueError("Rule not found") elif Rtype[0] == 'F': if stypes == [CartanType("C3"), CartanType("A1")]: - return BranchingRule(Rtype, Stype, lambda x : [x[0]-x[1],x[2]+x[3],x[2]-x[3],(-x[0]-x[1])/2,(x[0]+x[1])/2], "extended") + return BranchingRule(Rtype, Stype, lambda x: [x[0]-x[1],x[2]+x[3],x[2]-x[3],(-x[0]-x[1])/2,(x[0]+x[1])/2], "extended") elif stypes == [CartanType("A1"), CartanType("C3")]: - return BranchingRule(Rtype, Stype, lambda x : [(-x[0]-x[1])/2,(x[0]+x[1])/2,x[0]-x[1],x[2]+x[3],x[2]-x[3]], "extended") + return BranchingRule(Rtype, Stype, lambda x: [(-x[0]-x[1])/2,(x[0]+x[1])/2,x[0]-x[1],x[2]+x[3],x[2]-x[3]], "extended") elif stypes == [CartanType("A2"), CartanType("A2")]: M = matrix(QQ,[(-2, -1, -1, 0), (1, 2, -1, 0), (1, -1, 2, 0), (1, -1, -1, 3), (1, -1, -1, -3), (-2, 2, 2, 0)])/3 elif stypes == [CartanType("A3"), CartanType("A1")]: @@ -1626,72 +1667,83 @@ def f(x): M = matrix(QQ,[(2, -2, -2, -2), (-2, 2, 2, 2), (-3, -1, -1, -1), (1, 3, -1, -1), (1, -1, 3, -1), (1, -1, -1, 3)])/4 else: raise ValueError("Rule not found") - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif Rtype[0] == 'G': if stypes == [CartanType("A1"), CartanType("A1")]: - return BranchingRule(Rtype, Stype, lambda x : [(x[1]-x[2])/2,-(x[1]-x[2])/2, x[0]/2, -x[0]/2], "extended") + return BranchingRule(Rtype, Stype, lambda x: [(x[1]-x[2])/2,-(x[1]-x[2])/2, x[0]/2, -x[0]/2], "extended") raise ValueError("Rule not found") - else: # irreducible Stype + else: # irreducible Stype if Rtype[0] == 'B' and Stype[0] == 'D': - return BranchingRule(Rtype, Stype, lambda x : x, "extended") + return BranchingRule(Rtype, Stype, lambda x: x, "extended") elif Rtype == CartanType("E7"): if Stype == CartanType("A7"): - M = matrix(QQ, [(-1, -1, -1, -1, -1, -1, 2, -2), \ - (-1, -1, -1, -1, -1, -1, -2, 2), \ - (-3, 1, 1, 1, 1, 1, 0, 0), \ - (1, -3, 1, 1, 1, 1, 0, 0), \ - (1, 1, -3, 1, 1, 1, 0, 0), \ - (1, 1, 1, -3, 1, 1, 0, 0), \ - (1, 1, 1, 1, -3, 1, 2, 2), \ + M = matrix(QQ, [(-1, -1, -1, -1, -1, -1, 2, -2), + (-1, -1, -1, -1, -1, -1, -2, 2), + (-3, 1, 1, 1, 1, 1, 0, 0), + (1, -3, 1, 1, 1, 1, 0, 0), + (1, 1, -3, 1, 1, 1, 0, 0), + (1, 1, 1, -3, 1, 1, 0, 0), + (1, 1, 1, 1, -3, 1, 2, 2), (1, 1, 1, 1, 1, -3, 2, 2)])/4 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif Rtype == CartanType("E8"): if Stype == CartanType("D8"): - return BranchingRule(Rtype, Stype, lambda x : [-x[7],x[6],x[5],x[4],x[3],x[2],x[1],x[0]], "extended") + return BranchingRule(Rtype, Stype, lambda x: [-x[7],x[6],x[5],x[4],x[3],x[2],x[1],x[0]], "extended") elif Stype == CartanType("A8"): - M = matrix([(-2, -2, -2, -2, -2, -2, -2, 2), \ - (-5, 1, 1, 1, 1, 1, 1, -1), \ - (1, -5, 1, 1, 1, 1, 1, -1), \ - (1, 1, -5, 1, 1, 1, 1, -1), \ - (1, 1, 1, -5, 1, 1, 1, -1), \ - (1, 1, 1, 1, -5, 1, 1, -1), \ - (1, 1, 1, 1, 1, -5, 1, -1), \ - (1, 1, 1, 1, 1, 1, -5, -1), \ + M = matrix([(-2, -2, -2, -2, -2, -2, -2, 2), + (-5, 1, 1, 1, 1, 1, 1, -1), + (1, -5, 1, 1, 1, 1, 1, -1), + (1, 1, -5, 1, 1, 1, 1, -1), + (1, 1, 1, -5, 1, 1, 1, -1), + (1, 1, 1, 1, -5, 1, 1, -1), + (1, 1, 1, 1, 1, -5, 1, -1), + (1, 1, 1, 1, 1, 1, -5, -1), (1, 1, 1, 1, 1, 1, 1, 5)])/6 - return BranchingRule(Rtype, Stype, lambda x : tuple(M*vector(x)), "extended") + return BranchingRule(Rtype, Stype, lambda x: tuple(M*vector(x)), "extended") elif Rtype == CartanType("F4") and Stype == CartanType("B4"): - return BranchingRule(Rtype, Stype, lambda x : [-x[0], x[1], x[2], x[3]], "extended") + return BranchingRule(Rtype, Stype, lambda x: [-x[0], x[1], x[2], x[3]], "extended") elif Rtype == CartanType("G2") and Stype == CartanType("A2"): - return BranchingRule(Rtype, Stype, lambda x : [(-x[1]+x[2])/3, (-x[0]+x[1])/3, (x[0]-x[2])/3], "extended") + return BranchingRule(Rtype, Stype, lambda x: [(-x[1]+x[2])/3, (-x[0]+x[1])/3, (x[0]-x[2])/3], "extended") else: raise ValueError("Rule not found") elif rule == "isomorphic": if r != s: raise ValueError("Incompatible ranks") if Rtype == Stype: - return BranchingRule(Rtype, Stype, lambda x : x, "isomorphic") + return BranchingRule(Rtype, Stype, lambda x: x, "isomorphic") elif Rtype == CartanType("B2") and Stype == CartanType("C2"): - def rule(x) : [x1, x2] = x; return [x1+x2, x1-x2] + def rule(x): + [x1, x2] = x + return [x1 + x2, x1 - x2] return BranchingRule(Rtype, Stype, rule, "isomorphic") elif Rtype == CartanType("C2") and Stype == CartanType("B2"): - def rule(x) : [x1, x2] = x; return [(x1+x2)/2, (x1-x2)/2] + def rule(x): + [x1, x2] = x + return [(x1 + x2) / 2, (x1 - x2) / 2] return BranchingRule(Rtype, Stype, rule, "isomorphic") elif Rtype == CartanType("B1") and Stype == CartanType("A1"): - return BranchingRule(Rtype, Stype, lambda x : [x[0],-x[0]], "isomorphic") + return BranchingRule(Rtype, Stype, lambda x: [x[0],-x[0]], "isomorphic") elif Rtype == CartanType("A1") and Stype == CartanType("B1"): - return BranchingRule(Rtype, Stype, lambda x : [(x[0]-x[1])/2], "isomorphic") + return BranchingRule(Rtype, Stype, lambda x: [(x[0]-x[1])/2], "isomorphic") elif Rtype == CartanType("C1") and Stype == CartanType("A1"): - return BranchingRule(Rtype, Stype, lambda x : [x[0]/2,-x[0]/2], "isomorphic") + return BranchingRule(Rtype, Stype, lambda x: [x[0]/2,-x[0]/2], "isomorphic") elif Rtype == CartanType("A1") and Stype == CartanType("C1"): - return BranchingRule(Rtype, Stype, lambda x : [x[0]-x[1]], "isomorphic") + return BranchingRule(Rtype, Stype, lambda x: [x[0]-x[1]], "isomorphic") elif Rtype == CartanType("A3") and Stype == CartanType("D3"): - def rule(x): [x1, x2, x3, x4] = x; return [(x1+x2-x3-x4)/2, (x1-x2+x3-x4)/2, (x1-x2-x3+x4)/2] + def rule(x): + [x1, x2, x3, x4] = x + return [(x1+x2-x3-x4)/2, (x1-x2+x3-x4)/2, (x1-x2-x3+x4)/2] return BranchingRule(Rtype, Stype, rule, "isomorphic") elif Rtype == CartanType("D3") and Stype == CartanType("A3"): - def rule(x): [t1, t2, t3] = x; return [(t1+t2+t3)/2, (t1-t2-t3)/2, (-t1+t2-t3)/2, (-t1-t2+t3)/2] + def rule(x): + [t1, t2, t3] = x + return [(t1+t2+t3)/2, (t1-t2-t3)/2, + (-t1+t2-t3)/2, (-t1-t2+t3)/2] return BranchingRule(Rtype, Stype, rule, "isomorphic") elif Rtype == CartanType("D2") and Stype == CartanType("A1xA1"): - def rule(x): [t1, t2] = x; return [(t1-t2)/2, -(t1-t2)/2, (t1+t2)/2, -(t1+t2)/2] + def rule(x): + [t1, t2] = x + return [(t1-t2)/2, -(t1-t2)/2, (t1+t2)/2, -(t1+t2)/2] return BranchingRule(Rtype, Stype, rule, "isomorphic") else: raise ValueError("Rule not found") @@ -1773,7 +1825,7 @@ def rule(x): mat = matrix(rows).transpose() if rule == "tensor-debug": print(mat) - return BranchingRule(Rtype, Stype, lambda x : tuple(mat*vector(x)), "tensor") + return BranchingRule(Rtype, Stype, lambda x: tuple(mat*vector(x)), "tensor") elif rule == "symmetric_power": if Stype[0] == 'A' and s == 1: if Rtype[0] == 'B': @@ -1788,98 +1840,121 @@ def rule(x): return BranchingRule(Rtype, Stype, rule, "symmetric_power") elif rule == "miscellaneous": if Rtype[0] == 'B' and Stype[0] == 'G' and r == 3: - return BranchingRule(Rtype, Stype, lambda x : [x[0]+x[1], -x[1]+x[2], -x[0]-x[2]], "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[0]+x[1], -x[1]+x[2], -x[0]-x[2]], "miscellaneous") elif Rtype == CartanType("E6"): if Stype.is_compound(): if stypes == [CartanType("A2"),CartanType("G2")]: - return BranchingRule(Rtype, Stype, lambda x : [-2*x[5],x[5]+x[4],x[5]-x[4],x[2]+x[3],x[1]-x[2],-x[1]-x[3]], "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [-2*x[5],x[5]+x[4],x[5]-x[4],x[2]+x[3],x[1]-x[2],-x[1]-x[3]], "miscellaneous") elif stypes == [CartanType("G2"),CartanType("A2")]: - return BranchingRule(Rtype, Stype, lambda x : [x[2]+x[3],x[1]-x[2],-x[1]-x[3],-2*x[5],x[5]+x[4],x[5]-x[4]], "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[2]+x[3],x[1]-x[2],-x[1]-x[3],-2*x[5],x[5]+x[4],x[5]-x[4]], "miscellaneous") else: if Stype == CartanType("G2"): - return BranchingRule(Rtype, Stype, lambda x : [x[2]+x[3]+x[4]-3*x[5], x[1]-2*x[2]-x[3], -x[1]+x[2]-x[4]+3*x[5]],"miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[2]+x[3]+x[4]-3*x[5], x[1]-2*x[2]-x[3], -x[1]+x[2]-x[4]+3*x[5]],"miscellaneous") if Stype == CartanType("A2"): - return BranchingRule(Rtype, Stype, lambda x : [x[2]+x[3]+x[4]-3*x[5], x[1]-2*x[2]-x[3], -x[1]+x[2]-x[4]+3*x[5]],"miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[2]+x[3]+x[4]-3*x[5], x[1]-2*x[2]-x[3], -x[1]+x[2]-x[4]+3*x[5]],"miscellaneous") elif Rtype == CartanType("E7"): if Stype.is_compound(): - if stypes == [CartanType("C3"),CartanType("G2")]: - return BranchingRule(Rtype, Stype, lambda x : [-2*x[6],x[4]+x[5],-x[4]+x[5],x[1]+x[3],x[2]-x[3],-x[1]-x[2]],"miscellaneous") - elif stypes == [CartanType("G2"),CartanType("C3")]: - return BranchingRule(Rtype, Stype, lambda x : [x[1]+x[3],x[2]-x[3],-x[1]-x[2],-2*x[6],x[4]+x[5],-x[4]+x[5]],"miscellaneous") - elif stypes == [CartanType("F4"),CartanType("A1")]: - def f(x) : - [x0,x1,x2,x3,x4,x5,x6] = x[:7] - return [(x4-x5)/2-x6,(x0+x1+x2+x3)/2,(-x0-x1+x2+x3)/2,(-x0+x1-x2+x3)/2,x5-x6,x6-x5] + if stypes == [CartanType("C3"), CartanType("G2")]: + return BranchingRule(Rtype, Stype, lambda x: [-2*x[6],x[4]+x[5],-x[4]+x[5],x[1]+x[3],x[2]-x[3],-x[1]-x[2]], "miscellaneous") + elif stypes == [CartanType("G2"), CartanType("C3")]: + return BranchingRule(Rtype, Stype, lambda x: [x[1]+x[3],x[2]-x[3],-x[1]-x[2],-2*x[6],x[4]+x[5],-x[4]+x[5]], "miscellaneous") + elif stypes == [CartanType("F4"), CartanType("A1")]: + def f(x): + [x0, x1, x2, x3, x4, x5, x6] = x[:7] + return [(x4-x5)/2-x6, (x0+x1+x2+x3)/2, + (-x0-x1+x2+x3)/2, (-x0+x1-x2+x3)/2, + x5-x6, x6-x5] return BranchingRule(Rtype, Stype, f, "miscellaneous") - elif stypes == [CartanType("A1"),CartanType("F4")]: - def f(x) : - [x0,x1,x2,x3,x4,x5,x6] = x[:7] - return [x5-x6,x6-x5,(x4-x5)/2-x6,(x0+x1+x2+x3)/2,(-x0-x1+x2+x3)/2,(-x0+x1-x2+x3)/2] + elif stypes == [CartanType("A1"), CartanType("F4")]: + def f(x): + [x0, x1, x2, x3, x4, x5, x6] = x[:7] + return [x5-x6, x6-x5, (x4-x5)/2-x6, + (x0+x1+x2+x3)/2, + (-x0-x1+x2+x3)/2, + (-x0+x1-x2+x3)/2] return BranchingRule(Rtype, Stype, f, "miscellaneous") - elif stypes == [CartanType("A1"),CartanType("A1")]: - return BranchingRule(Rtype, Stype, lambda x : [x[1]+2*x[2]-2*x[3]-x[4]-2*x[6], -x[1]-2*x[2]+2*x[3]+x[4]+2*x[6], \ - (x[3]+x[4]+x[5]-3*x[6]),-(x[3]+x[4]+x[5]-3*x[6])], "miscellaneous") - elif stypes == [CartanType("G2"),CartanType("A1")]: - f = lambda x : [(x[0]-x[1]+x[2]+3*x[3]+x[4]-x[5]+2*x[6])/2,(-3*x[0]-x[1]-x[2]-x[3]+x[4]+x[5]-2*x[6])/2,(2*x[0]+2*x[1]-2*x[3]-2*x[4])/2, \ - (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2,-(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2] + elif stypes == [CartanType("A1"), CartanType("A1")]: + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+2*x[2]-2*x[3]-x[4]-2*x[6], -x[1]-2*x[2]+2*x[3]+x[4]+2*x[6], + (x[3]+x[4]+x[5]-3*x[6]),-(x[3]+x[4]+x[5]-3*x[6])], "miscellaneous") + elif stypes == [CartanType("G2"), CartanType("A1")]: + def f(x): + return [(x[0]-x[1]+x[2]+3*x[3]+x[4]-x[5]+2*x[6])/2, + (-3*x[0]-x[1]-x[2]-x[3]+x[4]+x[5]-2*x[6])/2, + (2*x[0]+2*x[1]-2*x[3]-2*x[4])/2, + (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2, + -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2] return BranchingRule(Rtype, Stype, f, "miscellaneous") - elif stypes == [CartanType("A1"),CartanType("G2")]: - f = lambda x : [(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2,-(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2, \ - (x[0]-x[1]+x[2]+3*x[3]+x[4]-x[5]+2*x[6])/2,(-3*x[0]-x[1]-x[2]-x[3]+x[4]+x[5]-2*x[6])/2,(2*x[0]+2*x[1]-2*x[3]-2*x[4])/2] + elif stypes == [CartanType("A1"), CartanType("G2")]: + def f(x): + return [(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2, + -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]-4*x[6])/2, + (x[0]-x[1]+x[2]+3*x[3]+x[4]-x[5]+2*x[6])/2, + (-3*x[0]-x[1]-x[2]-x[3]+x[4]+x[5]-2*x[6])/2, + (2*x[0]+2*x[1]-2*x[3]-2*x[4])/2] return BranchingRule(Rtype, Stype, f, "miscellaneous") elif Stype == CartanType("A2"): - return BranchingRule(Rtype, Stype, lambda x : (x[1]+x[2]+2*x[4]-4*x[6],-2*x[1]-x[2]+x[3]-2*x[4]+2*x[5],x[1]-x[3]-2*x[5]+4*x[6]), "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: (x[1]+x[2]+2*x[4]-4*x[6],-2*x[1]-x[2]+x[3]-2*x[4]+2*x[5],x[1]-x[3]-2*x[5]+4*x[6]), "miscellaneous") elif Rtype == CartanType("E8"): if Stype.is_compound(): if stypes == [CartanType("F4"),CartanType("G2")]: - return BranchingRule(Rtype, Stype, lambda x : [x[7], x[6], x[5], x[4], x[1]+x[3], -x[3]+x[2], -x[1]-x[2]], "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[7], x[6], x[5], x[4], x[1]+x[3], -x[3]+x[2], -x[1]-x[2]], "miscellaneous") elif stypes == [CartanType("G2"),CartanType("F4")]: - return BranchingRule(Rtype, Stype, lambda x : [x[1]+x[3], -x[3]+x[2], -x[1]-x[2], x[7], x[6], x[5], x[4]], "miscellaneous") + return BranchingRule(Rtype, Stype, lambda x: [x[1]+x[3], -x[3]+x[2], -x[1]-x[2], x[7], x[6], x[5], x[4]], "miscellaneous") elif stypes == [CartanType("A2"), CartanType("A1")]: - f = lambda x : [(x[0]-x[1]+x[2]+x[3]+3*x[4]+x[5]-x[6]-x[7])/2,(-3*x[0]-x[1]-x[2]-x[3]-x[4]+x[5]+x[6]+x[7])/2,(2*x[0]+2*x[1]-2*x[4]-2*x[5])/2, \ - (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2] + def f(x): + return [(x[0]-x[1]+x[2]+x[3]+3*x[4]+x[5]-x[6]-x[7])/2, + (-3*x[0]-x[1]-x[2]-x[3]-x[4]+x[5]+x[6]+x[7])/2, + (2*x[0]+2*x[1]-2*x[4]-2*x[5])/2, + (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, + -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2] return BranchingRule("E8","A2xA1",f,"miscellaneous") elif stypes == [CartanType("A1"), CartanType("A2")]: - f = lambda x : [(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, - (x[0]-x[1]+x[2]+x[3]+3*x[4]+x[5]-x[6]-x[7])/2,(-3*x[0]-x[1]-x[2]-x[3]-x[4]+x[5]+x[6]+x[7])/2,(2*x[0]+2*x[1]-2*x[4]-2*x[5])/2] - return BranchingRule("E8","A1xA2",f,"miscellaneous") + def f(x): + return [(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, + -(x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+5*x[7])/2, + (x[0]-x[1]+x[2]+x[3]+3*x[4]+x[5]-x[6]-x[7])/2, + (-3*x[0]-x[1]-x[2]-x[3]-x[4]+x[5]+x[6]+x[7])/2, + (2*x[0]+2*x[1]-2*x[4]-2*x[5])/2] + return BranchingRule("E8", "A1xA2", f, "miscellaneous") elif Stype == CartanType("B2"): - return BranchingRule("E8", "B2", lambda x : [-x[0] + x[2] + x[5] + 3*x[7], 2*x[0] - x[2] + x[3] + x[4] + 2*x[6] + x[7]], "miscellaneous") + return BranchingRule("E8", "B2", lambda x: [-x[0] + x[2] + x[5] + 3*x[7], 2*x[0] - x[2] + x[3] + x[4] + 2*x[6] + x[7]], "miscellaneous") elif Rtype[0] == 'F': if Stype.is_compound(): - if stypes == [CartanType("A1"),CartanType("G2")]: - return BranchingRule("F4","A1xG2", lambda x : [ 2*x[0], -2*x[0], x[1]+x[2], -x[2]+x[3], -x[1]-x[3]], "miscellaneous") - elif stypes == [CartanType("G2"),CartanType("A1")]: - return BranchingRule("F4","G2xA1", lambda x : [x[1]+x[2], -x[2]+x[3], -x[1]-x[3], 2*x[0], -2*x[0]], "miscellaneous") + if stypes == [CartanType("A1"), CartanType("G2")]: + return BranchingRule("F4", "A1xG2", lambda x: [2*x[0], -2*x[0], x[1]+x[2], -x[2]+x[3], -x[1]-x[3]], "miscellaneous") + elif stypes == [CartanType("G2"), CartanType("A1")]: + return BranchingRule("F4","G2xA1", lambda x: [x[1]+x[2], -x[2]+x[3], -x[1]-x[3], 2*x[0], -2*x[0]], "miscellaneous") raise ValueError("Rule not found") - elif rule in ["i","ii","iii","iv","v","vi","vii"]: + elif rule in ["i", "ii", "iii", "iv", "v", "vi", "vii"]: if Stype != CartanType("A1"): - raise ValueError("Wrong target Cartan Type for rule %s"%rule) + raise ValueError("Wrong target Cartan Type for rule %s" % rule) if rule == "i" and Rtype == CartanType("G2"): - return BranchingRule(Rtype, Stype, lambda x : [(5*x[0]-x[1]-4*x[2])/3,-(5*x[0]-x[1]-4*x[2])/3], "i") + return BranchingRule(Rtype, Stype, lambda x: [(5*x[0]-x[1]-4*x[2])/3,-(5*x[0]-x[1]-4*x[2])/3], "i") elif rule == "ii" and Rtype == CartanType("F4"): - return BranchingRule(Rtype, Stype, lambda x : [8*x[0]+3*x[1]+2*x[2]+x[3],-(8*x[0]+3*x[1]+2*x[2]+x[3])], "ii") + return BranchingRule(Rtype, Stype, lambda x: [8*x[0]+3*x[1]+2*x[2]+x[3],-(8*x[0]+3*x[1]+2*x[2]+x[3])], "ii") elif rule == "iii" and Rtype == CartanType("E7"): - return BranchingRule(Rtype, Stype, \ - lambda x : [x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]-17*x[6],-(x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]-17*x[6])],"iii") + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]-17*x[6],-(x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]-17*x[6])], "iii") elif rule == "iv" and Rtype == CartanType("E7"): - return BranchingRule(Rtype, Stype, \ - lambda x: [x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]-13*x[6],-(x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]-13*x[6])],"iv") + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]-13*x[6],-(x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]-13*x[6])], "iv") elif rule == "v" and Rtype == CartanType("E8"): - return BranchingRule(Rtype, Stype, \ - lambda x : [x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]+6*x[6]+23*x[7],-(x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]+6*x[6]+23*x[7])],"v") + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]+6*x[6]+23*x[7],-(x[1]+2*x[2]+3*x[3]+4*x[4]+5*x[5]+6*x[6]+23*x[7])], "v") elif rule == "vi" and Rtype == CartanType("E8"): - return BranchingRule(Rtype, Stype, \ - lambda x : [x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]+5*x[6]+18*x[7],-(x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]+5*x[6]+18*x[7])],"vi") + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]+5*x[6]+18*x[7],-(x[1]+x[2]+2*x[3]+3*x[4]+4*x[5]+5*x[6]+18*x[7])], "vi") elif rule == "vii" and Rtype == CartanType("E8"): - return BranchingRule(Rtype, Stype, \ - lambda x : [x[1]+x[2]+2*x[3]+2*x[4]+3*x[5]+4*x[6]+15*x[7],-(x[1]+x[2]+2*x[3]+2*x[4]+3*x[5]+4*x[6]+15*x[7])],"vii") - raise ValueError("Wrong source Cartan Type for rule %s"%rule) + return BranchingRule(Rtype, Stype, + lambda x: [x[1]+x[2]+2*x[3]+2*x[4]+3*x[5]+4*x[6]+15*x[7],-(x[1]+x[2]+2*x[3]+2*x[4]+3*x[5]+4*x[6]+15*x[7])], "vii") + raise ValueError("Wrong source Cartan Type for rule %s" % rule) raise ValueError("Rule not found") get_branching_rule = branching_rule -def branching_rule_from_plethysm(chi, cartan_type, return_matrix = False): + +def branching_rule_from_plethysm(chi, cartan_type, return_matrix=False): r""" Create the branching rule of a plethysm. @@ -1920,19 +1995,19 @@ def branching_rule_from_plethysm(chi, cartan_type, return_matrix = False): [A2(1,1), A2(0,3) + A2(1,1) + A2(3,0), A2(1,1), A2(1,1)] """ ct = CartanType(cartan_type) - if ct[0] not in ["A","B","C","D"]: + if ct[0] not in ["A", "B", "C", "D"]: raise ValueError("not implemented for type {}".format(ct[0])) if ct[0] == "A": ret = [] ml = chi.weight_multiplicities() for v in ml: n = ml[v] - ret.extend(n*[v.to_vector()]) + ret.extend(n * [v.to_vector()]) M = matrix(ret).transpose() if len(M.columns()) != ct[1] + 1: raise ValueError("representation has wrong degree for type {}".format(ct)) - return BranchingRule(ct, chi.parent().cartan_type(), lambda x : tuple(M*vector(x)), "plethysm (along %s)"%chi) - if ct[0] in ["B","D"]: + return BranchingRule(ct, chi.parent().cartan_type(), lambda x: tuple(M*vector(x)), "plethysm (along %s)" % chi) + if ct[0] in ["B", "D"]: if chi.frobenius_schur_indicator() != 1: raise ValueError("character is not orthogonal") if ct[0] == "C": @@ -1941,7 +2016,7 @@ def branching_rule_from_plethysm(chi, cartan_type, return_matrix = False): if ct[0] == "B": if is_even(chi.degree()): raise ValueError("degree is not odd") - if ct[0] in ["C","D"]: + if ct[0] in ["C", "D"]: if is_odd(chi.degree()): raise ValueError("degree is not even") ret = [] @@ -1949,21 +2024,22 @@ def branching_rule_from_plethysm(chi, cartan_type, return_matrix = False): for v in ml: n = ml[v] vec = v.to_vector() - if all(x==0 for x in vec): + if all(x == 0 for x in vec): if ct[0] == "B": n = (n-1)/2 else: n = n/2 - elif [x for x in vec if x !=0][0] < 0: + elif [x for x in vec if x != 0][0] < 0: continue - ret.extend(n*[vec]) + ret.extend(n * [vec]) M = matrix(ret).transpose() if len(M.columns()) != ct.root_system().ambient_space().dimension(): raise ValueError("representation has wrong degree for type {}".format(ct)) if return_matrix: return M else: - return BranchingRule(ct, chi.parent().cartan_type(), lambda x : tuple(M*vector(x)), "plethysm (along %s)"%chi) + return BranchingRule(ct, chi.parent().cartan_type(), lambda x: tuple(M*vector(x)), "plethysm (along %s)" % chi) + def maximal_subgroups(ct, mode="print_rules"): """ @@ -1994,216 +2070,216 @@ def maximal_subgroups(ct, mode="print_rules"): if CartanType(ct) == CartanType("A2"): rul = ["""A1:branching_rule("A2","A1","levi")"""] elif CartanType(ct) == CartanType("A3"): - rul = ["""A2:branching_rule("A3","A2","levi")""", \ - """A1xA1:branching_rule("A3","A1xA1","tensor")""", \ - """C2:branching_rule("A3","C2","symmetric")""", \ + rul = ["""A2:branching_rule("A3","A2","levi")""", + """A1xA1:branching_rule("A3","A1xA1","tensor")""", + """C2:branching_rule("A3","C2","symmetric")""", """A1xA1:branching_rule("A3","A1xA1","levi")"""] elif CartanType(ct) == CartanType("A4"): - rul = ["""A3:branching_rule("A4","A3","levi")""", \ - """B2:branching_rule("A4","B2","symmetric")""", \ + rul = ["""A3:branching_rule("A4","A3","levi")""", + """B2:branching_rule("A4","B2","symmetric")""", """A1xA2:branching_rule("A4","A1xA2","levi")"""] elif CartanType(ct) == CartanType("A5"): - rul = ["""A4:branching_rule("A5","A4","levi")""", \ - """A3:branching_rule("A5","D3","symmetric")*branching_rule("D3","A3","isomorphic")""", \ - """A3:branching_rule("A5","A3(0,1,0)","plethysm") # alternative""", \ - """C3:branching_rule("A5","C3","symmetric")""", \ - """A2:branching_rule("A5","A2(2,0)","plethysm")""", \ - """A1xA2:branching_rule("A5","A1xA2","tensor")""", \ - """A1xA3:branching_rule("A5","A1xA3","levi")""", \ + rul = ["""A4:branching_rule("A5","A4","levi")""", + """A3:branching_rule("A5","D3","symmetric")*branching_rule("D3","A3","isomorphic")""", + """A3:branching_rule("A5","A3(0,1,0)","plethysm") # alternative""", + """C3:branching_rule("A5","C3","symmetric")""", + """A2:branching_rule("A5","A2(2,0)","plethysm")""", + """A1xA2:branching_rule("A5","A1xA2","tensor")""", + """A1xA3:branching_rule("A5","A1xA3","levi")""", """A2xA2:branching_rule("A5","A2xA2","levi")"""] elif CartanType(ct) == CartanType("A6"): - rul = ["""A5:branching_rule("A6","A5","levi")""", \ - """B3:branching_rule("A6","B3","symmetric")""", \ - """A1xA4:branching_rule("A6","A1xA4","levi")""", \ + rul = ["""A5:branching_rule("A6","A5","levi")""", + """B3:branching_rule("A6","B3","symmetric")""", + """A1xA4:branching_rule("A6","A1xA4","levi")""", """A2xA3:branching_rule("A6","A2xA3","levi")"""] elif CartanType(ct) == CartanType("A7"): - rul = ["""A6:branching_rule("A7","A6","levi")""", \ - """C4:branching_rule("A7","C4","symmetric")""", \ - """D4:branching_rule("A7","D4","symmetric")""", \ - """A1xA3:branching_rule("A7","A1xA3","tensor")""", \ - """A1xA5:branching_rule("A7","A1xA5","levi")""", \ - """A2xA4:branching_rule("A7","A2xA4","levi")""", \ + rul = ["""A6:branching_rule("A7","A6","levi")""", + """C4:branching_rule("A7","C4","symmetric")""", + """D4:branching_rule("A7","D4","symmetric")""", + """A1xA3:branching_rule("A7","A1xA3","tensor")""", + """A1xA5:branching_rule("A7","A1xA5","levi")""", + """A2xA4:branching_rule("A7","A2xA4","levi")""", """A3xA3:branching_rule("A7","A3xA3","levi")"""] elif CartanType(ct) == CartanType("A8"): - rul = ["""A7:branching_rule("A8","A7","levi")""", \ - """B4:branching_rule("A8","B4","symmetric")""", \ - """A2xA2:branching_rule("A8","A2xA2","tensor")""", \ - """A1xA6:branching_rule("A8","A1xA6","levi")""", \ - """A2xA5:branching_rule("A8","A2xA5","levi")""", \ + rul = ["""A7:branching_rule("A8","A7","levi")""", + """B4:branching_rule("A8","B4","symmetric")""", + """A2xA2:branching_rule("A8","A2xA2","tensor")""", + """A1xA6:branching_rule("A8","A1xA6","levi")""", + """A2xA5:branching_rule("A8","A2xA5","levi")""", """A3xA4:branching_rule("A8","A3xA4","levi")"""] elif CartanType(ct) == CartanType("B3"): - rul = ["""G2:branching_rule("B3","G2","miscellaneous")""", \ - """A3:branching_rule("B3","D3","extended")*branching_rule("D3","A3","isomorphic")""", \ + rul = ["""G2:branching_rule("B3","G2","miscellaneous")""", + """A3:branching_rule("B3","D3","extended")*branching_rule("D3","A3","isomorphic")""", """A1xA1xA1:branching_rule("B3","D2xB1","orthogonal_sum")*branching_rule("D2xB1","A1xA1xA1",[branching_rule("D2","A1xA1","isomorphic"),branching_rule("B1","A1","isomorphic")])"""] elif CartanType(ct) == CartanType("B4"): - rul = ["""D4:branching_rule("B4","D4","extended")""", \ - """A1:branching_rule("B4","A1","symmetric_power")""", \ - """A1xA1:branching_rule("B4","B1xB1","tensor")*branching_rule("B1xB1","A1xA1",[branching_rule("B1","A1","isomorphic"),branching_rule("B1","A1","isomorphic")])""", \ - """A1xA1xB2:branching_rule("B4","D2xB2","extended")*branching_rule("D2xB2","A1xA1xB2",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ + rul = ["""D4:branching_rule("B4","D4","extended")""", + """A1:branching_rule("B4","A1","symmetric_power")""", + """A1xA1:branching_rule("B4","B1xB1","tensor")*branching_rule("B1xB1","A1xA1",[branching_rule("B1","A1","isomorphic"),branching_rule("B1","A1","isomorphic")])""", + """A1xA1xB2:branching_rule("B4","D2xB2","extended")*branching_rule("D2xB2","A1xA1xB2",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", """A1xA3:branching_rule("B4","B1xD3","extended")*branching_rule("B1xD3","A1xA3",[branching_rule("B1","A1","isomorphic"),branching_rule("D3","A3","isomorphic")])"""] elif CartanType(ct) == CartanType("B5"): - rul = ["""D5:branching_rule("B5","D5","extended")""", \ - """A1:branching_rule("B5","A1","symmetric_power")""", \ - """A1xA2xB3:branching_rule("B5","D2xB3","extended")*branching_rule("D2xB3","A1xA2xB3",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """A1xD4:branching_rule("B5","B1xD4","orthogonal_sum")*branching_rule("B1xD4","A1xD4",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ + rul = ["""D5:branching_rule("B5","D5","extended")""", + """A1:branching_rule("B5","A1","symmetric_power")""", + """A1xA2xB3:branching_rule("B5","D2xB3","extended")*branching_rule("D2xB3","A1xA2xB3",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """A1xD4:branching_rule("B5","B1xD4","orthogonal_sum")*branching_rule("B1xD4","A1xD4",[branching_rule("B1","A1","isomorphic"),"identity"])""", """A3xB2:branching_rule("B5","D3xB2","orthogonal_sum")*branching_rule("D3xB2","A3xB2",[branching_rule("D3","A3","isomorphic"),"identity"])"""] elif CartanType(ct) == CartanType("B6"): - rul = ["""D6:branching_rule("B6","D6","extended")""", \ - """A1:branching_rule("B6","A1","symmetric_power")""", \ - """A1xA1xB4:branching_rule("B6","D2xB4","orthogonal_sum")*branching_rule("D2xB4","A1xA1xB4",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """A1xD5:branching_rule("B6","B1xD5","orthogonal_sum")*branching_rule("B1xD5","A1xD5",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """A3xB3:branching_rule("B6","D3xB3","orthogonal_sum")*branching_rule("D3xB3","A3xB3",[branching_rule("D3","A3","isomorphic"),"identity"])""", \ + rul = ["""D6:branching_rule("B6","D6","extended")""", + """A1:branching_rule("B6","A1","symmetric_power")""", + """A1xA1xB4:branching_rule("B6","D2xB4","orthogonal_sum")*branching_rule("D2xB4","A1xA1xB4",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """A1xD5:branching_rule("B6","B1xD5","orthogonal_sum")*branching_rule("B1xD5","A1xD5",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """A3xB3:branching_rule("B6","D3xB3","orthogonal_sum")*branching_rule("D3xB3","A3xB3",[branching_rule("D3","A3","isomorphic"),"identity"])""", """B2xD4:branching_rule("B6","B2xD4","orthogonal_sum")"""] elif CartanType(ct) == CartanType("B7"): - rul = ["""D7:branching_rule("B7","D7","extended")""", \ - """A3:branching_rule("B7","A3(1,0,1)","plethysm")""", \ - """A1:branching_rule("B7","A1","symmetric_power")""", \ - """A1xB2:branching_rule("B7","B1xB2","tensor")*branching_rule("B1xB2","A1xB2",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """A1xD6:branching_rule("B7","B1xD6","extended")*branching_rule("B1xD6","A1xD6",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """A1xA1xB5:branching_rule("B7","D2xB5","extended")*branching_rule("D2xB5","A1xA1xB5",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """B2xD5:branching_rule("B7","B2xD5","orthogonal_sum")""", \ - """A3xB4:branching_rule("B7","D3xB4","orthogonal_sum")*branching_rule("D3xB4","A3xB4",[branching_rule("D3","A3","isomorphic"),"identity"])""", \ + rul = ["""D7:branching_rule("B7","D7","extended")""", + """A3:branching_rule("B7","A3(1,0,1)","plethysm")""", + """A1:branching_rule("B7","A1","symmetric_power")""", + """A1xB2:branching_rule("B7","B1xB2","tensor")*branching_rule("B1xB2","A1xB2",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """A1xD6:branching_rule("B7","B1xD6","extended")*branching_rule("B1xD6","A1xD6",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """A1xA1xB5:branching_rule("B7","D2xB5","extended")*branching_rule("D2xB5","A1xA1xB5",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """B2xD5:branching_rule("B7","B2xD5","orthogonal_sum")""", + """A3xB4:branching_rule("B7","D3xB4","orthogonal_sum")*branching_rule("D3xB4","A3xB4",[branching_rule("D3","A3","isomorphic"),"identity"])""", """B3xD4:branching_rule("B7","B3xD4","orthogonal_sum")"""] elif CartanType(ct) == CartanType("B8"): - rul = ["""D8:branching_rule("B8","D8","extended")""", \ - """A1:branching_rule("B8","A1","symmetric_power")""", \ - """A1xD7:branching_rule("B8","B1xD7","orthogonal_sum")*branching_rule("B1xD7","A1xD7",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """A1xA1xB6:branching_rule("B8","D2xB6","orthogonal_sum")*branching_rule("D2xB6","A1xA1xB6",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """B2xD6:branching_rule("B8","B2xD6","orthogonal_sum")""", \ - """A3xB5:branching_rule("B8","D3xB5","orthogonal_sum")*branching_rule("D3xB5","A3xB5",[branching_rule("D3","A3","isomorphic"),"identity"])""", \ - """B3xD5:branching_rule("B8","B3xD5","orthogonal_sum")""", \ + rul = ["""D8:branching_rule("B8","D8","extended")""", + """A1:branching_rule("B8","A1","symmetric_power")""", + """A1xD7:branching_rule("B8","B1xD7","orthogonal_sum")*branching_rule("B1xD7","A1xD7",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """A1xA1xB6:branching_rule("B8","D2xB6","orthogonal_sum")*branching_rule("D2xB6","A1xA1xB6",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """B2xD6:branching_rule("B8","B2xD6","orthogonal_sum")""", + """A3xB5:branching_rule("B8","D3xB5","orthogonal_sum")*branching_rule("D3xB5","A3xB5",[branching_rule("D3","A3","isomorphic"),"identity"])""", + """B3xD5:branching_rule("B8","B3xD5","orthogonal_sum")""", """B4xD4:branching_rule("B8","B4xD4","orthogonal_sum")"""] elif CartanType(ct) == CartanType("C2"): - rul = ["""A1:branching_rule("C2","A1","symmetric_power")""", \ + rul = ["""A1:branching_rule("C2","A1","symmetric_power")""", """A1xA1:branching_rule("C2","C1xC1","orthogonal_sum")*branching_rule("C1xC1","A1xA1",[branching_rule("C1","A1","isomorphic"),branching_rule("C1","A1","isomorphic")])"""] elif CartanType(ct) == CartanType("C3"): - rul = ["""A2:branching_rule("C3","A2","levi")""", \ - """A1:branching_rule("C3","A1","symmetric_power")""", \ - """A1xA1:branching_rule("C3","B1xC1","tensor")*branching_rule("B1xC1","A1xA1",[branching_rule("B1","A1","isomorphic"),branching_rule("C1","A1","isomorphic")])""", \ + rul = ["""A2:branching_rule("C3","A2","levi")""", + """A1:branching_rule("C3","A1","symmetric_power")""", + """A1xA1:branching_rule("C3","B1xC1","tensor")*branching_rule("B1xC1","A1xA1",[branching_rule("B1","A1","isomorphic"),branching_rule("C1","A1","isomorphic")])""", """A1xC2:branching_rule("C3","C1xC2","orthogonal_sum")*branching_rule("C1xC2","A1xC2",[branching_rule("C1","A1","isomorphic"),"identity"])"""] elif CartanType(ct) == CartanType("C4"): - rul = ["""A3:branching_rule("C4","A3","levi")""", \ - """A1:branching_rule("C4","A1","symmetric_power")""", \ - """A1xA3:branching_rule("C4","C1xC3","orthogonal_sum")*branching_rule("C1xC3","A1xA3",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """C2xC2:branching_rule("C4","C2xC2","orthogonal_sum")""", \ + rul = ["""A3:branching_rule("C4","A3","levi")""", + """A1:branching_rule("C4","A1","symmetric_power")""", + """A1xA3:branching_rule("C4","C1xC3","orthogonal_sum")*branching_rule("C1xC3","A1xA3",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """C2xC2:branching_rule("C4","C2xC2","orthogonal_sum")""", """A1xA1xA1:branching_rule("C4","C1xD2","tensor")*branching_rule("C1xD2","A1xA1xA1",[branching_rule("C1","A1","isomorphic"),branching_rule("D2","A1xA1","isomorphic")])"""] elif CartanType(ct) == CartanType("C5"): - rul = ["""A4:branching_rule("C5","A4","levi")""", \ - """A1:branching_rule("C5","A1","symmetric_power")""", \ - """A1xC4:branching_rule("C5","C1xC4","orthogonal_sum")*branching_rule("C1xC4","A1xC4",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """C2xC3:branching_rule("C5","C2xC3","orthogonal_sum")""", \ + rul = ["""A4:branching_rule("C5","A4","levi")""", + """A1:branching_rule("C5","A1","symmetric_power")""", + """A1xC4:branching_rule("C5","C1xC4","orthogonal_sum")*branching_rule("C1xC4","A1xC4",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """C2xC3:branching_rule("C5","C2xC3","orthogonal_sum")""", """A1xB2:branching_rule("C5","C1xB2","tensor")*branching_rule("C1xB2","A1xB2",[branching_rule("C1","A1","isomorphic"),"identity"])"""] elif CartanType(ct) == CartanType("C6"): - rul = ["""A5:branching_rule("C6","A5","levi")""", \ - """A1:branching_rule("C6","A1","symmetric_power")""", \ - """A1xA3:branching_rule("C6","C1xD3","tensor")*branching_rule("C1xD3","A1xA3",[branching_rule("C1","A1","isomorphic"),branching_rule("D3","A3","isomorphic")])""", \ - """A1xC2:branching_rule("C6","B1xC2","tensor")*branching_rule("B1xC2","A1xC2",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """A1xC5:branching_rule("C6","C1xC5","orthogonal_sum")*branching_rule("C1xC5","A1xC5",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """C2xC4:branching_rule("C6","C2xC4","orthogonal_sum")""", \ + rul = ["""A5:branching_rule("C6","A5","levi")""", + """A1:branching_rule("C6","A1","symmetric_power")""", + """A1xA3:branching_rule("C6","C1xD3","tensor")*branching_rule("C1xD3","A1xA3",[branching_rule("C1","A1","isomorphic"),branching_rule("D3","A3","isomorphic")])""", + """A1xC2:branching_rule("C6","B1xC2","tensor")*branching_rule("B1xC2","A1xC2",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """A1xC5:branching_rule("C6","C1xC5","orthogonal_sum")*branching_rule("C1xC5","A1xC5",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """C2xC4:branching_rule("C6","C2xC4","orthogonal_sum")""", """C3xC3:branching_rule("C6","C3xC3","orthogonal_sum")"""] elif CartanType(ct) == CartanType("C7"): - rul = ["""A6:branching_rule("C7","A6","levi")""", \ - """A1:branching_rule("C7","A1","symmetric_power")""", \ - """A1xB3:branching_rule("C7","C1xB3","tensor")*branching_rule("C1xB3","A1xB3",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """A1xC6:branching_rule("C7","C1xC6","orthogonal_sum")*branching_rule("C1xC6","A1xC6",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """C2xC5:branching_rule("C7","C2xC5","orthogonal_sum")""", \ - """C3xC4:branching_rule("C7","C3xC4","orthogonal_sum")""", \ + rul = ["""A6:branching_rule("C7","A6","levi")""", + """A1:branching_rule("C7","A1","symmetric_power")""", + """A1xB3:branching_rule("C7","C1xB3","tensor")*branching_rule("C1xB3","A1xB3",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """A1xC6:branching_rule("C7","C1xC6","orthogonal_sum")*branching_rule("C1xC6","A1xC6",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """C2xC5:branching_rule("C7","C2xC5","orthogonal_sum")""", + """C3xC4:branching_rule("C7","C3xC4","orthogonal_sum")""", """C3:branching_rule("C7","C3(0,0,1)","plethysm") # overlooked by Patera and McKay"""] elif CartanType(ct) == CartanType("C8"): - rul = ["""A7:branching_rule("C8","A7","levi")""", \ - """A1:branching_rule("C8","A1","symmetric_power")""", \ - """C2:branching_rule("C8","C2(1,1)","plethysm")""", \ - """A1xD4:branching_rule("C8","C1xD4","tensor")*branching_rule("C1xD4","A1xD4",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """A1xC7:branching_rule("C8","C1xC7","orthogonal_sum")*branching_rule("C1xC7","A1xC7",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """C2xC6:branching_rule("C8","C2xC6","orthogonal_sum")""", \ - """C3xC5:branching_rule("C8","C3xC5","orthogonal_sum")""", \ + rul = ["""A7:branching_rule("C8","A7","levi")""", + """A1:branching_rule("C8","A1","symmetric_power")""", + """C2:branching_rule("C8","C2(1,1)","plethysm")""", + """A1xD4:branching_rule("C8","C1xD4","tensor")*branching_rule("C1xD4","A1xD4",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """A1xC7:branching_rule("C8","C1xC7","orthogonal_sum")*branching_rule("C1xC7","A1xC7",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """C2xC6:branching_rule("C8","C2xC6","orthogonal_sum")""", + """C3xC5:branching_rule("C8","C3xC5","orthogonal_sum")""", """C4xC4:branching_rule("C8","C4xC4","orthogonal_sum")"""] elif CartanType(ct) == CartanType("D4"): - rul = ["""B3:branching_rule("D4","B3","symmetric")""", \ - """A2:branching_rule("D4","A2(1,1)","plethysm")""", \ - """A1xC2:branching_rule("D4","C1xC2","tensor")*branching_rule("C1xC2","A1xC2",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ + rul = ["""B3:branching_rule("D4","B3","symmetric")""", + """A2:branching_rule("D4","A2(1,1)","plethysm")""", + """A1xC2:branching_rule("D4","C1xC2","tensor")*branching_rule("C1xC2","A1xC2",[branching_rule("C1","A1","isomorphic"),"identity"])""", """A1xA1xA1xA1:branching_rule("D4","D2xD2","orthogonal_sum")*branching_rule("D2xD2","A1xA1xA1xA1",[branching_rule("D2","A1xA1","isomorphic"),branching_rule("D2","A1xA1","isomorphic")])"""] elif CartanType(ct) == CartanType("D5"): - rul = ["""A4:branching_rule("D5","A4","levi")""", \ - """B4:branching_rule("D5","B4","symmetric")""", \ - """C2:branching_rule("D5","C2(2,0)","plethysm")""", \ - """A1xA1xA3:branching_rule("D5","D2xD3","orthogonal_sum")*branching_rule("D2xD3","A1xA1xA3",[branching_rule("D2","A1xA1","isomorphic"),branching_rule("D3","A3","isomorphic")])""", \ - """A1xA3:branching_rule("D5","B1xB3","orthogonal_sum")*branching_rule("B1xB3","A1xA3",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ + rul = ["""A4:branching_rule("D5","A4","levi")""", + """B4:branching_rule("D5","B4","symmetric")""", + """C2:branching_rule("D5","C2(2,0)","plethysm")""", + """A1xA1xA3:branching_rule("D5","D2xD3","orthogonal_sum")*branching_rule("D2xD3","A1xA1xA3",[branching_rule("D2","A1xA1","isomorphic"),branching_rule("D3","A3","isomorphic")])""", + """A1xA3:branching_rule("D5","B1xB3","orthogonal_sum")*branching_rule("B1xB3","A1xA3",[branching_rule("B1","A1","isomorphic"),"identity"])""", """B2xB2:branching_rule("D5","B2xB2","orthogonal_sum")"""] elif CartanType(ct) == CartanType("D6"): - rul = ["""A5:branching_rule("D6","A5","levi")""", \ - """B5:branching_rule("D6","B5","symmetric")""", \ - """A1xA3:branching_rule("D6","C1xC3","tensor")*branching_rule("C1xC3","A1xA3",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """A1xA1xD4:branching_rule("D6","D2xD4","orthogonal_sum")*branching_rule("D2xD4","A1xA1xD4",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """A3xA3:branching_rule("D6","D3xD3","orthogonal_sum")*branching_rule("D3xD3","A3xA3",[branching_rule("D3","A3","isomorphic"),branching_rule("D3","A3","isomorphic")])""", \ - """A1xB4:branching_rule("D6","B1xB4","orthogonal_sum")*branching_rule("B1xB4","A1xB4",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """B2xB3:branching_rule("D6","B2xB3","orthogonal_sum")""", \ + rul = ["""A5:branching_rule("D6","A5","levi")""", + """B5:branching_rule("D6","B5","symmetric")""", + """A1xA3:branching_rule("D6","C1xC3","tensor")*branching_rule("C1xC3","A1xA3",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """A1xA1xD4:branching_rule("D6","D2xD4","orthogonal_sum")*branching_rule("D2xD4","A1xA1xD4",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """A3xA3:branching_rule("D6","D3xD3","orthogonal_sum")*branching_rule("D3xD3","A3xA3",[branching_rule("D3","A3","isomorphic"),branching_rule("D3","A3","isomorphic")])""", + """A1xB4:branching_rule("D6","B1xB4","orthogonal_sum")*branching_rule("B1xB4","A1xB4",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """B2xB3:branching_rule("D6","B2xB3","orthogonal_sum")""", """A1xA1xA1:branching_rule("D6","B1xD2","tensor")*branching_rule("B1xD2","A1xA1xA1",[branching_rule("B1","A1","isomorphic"),branching_rule("D2","A1xA1","isomorphic")])"""] elif CartanType(ct) == CartanType("D7"): - rul = ["""A6:branching_rule("D7","A6","levi")""", \ - """B6:branching_rule("D7","B6","symmetric")""", \ - """C3:branching_rule("D7","C3(0,1,0)","plethysm")""", \ - """C2:branching_rule("D7","C2(0,2)","plethysm")""", \ - """G2:branching_rule("D7","G2(0,1)","plethysm")""", \ - """A1xA1xD5:branching_rule("D7","D2xD5","orthogonal_sum")*branching_rule("D2xD5","A1xA1xD5",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """A3xD4:branching_rule("D7","D3xD4","orthogonal_sum")*branching_rule("D3xD4","A3xD4",[branching_rule("D3","A3","isomorphic"),"identity"])""", \ - """A1xB5:branching_rule("D7","B1xB5","orthogonal_sum")*branching_rule("B1xB5","A1xB5",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """B2xB4:branching_rule("D7","B2xB4","orthogonal_sum")""", \ + rul = ["""A6:branching_rule("D7","A6","levi")""", + """B6:branching_rule("D7","B6","symmetric")""", + """C3:branching_rule("D7","C3(0,1,0)","plethysm")""", + """C2:branching_rule("D7","C2(0,2)","plethysm")""", + """G2:branching_rule("D7","G2(0,1)","plethysm")""", + """A1xA1xD5:branching_rule("D7","D2xD5","orthogonal_sum")*branching_rule("D2xD5","A1xA1xD5",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """A3xD4:branching_rule("D7","D3xD4","orthogonal_sum")*branching_rule("D3xD4","A3xD4",[branching_rule("D3","A3","isomorphic"),"identity"])""", + """A1xB5:branching_rule("D7","B1xB5","orthogonal_sum")*branching_rule("B1xB5","A1xB5",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """B2xB4:branching_rule("D7","B2xB4","orthogonal_sum")""", """B3xB3:branching_rule("D7","B3xB3","orthogonal_sum")"""] elif CartanType(ct) == CartanType("D8"): - rul = ["""A7:branching_rule("D8","A7","levi")""", \ - """B7:branching_rule("D8","B7","symmetric")""", \ - """B4:branching_rule("D8","B4(0,0,0,1)","plethysm")""", \ - """A1xC4:branching_rule("D8","C1xC4","tensor")*branching_rule("C1xC4","A1xC4",[branching_rule("C1","A1","isomorphic"),"identity"])""", \ - """A1xA1xD6:branching_rule("D8","D2xD6","orthogonal_sum")*branching_rule("D2xD6","A1xA1xD6",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", \ - """A3xD5:branching_rule("D8","D3xD5","orthogonal_sum")*branching_rule("D3xD5","A3xD5",[branching_rule("D3","A3","isomorphic"),"identity"])""", \ - """D4xD4:branching_rule("D8","D4xD4","orthogonal_sum")""", \ - """A1xB6:branching_rule("D8","B1xB6","orthogonal_sum")*branching_rule("B1xB6","A1xB6",[branching_rule("B1","A1","isomorphic"),"identity"])""", \ - """B2xB5:branching_rule("D8","B2xB5","orthogonal_sum")""", \ - """B3xB4:branching_rule("D8","B3xB4","orthogonal_sum")""", \ + rul = ["""A7:branching_rule("D8","A7","levi")""", + """B7:branching_rule("D8","B7","symmetric")""", + """B4:branching_rule("D8","B4(0,0,0,1)","plethysm")""", + """A1xC4:branching_rule("D8","C1xC4","tensor")*branching_rule("C1xC4","A1xC4",[branching_rule("C1","A1","isomorphic"),"identity"])""", + """A1xA1xD6:branching_rule("D8","D2xD6","orthogonal_sum")*branching_rule("D2xD6","A1xA1xD6",[branching_rule("D2","A1xA1","isomorphic"),"identity"])""", + """A3xD5:branching_rule("D8","D3xD5","orthogonal_sum")*branching_rule("D3xD5","A3xD5",[branching_rule("D3","A3","isomorphic"),"identity"])""", + """D4xD4:branching_rule("D8","D4xD4","orthogonal_sum")""", + """A1xB6:branching_rule("D8","B1xB6","orthogonal_sum")*branching_rule("B1xB6","A1xB6",[branching_rule("B1","A1","isomorphic"),"identity"])""", + """B2xB5:branching_rule("D8","B2xB5","orthogonal_sum")""", + """B3xB4:branching_rule("D8","B3xB4","orthogonal_sum")""", """C2xC2:branching_rule("D8","C2xC2","tensor")"""] elif CartanType(ct) == CartanType("G2"): - rul = ["""A2:branching_rule("G2","A2","extended")""", \ - """A1:branching_rule("G2","A1","i")""", \ + rul = ["""A2:branching_rule("G2","A2","extended")""", + """A1:branching_rule("G2","A1","i")""", """A1xA1:branching_rule("G2","A1xA1","extended")"""] elif CartanType(ct) == CartanType("F4"): - rul = ["""B4:branching_rule("F4","B4","extended")""", \ - """A1:branching_rule("F4","A1","ii")""", \ - """A1xG2:branching_rule("F4","A1xG2","miscellaneous")""", \ - """A1xC3:branching_rule("F4","A1xC3","extended")""", \ + rul = ["""B4:branching_rule("F4","B4","extended")""", + """A1:branching_rule("F4","A1","ii")""", + """A1xG2:branching_rule("F4","A1xG2","miscellaneous")""", + """A1xC3:branching_rule("F4","A1xC3","extended")""", """A2xA2:branching_rule("F4","A2xA2","extended")"""] elif CartanType(ct) == CartanType("E6"): - rul = ["""D5:branching_rule("E6","D5","levi")""", \ - """C4:branching_rule("E6","C4","symmetric")""", \ - """F4:branching_rule("E6","F4","symmetric")""", \ - """A2:branching_rule("E6","A2","miscellaneous")""", \ - """G2:branching_rule("E6","G2","miscellaneous")""", \ - """A2xG2:branching_rule("E6","A2xG2","miscellaneous")""", \ - """A1xA5:branching_rule("E6","A1xA5","extended")""", \ + rul = ["""D5:branching_rule("E6","D5","levi")""", + """C4:branching_rule("E6","C4","symmetric")""", + """F4:branching_rule("E6","F4","symmetric")""", + """A2:branching_rule("E6","A2","miscellaneous")""", + """G2:branching_rule("E6","G2","miscellaneous")""", + """A2xG2:branching_rule("E6","A2xG2","miscellaneous")""", + """A1xA5:branching_rule("E6","A1xA5","extended")""", """A2xA2xA2:branching_rule("E6","A2xA2xA2","extended")"""] elif CartanType(ct) == CartanType("E7"): - rul = ["""A7:branching_rule("E7","A7","extended")""", \ - """E6:branching_rule("E7","E6","levi")""", \ - """A2:branching_rule("E7","A2","miscellaneous")""", \ - """A1:branching_rule("E7","A1","iii")""", \ - """A1:branching_rule("E7","A1","iv")""", \ - """A1xF4:branching_rule("E7","A1xF4","miscellaneous")""", \ - """G2xC3:branching_rule("E7","G2xC3","miscellaneous")""", \ - """A1xG2:branching_rule("E7","A1xG2","miscellaneous")""", \ - """A1xA1:branching_rule("E7","A1xA1","miscellaneous")""", \ - """A1xD6:branching_rule("E7","A1xD6","extended")""", \ + rul = ["""A7:branching_rule("E7","A7","extended")""", + """E6:branching_rule("E7","E6","levi")""", + """A2:branching_rule("E7","A2","miscellaneous")""", + """A1:branching_rule("E7","A1","iii")""", + """A1:branching_rule("E7","A1","iv")""", + """A1xF4:branching_rule("E7","A1xF4","miscellaneous")""", + """G2xC3:branching_rule("E7","G2xC3","miscellaneous")""", + """A1xG2:branching_rule("E7","A1xG2","miscellaneous")""", + """A1xA1:branching_rule("E7","A1xA1","miscellaneous")""", + """A1xD6:branching_rule("E7","A1xD6","extended")""", """A5xA2:branching_rule("E7","A5xA2","extended")"""] elif CartanType(ct) == CartanType("E8"): - rul = ["""A4xA4:branching_rule("E8","A4xA4","extended")""", \ - """G2xF4:branching_rule("E8","G2xF4","miscellaneous")""", \ - """E6xA2:branching_rule("E8","E6xA2","extended")""", \ - """E7xA1:branching_rule("E8","E7xA1","extended")""", \ - """D8:branching_rule("E8","D8","extended")""", \ - """A8:branching_rule("E8","A8","extended")""", \ - """B2:branching_rule("E8","B2","miscellaneous")""", \ - """A1xA2:branching_rule("E8","A1xA2","miscellaneous")""", \ - """A1:branching_rule("E8","A1","v")""", \ - """A1:branching_rule("E8","A1","vi")""", \ + rul = ["""A4xA4:branching_rule("E8","A4xA4","extended")""", + """G2xF4:branching_rule("E8","G2xF4","miscellaneous")""", + """E6xA2:branching_rule("E8","E6xA2","extended")""", + """E7xA1:branching_rule("E8","E7xA1","extended")""", + """D8:branching_rule("E8","D8","extended")""", + """A8:branching_rule("E8","A8","extended")""", + """B2:branching_rule("E8","B2","miscellaneous")""", + """A1xA2:branching_rule("E8","A1xA2","miscellaneous")""", + """A1:branching_rule("E8","A1","v")""", + """A1:branching_rule("E8","A1","vi")""", """A1:branching_rule("E8","A1","vii")"""] else: raise ValueError("Argument must be an irreducible classical Cartan Type with rank less than or equal to 8") diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 3bce7c67613..d09bda5a7c3 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -9,6 +9,9 @@ - Travis Scrimshaw (2013-02-28): Removed ``CombinatorialClass`` and added entry point through :class:`SetPartition`. + +This module defines a class for immutable partitioning of a set. For +mutable version see :func:`DisjointSet`. """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 0544aee97c5..83eb1d9f06d 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -123,6 +123,7 @@ # just used for handy .load, .save, etc. from __future__ import print_function, absolute_import +from six.moves import range import inspect from sage.structure.sage_object import SageObject @@ -306,7 +307,7 @@ def __getitem__(self, n): # The dirty work of generating indices is left to a range list # This could be slow but in practice seems fine # NOTE: n is a SLICE, not an index - return [ self(i) for i in range(0, LENGTH)[n] if i >= self.offset ] + return [self(i) for i in list(range(LENGTH))[n] if i >= self.offset] ######################################################################## # II. Actual implementations of Sloane sequences. diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index 60145da31d2..9506db034b1 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -283,6 +283,23 @@ cdef class WordDatatype_char(WordDatatype): (False, False, True, False) sage: (w>=w, z>=z, w>=z, z>=w) (True, True, True, False) + + Testing that :trac:`22717` is fixed:: + + sage: w = Word([1,2], alphabet=[1,2,3]) + sage: z = Word([1,2,3], alphabet=[1,2,3]) + sage: (ww, z>z, w>z, z>w) + (False, False, False, True) + sage: (w>=w, z>=z, w>=z, z>=w) + (True, True, False, True) """ # 0: < # 1: <= @@ -314,8 +331,8 @@ cdef class WordDatatype_char(WordDatatype): sig_off() if test == 0: - return 0 - if test < 0: + return self._length - other._length + elif test < 0: return -1 else: return 1 diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 40c854bc425..49488c9c805 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -106,6 +106,11 @@ def init_sage(): import sage.all_cmdline sage.interfaces.quit.invalidate_all() + # Disable cysignals debug messages in doctests: this is needed to + # make doctests pass when cysignals was built with debugging enabled + from cysignals.signals import set_debug_level + set_debug_level(0) + # Use the rich output backend for doctest from sage.repl.rich_output import get_display_manager dm = get_display_manager() diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index d6f6a017864..1adeca1ea4a 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -117,7 +117,8 @@ def __init__(self): BuiltinFunction.__init__(self, "dirac_delta", latex_name=r"\delta", conversions=dict(maxima='delta', mathematica='DiracDelta', - sympy='DiracDelta')) + sympy='DiracDelta', + giac='Dirac')) def _eval_(self, x): """ @@ -222,11 +223,14 @@ def __init__(self): H\left(x\right) sage: heaviside(x)._sympy_() Heaviside(x) + sage: heaviside(x)._giac_() + Heaviside(x) """ BuiltinFunction.__init__(self, "heaviside", latex_name="H", conversions=dict(maxima='hstep', mathematica='HeavisideTheta', - sympy='Heaviside')) + sympy='Heaviside', + giac='Heaviside')) def _eval_(self, x): """ diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index eafd1925755..ebbefb1be32 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -211,7 +211,7 @@ sage: p.is_ranked() True sage: p.order_polytope() - A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 10 vertices + A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 10 vertices The characteristic polynomial is a basic invariant of a hyperplane arrangement. It is defined as diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py index 2fc2857a748..8777b95c4be 100644 --- a/src/sage/graphs/generators/basic.py +++ b/src/sage/graphs/generators/basic.py @@ -652,7 +652,7 @@ def EmptyGraph(): 4 sage: for i in range(3): ....: empty2.add_edge(i,i+1) # add edges {[0:1],[1:2],[2:3]} - sage: for i in range(4)[1:]: + sage: for i in range(1, 4): ....: empty2.add_edge(4,i) # add edges {[1:4],[2:4],[3:4]} sage: empty2.show() # long time """ @@ -1040,61 +1040,6 @@ def LadderGraph(n): G.add_edges( (i,i+n) for i in range(n) ) return G -def LollipopGraph(n1, n2): - """ - Returns a lollipop graph with n1+n2 nodes. - - A lollipop graph is a path graph (order n2) connected to a complete - graph (order n1). (A barbell graph minus one of the bells). - - PLOTTING: Upon construction, the position dictionary is filled to - override the spring-layout algorithm. By convention, the complete - graph will be drawn in the lower-left corner with the (n1)th node - at a 45 degree angle above the right horizontal center of the - complete graph, leading directly into the path graph. - - EXAMPLES: Construct and show a lollipop graph Candy = 13, Stick = - 4 - - :: - - sage: g = graphs.LollipopGraph(13,4) - sage: g.show() # long time - - Create several lollipop graphs in a Sage graphics array - - :: - - sage: g = [] - sage: j = [] - sage: for i in range(6): - ....: k = graphs.LollipopGraph(i+3,4) - ....: g.append(k) - sage: for i in range(2): - ....: n = [] - ....: for m in range(3): - ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) - ....: j.append(n) - sage: G = sage.plot.graphics.GraphicsArray(j) - sage: G.show() # long time - """ - pos_dict = {} - - for i in range(n1): - x = float(cos((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) - y = float(sin((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) - j = n1-1-i - pos_dict[j] = (x,y) - for i in range(n1, n1+n2): - x = float(i - n1 - n2/2 + 1) - y = float(i - n1 - n2/2 + 1) - pos_dict[i] = (x,y) - G = graph.Graph({i: list(range(i+1,n1)) for i in range(n1)}, - pos=pos_dict, name="Lollipop Graph") - G.add_vertices(range(n1+n2)) - G.add_path(list(range(n1-1, n1+n2))) - return G - def PathGraph(n, pos=None): """ diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index c05b2b22e73..39213551145 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -217,6 +217,7 @@ def BalancedTree(r, h): import networkx return Graph(networkx.balanced_tree(r, h), name="Balanced tree") + def BarbellGraph(n1, n2): r""" Returns a barbell graph with ``2*n1 + n2`` nodes. The argument ``n1`` @@ -225,11 +226,6 @@ def BarbellGraph(n1, n2): A barbell graph is a basic structure that consists of a path graph of order ``n2`` connecting two complete graphs of order ``n1`` each. - This constructor depends on `NetworkX `_ - numeric labels. In this case, the ``n1``-th node connects to the - path graph from one complete graph and the ``n1 + n2 + 1``-th node - connects to the path graph from the other complete graph. - INPUT: - ``n1`` -- integer `\geq 2`. The order of each of the two @@ -243,10 +239,6 @@ def BarbellGraph(n1, n2): A barbell graph of order ``2*n1 + n2``. A ``ValueError`` is returned if ``n1 < 2`` or ``n2 < 0``. - ALGORITHM: - - Uses `NetworkX `_. - PLOTTING: Upon construction, the position dictionary is filled to @@ -285,56 +277,50 @@ def BarbellGraph(n1, n2): sage: P_n2.is_isomorphic(s_P) True - Create several barbell graphs in a Sage graphics array:: - - sage: g = [] - sage: j = [] - sage: for i in range(6): - ....: k = graphs.BarbellGraph(i + 2, 4) - ....: g.append(k) - ... - sage: for i in range(2): - ....: n = [] - ....: for m in range(3): - ....: n.append(g[3*i + m].plot(vertex_size=50, vertex_labels=False)) - ....: j.append(n) - ... - sage: G = sage.plot.graphics.GraphicsArray(j) - sage: G.show() # long time - TESTS: + sage: n1, n2 = randint(3, 10), randint(0, 10) + sage: g = graphs.BarbellGraph(n1, n2) + sage: g.num_verts() == 2 * n1 + n2 + True + sage: g.num_edges() == 2 * binomial(n1, 2) + n2 + 1 + True + sage: g.is_connected() + True + sage: g.girth() == 3 + True + The input ``n1`` must be `\geq 2`:: sage: graphs.BarbellGraph(1, randint(0, 10^6)) Traceback (most recent call last): ... - ValueError: Invalid graph description, n1 should be >= 2 + ValueError: invalid graph description, n1 should be >= 2 sage: graphs.BarbellGraph(randint(-10^6, 1), randint(0, 10^6)) Traceback (most recent call last): ... - ValueError: Invalid graph description, n1 should be >= 2 + ValueError: invalid graph description, n1 should be >= 2 The input ``n2`` must be `\geq 0`:: sage: graphs.BarbellGraph(randint(2, 10^6), -1) Traceback (most recent call last): ... - ValueError: Invalid graph description, n2 should be >= 0 + ValueError: invalid graph description, n2 should be >= 0 sage: graphs.BarbellGraph(randint(2, 10^6), randint(-10^6, -1)) Traceback (most recent call last): ... - ValueError: Invalid graph description, n2 should be >= 0 + ValueError: invalid graph description, n2 should be >= 0 sage: graphs.BarbellGraph(randint(-10^6, 1), randint(-10^6, -1)) Traceback (most recent call last): ... - ValueError: Invalid graph description, n1 should be >= 2 + ValueError: invalid graph description, n1 should be >= 2 """ # sanity checks if n1 < 2: - raise ValueError("Invalid graph description, n1 should be >= 2") + raise ValueError("invalid graph description, n1 should be >= 2") if n2 < 0: - raise ValueError("Invalid graph description, n2 should be >= 0") + raise ValueError("invalid graph description, n2 should be >= 0") pos_dict = {} @@ -356,9 +342,215 @@ def BarbellGraph(n1, n2): + (n2 / 2) + 2) pos_dict[i] = (x, y) - import networkx - G = networkx.barbell_graph(n1, n2) - return Graph(G, pos=pos_dict, name="Barbell graph") + G = Graph(pos=pos_dict, name="Barbell graph") + G.add_edges(((i, j) for i in range(n1) for j in range(i + 1, n1))) + G.add_path(list(range(n1, n1 + n2))) + G.add_edges(((i, j) for i in range(n1 + n2, n1 + n2 + n1) + for j in range(i + 1, n1 + n2 + n1))) + if n1 > 0: + G.add_edge(n1 - 1, n1) + G.add_edge(n1 + n2 - 1, n1 + n2) + + return G + + +def LollipopGraph(n1, n2): + r""" + Returns a lollipop graph with n1+n2 nodes. + + A lollipop graph is a path graph (order n2) connected to a complete + graph (order n1). (A barbell graph minus one of the bells). + + PLOTTING: Upon construction, the position dictionary is filled to + override the spring-layout algorithm. By convention, the complete + graph will be drawn in the lower-left corner with the (n1)th node + at a 45 degree angle above the right horizontal center of the + complete graph, leading directly into the path graph. + + EXAMPLES: + + Construct and show a lollipop graph Candy = 13, Stick = 4:: + + sage: g = graphs.LollipopGraph(13,4); g + Lollipop graph: Graph on 17 vertices + sage: g.show() # long time + + TESTS: + + sage: n1, n2 = randint(3, 10), randint(0, 10) + sage: g = graphs.LollipopGraph(n1, n2) + sage: g.num_verts() == n1 + n2 + True + sage: g.num_edges() == binomial(n1, 2) + n2 + True + sage: g.is_connected() + True + sage: g.girth() == 3 + True + sage: graphs.LollipopGraph(n1, 0).is_isomorphic(graphs.CompleteGraph(n1)) + True + sage: graphs.LollipopGraph(0, n2).is_isomorphic(graphs.PathGraph(n2)) + True + sage: graphs.LollipopGraph(0, 0).is_isomorphic(graphs.EmptyGraph()) + True + + The input ``n1`` must be `\geq 0`:: + + sage: graphs.LollipopGraph(-1, randint(0, 10^6)) + Traceback (most recent call last): + ... + ValueError: invalid graph description, n1 should be >= 0 + + The input ``n2`` must be `\geq 0`:: + + sage: graphs.LollipopGraph(randint(2, 10^6), -1) + Traceback (most recent call last): + ... + ValueError: invalid graph description, n2 should be >= 0 + """ + # sanity checks + if n1 < 0: + raise ValueError("invalid graph description, n1 should be >= 0") + if n2 < 0: + raise ValueError("invalid graph description, n2 should be >= 0") + + pos_dict = {} + + for i in range(n1): + x = float(cos((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) + y = float(sin((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) + j = n1-1-i + pos_dict[j] = (x,y) + for i in range(n1, n1+n2): + x = float(i - n1 - n2/2 + 1) + y = float(i - n1 - n2/2 + 1) + pos_dict[i] = (x,y) + + G = Graph(pos=pos_dict, name="Lollipop graph") + G.add_edges(((i, j) for i in range(n1) for j in range(i + 1, n1))) + G.add_path(list(range(n1, n1 + n2))) + if n1 * n2 > 0: + G.add_edge(n1 - 1, n1) + + return G + + +def TadpoleGraph(n1, n2): + r""" + Returns a tadpole graph with n1+n2 nodes. + + A tadpole graph is a path graph (order n2) connected to a cycle graph + (order n1). + + PLOTTING: Upon construction, the position dictionary is filled to override + the spring-layout algorithm. By convention, the cycle graph will be drawn + in the lower-left corner with the (n1)th node at a 45 degree angle above + the right horizontal center of the cycle graph, leading directly into the + path graph. + + EXAMPLES: + + Construct and show a tadpole graph Cycle = 13, Stick = 4:: + + sage: g = graphs.TadpoleGraph(13, 4); g + Tadpole graph: Graph on 17 vertices + sage: g.show() # long time + + TESTS: + + sage: n1, n2 = randint(3, 10), randint(0, 10) + sage: g = graphs.TadpoleGraph(n1, n2) + sage: g.num_verts() == n1 + n2 + True + sage: g.num_edges() == n1 + n2 + True + sage: g.girth() == n1 + True + sage: graphs.TadpoleGraph(n1, 0).is_isomorphic(graphs.CycleGraph(n1)) + True + + The input ``n1`` must be `\geq 3`:: + + sage: graphs.TadpoleGraph(2, randint(0, 10^6)) + Traceback (most recent call last): + ... + ValueError: invalid graph description, n1 should be >= 3 + + The input ``n2`` must be `\geq 0`:: + + sage: graphs.TadpoleGraph(randint(2, 10^6), -1) + Traceback (most recent call last): + ... + ValueError: invalid graph description, n2 should be >= 0 + """ + # sanity checks + if n1 < 3: + raise ValueError("invalid graph description, n1 should be >= 3") + if n2 < 0: + raise ValueError("invalid graph description, n2 should be >= 0") + + pos_dict = {} + + for i in range(n1): + x = float(cos((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) + y = float(sin((pi/4) - ((2*pi)/n1)*i) - n2/2 - 1) + j = n1-1-i + pos_dict[j] = (x,y) + for i in range(n1, n1+n2): + x = float(i - n1 - n2/2 + 1) + y = float(i - n1 - n2/2 + 1) + pos_dict[i] = (x,y) + + G = Graph(pos=pos_dict, name="Tadpole graph") + G.add_cycle(list(range(n1))) + G.add_path(list(range(n1, n1 + n2))) + if n1 * n2 > 0: + G.add_edge(n1 - 1, n1) + + return G + + +def DipoleGraph(n): + r""" + Returns a dipole graph with n edges. + + A dipole graph is a multigraph consisting of 2 vertices connected with n + parallel edges. + + EXAMPLES: + + Construct and show a dipole graph with 13 edges:: + + sage: g = graphs.DipoleGraph(13); g + Dipole graph: Multi-graph on 2 vertices + sage: g.show() # long time + + TESTS: + + sage: n = randint(0, 10) + sage: g = graphs.DipoleGraph(n) + sage: g.num_verts() == 2 + True + sage: g.num_edges() == n + True + sage: g.is_connected() == (n > 0) + True + sage: g.diameter() == (1 if n > 0 else infinity) + True + + The input ``n`` must be `\geq 0`:: + + sage: graphs.DipoleGraph(-randint(1, 10)) + Traceback (most recent call last): + ... + ValueError: invalid graph description, n should be >= 0 + """ + # sanity checks + if n < 0: + raise ValueError("invalid graph description, n should be >= 0") + + return Graph([[0,1], [(0,1)]*n], name="Dipole graph", multiedges=True) + def BubbleSortGraph(n): r""" @@ -994,6 +1186,7 @@ def FuzzyBallGraph(partition, q): curr_vertex+=p return g + def FibonacciTree(n): r""" Returns the graph of the Fibonacci Tree `F_{i}` of order `n`. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 5422b634d2b..82ec7a2e1f7 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -14045,10 +14045,10 @@ def distance_graph(self, dist): sage: G = graphs.OddGraph(4) sage: d = G.diameter() sage: n = G.num_verts() - sage: H = G.distance_graph(range(d+1)) + sage: H = G.distance_graph(list(range(d+1))) sage: H.is_isomorphic(graphs.CompleteGraph(n)) False - sage: H = G.distance_graph(range(1,d+1)) + sage: H = G.distance_graph(list(range(1,d+1))) sage: H.is_isomorphic(graphs.CompleteGraph(n)) True @@ -18547,7 +18547,7 @@ def plot(self, **options): ....: y = float(sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] ... - sage: for i in range(10)[5:]: + sage: for i in range(5, 10): ....: x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] diff --git a/src/sage/graphs/graph_database.py b/src/sage/graphs/graph_database.py index e9a4ec8b499..19a20ea0104 100644 --- a/src/sage/graphs/graph_database.py +++ b/src/sage/graphs/graph_database.py @@ -137,13 +137,15 @@ def subgraphs_to_query(subgraphs, db): 'SELECT ,,,,, FROM misc WHERE ( ( misc.induced_subgraphs regexp ? ) AND ( misc.induced_subgraphs regexp ? ) ) AND ( misc.induced_subgraphs regexp ? )' """ - q = GraphQuery(graph_db=db,induced_subgraphs=subgraphs[1]) + q = GraphQuery(graph_db=db, induced_subgraphs=subgraphs[1]) if subgraphs[0] == 'all_of': - for i in range(len(subgraphs))[2:]: - q.intersect(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True) + for i in range(2, len(subgraphs)): + q.intersect(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]), + in_place=True) elif subgraphs[0] == 'one_of': - for i in range(len(subgraphs))[2:]: - q.union(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True) + for i in range(2, len(subgraphs)): + q.union(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]), + in_place=True) else: raise KeyError('Unable to initiate query: Illegal input format for induced_subgraphs.') return q diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index fce1fe2d341..eb744974a57 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -442,7 +442,7 @@ def linear_ordering_to_path_decomposition(G, L): ... ValueError: the first parameter must be a Graph sage: g = graphs.CycleGraph(6) - sage: linear_ordering_to_path_decomposition(g, range(7)) + sage: linear_ordering_to_path_decomposition(g, list(range(7))) Traceback (most recent call last): ... ValueError: the input linear vertex ordering L is not valid for G diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 5595d7ec38f..acd3f772898 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -64,6 +64,7 @@ def __append_to_doc(methods): "CompleteGraph", "CompleteMultipartiteGraph", "DiamondGraph", + "DipoleGraph", "EmptyGraph", "Grid2dGraph", "GridGraph", @@ -73,6 +74,7 @@ def __append_to_doc(methods): "LollipopGraph", "PathGraph", "StarGraph", + "TadpoleGraph", "ToroidalGrid2dGraph", "Toroidal6RegularGrid2dGraph"] ) @@ -1868,7 +1870,6 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None HouseGraph = staticmethod(sage.graphs.generators.basic.HouseGraph) HouseXGraph = staticmethod(sage.graphs.generators.basic.HouseXGraph) LadderGraph = staticmethod(sage.graphs.generators.basic.LadderGraph) - LollipopGraph = staticmethod(sage.graphs.generators.basic.LollipopGraph) PathGraph = staticmethod(sage.graphs.generators.basic.PathGraph) StarGraph = staticmethod(sage.graphs.generators.basic.StarGraph) Toroidal6RegularGrid2dGraph = staticmethod(sage.graphs.generators.basic.Toroidal6RegularGrid2dGraph) @@ -1985,6 +1986,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None chang_graphs = staticmethod(sage.graphs.generators.families.chang_graphs) CirculantGraph = staticmethod(sage.graphs.generators.families.CirculantGraph) CubeGraph = staticmethod(sage.graphs.generators.families.CubeGraph) + DipoleGraph = staticmethod(sage.graphs.generators.families.DipoleGraph) DorogovtsevGoltsevMendesGraph = staticmethod(sage.graphs.generators.families.DorogovtsevGoltsevMendesGraph) FibonacciTree = staticmethod(sage.graphs.generators.families.FibonacciTree) FoldedCubeGraph = staticmethod(sage.graphs.generators.families.FoldedCubeGraph) @@ -1999,6 +2001,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None KneserGraph = staticmethod(sage.graphs.generators.families.KneserGraph) LCFGraph = staticmethod(sage.graphs.generators.families.LCFGraph) line_graph_forbidden_subgraphs = staticmethod(sage.graphs.generators.families.line_graph_forbidden_subgraphs) + LollipopGraph = staticmethod(sage.graphs.generators.families.LollipopGraph) MathonPseudocyclicMergingGraph = staticmethod(sage.graphs.generators.families.MathonPseudocyclicMergingGraph) MathonPseudocyclicStronglyRegularGraph = staticmethod(sage.graphs.generators.families.MathonPseudocyclicStronglyRegularGraph) MuzychukS6Graph = staticmethod(sage.graphs.generators.families.MuzychukS6Graph) @@ -2015,6 +2018,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None SquaredSkewHadamardMatrixGraph = staticmethod(sage.graphs.generators.families.SquaredSkewHadamardMatrixGraph) SwitchedSquaredSkewHadamardMatrixGraph = staticmethod(sage.graphs.generators.families.SwitchedSquaredSkewHadamardMatrixGraph) strongly_regular_graph = staticmethod(sage.graphs.strongly_regular_db.strongly_regular_graph) + TadpoleGraph = staticmethod(sage.graphs.generators.families.TadpoleGraph) trees = staticmethod(sage.graphs.generators.families.trees) TuranGraph = staticmethod(sage.graphs.generators.families.TuranGraph) WheelGraph = staticmethod(sage.graphs.generators.families.WheelGraph) diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/homology/simplicial_set_morphism.py index f6f66814e7b..e79f24597d1 100644 --- a/src/sage/homology/simplicial_set_morphism.py +++ b/src/sage/homology/simplicial_set_morphism.py @@ -29,6 +29,7 @@ # http://www.gnu.org/licenses/ # #***************************************************************************** +from six.moves import range import itertools @@ -634,7 +635,7 @@ def __call__(self, x): raise ValueError('element is not a simplex in the domain') if self.is_constant(): target = self._constant - return target.apply_degeneracies(*range(x.dimension()-1,-1,-1)) + return target.apply_degeneracies(*range(x.dimension()-1, -1, -1)) if self._is_identity: return x return self._dictionary[x.nondegenerate()].apply_degeneracies(*x.degeneracies()) @@ -1333,7 +1334,7 @@ def associated_chain_complex_morphism(self, base_ring=ZZ, Y_faces = list(self.codomain().n_cells(dim)) num_faces_X = len(X_faces) num_faces_Y = len(Y_faces) - mval = [0 for i in range(num_faces_X * num_faces_Y)] + mval = [0 for _ in range(num_faces_X * num_faces_Y)] for idx,x in enumerate(X_faces): y = self(x) if y.is_nondegenerate(): diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 00866f84b8a..cad0951e91b 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -56,11 +56,16 @@ - Gregg Musiker (2006-02-02): initial version. -(adapted to giac by F.Han) +- Frederic Han: adapted to giac. + +- Marcelo Forets (2017-04-06): conversions and cleanup. This tutorial is based on the Maple Tutorial for number theory from http://www.math.mun.ca/~drideout/m3370/numtheory.html. +Syntax +~~~~~~~ + There are several ways to use the Giac Interface in Sage. We will discuss two of those ways in this tutorial. @@ -100,6 +105,8 @@ sage: giac('(x^12-1)/(x-1)').normal() x^11+x^10+x^9+x^8+x^7+x^6+x^5+x^4+x^3+x^2+x+1 +Some typical input +~~~~~~~~~~~~~~~~~~ The normal command will reduce a rational function to the lowest terms. In giac, simplify is slower than normal because it @@ -120,6 +127,9 @@ sage: giac('(x^28-1)').factor( ) (x-1)*(x+1)*(x^2+1)*(x^6-x^5+x^4-x^3+x^2-x+1)*(x^6+x^5+x^4+x^3+x^2+x+1)*(x^12-x^10+x^8-x^6+x^4-x^2+1) +Giac console +~~~~~~~~~~~~~ + Another important feature of giac is its online help. We can access this through sage as well. After reading the description of the command, you can press q to immediately get back to your @@ -150,6 +160,9 @@ sage: (f19-(5778*sqrt(5)+33825)/5).normal() 0 +Function definitions +~~~~~~~~~~~~~~~~~~~~ + Let's say we want to write a giac program now that squares a number if it is positive and cubes it if it is negative. In giac, that would look like @@ -173,6 +186,32 @@ More complicated programs should be put in a separate file and loaded. + +Conversions +~~~~~~~~~~~~ + +The ``GiacElement.sage()`` method tries to convert a Giac object to a Sage +object. In many cases, it will just work. In particular, it should be able to +convert expressions entirely consisting of: + +- numbers, i.e. integers, floats, complex numbers; +- functions and named constants also present in Sage, where Sage knows how to + translate the function or constant's name from Giac's +- symbolic variables whose names don't pathologically overlap with + objects already defined in Sage. + +This method will not work when Giac's output includes functions unknown to Sage. + +If you want to convert more complicated Giac expressions, you can +instead call ``GiacElement._sage_()`` and supply a translation dictionary:: + + sage: g = giac('NewFn(x)') + sage: g._sage_(locals={'NewFn': sin}) + sin(x) + +Moreover, new conversions can be permanently added using Pynac's +``register_symbol``, and this is the recommended approach for library code. +For more details, see the documentation for ``._sage_()``. """ ############################################################################# @@ -992,12 +1031,23 @@ def _matrix_(self, R): return M(entries) - def _sage_(self): + def _sage_(self, locals={}): r""" - Convert a giac expression back to a Sage expression. + Convert a giac expression back to a Sage expression, if possible. + + NOTES: + + This method works successfully when Giac returns a result + or list of results that consist only of: + - numbers, i.e. integers, floats, complex numbers; + - functions and named constants also present in Sage, where: + - Sage knows how to translate the function or constant's name + from Giac's naming scheme through the symbols_table, or + - you provide a translation dictionary ``locals``. + + New conversions can be added using Pynac's ``register_symbol``. + This is the recommended approach for library code. - This currently does not implement a parser for the Giac output language, - therefore only very simple expressions will convert successfully. Warning: List conversion is slow. EXAMPLES:: @@ -1012,12 +1062,35 @@ def _sage_(self): sage: m.trigexpand().sage() 2*cos(sqrt(-x^2 + 1))*cos(1/x)^2*sin(sqrt(-x^2 + 1)) - 4*cos(sqrt(-x^2 + 1))*cos(1/x)*sin(sqrt(-x^2 + 1)) + 2*cos(sqrt(-x^2 + 1))*sin(sqrt(-x^2 + 1)) - """ - result = repr(self) - if str(self.type()) != 'DOM_LIST' : + Converting a custom name using the ``locals`` dictionary:: + + sage: ex = giac('myFun(x)') + sage: ex._sage_({'myFun': sin}) + sin(x) + + Same but by adding a new entry to the ``symbols_table``:: + + sage: ex = giac('myFun(x)') + sage: sage.libs.pynac.pynac.register_symbol(sin, {'giac':'myFun'}) + sage: ex._sage_() + sin(x) + """ + from sage.libs.pynac.pynac import symbol_table + from sage.calculus.calculus import symbolic_expression_from_string + + result = repr(self) # string representation + + if str(self.type()) != 'DOM_LIST' : + + # Merge the user-specified locals dictionary and the symbol_table + # (locals takes priority) + lsymbols = symbol_table['giac'].copy() + lsymbols.update(locals) + try: - from sage.symbolic.all import SR - return SR(result) + return symbolic_expression_from_string(result, lsymbols, + accept_sequence=True) + except Exception: raise NotImplementedError("Unable to parse Giac output: %s" % result) else: @@ -1143,6 +1216,3 @@ def __doctest_cleanup(): """ import sage.interfaces.quit sage.interfaces.quit.expect_quitall() - - - diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index d0895903388..cd65392a4c2 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -582,7 +582,7 @@ def poincare_polynomial(self): t^5 + 4*t^4 + 6*t^3 + 5*t^2 + 3*t + 1 sage: rw = sage.combinat.permutation.from_reduced_word # optional - coxeter3 - sage: p = map(attrcall('poincare_polynomial'), W) # optional - coxeter3 + sage: p = [w.poincare_polynomial() for w in W] # optional - coxeter3 sage: [rw(w.reduced_word()) for i,w in enumerate(W) if p[i] != p[i].reverse()] # optional - coxeter3 [[3, 4, 1, 2], [4, 2, 3, 1]] """ diff --git a/src/sage/libs/singular/function.pxd b/src/sage/libs/singular/function.pxd index 02937eb697b..c5b967cbb5e 100644 --- a/src/sage/libs/singular/function.pxd +++ b/src/sage/libs/singular/function.pxd @@ -82,4 +82,4 @@ cdef class SingularKernelFunction(SingularFunction): pass # the most direct function call interface -cdef inline call_function(SingularFunction self, tuple args, object R, bint signal_handler=?, object attributes=?) +cdef call_function(SingularFunction self, tuple args, object R, bint signal_handler=?, object attributes=?) diff --git a/src/sage/libs/sirocco.pyx b/src/sage/libs/sirocco.pyx index 27870493b08..77e0484ed99 100644 --- a/src/sage/libs/sirocco.pyx +++ b/src/sage/libs/sirocco.pyx @@ -1,117 +1,120 @@ +#cython: boundscheck=False, wraparound=False r""" Cython wrapper for sirocco library -This is used to call the sirocco library directly from python. - -The function contpath takes the following input: - -- An integer, representing the degree of the polynomial - -- A list of floating point numbers. Each four consecutive elements - of this list represent the interval corresponding to a coefficient. - Coefficients are listed in increasing deglex order, and inside each - coefficients, the four numbers represent the lower real, upper real, - lower imaginary and real imaginary limits of the interval. - -- A float representing the real part of the initial root approximation - -- A float representing the imaginary part of the initial root. - -The output is a list of tuples. Each tuple represents the x value (between 0 and 1) -and the real and imaginary parts of the y value of a vertex in the piecewise linear -approximation of the path tracked by the root. - -The function contpath_mp mimics contpath, but with the following differences: - -- The floating point numbers can be arbitrary precission RealNumbers - -- A extra argument is needed, indicating the bits of precission used in the - computations. +This is used to call the sirocco library directly from Python. AUTHORS: - Miguel Marco (2016-07-19): initial version. """ - -include 'cysignals/signals.pxi' -include "sage/ext/stdsage.pxi" - +include "cysignals/signals.pxi" +include "sage/ext/stdsage.pxi" 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) + 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) cpdef list[list] contpath_mp(int deg, list values, RealNumber y0r, RealNumber y0i, int prec): - cdef int cdeg = deg + """ + Mimics :func:`contpath`, but with the following differences: + + - The floating point numbers can be arbitrary precission RealNumbers. + + - A extra argument is needed, indicating the bits of precission used + in the computations. + """ cdef mpfr_t* cvalues = sage_malloc(sizeof(mpfr_t)*len(values)) cdef mpfr_t* rop - cdef int j + cdef int i, j + cdef mpfr_t y0R + cdef mpfr_t y0I + for j in range(len(values)): - sig_on() mpfr_init2(cvalues[j], prec) mpfr_set(cvalues[j], (values[j]).value, MPFR_RNDN) - sig_off() - cdef mpfr_t y0R - cdef mpfr_t y0I + sig_on() mpfr_init2(y0R, prec) mpfr_set(y0R, (y0r).value, MPFR_RNDN) mpfr_init2(y0I, prec) mpfr_set(y0I, (y0i).value, MPFR_RNDN) - rop = homotopyPath_mp(cdeg, cvalues, y0R, y0I, prec) + rop = homotopyPath_mp(deg, cvalues, y0R, y0I, prec) sig_off() + for j in range(len(values)): - sig_on() mpfr_clear(cvalues[j]) - sig_off() free(cvalues) + if rop == NULL: raise ValueError("libsirocco could not guarantee one step") + cdef int n = mpfr_get_si(rop[0], MPFR_RNDN) - l = [] + cdef list l = [] + cdef list inner + cdef RealNumber RN field = RealField(prec) - for i in range(1, 3*n+1): - RN = field() - sig_on() - mpfr_set((RN).value, rop[i], MPFR_RNDN) - mpfr_clear(rop[i]) - sig_off() - l.append(RN) + for i in range(n): + inner = [] + for j in range(3*i+1, 3*(i+1)+1): + RN = RealNumber.__new__(RealNumber, field) + mpfr_set(RN.value, rop[j], MPFR_RNDN) + mpfr_clear(rop[j]) + inner.append(RN) + l.append(tuple(inner)) free(rop) - return [(l[3*i], l[3*i+1], l[3*i+2]) for i in range(len(l)/3)] + return l + +cpdef list[list] contpath(int deg, list values, double y0r, double y0i): + """ + INPUT: + - An integer, representing the degree of the polynomial + - A list of floating point numbers. Each four consecutive elements + of this list represent the interval corresponding to a coefficient. + Coefficients are listed in increasing deglex order, and inside each + coefficients, the four numbers represent the lower real, upper real, + lower imaginary and real imaginary limits of the interval. + - A float representing the real part of the initial root approximation -cpdef list[list] contpath(int deg, list values, double y0r, double y0i): + - A float representing the imaginary part of the initial root. + + OUTPUT: + + A list of tuples. Each tuple represents the `x` value (between 0 and 1) + and the real and imaginary parts of the `y` value of a vertex in + 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 int clen = len(values) + cdef int i for i,v in enumerate(values): c_values[i] = values[i] cdef double y0R = y0r cdef double y0I = y0i sig_on() - rop = homotopyPath (deg, c_values, y0R, y0I) + rop = homotopyPath(deg, c_values, y0R, y0I) sig_off() if rop == NULL: raise ValueError("libsirocco could not guarantee one step") - n = int(rop[0]) - l = [0 for i in range(n)] + cdef int n = int(rop[0]) + cdef list l = [0] * n for i in range(n): - l[i]=(rop[3*i+1],rop[3*i+2],rop[3*i+3]) + l[i] = (rop[3*i+1], rop[3*i+2], rop[3*i+3]) free(rop) free(c_values) return l + diff --git a/src/sage/manifolds/coord_func.py b/src/sage/manifolds/coord_func.py index be1b31fb9bd..41600240aa6 100644 --- a/src/sage/manifolds/coord_func.py +++ b/src/sage/manifolds/coord_func.py @@ -1413,39 +1413,13 @@ def jacobian_det(self): True """ - def simple_determinant(aa): - r""" - Compute the determinant of a square matrix represented as an array. - - This function is based on Laplace's cofactor expansion. - """ - n = len(aa) - if n == 1: - return aa[0][0] - res = 0 - sign = True - for i in range(n): - b = [] - for k in range(i): - r = [] - for l in range(1,n): - r.append(aa[k][l]) - b.append(r) - for k in range(i+1,n): - r = [] - for l in range(1,n): - r.append(aa[k][l]) - b.append(r) - if sign: - res += aa[i][0] * simple_determinant(b) - else: - res -= aa[i][0] * simple_determinant(b) - sign = not sign - return res - + from sage.matrix.constructor import matrix if self._nf != self._nc: raise ValueError("the Jacobian matrix is not a square matrix") - J = self.jacobian() - J = [[J[i,j] for i in range(self._nc)] for j in range(self._nc)] - return simple_determinant(J) + mat = self.jacobian() + mat_expr = matrix([[mat[i,j].expr() for i in range(self._nc)] + for j in range(self._nc)]) + det = mat_expr.det() # the unsimplified determinant + func = self._functions[0] + return type(func)(func.parent(), func._simplify(det)) diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index f29d867d2d6..bf7b8d18c5d 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -33,6 +33,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.cachefunc import cached_method from sage.manifolds.chart import Chart, RealChart, CoordChange from sage.manifolds.differentiable.vectorframe import CoordFrame @@ -970,9 +971,6 @@ def __init__(self, chart1, chart2, *transformations): CoordChange.__init__(self, chart1, chart2, *transformations) # Jacobian matrix: self._jacobian = self._transf.jacobian() - # Jacobian determinant: - if self._n1 == self._n2: - self._jacobian_det = self._transf.jacobian_det() # If the two charts are on the same open subset, the Jacobian matrix is # added to the dictionary of changes of frame: if chart1._domain == chart2._domain: @@ -994,3 +992,82 @@ def __init__(self, chart1, chart2, *transformations): vf_module._basis_changes[(frame1, frame2)] = ch_basis_inv for sdom in domain._supersets: sdom._frame_changes[(frame1, frame2)] = ch_basis_inv + + def jacobian(self): + r""" + Return the Jacobian matrix of ``self``. + + If ``self`` corresponds to the change of coordinates + + .. MATH:: + + y^i = Y^i(x^1,\ldots,x^n)\qquad 1\leq i \leq n + + the Jacobian matrix `J` is given by + + .. MATH:: + + J_{ij} = \frac{\partial Y^i}{\partial x^j} + + where `i` is the row index and `j` the column one. + + OUTPUT: + + - Jacobian matrix `J`, the elements `J_{ij}` of which being + coordinate functions + (cf. :class:`~sage.manifolds.coord_func.CoordFunction`) + + EXAMPLES: + + Jacobian matrix of a 2-dimensional transition map:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y^2, 3*x-y]) + sage: X_to_Y.jacobian() + [ 1 2*y] + [ 3 -1] + + Each element of the Jacobian matrix is a coordinate function:: + + sage: parent(X_to_Y.jacobian()[0,0]) + Ring of coordinate functions on Chart (M, (x, y)) + + """ + return self._jacobian # has been computed in __init__ + + @cached_method + def jacobian_det(self): + r""" + Return the Jacobian determinant of ``self``. + + The Jacobian determinant is the determinant of the Jacobian + matrix (see :meth:`jacobian`). + + OUTPUT: + + - determinant of the Jacobian matrix `J` as a coordinate + function + (cf. :class:`~sage.manifolds.coord_func.CoordFunction`) + + EXAMPLES: + + Jacobian determinant of a 2-dimensional transition map:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y^2, 3*x-y]) + sage: X_to_Y.jacobian_det() + -6*y - 1 + sage: X_to_Y.jacobian_det() == det(X_to_Y.jacobian()) + True + + The Jacobian determinant is a coordinate function:: + + sage: parent(X_to_Y.jacobian_det()) + Ring of coordinate functions on Chart (M, (x, y)) + + """ + return self._transf.jacobian_det() diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 672626e56d9..4b7aae84c5d 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -11,8 +11,7 @@ # (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, division from six.moves import range import sage.rings.all as rings @@ -2531,13 +2530,13 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): A matrix without size control may have very large entry sizes. :: sage: D=random_matrix(ZZ, 7, 8, algorithm='echelonizable', rank=6); D - [ 1 2 8 -35 -178 -673 -284 778] - [ 4 9 37 -163 -827 -3128 -1324 3624] - [ 5 6 21 -88 -454 -1712 -708 1951] - [ -4 -5 -22 97 491 1854 779 -2140] - [ 4 4 13 -55 -283 -1066 -436 1206] - [ 4 11 43 -194 -982 -3714 -1576 4310] - [ -1 -2 -13 59 294 1113 481 -1312] + [ 1 2 8 -35 -178 -239 -284 778] + [ 4 9 37 -163 -827 -1111 -1324 3624] + [ 5 6 21 -88 -454 -607 -708 1951] + [ -4 -5 -22 97 491 656 779 -2140] + [ 4 4 13 -55 -283 -377 -436 1206] + [ 4 11 43 -194 -982 -1319 -1576 4310] + [ -1 -2 -13 59 294 394 481 -1312] Matrices can be generated over any exact ring. :: @@ -2603,30 +2602,32 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): ring = parent.base_ring() rows = parent.nrows() - if rank<0: + if rank < 0: raise ValueError("matrices must have rank zero or greater.") - if rank>min(rows,parent.ncols()): + if rank > min(rows,parent.ncols()): raise ValueError("matrices cannot have rank greater than min(ncols,nrows).") matrix = random_rref_matrix(parent, rank) # Entries of matrices over the ZZ or QQ can get large, entry size is regulated by finding the largest # entry of the resultant matrix after addition of scalar multiple of a row. - if ring==QQ or ring==ZZ: + if ring == QQ or ring == ZZ: # If upper_bound is not set, don't control entry size. if upper_bound is None: # If size control is not desired, the routine will run slightly faster, particularly with large matrices. - for pivots in range(rank-1,-1,-1): - row_index=0 - while row_index1: - matrix.add_multiple_of_row(0,randint(1,rows-1),randint(-3,3)) + for pivots in range(rank-1, -1, -1): + row_index = 0 + while row_index < rows: + if pivots == row_index: + row_index += 1 + if pivots != row_index and row_index != rows: + matrix.add_multiple_of_row(row_index, + matrix.pivot_rows()[pivots], + randint(-5, 5)) + row_index += 1 + if rows > 1: + matrix.add_multiple_of_row(0, randint(1,rows-1), randint(-3,3)) else: - if rank==1: # would be better just to have a special generator... + if rank == 1: # would be better just to have a special generator... tries = 0 while max(map(abs,matrix.list())) >= upper_bound: matrix = random_rref_matrix(parent, rank) @@ -3396,7 +3397,7 @@ def ith_to_zero_rotation_matrix(v, i, ring=None): AUTHORS: - Sebastien Labbe (April 2010) + Sebastien Labbe (April 2010) """ if not ring is None: # coerce the vector so that computations @@ -3415,5 +3416,5 @@ def ith_to_zero_rotation_matrix(v, i, ring=None): entries = {} for k in range(dim): entries[(k, k)] = 1 - entries.update({(j,j):aa, (j,i):bb, (i,j):-bb, (i,i):aa}) + entries.update({(j, j): aa, (j, i): bb, (i, j): -bb, (i, i): aa}) return matrix(entries, nrows=dim, ring=ring) diff --git a/src/sage/matroids/set_system.pxd b/src/sage/matroids/set_system.pxd index 4ea5813449b..b3af6249e23 100644 --- a/src/sage/matroids/set_system.pxd +++ b/src/sage/matroids/set_system.pxd @@ -12,10 +12,10 @@ cdef class SetSystem: cdef _relabel(self, l) cpdef _complements(self) - cdef inline resize(self, k=*) - cdef inline _append(self, bitset_t X) - cdef inline append(self, X) - cdef inline _subset(self, long k) + cdef resize(self, k=*) + cdef _append(self, bitset_t X) + cdef append(self, X) + cdef _subset(self, long k) cdef subset(self, k) cpdef _get_groundset(self) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 734f04da5e2..ab9772b411f 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -289,7 +289,7 @@ def cython(filename, verbose=False, compile_message=False, Cython file, don't recompile, just reuse the .so file. - ``create_local_c_file`` (bool, default False) - if True, save a - copy of the .c file in the current directory. + copy of the ``.c`` or ``.cpp`` file in the current directory. - ``annotate`` (bool, default True) - if True, create an html file which annotates the conversion from .pyx to .c. By default this is only created @@ -343,12 +343,12 @@ def cython(filename, verbose=False, compile_message=False, sage: import sage.misc.cython sage: d = sage.misc.temporary_file.tmp_dir() - sage: pyxfile = os.path.join(d, "src.pyx") - sage: with open(pyxfile, 'w') as f: + sage: os.chdir(d) + sage: with open("test.pyx", 'w') as f: ....: f.write("#clang C++\n" ....: "from libcpp.vector cimport vector\n" ....: "cdef vector[int] * v = new vector[int](4)\n") - sage: output = sage.misc.cython.cython(pyxfile, create_local_c_file=True) + sage: output = sage.misc.cython.cython("test.pyx", create_local_c_file=True) """ if not filename.endswith('pyx'): print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr) diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index 80dff585b4c..336cd172c17 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -906,7 +906,7 @@ cdef class LazyImport(object): sage: lazy_import('sage.all', 'foo') sage: type(foo) - sage: range(100)[foo] + sage: list(range(100))[foo] 10 """ return operator.index(self._get_object()) diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 2230bd036ef..a30256add0e 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -509,8 +509,6 @@ def riemann_sum(self, f, center=1, level=0, E=None): 1 + 5 + 2*5^3 + 4*5^4 + 2*5^5 + 3*5^6 + 3*5^7 + 2*5^8 + 4*5^9 + O(5^10) """ R1 = LaurentSeriesRing(f.base_ring(), 'r1') - # R1.set_default_prec(self.parent()._k - 1) - if E is None: E = self.parent()._X._BT.get_balls(center, level) else: @@ -566,6 +564,24 @@ def modular_form(self, z=None, level=0): a + (a + 2)*3 + (2*a + 2)*3^2 + (2*a + 2)*3^3 + 2*a*3^5 + a*3^6 + O(3^7) sage: (x4-x3).valuation() 3 + + TESTS: + + Check that :trac:`22634` is fixed:: + + sage: X = BruhatTitsQuotient(7,2) + sage: H = X.harmonic_cocycles(4,20) + sage: f0, g0 = H.basis() + sage: A = X.padic_automorphic_forms(4,20,overconvergent=True) + sage: f = A.lift(f0).modular_form(method='moments') + sage: T. = Qq(7^2,20) + sage: a,b,c,d = X.embed_quaternion(X.get_units_of_order()[1]).change_ring(Qp(7,20)).list() + sage: (c*x + d)^4 * f(x) == f((a*x + b)/(c*x + d)) + True + sage: g = A.lift(g0).modular_form(method='moments') + sage: (c*x + d)^4 * f(x) == f((a*x + b)/(c*x + d)) + True + """ return self.derivative(z, level, order=0) @@ -1057,9 +1073,7 @@ def _element_constructor_(self, x): if isinstance(parent, BruhatTitsHarmonicCocycles): return self.element_class(self, [self._U(o) for o in x._F]) elif isinstance(parent, pAdicAutomorphicForms): - tmp = [self._U(x._F[ii]).l_act_by(self._E[ii].rep) - for ii in range(self._nE)] - # tmp = [self._E[ii].rep * self._U(x._F[ii]) for ii in range(self._nE)] + tmp = [self._E[ii].rep * self._U(x._F[ii]) for ii in range(self._nE)] return self.element_class(self, tmp) if x == 0: tmp = [self._U([0] * (self.weight() - 1))] * self._X._num_edges @@ -1699,7 +1713,8 @@ def evaluate(self, e1): p = self.parent().prime() u = DoubleCosetReduction(X, e1) tmp = ((u.t(self.parent()._U.base_ring().precision_cap())) * p ** (u.power)).adjoint() - return self.parent()._Sigma0(tmp, check=False) * self._value[u.label] + S0 = self.parent()._Sigma0 + return S0(tmp, check=False) * self._value[u.label] # Warning! Should remove check=False... def _lmul_(self, a): @@ -1803,29 +1818,26 @@ def _improve(self, hc): """ MMM = self.parent() U = MMM._U - h1 = MMM(self) - try: - h1._value = [o.lift(M=MMM.precision_cap()) for o in h1._value] - except AttributeError: - pass - h2 = MMM._apply_Up_operator(h1, True, hc) + S0 = MMM._Sigma0 + + h1 = MMM([o.lift(M=MMM.precision_cap()) for o in self._value]) + h2 = MMM._apply_Up_operator(h1, True) verbose("Applied Up once") ii = 0 current_val = 0 - # old_val = -Infinity init_val = self.valuation() - while ii < MMM.precision_cap(): # current_val > old_val: - # old_val = current_val + old_val = init_val - 1 + while current_val > old_val: + old_val = current_val ii += 1 - self._value = [U(c) for c in h2._value] - h2 = MMM._apply_Up_operator(self, True, hc) - current_val = (h2 - self).valuation() - init_val + h1._value = [U(c) for c in h2._value] + h2 = MMM._apply_Up_operator(h1, True) + current_val = (h2 - h1).valuation() - init_val verbose('val = %s' % current_val) if current_val is Infinity: break verbose('Applied Up %s times' % (ii + 1)) - self._value = [U(c) for c in h2._value] - return self + return h2 def integrate(self, f, center=1, level=0, method='moments'): r""" @@ -1872,7 +1884,7 @@ def integrate(self, f, center=1, level=0, method='moments'): sage: A = X.padic_automorphic_forms(2,prec = 5,overconvergent=True) sage: a = A.lift(h) sage: a._value[0].moment(2) - 2 + 6*7 + 4*7^2 + O(7^3) + 2 + 6*7 + 4*7^2 + 4*7^3 + 6*7^4 + O(7^5) Now that we've lifted our harmonic cocycle to an overconvergent automorphic form we simply need to define the @@ -1890,13 +1902,12 @@ def integrate(self, f, center=1, level=0, method='moments'): - Marc Masdeu (2012-02-20) """ E = self.parent()._source._BT.get_balls(center, level) - R1 = LaurentSeriesRing(f.base_ring(), 'r1') + R1 = LaurentSeriesRing(f.base_ring(), 'r1', default_prec = self.parent()._U.base_ring().precision_cap() + 1) R2 = PolynomialRing(f.base_ring(), 'x') x = R2.gen() value = 0 ii = 0 if method == 'riemann_sum': - # R1.set_default_prec(self.parent()._U.weight() + 1) for e in E: ii += 1 #print(ii,"/",len(E)) @@ -1905,7 +1916,6 @@ def integrate(self, f, center=1, level=0, method='moments'): new = eval_dist_at_powseries(self.evaluate(e), exp.truncate(self.parent()._U.weight() + 1)) value += new elif method == 'moments': - # R1.set_default_prec(self.parent()._U.base_ring().precision_cap()) n = self.parent()._U.weight() for e in E: ii += 1 @@ -2135,7 +2145,7 @@ def coleman(self, t1, t2, E=None, method='moments', mult=False, K = t1.parent() R = PolynomialRing(K, 'x') x = R.gen() - R1 = LaurentSeriesRing(K, 'r1') + R1 = LaurentSeriesRing(K, 'r1', default_prec=self.parent()._U.base_ring().precision_cap()) r1 = R1.gen() if E is None: E = self.parent()._source._BT.find_covering(t1, t2) @@ -2144,7 +2154,6 @@ def coleman(self, t1, t2, E=None, method='moments', mult=False, ii = 0 value_exp = K(1) if method == 'riemann_sum': - # R1.set_default_prec(self.parent()._U.weight() + 1) for e in E: ii += 1 b = e[0, 1] @@ -2158,7 +2167,6 @@ def coleman(self, t1, t2, E=None, method='moments', mult=False, value_exp *= K.teichmuller(y) ** Integer(c_e.moment(0).rational_reconstruction()) elif method == 'moments': - # R1.set_default_prec(self.parent()._U.base_ring().precision_cap()) for e in E: ii += 1 f = (x - t1) / (x - t2) @@ -2449,10 +2457,11 @@ def _element_constructor_(self, data): # Code how to coerce x into the space # Admissible values of x? if type(data) is list: - return self.element_class(self, [self._U(o) for o in data]) + return self.element_class(self, [self._U(o, normalize=False) for o in data]) if isinstance(data, pAdicAutomorphicFormElement): - return self.element_class(self, [self._U(o) for o in data._value]) + vals = [self._U(o, normalize=False) for o in data._value] + return self.element_class(self, vals) if isinstance(data, BruhatTitsHarmonicCocycleElement): E = self._list @@ -2460,13 +2469,13 @@ def _element_constructor_(self, data): F = [] Uold = data.parent()._U for ii in range(len(data._F)): - newtmp = data.parent()._Sigma0(E[ii].rep.inverse(), check=False) * Uold(data._F[ii]) # Warning, should remove check=False! + newtmp = data.parent()._Sigma0(E[ii].rep.inverse(), check=False) * Uold(data._F[ii],normalize=False) tmp.append(newtmp) F.append(newtmp) - A = Matrix(QQ, 2, 2, [0, -1 / self.prime(), -1, 0]) + A = data.parent()._Sigma0(Matrix(QQ,2,2,[0,1/self.prime(),1,0]),check=False) for ii in range(len(data._F)): - F.append(-(data.parent()._Sigma0(A.adjoint(), check=False) * tmp[ii])) - vals = self._make_invariant([self._U(o) for o in F]) + F.append(-(A * tmp[ii])) + vals = self._make_invariant([self._U(o,normalize=False) for o in F]) return self.element_class(self, vals) if data == 0: return self.zero_element() @@ -2542,9 +2551,7 @@ def lift(self, f): sage: A2.lift(a) # long time p-adic automorphic form of cohomological weight 0 """ - F = self(f) - F._improve(f) - return F + return self(f)._improve(f) def _make_invariant(self, F): r""" @@ -2574,7 +2581,7 @@ def _make_invariant(self, F): newF = [] for ii in range(len(S)): Si = S[ii] - x = self._U(F[ii]) + x = self._U(F[ii], normalize=False) if any(v[2] for v in Si): newFi = self._U(0) @@ -2582,13 +2589,14 @@ def _make_invariant(self, F): m = M[ii] for v in Si: s += 1 - newFi += self._Sigma0((m.adjoint() * self._source.embed_quaternion(v[0], prec=self._prec) * m).adjoint(), check=False) * self._U(x) - newF.append((1 / s) * newFi) + g = self._Sigma0(m.adjoint() * self._source.embed_quaternion(v[0], prec=self._prec).adjoint() * m,check = False) + newFi += g * x + newF.append((QQ(1) / s) * newFi) else: - newF.append(self._U(x)) + newF.append(self._U(x,normalize=False)) return newF - def _apply_Up_operator(self, f, scale=True, hc=None): + def _apply_Up_operator(self, f, scale=False, original_moments=None): r""" Apply the Up operator to ``f``. @@ -2607,34 +2615,29 @@ def _apply_Up_operator(self, f, scale=True, hc=None): p-adic automorphic form of cohomological weight 2 """ HeckeData = self._source._get_Up_data() - if scale: - factor = self._p ** (self._U.weight() / 2) + S0 = f._value[0].parent()._act._Sigma0 + prec_cap = self._U.base_ring().precision_cap() + + if not scale: + factor = self._p ** (self._U.weight() // 2) else: factor = 1 # Save original moments - if hc is None: - orig_moments = [[fval._moments[ii] for ii in range(self._n + 1)] + if original_moments is None: + original_moments = [[fval._moments[ii] for ii in range(self._n + 1)] for fval in f._value] - else: - orig_moments = [[fval._moments[ii] for ii in range(self._n + 1)] - for fval in hc._F] - orig_moments += [[-fval._moments[ii] for ii in range(self._n + 1)] - for fval in hc._F] Tf = [] - S0 = f._value[0].parent()._act._Sigma0 for jj in range(len(self._list)): - tmp = self._U(0) + tmp = self._U(0,normalize=False) for gg, edge_list in HeckeData: u = edge_list[jj] - r = (self._p ** (-(u.power)) - * (u.t(self._U.base_ring().precision_cap() - + 2 * u.power + 1) * gg)).adjoint() - tmp += S0(r, check=False) * f._value[u.label] - # Warning: should activate check... + tprec = 2 * (prec_cap + u.power) + 1 + r = S0(self._p ** -u.power * (u.t(tprec) * gg).adjoint(),check=False) + tmp += r * f._value[u.label] tmp *= factor for ii in range(self._n + 1): - tmp._moments[ii] = orig_moments[jj][ii] + tmp._moments[ii] = original_moments[jj][ii] Tf.append(tmp) return self(Tf) diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 38f98dbcc7d..34d0ab1f5b0 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -789,7 +789,7 @@ cdef class Dist_vector(Dist): sage: D = OverconvergentDistributions(3,5,6) # indirect doctest sage: v = D([1,1,1]) """ - def __init__(self, moments, parent, ordp=0, check=True): + def __init__(self, moments, parent, ordp=0, check=True, normalize=True): """ Initialization. @@ -824,7 +824,8 @@ cdef class Dist_vector(Dist): self._moments = moments self.ordp = ordp - self.normalize() # DEBUG + if normalize: + self.normalize() def __reduce__(self): r""" @@ -871,7 +872,6 @@ cdef class Dist_vector(Dist): sage: repr(v) '(1 + O(7^5), 2 + O(7^4), 3 + O(7^3), 4 + O(7^2), 5 + O(7))' """ - # self.normalize() # Should normalize only when absolutely needed. valstr = "" if self.ordp == 1: valstr = "%s * " % (self.parent().prime()) @@ -1886,7 +1886,6 @@ cdef class WeightKAction_vector(WeightKAction): v_moments = v._moments ans._moments = v_moments * self.acting_matrix(g, len(v_moments)) ans.ordp = v.ordp - ans.normalize() return ans # cdef inline long mymod(long a, unsigned long pM): diff --git a/src/sage/modular/pollack_stevens/distributions.py b/src/sage/modular/pollack_stevens/distributions.py index 00e3fb86553..e9660798581 100644 --- a/src/sage/modular/pollack_stevens/distributions.py +++ b/src/sage/modular/pollack_stevens/distributions.py @@ -41,6 +41,8 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six.moves import range + from sage.modules.module import Module from sage.structure.parent import Parent from sage.rings.padics.factory import ZpCA, QpCR @@ -305,7 +307,7 @@ def __init__(self, k, p=None, prec_cap=None, base=None, character=None, self._populate_coercion_lists_(action_list=[self._act]) - def _element_constructor_(self, val): + def _element_constructor_(self, val, **kwargs): """ Construct a distribution from data in ``val`` @@ -315,7 +317,10 @@ def _element_constructor_(self, val): sage: v = V([1,2,3,4,5,6,7]); v (1, 2, 3, 4, 5, 6, 7) """ - return self.Element(val, self) + ordp = kwargs.get('ord',0) + check = kwargs.get('check',True) + normalize= kwargs.get('normalize',True) + return self.Element(val, self, ordp, check, normalize) def _coerce_map_from_(self, other): """ @@ -663,7 +668,7 @@ def _an_element_(self): sage: D.an_element() # indirect doctest (0, 1, 2, 3) """ - return self(range(self.weight() + 1)) + return self(list(range(self.weight() + 1))) def _repr_(self): """ diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index 623d26931e9..f56b045da43 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -687,7 +687,7 @@ cdef class CoinBackend(GenericBackend): sage: p.nrows() # optional - cbc 0 sage: p.add_linear_constraints(5, 0, None) # optional - cbc - sage: p.add_col(range(5), range(5)) # optional - cbc + sage: p.add_col(list(range(5)), list(range(5))) # optional - cbc sage: p.nrows() # optional - cbc 5 """ @@ -721,7 +721,7 @@ cdef class CoinBackend(GenericBackend): sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Coin") # optional - cbc sage: p.add_linear_constraints(5, 0, None) # optional - cbc - sage: p.add_col(range(5), [1,2,3,4,5]) # optional - cbc + sage: p.add_col(list(range(5)), [1,2,3,4,5]) # optional - cbc sage: p.solve() # optional - cbc 0 diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index 7908b27632c..4e4b5707af6 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -586,7 +586,7 @@ cdef class PPLBackend(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 """ @@ -655,7 +655,7 @@ cdef class PPLBackend(GenericBackend): sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") 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.solve() 0 diff --git a/src/sage/plot/circle.py b/src/sage/plot/circle.py index b7e1fc56643..da49684fb03 100644 --- a/src/sage/plot/circle.py +++ b/src/sage/plot/circle.py @@ -23,6 +23,7 @@ from sage.plot.colors import to_mpl_color from math import sin, cos, pi + class Circle(GraphicPrimitive): """ Primitive class for the Circle graphics type. See circle? for information @@ -108,17 +109,17 @@ def _allowed_options(self): sage: p[0]._allowed_options()['facecolor'] '2D only: The color of the face as an RGB tuple.' """ - return {'alpha':'How transparent the figure is.', - 'fill':'Whether or not to fill the circle.', - 'legend_label':'The label for this item in the legend.', - 'legend_color':'The color of the legend text.', - 'thickness':'How thick the border of the circle is.', - 'edgecolor':'2D only: The color of the edge as an RGB tuple.', - 'facecolor':'2D only: The color of the face as an RGB tuple.', - 'rgbcolor':'The color (edge and face) as an RGB tuple.', - 'hue':'The color given as a hue.', - 'zorder':'2D only: The layer level in which to draw', - 'linestyle':"2D only: The style of the line, which is one of " + return {'alpha': 'How transparent the figure is.', + 'fill': 'Whether or not to fill the circle.', + 'legend_label': 'The label for this item in the legend.', + 'legend_color': 'The color of the legend text.', + 'thickness': 'How thick the border of the circle is.', + 'edgecolor': '2D only: The color of the edge as an RGB tuple.', + 'facecolor': '2D only: The color of the face as an RGB tuple.', + 'rgbcolor': 'The color (edge and face) as an RGB tuple.', + 'hue': 'The color given as a hue.', + 'zorder': '2D only: The layer level in which to draw', + 'linestyle': "2D only: The style of the line, which is one of " "'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', " "respectively.", 'clip': 'Whether or not to clip the circle.'} @@ -185,6 +186,11 @@ def plot3d(self, z=0, **kwds): sage: sum([circle((random(),random()), random()).plot3d(z=random()) for _ in range(20)]) Graphics3d Object + .. PLOT:: + + P = sum([circle((random(),random()), random()).plot3d(z=random()) for _ in range(20)]) + sphinx_plot(P) + These examples are explicit, and pass z to this method:: sage: C = circle((2,pi), 2, hue=.8, alpha=.3, fill=True) @@ -220,6 +226,7 @@ def plot3d(self, z=0, **kwds): from .line import Line return Line(xdata, ydata, options).plot3d().translate((0,0,z)) + @rename_keyword(color='rgbcolor') @options(alpha=1, fill=False, thickness=1, edgecolor='blue', facecolor='blue', linestyle='solid', zorder=5, legend_label=None, legend_color=None, clip=True, aspect_ratio=1.0) @@ -261,12 +268,21 @@ def circle(center, radius, **options): sage: c Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(circle((1,1), 1)) + :: sage: c = circle((1,1), 1, rgbcolor=(1,0,0), linestyle='-.') sage: c Graphics object consisting of 1 graphics primitive + .. PLOT:: + + c = circle((1,1), 1, rgbcolor=(1,0,0), linestyle='-.') + sphinx_plot(c) + We can also use this command to plot three-dimensional circles parallel to the `xy`-plane:: @@ -276,6 +292,11 @@ def circle(center, radius, **options): sage: type(c) + .. PLOT:: + + c = circle((1,1,3), 1, rgbcolor=(1,0,0)) + sphinx_plot(c) + To correct the aspect ratio of certain graphics, it is necessary to show with a ``figsize`` of square dimensions:: @@ -291,34 +312,71 @@ def circle(center, radius, **options): ....: g += circle((x,y), ocur, rgbcolor=hue(r/paths)) ....: rnext = (r+1)^2 ....: ocur = (rnext-r)-ocur - ... sage: g.show(xmin=-(paths+1)^2, xmax=(paths+1)^2, ymin=-(paths+1)^2, ymax=(paths+1)^2, figsize=[6,6]) + .. PLOT:: + + g = Graphics() + step=6; ocur=1/5; paths=16; + PI = math.pi # numerical for speed -- fine for graphics + for r in range(1,paths+1): + for x,y in [((r+ocur)*math.cos(n), (r+ocur)*math.sin(n)) for n in srange(0, 2*PI+PI/step, PI/step)]: + g += circle((x,y), ocur, rgbcolor=hue(r*1.0/paths)) + rnext = (r+1)**2 + ocur = (rnext-r)-ocur + g.set_axes_range(-(paths+1)**2,(paths+1)**2,-(paths+1)**2,(paths+1)**2) + sphinx_plot(g) + Note that the ``rgbcolor`` option overrides the other coloring options. This produces red fill in a blue circle:: - sage: circle((2,3), 1, fill=True, edgecolor='blue') + sage: circle((2,3), 1, fill=True, edgecolor='blue', facecolor='red') Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(circle((2,3), 1, fill=True, edgecolor='blue', facecolor='red')) + This produces an all-green filled circle:: sage: circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green') Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green')) + The option ``hue`` overrides *all* other options, so be careful with its use. This produces a purplish filled circle:: sage: circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green', hue=.8) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + C = circle((2,3), 1, fill=True, edgecolor='blue', rgbcolor='green', hue=.8) + sphinx_plot(C) + And circles with legends:: sage: circle((4,5), 1, rgbcolor='yellow', fill=True, legend_label='the sun').show(xmin=0, ymin=0) + .. PLOT:: + + C = circle((4,5), 1, rgbcolor='yellow', fill=True, legend_label='the sun') + C.set_axes_range(xmin=0, ymin=0) + sphinx_plot(C) + :: sage: circle((4,5), 1, legend_label='the sun', legend_color='yellow').show(xmin=0, ymin=0) + .. PLOT:: + + C = circle((4,5), 1, legend_label='the sun', legend_color='yellow') + C.set_axes_range(xmin=0, ymin=0) + sphinx_plot(C) + Extra options will get passed on to show(), as long as they are valid:: sage: circle((0, 0), 2, figsize=[10,10]) # That circle is huge! @@ -359,9 +417,9 @@ def circle(center, radius, **options): if options['legend_label']: g.legend(True) g._legend_colors = [options['legend_color']] - if len(center)==2: + if len(center) == 2: return g - elif len(center)==3: + elif len(center) == 3: return g[0].plot3d(z=center[2]) else: raise ValueError('The center of a plotted circle should have two or three coordinates.') diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 688dfbd255f..e0c11914b8b 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -356,49 +356,23 @@ cdef class Graphics3d(SageObject): EXAMPLES:: - sage: sphere()._rich_repr_threejs() + sage: sphere(online=True)._rich_repr_threejs() OutputSceneThreejs container """ - options = {} - options['aspect_ratio'] = [float(i) for i in kwds.get('aspect_ratio', [1,1,1])] - options['axes'] = kwds.get('axes', False) - options['axes_labels'] = kwds.get('axes_labels', ['x','y','z']) - options['decimals'] = int(kwds.get('decimals', 2)) - options['frame'] = kwds.get('frame', True) - options['online'] = kwds.get('online', False) + options = self._process_viewing_options(kwds) + # Threejs specific options + options.setdefault('axes_labels', ['x','y','z']) + options.setdefault('decimals', 2) + options.setdefault('online', False) + # Normalization of options values for proper JSONing + options['aspect_ratio'] = [float(i) for i in options['aspect_ratio']] + options['decimals'] = int(options['decimals']) if not options['frame']: options['axes_labels'] = False from sage.repl.rich_output import get_display_manager - backend = get_display_manager()._backend - from sage.repl.rich_output.backend_sagenb import BackendSageNB - if isinstance(backend, BackendSageNB): - options['online'] = True - - if options['online']: - scripts = ( """ - - - """ ) - else: - from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook - if isinstance(backend, BackendIPythonNotebook): - scripts = ( """ - - - - """ ) - else: - from sage.env import SAGE_SHARE - scripts = ( """ - - - """.format( SAGE_SHARE ) ) + scripts = get_display_manager().threejs_scripts(options['online']) b = self.bounding_box() bounds = '[{{"x":{}, "y":{}, "z":{}}}, {{"x":{}, "y":{}, "z":{}}}]'.format( @@ -440,13 +414,14 @@ cdef class Graphics3d(SageObject): surfaces = '[' + ','.join(surfaces) + ']' from sage.env import SAGE_EXTCODE - filename = os.path.join(SAGE_EXTCODE, 'threejs', 'threejs_template.html') - f = open(filename, 'r') - html = f.read() - f.close() + with open(os.path.join( + SAGE_EXTCODE, 'threejs', 'threejs_template.html')) as f: + html = f.read() html = html.replace('SAGE_SCRIPTS', scripts) - html = html.replace('SAGE_OPTIONS', json.dumps(options)) + js_options = dict((key, options[key]) for key in + ['aspect_ratio', 'axes', 'axes_labels', 'decimals', 'frame']) + html = html.replace('SAGE_OPTIONS', json.dumps(js_options)) html = html.replace('SAGE_BOUNDS', bounds) html = html.replace('SAGE_LIGHTS', lights) html = html.replace('SAGE_AMBIENT', ambient) diff --git a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py index f2b8fe5f5dd..2170400362d 100644 --- a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +++ b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py @@ -37,7 +37,7 @@ def disc(self): r""" - Returns the discriminant of the quadratic form, defined as + Return the discriminant of the quadratic form, defined as - `(-1)^n {\rm det}(B)` for even dimension `2n` - `{\rm det}(B)/2` for odd dimension @@ -66,7 +66,7 @@ def disc(self): def content(self): """ - Returns the GCD of the coefficients of the quadratic form. + Return the GCD of the coefficients of the quadratic form. .. warning:: @@ -103,7 +103,7 @@ def content(self): ## in quadratic_form.py #def primitive(self): # """ -# Returns a primitive quadratic forms in the similarity class of the given form. +# Return a primitive quadratic forms in the similarity class of the given form. # # This only works when we have GCDs... so over ZZ. # """ @@ -250,7 +250,7 @@ def delta(self): def level__Tornaria(self): """ - Returns the level of the quadratic form, + Return the level of the quadratic form, defined as level(B) for even dimension @@ -277,7 +277,7 @@ def level__Tornaria(self): def discrec(self): """ - Returns the discriminant of the reciprocal form. + Return the discriminant of the reciprocal form. EXAMPLES:: @@ -489,7 +489,7 @@ def xi(self,p): def xi_rec(self,p): """ - Returns Xi(`p`) for the reciprocal form. + Return Xi(`p`) for the reciprocal form. EXAMPLES:: @@ -514,7 +514,7 @@ def xi_rec(self,p): def lll(self): """ - Returns an LLL-reduced form of Q (using Pari). + Return an LLL-reduced form of Q (using Pari). EXAMPLES:: @@ -534,22 +534,23 @@ def lll(self): def representation_number_list(self, B): """ - Returns the vector of representation numbers < B. + Return the vector of representation numbers < B. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ,[1,1,1,1,1,1,1,1]) sage: Q.representation_number_list(10) [1, 16, 112, 448, 1136, 2016, 3136, 5504, 9328, 12112] - """ - ans = pari(1).concat(self.__pari__().qfrep(B-1, 1) * 2) + ans = pari(1).concat(self.__pari__().qfrep(B - 1, 1) * 2) return ans.sage() -def representation_vector_list(self, B, maxvectors = 10**8): +def representation_vector_list(self, B, maxvectors=10**8): """ - Find all vectors v where Q(v) < B. + Find all vectors `v` where `Q(v) < B`. + + This only works for positive definite quadratic forms. EXAMPLES:: @@ -570,8 +571,16 @@ def representation_vector_list(self, B, maxvectors = 10**8): sage: Q.representation_number_list(10) [1, 4, 4, 0, 4, 8, 0, 0, 4, 4] + TESTS:: + + sage: R = QuadraticForm(ZZ,2,[-4,-3,0]) + sage: R.representation_vector_list(10) + Traceback (most recent call last): + ... + PariError: domain error in minim0: form is not positive definite """ - n, m, vs = self.__pari__().qfminim(2*(B-1), maxvectors) + n, m, vs = self.__pari__().qfminim(2 * (B - 1), maxvectors) + if n != 2 * len(vs): raise RuntimeError("insufficient number of vectors") ms = [[] for _ in range(B)] diff --git a/src/sage/quivers/paths.pyx b/src/sage/quivers/paths.pyx index 112b660a7e7..88c5a1c952c 100644 --- a/src/sage/quivers/paths.pyx +++ b/src/sage/quivers/paths.pyx @@ -380,7 +380,7 @@ cdef class QuiverPath(MonoidElement): -1 is not explicitly given, then a path of length zero is returned, which is compatible with Python lists:: - sage: range(6)[4:1] + sage: list(range(6))[4:1] [] The following was fixed in :trac:`22278`. A path slice of length @@ -396,7 +396,7 @@ cdef class QuiverPath(MonoidElement): If the slice boundaries are out of bound, then no error is raised, which is compatible with Python lists:: - sage: range(6)[20:40] + sage: list(range(6))[20:40] [] In that case, the startpoint of the slice of length zero is the diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index e45fca731f0..5b60d1b1128 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -1,9 +1,9 @@ # -*- encoding: utf-8 -*- r""" -Base class for Backends +Base Class for Backends The display backends are the commandline, the SageNB notebook, the -ipython notebook, the Emacs sage mode, the Sage doctester, .... All of +IPython notebook, the Emacs sage mode, the Sage doctester, .... All of these have different capabilities for what they can display. To implement a new display backend, you need to subclass diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index e5e85105df8..1a974b684c7 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -396,6 +396,27 @@ def is_in_terminal(self): """ return True + def threejs_offline_scripts(self): + """ + Three.js scripts for the IPython command line + + OUTPUT: + + String containing script tags + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline + sage: backend = BackendIPythonCommandline() + sage: backend.threejs_offline_scripts() + '... + + """.format(SAGE_SHARE) + IFRAME_TEMPLATE = \ """ @@ -554,4 +575,27 @@ def displayhook(self, plain_text, rich_output): else: raise TypeError('rich_output type not supported') - + def threejs_offline_scripts(self): + """ + Three.js scripts for the IPython notebook + + OUTPUT: + + String containing script tags + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook + sage: backend = BackendIPythonNotebook() + sage: backend.threejs_offline_scripts() + '... + + + """.format(CDN_scripts.replace('', r'<\/script>')) diff --git a/src/sage/repl/rich_output/backend_sagenb.py b/src/sage/repl/rich_output/backend_sagenb.py index 7b3abc584ad..287527747f2 100644 --- a/src/sage/repl/rich_output/backend_sagenb.py +++ b/src/sage/repl/rich_output/backend_sagenb.py @@ -452,3 +452,21 @@ def embed_video(self, video_output): url='cell://' + filename, link_attrs='class="file_link"', )) + + def threejs_offline_scripts(self): + """ + Three.js scripts for the Sage notebook + + OUTPUT: + + String containing script tags + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_sagenb import BackendSageNB + sage: backend = BackendSageNB() + sage: backend.threejs_offline_scripts() + '... + + """.format(version) + try: + return self._backend.threejs_offline_scripts() + except AttributeError: + raise ValueError( + 'current backend does not support offline threejs graphics') + def supported_output(self): """ Return the output container classes that can be used. diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 5baee260d9d..2c1b137d57e 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -74,6 +74,7 @@ from .complex_number cimport ComplexNumber from .complex_field import ComplexField_class from sage.misc.randstate cimport randstate, current_randstate +from sage.misc.superseded import deprecated_function_alias from .real_mpfr cimport RealField_class, RealNumber from .real_mpfr import mpfr_prec_min, mpfr_prec_max from sage.structure.sage_object cimport rich_to_bool, richcmp @@ -1309,11 +1310,8 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): def algebraic_dependency(self, n, **kwds): """ - Returns a polynomial of degree at most `n` which is - approximately satisfied by this complex number. Note that the - returned polynomial need not be irreducible, and indeed usually - won't be if `z` is a good approximation to an algebraic - number of degree less than `n`. + Return an irreducible polynomial of degree at most `n` which is + approximately satisfied by this complex number. ALGORITHM: Uses the PARI C-library algdep command. @@ -1326,16 +1324,23 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: z = (1/2)*(1 + sqrt(3.0) * MPC.0); z 0.500000000000000 + 0.866025403784439*I sage: p = z.algebraic_dependency(5) - sage: p.factor() - (x + 1) * x^2 * (x^2 - x + 1) - sage: z^2 - z + 1 + sage: p + x^2 - x + 1 + sage: p(z) 1.11022302462516e-16 + + TESTS:: + + sage: z.algebraic_dependancy(2) + doctest:...: DeprecationWarning: algebraic_dependancy is deprecated. Please use algebraic_dependency instead. + See http://trac.sagemath.org/22714 for details. + x^2 - x + 1 """ - import sage.arith.all - return sage.arith.all.algdep(self, n, **kwds) + from sage.arith.all import algdep + return algdep(self, n, **kwds) # Former misspelling - algebraic_dependancy = algebraic_dependency + algebraic_dependancy = deprecated_function_alias(22714, algebraic_dependency) ################################ # Basic Arithmetic diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index bb299e9f642..e6f62227288 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -35,6 +35,7 @@ from .complex_double cimport ComplexDoubleElement from .real_mpfr cimport RealNumber import sage.misc.misc +from sage.misc.superseded import deprecated_function_alias import sage.rings.integer as integer import sage.rings.infinity as infinity @@ -2360,13 +2361,10 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): return infinity.unsigned_infinity return self._parent(self.__pari__().zeta()) - def algdep(self, n, **kwds): + def algebraic_dependency(self, n, **kwds): """ - Returns a polynomial of degree at most `n` which is - approximately satisfied by this complex number. Note that the - returned polynomial need not be irreducible, and indeed usually - won't be if `z` is a good approximation to an algebraic - number of degree less than `n`. + Return an irreducible polynomial of degree at most `n` which is + approximately satisfied by this complex number. ALGORITHM: Uses the PARI C-library algdep command. @@ -2379,41 +2377,28 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: z = (1/2)*(1 + sqrt(3.0) *C.0); z 0.500000000000000 + 0.866025403784439*I sage: p = z.algdep(5); p - x^3 + 1 - sage: p.factor() - (x + 1) * (x^2 - x + 1) - sage: z^2 - z + 1 + x^2 - x + 1 + sage: p(z) 1.11022302462516e-16 - """ - import sage.arith.all - return sage.arith.all.algdep(self, n, **kwds) - def algebraic_dependancy( self, n ): + TESTS:: + + sage: z.algdep(2) + x^2 - x + 1 + sage: z.algebraic_dependancy(2) + doctest:...: DeprecationWarning: algebraic_dependancy is deprecated. Please use algebraic_dependency instead. + See http://trac.sagemath.org/22714 for details. + x^2 - x + 1 """ - Returns a polynomial of degree at most `n` which is - approximately satisfied by this complex number. Note that the - returned polynomial need not be irreducible, and indeed usually - won't be if `z` is a good approximation to an algebraic - number of degree less than `n`. + from sage.arith.all import algdep + return algdep(self, n, **kwds) - ALGORITHM: Uses the PARI C-library algdep command. + # Alias + algdep = algebraic_dependency - INPUT: Type algdep? at the top level prompt. All additional - parameters are passed onto the top-level algdep command. + # Former misspelling + algebraic_dependancy = deprecated_function_alias(22714, algebraic_dependency) - EXAMPLES:: - - sage: C = ComplexField() - sage: z = (1/2)*(1 + sqrt(3.0) *C.0); z - 0.500000000000000 + 0.866025403784439*I - sage: p = z.algebraic_dependancy(5); p - x^3 + 1 - sage: p.factor() - (x + 1) * (x^2 - x + 1) - sage: z^2 - z + 1 - 1.11022302462516e-16 - """ - return self.algdep( n ) def make_ComplexNumber0( fld, mult_order, re, im ): """ diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 1331c778b4a..d2c002df3e1 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -29,9 +29,6 @@ - David Roe (2008-2-23): created - David Loeffler (2009-07-10): cleaned up docstrings """ -from __future__ import absolute_import -from six import iteritems, iterkeys - #***************************************************************************** # Copyright (C) 2008 David Roe , # William Stein , @@ -43,7 +40,9 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - +from __future__ import absolute_import +from six import iteritems, iterkeys +from six.moves import range from sage.structure.category_object import normalize_names from sage.structure.element import is_Element, parent @@ -518,7 +517,7 @@ def value(d, R): return R(M(d)) group_by = tuple(index(vars_M, var) for var in vars_P) - indices = range(len(vars_M)) + indices = list(range(len(vars_M))) for g in group_by: if g is not None: indices[g] = None @@ -529,6 +528,7 @@ def value(d, R): pass return sum(P({k: 1}) * value(v, P) for k, v in iteritems(D)).dict() + class LaurentPolynomialRing_generic(CommutativeRing, ParentWithGens): """ Laurent polynomial ring (base class). diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index ac66bd6ccaa..f263e37c2d4 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -6200,9 +6200,9 @@ cdef class Polynomial(CommutativeAlgebraElement): ....: for p2 in [5*y^2 - 7, -3*y - 1]: ....: for monic in [True,False]: ....: for op in [operator.add, operator.sub, operator.mul, operator.div]: - ....: pr = p1.composed_op(p2, operator.add, "resultant", monic=monic) - ....: pb = p1.composed_op(p2, operator.add, "BFSS", monic=monic) - ....: assert pr == pb and parent(pr) is parent(pb) + ....: pr = p1.composed_op(p2, op, "resultant", monic=monic) + ....: pb = p1.composed_op(p2, op, "BFSS", monic=monic) + ....: assert ((pr == pb) or ((not monic) and pr == -pb) and (parent(pr) is parent(pb))) REFERENCES: @@ -6317,6 +6317,213 @@ cdef class Polynomial(CommutativeAlgebraElement): else: raise ValueError('algorithm must be "resultant" or "BFSS"') + def compose_power(self, k, algorithm=None, monic=False): + r""" + Return the `k`-th iterate of the composed product of this + polynomial with itself. + + INPUT: + + - `k` -- a non-negative integer + + - ``algorithm`` -- ``None`` (default), ``"resultant"`` or ``"BFSS"``. + See :meth:`.composed_op` + + - ``monic`` - ``False`` (default) or ``True``. + See :meth:`.composed_op` + + OUTPUT: + + The polynomial of degree `d^k` where `d` is the degree, whose + roots are all `k`-fold products of roots of this polynomial. + That is, `f*f*\dots*f` where this is `f` and + `f*f=` f.composed_op(f,operator.mul). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: x = polygen(R) + sage: f = (x-a)*(x-b)*(x-c) + sage: f.compose_power(2).factor() + (x - c^2) * (x - b^2) * (x - a^2) * (x - b*c)^2 * (x - a*c)^2 * (x - a*b)^2 + + sage: x = polygen(QQ) + sage: f = x^2-2*x+2 + sage: f2 = f.compose_power(2); f2 + x^4 - 4*x^3 + 8*x^2 - 16*x + 16 + sage: f2 == f.composed_op(f,operator.mul) + True + sage: f3 = f.compose_power(3); f3 + x^8 - 8*x^7 + 32*x^6 - 64*x^5 + 128*x^4 - 512*x^3 + 2048*x^2 - 4096*x + 4096 + sage: f3 == f2.composed_op(f,operator.mul) + True + sage: f4 = f.compose_power(4) + sage: f4 == f3.composed_op(f,operator.mul) + True + """ + try: + k = ZZ(k) + except ValueError("Cannot iterate {} times".format(k)): + return self + if k < 0: + raise ValueError("Cannot iterate a negative number {} of times".format(k)) + if k == 0: + return self.variables()[0] - 1 + if k == 1: + return self + if k == 2: + return self.composed_op(self, operator.mul, + algorithm=algorithm, monic=monic) + k2, k1 = k.quo_rem(2) + # recurse to get the k/2 -iterate where k=2*k2+k1: + R = self.compose_power(k2, algorithm=algorithm, monic=monic) + # square: + R = R.composed_op(R, operator.mul, algorithm=algorithm, monic=monic) + # one more factor if k odd: + if k1: + R = R.composed_op(self, operator.mul) + return R + + def adams_operator(self, n, monic=False): + r""" + Return the polynomial whose roots are the `n`-th power + of the roots of this. + + INPUT: + + - `n` -- an integer + + - ``monic`` -- boolean (default ``False``) + if set to ``True``, force the output to be monic + + EXAMPLES:: + + sage: f = cyclotomic_polynomial(30) + sage: f.adams_operator(7)==f + True + sage: f.adams_operator(6) == cyclotomic_polynomial(5)**2 + True + sage: f.adams_operator(10) == cyclotomic_polynomial(3)**4 + True + sage: f.adams_operator(15) == cyclotomic_polynomial(2)**8 + True + sage: f.adams_operator(30) == cyclotomic_polynomial(1)**8 + True + + sage: x = polygen(QQ) + sage: f = x^2-2*x+2 + sage: f.adams_operator(10) + x^2 + 1024 + + When f is monic the output will have leading coefficient + `\pm1` depending on the degree, but we can force it to be + monic:: + + sage: R. = ZZ[] + sage: x = polygen(R) + sage: f = (x-a)*(x-b)*(x-c) + sage: f.adams_operator(3).factor() + (-1) * (x - c^3) * (x - b^3) * (x - a^3) + sage: f.adams_operator(3,monic=True).factor() + (x - c^3) * (x - b^3) * (x - a^3) + + """ + u, v = PolynomialRing(self.parent().base_ring(), ['u', 'v']).gens() + R = (u - v**n).resultant(self(v), v) + R = R([self.variables()[0], 0]) + if monic: + R = R.monic() + return R + + def symmetric_power(self, k, monic=False): + r""" + Return the polynomial whose roots are products of `k`-th distinct + roots of this. + + EXAMPLES:: + + sage: x = polygen(QQ) + sage: f = x^4-x+2 + sage: [f.symmetric_power(k) for k in range(5)] + [x - 1, x^4 - x + 2, x^6 - 2*x^4 - x^3 - 4*x^2 + 8, x^4 - x^3 + 8, x - 2] + + sage: f = x^5-2*x+2 + sage: [f.symmetric_power(k) for k in range(6)] + [x - 1, + x^5 - 2*x + 2, + x^10 + 2*x^8 - 4*x^6 - 8*x^5 - 8*x^4 - 8*x^3 + 16, + x^10 + 4*x^7 - 8*x^6 + 16*x^5 - 16*x^4 + 32*x^2 + 64, + x^5 + 2*x^4 - 16, + x + 2] + + sage: R. = ZZ[] + sage: x = polygen(R) + sage: f = (x-a)*(x-b)*(x-c)*(x-d) + sage: [f.symmetric_power(k).factor() for k in range(5)] + [x - 1, + (-x + d) * (-x + c) * (-x + b) * (-x + a), + (x - c*d) * (x - b*d) * (x - a*d) * (x - b*c) * (x - a*c) * (x - a*b), + (x - b*c*d) * (x - a*c*d) * (x - a*b*d) * (x - a*b*c), + x - a*b*c*d] + """ + try: + k = ZZ(k) + except (ValueError, TypeError): + raise ValueError("Cannot compute k'th symmetric power for k={}".format(k)) + n = self.degree() + if k < 0 or k > n: + raise ValueError("Cannot compute k'th symmetric power for k={}".format(k)) + x = self.variables()[0] + if k == 0: + return x - 1 + if k == 1: + if monic: + return self.monic() + return self + c = (-1)**n * self(0) + if k == n: + return x - c + if k > n - k: # use (n-k)'th symmetric power + g = self.symmetric_power(n - k, monic=monic) + from sage.arith.all import binomial + g = ((-x)**binomial(n,k) * g(c/x) / c**binomial(n-1,k)).numerator() + if monic: + g = g.monic() + return g + + def star(g, h): + return g.composed_op(h, operator.mul, monic=True) + + def rpow(g, n): + return g.adams_operator(n, monic=True) + if k == 2: + g = (star(self, self) // rpow(self, 2)).nth_root(2) + if monic: + g = g.monic() + return g + if k == 3: + g = star(self.symmetric_power(2, monic=monic), self) * rpow(self, 3) + h = star(rpow(self, 2), self) + g = (g // h).nth_root(3) + if monic: + g = g.monic() + return g + + fkn = fkd = self.parent().one() + for j in range(1, k + 1): + g = star(rpow(self, j), self.symmetric_power(k - j)) + if j % 2: + fkn *= g + else: + fkd *= g + + fk = fkn // fkd + assert fk * fkd == fkn + g = fk.nth_root(k) + if monic: + g = g.monic() + return g + def discriminant(self): r""" Returns the discriminant of self. @@ -10007,7 +10214,7 @@ cdef class Polynomial_generic_dense_inexact(Polynomial_generic_dense): indistinguishable from 0), an error is raised If ``secure`` is False, the returned value is the largest - $n$ so that the coefficient of $x^n$ does not compare equal + `n` so that the coefficient of `x^n` does not compare equal to `0`. EXAMPLES:: diff --git a/src/sage/rings/polynomial/toy_d_basis.py b/src/sage/rings/polynomial/toy_d_basis.py index 1878e1237ce..adca6cd8cca 100644 --- a/src/sage/rings/polynomial/toy_d_basis.py +++ b/src/sage/rings/polynomial/toy_d_basis.py @@ -4,7 +4,7 @@ No attempt was made to optimize this algorithm as the emphasis of this implementation is a clean and easy presentation. -.. note:: +.. NOTE:: The notion of 'term' and 'monomial' in [BW93]_ is swapped from the notion of those words in Sage (or the other way around, however you @@ -78,16 +78,19 @@ over `\QQ` or an algebraic closure of it (this is not surprising as there are 4 equations in 3 unknowns).:: - sage: P. = PolynomialRing(IntegerRing(), 3) + sage: P. = PolynomialRing(IntegerRing(), 3, order='degneglex') sage: I = ideal( x^2 - 3*y, y^3 - x*y, z^3 - x, x^4 - y*z + 1 ) - sage: I.change_ring( P.change_ring( RationalField() ) ).groebner_basis() + sage: I.change_ring(P.change_ring(RationalField())).groebner_basis() [1] However, when we compute the Groebner basis of I (defined over `\ZZ`), we -note that there is a certain integer in the ideal which is not 1.:: +note that there is a certain integer in the ideal which is not 1:: - sage: d_basis(I) # random -- waiting on upstream singular fixes at #6051 - [x + 170269749119, y + 2149906854, z + ..., 282687803443] + sage: gb = d_basis(I); gb + [z - 107196348594952664476180297953816049406949517534824683390654620424703403993052759002989622, + y + 84382748470495086324437828161121754084154498572003307352857967748090984550697850484197972764799434672877850291328840, + x + 105754645239745824529618668609551113725317621921665293762587811716173, + 282687803443] Now for each prime `p` dividing this integer 282687803443, the Groebner basis of I modulo `p` will be non-trivial and will thus give a solution @@ -97,13 +100,13 @@ 101 * 103 * 27173681 sage: I.change_ring( P.change_ring( GF(101) ) ).groebner_basis() - [x + 19, y + 48, z - 33] + [z - 33, y + 48, x + 19] sage: I.change_ring( P.change_ring( GF(103) ) ).groebner_basis() - [x + 39, y + 8, z - 18] + [z - 18, y + 8, x + 39] sage: I.change_ring( P.change_ring( GF(27173681) ) ).groebner_basis() - [x - 536027, y + 3186055, z + 10380032] + [z + 10380032, y + 3186055, x - 536027] Of course, modulo any other prime the Groebner basis is trivial so there are no other solutions. For example:: @@ -115,13 +118,13 @@ - Martin Albrecht (2008-08): initial version """ - from sage.rings.integer_ring import ZZ from sage.arith.all import xgcd, lcm, gcd from sage.rings.polynomial.toy_buchberger import inter_reduction from sage.structure.sequence import Sequence -def spol(g1,g2): + +def spol(g1, g2): """ Return S-Polynomial of ``g_1`` and ``g_2``. @@ -153,7 +156,8 @@ def spol(g1,g2): return b1*s1*g1 - b2*s2*g2 -def gpol(g1,g2): + +def gpol(g1, g2): """ Return G-Polynomial of ``g_1`` and ``g_2``. @@ -188,6 +192,7 @@ def gpol(g1,g2): LM = lambda f: f.lm() LC = lambda f: f.lc() + def d_basis(F, strat=True): r""" Return the `d`-basis for the Ideal ``F`` as defined in [BW93]_. @@ -259,6 +264,7 @@ def d_basis(F, strat=True): return Sequence(sorted(inter_reduction(G),reverse=True)) + def select(P): """ The normal selection strategy. @@ -291,7 +297,8 @@ def select(P): min_pair = fi,fj return min_pair -def update(G,B,h): + +def update(G, B, h): """ Update ``G`` using the list of critical pairs ``B`` and the polynomial ``h`` as presented in [BW93]_, page 230. For this, @@ -371,4 +378,4 @@ def update(G,B,h): G_new.add(h) - return G_new,B_new + return G_new, B_new diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index ab13484e70b..2eeff8ee879 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -29,6 +29,7 @@ from operator import add, sub, mul, div, pow, neg, inv cdef canonical_coercion from sage.structure.element import canonical_coercion from sage.structure.all import parent +from sage.structure.sage_object cimport richcmp import sage.categories.map from sage.categories.morphism cimport Morphism @@ -657,7 +658,7 @@ cdef class LazyFieldElement(FieldElement): """ return self._new_unop(self, inv) - cpdef int _cmp_(self, other) except -2: + cpdef _richcmp_(self, other, int op): """ If things are being wrapped, tries to compare values. That failing, it tries to compare intervals, which may return a false negative. @@ -693,11 +694,11 @@ cdef class LazyFieldElement(FieldElement): try: if isinstance(self, LazyWrapper) and isinstance(other, LazyWrapper): left, right = canonical_coercion((self)._value, (other)._value) - return cmp(left, right) + return richcmp(left, right, op) except TypeError: pass left, right = self.approx(), other.approx() - return cmp(left, right) + return richcmp(left.endpoints(), right.endpoints(), op) def __hash__(self): """ @@ -915,7 +916,7 @@ def make_element(parent, *args): EXAMPLES:: sage: a = RLF(pi) + RLF(sqrt(1/2)) # indirect doctest - sage: loads(dumps(a)) == a + sage: bool(loads(dumps(a)) == a) True """ return parent(*args) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index a6ea987c8e2..ea0ae521507 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1484,22 +1484,27 @@ def rational_parameterization(self): # the zero polynomial, and dehomogenization won't change this H = Hom(A_line, C) return H([para[1]/para[0], para[2]/para[0]]) - + def fundamental_group(self): - """ - Return a presentation of the fundamental group of the complement of the curve. - In order to compute it, the curve must be defined over the rationals or a number - field with an embedding over QQbar. - + r""" + Return a presentation of the fundamental group of the complement + of ``self``. + + .. NOTE:: + + The curve must be defined over the rationals or a number field + with an embedding over `\QQbar`. + EXAMPLES:: - + sage: A. = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > - - In the case of number fields, they need to have an embedding to the algebraic field:: - + + In the case of number fields, they need to have an embedding + to the algebraic field:: + sage: a = QQ[x](x^2+5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) sage: F.inject_variables() @@ -1508,17 +1513,20 @@ def fundamental_group(self): sage: C = A.curve(y^2 - a*x^3 - x^2) sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > - - .. WARNING: This functionality requires the sirocco package to be installed. + + .. WARNING:: + + This functionality requires the sirocco package to be installed. """ from sage.schemes.curves.zariski_vankampen import fundamental_group F = self.base_ring() from sage.rings.qqbar import QQbar if QQbar.coerce_map_from(F) is None: - raise NotImplementedError('The base field must have an embeding to the algebraic field') + raise NotImplementedError("the base field must have an embedding" + " to the algebraic field") f = self.defining_polynomial() return fundamental_group(f, projective=False) - + class AffinePlaneCurve_finite_field(AffinePlaneCurve): diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 50390a4ff9e..90d3f6f4779 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1489,22 +1489,27 @@ def is_transverse(self, C, P): # there is only one tangent at a nonsingular point of a plane curve return not self.tangents(P)[0] == C.tangents(P)[0] - + def fundamental_group(self): - """ - Return a presentation of the fundamental group of the complement of the curve. - In order to compute it, the curve must be defined over the rationals or a number - field with an embedding over QQbar. - + r""" + Return a presentation of the fundamental group of the complement + of ``self``. + + .. NOTE:: + + The curve must be defined over the rationals or a number field + with an embedding over `\QQbar`. + EXAMPLES:: - + sage: P. = ProjectiveSpace(QQ,2) sage: C = P.curve(x^2*z-y^3) sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | x0^3 > - In the case of number fields, they need to have an embedding into the algebraic field:: - + In the case of number fields, they need to have an embedding + into the algebraic field:: + sage: a = QQ[x](x^2+5).roots(QQbar)[0][0] sage: a -2.236067977499790?*I @@ -1515,20 +1520,22 @@ def fundamental_group(self): sage: C = P.curve(x^2 + a * y^2) sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > - - .. WARNING: This functionality requires the sirocco package to be installed. + + .. WARNING:: + + This functionality requires the sirocco package to be installed. """ from sage.schemes.curves.zariski_vankampen import fundamental_group F = self.base_ring() from sage.rings.qqbar import QQbar if QQbar.coerce_map_from(F) is None: - raise NotImplementedError('The base field must have an embeding to the algebraic field') + raise NotImplementedError("the base field must have an embedding" + " to the algebraic field") f = self.affine_patch(2).defining_polynomial() if f.degree() == self.degree(): return fundamental_group(f, projective=True) else: #in this case, the line at infinity is part of the curve, so the complement lies in the affine patch return fundamental_group(f, projective=False) - def rational_parameterization(self): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0acea0b5b65..ca663305488 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1,27 +1,29 @@ -""" +r""" Zariski-Van Kampen method implementation This file contains functions to compute the fundamental group of the complement of a curve in the complex affine or projective plane, using Zariski-Van Kampen approach. It deppends on the package sirocco. -The current implementation allows to compute a presentation of the fundamental -group of curves over the rationals or number fields with a fixed embedding on -QQbar. +The current implementation allows to compute a presentation of the +fundamental group of curves over the rationals or number fields with +a fixed embedding on `\QQbar`. + +Instead of computing a representation of the braid monodromy, we +choose several base points and a system of paths joining them that +generate all the necessary loops around the points of the discriminant. +The group is generated by the free groups over these points, and +braids over this paths gives relations between these generators. +This big group presentation is simplified at the end. -Instead of computing a representation of the braid monodromy, we choose several -base points and a system of paths joining them that generate all the necessary loops -around the points of the discriminant. The group is generated by the free groups -over these points, and braids over this paths gives relations between these generators. -This big group presentation is simplified at the end. (To do: implement the complete -braid monodromy approach). +.. TODO:: + Implement the complete braid monodromy approach. AUTHORS: - Miguel Marco (2015-09-30): Initial version - EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco @@ -41,15 +43,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import division -from __future__ import absolute_import +from __future__ import division, absolute_import from sage.groups.braid import BraidGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.rings.rational_field import QQ from sage.rings.qqbar import QQbar -from scipy.spatial import Voronoi -from numpy import array, vstack from sage.rings.all import CC, CIF from sage.parallel.decorate import parallel from sage.misc.flatten import flatten @@ -57,55 +56,61 @@ from sage.misc.misc_c import prod from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField -from sage.libs.sirocco import contpath, contpath_mp from sage.combinat.permutation import Permutation from sage.functions.generalized import sign def braid_from_piecewise(strands): - """ + r""" Compute the braid corresponding to the piecewise linear curves strands. - strands is a list whose elements are a list of pairs (x,y), where x is - a real number between 0 and 1, and y is a complex number. - - INPUT: - - - A list of lists of tuples (t, c), where t is a number bewteen 0 and 1, - and c is a complex number. - - OUTPUT: - + + INPUT: + + - ``strands`` -- a list of lists of tuples ``(t, c)``, where ``t`` + is a number bewteen 0 and 1, and ``c`` is a complex number + + OUTPUT: + The braid formed by the piecewise linear strands. - + EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise # optional - sirocco - sage: paths = [[(0, I), (0.2, -1 - 0.5*I), (0.8, -1), (1, -I)], [(0, -1), (0.5, -I), (1, 1)], [(0, 1), (0.5, 1 + I), (1, I)]] + sage: paths = [[(0, I), (0.2, -1 - 0.5*I), (0.8, -1), (1, -I)], + ....: [(0, -1), (0.5, -I), (1, 1)], + ....: [(0, 1), (0.5, 1 + I), (1, I)]] sage: braid_from_piecewise(paths) # optional - sirocco s0*s1 - """ L = strands - i = min([L[k][1][0] for k in range(len(L))]) + i = min(val[1][0] for val in L) totalpoints = [[[a[0][1].real(), a[0][1].imag()]] for a in L] indices = [1 for a in range(len(L))] while i < 1: - for j in range(len(L)): - if L[j][indices[j]][0] > i: - xaux = L[j][indices[j] - 1][1] - yaux = L[j][indices[j]][1] - aaux = L[j][indices[j] - 1][0] - baux = L[j][indices[j]][0] + for j, val in enumerate(L): + if val[indices[j]][0] > i: + xaux = val[indices[j] - 1][1] + yaux = val[indices[j]][1] + aaux = val[indices[j] - 1][0] + baux = val[indices[j]][0] interpola = xaux + (yaux - xaux)*(i - aaux)/(baux - aaux) totalpoints[j].append([interpola.real(), interpola.imag()]) else: - totalpoints[j].append([L[j][indices[j]][1].real(), L[j][indices[j]][1].imag()]) + totalpoints[j].append([val[indices[j]][1].real(), val[indices[j]][1].imag()]) indices[j] = indices[j] + 1 - i = min([L[k][indices[k]][0] for k in range(len(L))]) - for j in range(len(L)): - totalpoints[j].append([L[j][-1][1].real(), L[j][-1][1].imag()]) + i = min(val[indices[k]][0] for k,val in enumerate(L)) + + for j, val in enumerate(L): + totalpoints[j].append([val[-1][1].real(), val[-1][1].imag()]) + braid = [] G = SymmetricGroup(len(totalpoints)) + def sgn(x, y): # Opposite sign of cmp + if x < y: + return 1 + if x > y: + return -1 + return 0 for i in range(len(totalpoints[0]) - 1): l1 = [totalpoints[j][i] for j in range(len(L))] l2 = [totalpoints[j][i+1] for j in range(len(L))] @@ -118,9 +123,9 @@ def braid_from_piecewise(strands): for k in range(j): if l2[j] < l2[k]: t = (l1[j][0] - l1[k][0])/(l2[k][0] - l1[k][0] + l1[j][0] - l2[j][0]) - s = cmp(l1[k][1]*(1 - t) + t*l2[k][1], l1[j][1]*(1 - t) + t*l2[j][1]) - cruces.append([t, k, j, -s]) - if len(cruces) > 0: + s = sgn(l1[k][1]*(1 - t) + t*l2[k][1], l1[j][1]*(1 - t) + t*l2[j][1]) + cruces.append([t, k, j, s]) + if cruces: cruces.sort() P = G(Permutation([])) while cruces: @@ -134,58 +139,59 @@ def braid_from_piecewise(strands): braid.append(c[3]*min(map(P, [c[1] + 1, c[2] + 1]))) P = G(Permutation([(c[1] + 1, c[2] + 1)]))*P crossesl = [(P(c[2]+1) - P(c[1]+1),c[1],c[2],c[3]) for c in crossesl] + B = BraidGroup(len(L)) return B(braid) - def discrim(f): - """ - Return the points in the discriminant of f. f must be a polynomial in two variables with coefficients in a - number field with a fixed embedding in QQbar. The result is the set of values of the first variable for which + r""" + Return the points in the discriminant of ``f``. + + The result is the set of values of the first variable for which two roots in the second variable coincide. - - INPUT: - - - ``f`` -- a polynomial in two variables, with coefficients in a number field with a fixed embedding in QQbar. - - OUTPUT: - - A list with the values of the discriminant in QQbar - + + INPUT: + + - ``f`` -- a polynomial in two variables with coefficients in a + number field with a fixed embedding in `\QQbar` + + OUTPUT: + + A list with the values of the discriminant in `\QQbar`. + EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import discrim # optional - sirocco sage: R. = QQ[] - sage: f = (y^3 + x^3 -1) * (x+y) + sage: f = (y^3 + x^3 - 1) * (x + y) sage: discrim(f) # optional - sirocco [1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I] - """ x, y = f.variables() F = f.base_ring() - disc = F[x](f.discriminant(y).resultant(f, y)).roots(QQbar, multiplicities = False) + disc = F[x](f.discriminant(y).resultant(f, y)).roots(QQbar, multiplicities=False) return disc - def segments(points): """ Return the bounded segments of the Voronoi diagram of the given points. - - INPUT: - - - A list of complex points. - - OUTPUT: - - A list of pairs (p1, p2) where p1 and p2 are the endpoints of the segments in the Voronoi diagram - + + INPUT: + + - ``points`` -- a list of complex points + + OUTPUT: + + A list of pairs ``(p1, p2)``, where ``p1`` and ``p2`` are the + endpoints of the segments in the Voronoi diagram. + EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import discrim, segments # optional - sirocco sage: R. = QQ[] - sage: f = y^3 + x^3 -1 + sage: f = y^3 + x^3 - 1 sage: disc = discrim(f) # optional - sirocco sage: segments(disc) # optional - sirocco [(-2.84740787203333 - 2.84740787203333*I, @@ -205,11 +211,13 @@ def segments(points): (-2.14285714285714 + 1.11022302462516e-16*I, 0.000000000000000), (-2.84740787203333 - 2.84740787203333*I, 1.26513881334184 - 2.19128470333546*I)] - """ + from numpy import array, vstack + from scipy.spatial import Voronoi discpoints = array([(CC(a).real(), CC(a).imag()) for a in points]) - added_points = 3*abs(discpoints).max() + 1.0 - configuration = vstack([discpoints, array([[added_points, 0], [-added_points, 0], [0, added_points],[0, -added_points]])]) + added_points = 3 * abs(discpoints).max() + 1.0 + configuration = vstack([discpoints, array([[added_points, 0], [-added_points, 0], + [0, added_points], [0, -added_points]])]) V = Voronoi(configuration) res = [] for rv in V.ridge_vertices: @@ -220,29 +228,31 @@ def segments(points): return res def followstrand(f, x0, x1, y0a, prec=53): - """ - Return a piecewise linear aproximation of the homotopy continuation of the root y0a - from x0 to x1 - + r""" + Return a piecewise linear aproximation of the homotopy continuation + of the root ``y0a`` from ``x0`` to ``x1``. + INPUT: - + - ``f`` -- a polynomial in two variables - ``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)$ + - ``y0a`` -- an approximate solution of the polynomial `F(y) = f(x_0, y)` - ``prec`` -- the precission to use - + OUTPUT: - - A list of values (t, ytr, yti) such that: - - ``t`` is a real number between zero and one - - $f(t\cdot x_1+(1-t)\cdot x_0, y_{tr} + I\cdot y_{ti})$ is zero (or a good enough aproximation) - - the piecewise linear path determined by the points has a tubular - neighborhood where the actual homotopy continuation path lies, and - no other root intersects it. - + + A list of values `(t, y_{tr}, y_{ti})` such that: + + - ``t`` is a real number between zero and one + - `f(t \cdot x_1 + (1-t) \cdot x_0, y_{tr} + I \cdot y_{ti})` + is zero (or a good enough aproximation) + - the piecewise linear path determined by the points has a tubular + neighborhood where the actual homotopy continuation path lies, and + no other root intersects it. + EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import followstrand # optional - sirocco sage: R. = QQ[] sage: f = x^2 + y^3 @@ -252,8 +262,6 @@ def followstrand(f, x0, x1, y0a, prec=53): [(0.0, -1.0, 0.0), (0.7500000000000001, -1.015090921153253, -0.24752813818386948), (1.0, -1.026166099551513, -0.32768940253604323)] - - """ CIF = ComplexIntervalField(prec) CC = ComplexField(prec) @@ -271,6 +279,7 @@ def followstrand(f, x0, x1, y0a, prec=53): coefs += list(ci.endpoints()) yr = CC(y0a).real() yi = CC(y0a).imag() + from sage.libs.sirocco import contpath, contpath_mp try: if prec == 53: points = contpath(deg, coefs, yr, yi) @@ -283,21 +292,21 @@ def followstrand(f, x0, x1, y0a, prec=53): @parallel def braid_in_segment(f, x0, x1): """ - Return the braid formed by the y roots of f when x moves from - x0 to x1 - + Return the braid formed by the `y` roots of ``f`` when `x` moves + from ``x0`` to ``x1``. + INPUT: - + - ``f`` -- a polynomial in two variables - ``x0`` -- a complex number - ``x1`` -- a complex number - - - OUTPUT: - - A braid - + + OUTPUT: + + A braid. + EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco sage: R. = QQ[] sage: f = x^2 + y^3 @@ -305,7 +314,6 @@ def braid_in_segment(f, x0, x1): sage: x1 = CC(1, 0.5) sage: braid_in_segment(f, x0, x1) # optional - sirocco s1 - """ CC = ComplexField(64) (x, y) = f.variables() @@ -316,7 +324,7 @@ def braid_in_segment(f, x0, x1): y0s = F0.roots(multiplicities=False) strands = [followstrand(f, x0, x1, CC(a)) for a in y0s] complexstrands = [[(a[0], CC(a[1], a[2])) for a in b] for b in strands] - centralbraid = braid_from_piecewise(complexstrands) + centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] y0aps = [c[0][1] for c in complexstrands] used = [] @@ -341,35 +349,38 @@ def braid_in_segment(f, x0, x1): used.append(y1) finalstrands.append([(0, y1ap), (1, CC(y1))]) finallbraid = braid_from_piecewise(finalstrands) - return initialbraid*centralbraid*finallbraid + return initialbraid * centralbraid * finallbraid + +def fundamental_group(f, simplified=True, projective=False): + r""" + Return a presentation of the fundamental group of the complement of + the algebraic set defined by the polynomial ``f``. + + INPUT: + + - ``f`` -- a polynomial in two variables, with coefficients in either + the rationals or a number field with a fixed embedding in `\QQbar` + + - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the + presentation will be simplified (see below) + + - ``projective`` -- boolean (default: ``False``); if set to ``True``, + the fundamental group of the complement of the projective completion + of the curve will be computed, otherwise, the fundamental group of + the complement in the affine plane will be computed + + If ``simplified`` is ``False``, the returned presentation has as + many generators as degree of the polynomial times the points in the + base used to create the segments that surround the discriminant. In + this case, the generators are granted to be meridians of the curve. + + OUTPUT: + + A presentation of the fundamental group of the complement of the + curve defined by ``f``. -def fundamental_group(f, simplified=True, projective = False): - """ - Return a presentation of the fundamental group of the complement of the algebraic - set defined by the polynomial f. - - INPUT: - - - ``f`` -- a polynomial in two variables, with coefficients in either the rationals - or a number field with a fixed embedding in QQbar. - - - ``simplified`` -- boolean (default: True). If set to True the presentation will be - simplified. Otherwise, the returned presentation has as many generators - as degree of the polynomial times the points in the base used to create - the segments that surround the discriminant. In this case, the generators - are granted to be meridians of the curve. - - -- ``projective`` -- boolean (default: False). If set to True, the fundamental group of - the complement of the projective completion of the curve will be computed. - Otherwise, the fundamental group of the complement in the affine plane will - be computed. - - OUTPUT: - - A presentation of the fundamental group of the complement of the curve defined by ``f``. - EXAMPLES:: - + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R. = QQ[] sage: f = x^2 + y^3 @@ -378,31 +389,28 @@ def fundamental_group(f, simplified=True, projective = False): sage: fundamental_group(f, simplified=False) # optional - sirocco Finitely presented group < ... > - :: - + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R. = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > - - It is also possible to have coefficients in a number field with a fixed embedding in QQbar:: - + It is also possible to have coefficients in a number field with a + fixed embedding in `\QQbar`:: + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: zeta = QQbar['x']('x^2+x+1').roots(multiplicities = False)[0] + sage: zeta = QQbar['x']('x^2+x+1').roots(multiplicities=False)[0] sage: zeta -0.50000000000000000? - 0.866025403784439?*I - sage: F = NumberField(zeta.minpoly(), 'zeta', embedding = zeta) + sage: F = NumberField(zeta.minpoly(), 'zeta', embedding=zeta) sage: F.inject_variables() Defining zeta - sage: R. = F[] + sage: R. = F[] sage: f = y^3 + x^3 +zeta *x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > - - """ (x, y) = f.variables() F = f.base_ring() @@ -420,7 +428,6 @@ def fundamental_group(f, simplified=True, projective = False): if projective: rels.append(prod(F.gen(i) for i in range(d))) braidscomputed = braid_in_segment([(g, seg[0], seg[1]) for seg in segs]) - #braidscomputed = [(((g, seg[0], seg[1]), ), braid_in_segment(g,seg[0], seg[1])) for seg in segs] for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) b = braidcomputed[1] @@ -432,8 +439,9 @@ def fundamental_group(f, simplified=True, projective = False): w1 = F([sign(a)*d*i + a for a in el1.Tietze()]) w2 = F([d*j + el2]) rels.append(w1/w2) - G = F/rels + G = F / rels if simplified: return G.simplified() else: return G + diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 2bb2abd113c..068cf7575f8 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -116,8 +116,7 @@ # the License, or (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, division, absolute_import import math @@ -129,6 +128,7 @@ import sage.rings.all as rings from sage.rings.real_mpfr import is_RealField from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ from sage.groups.all import AbelianGroup import sage.groups.generic as generic from sage.libs.pari import pari @@ -3001,7 +3001,7 @@ def non_archimedean_local_height(self, v=None, prec=None, if is_minimal: E = self.curve() P = self - offset = 0 + offset = ZZ.zero() else: E = self.curve().local_minimal_model(v) P = self.curve().isomorphism_to(E)(self) @@ -3029,14 +3029,14 @@ def non_archimedean_local_height(self, v=None, prec=None, r = -C/4 r -= offset/6 if not r: - return rings.QQ(0) + return rings.QQ.zero() else: if E.base_ring() is rings.QQ: Nv = Integer(v) else: Nv = v.norm() if not weighted: - r /= v.ramification_index() * v.residue_class_degree() + r = r / (v.ramification_index() * v.residue_class_degree()) return r * log(Nv) nonarchimedian_local_height = deprecated_function_alias(13951, non_archimedean_local_height) diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py index 625a6b14702..36dd613dd7d 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -5,7 +5,7 @@ This file contains the code to compute for which primes the Galois representation attached to an elliptic curve (over an arbitrary number field) is surjective. The functions in this file are called by -the `is_surjective` and `non_surjective` methods of an elliptic curve +the ``is_surjective`` and ``non_surjective`` methods of an elliptic curve over a number field. EXAMPLES:: @@ -28,6 +28,7 @@ - Eric Larson (2012-05-28): initial version. - Eric Larson (2014-08-13): added isogeny_bound function. +- John Cremona (2016, 2017): various efficiency improvements to _semistable_reducible_primes REFERENCES: @@ -35,7 +36,7 @@ courbes elliptiques. Inventiones mathematicae, 1972. .. [Sutherland12] Sutherland. A local-global principle for rational - isogenies of prime degree. Journal de Theorie des Nombres de Bordeaux, + isogenies of prime degree. Journal de Théorie des Nombres de Bordeaux, 2012. """ @@ -53,13 +54,12 @@ from sage.structure.sage_object import SageObject from sage.rings.number_field.number_field import NumberField, QuadraticField from sage.schemes.elliptic_curves.cm import cm_j_invariants -from sage.rings.rational_field import QQ from sage.modules.free_module import VectorSpace from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.integer import Integer from sage.misc.functional import cyclotomic_polynomial -from sage.arith.all import legendre_symbol +from sage.arith.all import legendre_symbol, primes from sage.sets.set import Set +from sage.rings.all import PolynomialRing, Integer, ZZ, QQ, Infinity class GaloisRepresentation(SageObject): r""" @@ -70,7 +70,7 @@ class GaloisRepresentation(SageObject): and a rational prime number `p`, the `p^n`-torsion `E[p^n]` points of `E` is a representation of the absolute Galois group `G_K` of `K`. As `n` varies - we obtain the Tate module `T_p E` which is a + we obtain the Tate module `T_p E` which is a representation of `G_K` on a free `\ZZ_p`-module of rank `2`. As `p` varies the representations are compatible. @@ -169,12 +169,12 @@ def non_surjective(self, A=100): INPUT: - * ``A`` - int (a bound on the number of traces of Frobenius to use - while trying to prove surjectivity). + - ``A`` -- int (a bound on the number of traces of Frobenius to use + while trying to prove surjectivity). OUTPUT: - - ``list`` - A list of primes where mod-`p` representation is + - ``list`` -- A list of primes where mod-`p` representation is very likely not surjective. At any prime not in this list, the representation is definitely surjective. If `E` has CM, the list [0] is returned. @@ -271,7 +271,7 @@ def is_surjective(self, p, A=100): def isogeny_bound(self, A=100): r""" - Returns a list of primes `p` including all primes for which + Return a list of primes `p` including all primes for which the image of the mod-`p` representation is contained in a Borel. @@ -282,9 +282,9 @@ def isogeny_bound(self, A=100): INPUT: - - ``A`` - int (a bound on the number of traces of Frobenius to - use while trying to prove the mod-`p` - representation is not contained in a Borel). + - ``A`` -- int (a bound on the number of traces of Frobenius to + use while trying to prove the mod-`p` + representation is not contained in a Borel). OUTPUT: @@ -361,7 +361,7 @@ def isogeny_bound(self, A=100): def reducible_primes(self): r""" - Returns a list of primes `p` for which the mod-`p` + Return a list of primes `p` for which the mod-`p` representation is reducible, or [0] for CM curves. OUTPUT: @@ -413,7 +413,7 @@ def reducible_primes(self): def _non_surjective(E, patience=100): r""" - Returns a list of primes `p` including all primes for which the mod-`p` + Return a list of primes `p` including all primes for which the mod-`p` representation might not be surjective. INPUT: @@ -491,17 +491,19 @@ def _maybe_borels(E, L, patience=100): INPUT: - - ``E`` - EllipticCurve - over a number field. + - ``E`` -- EllipticCurve - over a number field. - - ``L`` - list - a list of prime numbers. + - ``L`` -- list - a list of prime numbers. - - ``patience`` - int (a positive integer bounding the number of - traces of Frobenius to use while trying to - prove irreducibility). + - ``patience`` -- int (a positive integer bounding the number of + traces of Frobenius to use while trying to + prove irreducibility). + + OUTPUT: - OUTPUT: list - The list of all primes `\ell` in L for which the - mod `\ell` image might be contained in a Borel - subgroup of `GL_2(\mathbf{F}_{\ell})`. + - list -- The list of all primes `\ell` in L for which the + mod `\ell` image might be contained in a Borel + subgroup of `GL_2(\mathbf{F}_{\ell})`. EXAMPLES:: @@ -585,10 +587,12 @@ def _exceptionals(E, L, patience=1000): - ``L`` - list - a list of prime numbers. - ``patience`` - int (a bound on the number of traces of Frobenius to - use while trying to prove surjectivity). + use while trying to prove surjectivity). - OUTPUT: list - The list of all primes l in L for which the mod l image - might fail to be surjective. + OUTPUT: + + - list -- The list of all primes l in L for which the mod l image + might fail to be surjective. EXAMPLES:: @@ -751,28 +755,6 @@ def _over_numberfield(E): return E -def _tr12(tr, det): - r"""Compute `X^{12} + Y^{12}` given `X + Y` and `X * Y`. - - INPUT: - - - ``tr`` - The value of `X + Y`. - - - ``det`` - The value of `X * Y`. - - OUTPUT: The value of `X^{12} + Y^{12}`. - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.gal_reps_number_field import * - sage: X, Y = QQ['X, Y'].gens() - sage: sage.schemes.elliptic_curves.gal_reps_number_field._tr12(X + Y, X * Y) - X^12 + Y^12 - """ - - det3 = det**3 - return ((tr * (tr**2 - 3 * det))**2 - 2 * det3)**2 - 2 * det3**2 - def deg_one_primes_iter(K, principal_only=False): r""" Return an iterator over degree 1 primes of ``K``. @@ -811,14 +793,18 @@ def deg_one_primes_iter(K, principal_only=False): # imaginary quadratic fields have no principal primes of norm < disc / 4 start = K.discriminant().abs() // 4 if principal_only and K.signature() == (0,1) else 2 + K_is_Q = (K==QQ) from sage.arith.misc import primes from sage.rings.infinity import infinity for p in primes(start=start, stop=infinity): - for P in K.primes_above(p, degree=1): - if not principal_only or P.is_principal(): - yield P + if K_is_Q: + yield ZZ.ideal(p) + else: + for P in K.primes_above(p, degree=1): + if not principal_only or P.is_principal(): + yield P -def _semistable_reducible_primes(E): +def _semistable_reducible_primes(E, verbose=False): r"""Find a list containing all semistable primes l unramified in K/QQ for which the Galois image for E could be reducible. @@ -838,10 +824,19 @@ def _semistable_reducible_primes(E): sage: E = EllipticCurve([0, -1, 1, -10, -20]) # X_0(11) sage: 5 in sage.schemes.elliptic_curves.gal_reps_number_field._semistable_reducible_primes(E) True - """ - E = _over_numberfield(E) + This example, over a quintic field with Galois group `S_5`, took a + very long time before :trac:`22343`:: + + sage: K. = NumberField(x^5 - 6*x^3 + 8*x - 1) + sage: E = EllipticCurve(K, [a^3 - 2*a, a^4 - 2*a^3 - 4*a^2 + 6*a + 1, a + 1, -a^3 + a + 1, -a]) + sage: from sage.schemes.elliptic_curves.gal_reps_number_field import _semistable_reducible_primes + sage: _semistable_reducible_primes(E) + [2, 5, 53, 1117] + """ + if verbose: print("In _semistable_reducible_primes with E={}".format(E.ainvs())) K = E.base_field() + d = K.degree() deg_one_primes = deg_one_primes_iter(K, principal_only=True) @@ -849,132 +844,123 @@ def _semistable_reducible_primes(E): # We find two primes (of distinct residue characteristics) which are # of degree 1, unramified in K/Q, and at which E has good reduction. - # Both of these primes will give us a nontrivial divisibility constraint + # Each of these primes will give us a nontrivial divisibility constraint # on the exceptional primes l. For both of these primes P, we precompute - # a generator and the trace of Frob_P^12. + # a generator and the characteristic polynomial of Frob_P^12. precomp = [] - last_char = 0 # The residue characteristic of the most recent prime. + last_p = 0 # The residue characteristic of the most recent prime. while len(precomp) < 2: P = next(deg_one_primes) - - # the iterator tests this already - # if not P.is_principal(): - # continue - - det = P.norm() - if det == last_char: - continue - - if P.ramification_index() != 1: + p = P.norm() + if p != last_p and (d==1 or P.ramification_index() == 1) and E.has_good_reduction(P): + precomp.append(P) + last_p = p + + Px, Py = precomp + x, y = [P.gens_reduced()[0] for P in precomp] + EmodPx = E.reduction(Px) if d>1 else E.reduction(x) + EmodPy = E.reduction(Py) if d>1 else E.reduction(y) + fxpol = EmodPx.frobenius_polynomial() + fypol = EmodPy.frobenius_polynomial() + fx12pol = fxpol.adams_operator(12) # roots are 12th powers of those of fxpol + fy12pol = fypol.adams_operator(12) + px = x.norm() if d>1 else x + py = y.norm() if d>1 else x + Zx = fxpol.parent() + xpol = x.charpoly() if d>1 else Zx([-x,1]) + ypol = y.charpoly() if d>1 else Zx([-y,1]) + + if verbose: print("Finished precomp, x={} (p={}), y={} (p={})".format(x,px,y,py)) + + for w in range(1+d/2): + if verbose: print("w = {}".format(w)) + gx = xpol.symmetric_power(w).adams_operator(12).resultant(fx12pol) + gy = ypol.symmetric_power(w).adams_operator(12).resultant(fy12pol) + if verbose: print("computed gx and gy") + + gxn = Integer(gx.absolute_norm()) if d>1 else gx + gyn = Integer(gy.absolute_norm()) if d>1 else gy + gxyn = gxn.gcd(gyn) + if gxyn: + xprimes = gxyn.prime_factors() + if verbose: print("adding prime factors {} of {} to {}".format(xprimes, gxyn, sorted(bad_primes))) + bad_primes.update(xprimes) + if verbose: print("...done, bad_primes now {}".format(sorted(bad_primes))) continue + else: + if verbose: print("gx and gy both 0!") - if E.has_bad_reduction(P): - continue - - tr = E.reduction(P).trace_of_frobenius() - x = P.gens_reduced()[0] - - precomp.append((x, _tr12(tr, det))) - last_char = det - - x, tx = precomp[0] - y, ty = precomp[1] - - Kgal = K.galois_closure('b') - maps = K.embeddings(Kgal) - - for i in range(2 ** (K.degree() - 1)): - ## We iterate through all possible characters. ## - - # Here, if i = i_{l-1} i_{l-2} cdots i_1 i_0 in binary, then i - # corresponds to the character prod sigma_j^{i_j}. - - phi1x = 1 - phi2x = 1 - phi1y = 1 - phi2y = 1 - - # We compute the two algebraic characters at x and y: - for j in range(K.degree()): - if i % 2 == 1: - phi1x *= maps[j](x) - phi1y *= maps[j](y) - else: - phi2x *= maps[j](x) - phi2y *= maps[j](y) - i = int(i/2) - - # Any prime with reducible image must divide both of: - gx = phi1x**12 + phi2x**12 - tx - gy = phi1y**12 + phi2y**12 - ty - - if (gx != 0) or (gy != 0): - for prime in Integer(Kgal.ideal([gx, gy]).norm()).prime_factors(): - bad_primes.add(prime) - - continue ## It is possible that our curve has CM. ## # Our character must be of the form Nm^K_F for an imaginary # quadratic subfield F of K (which is the CM field if E has CM). - # We compute F: - a = (Integer(phi1x + phi2x)**2 - 4 * x.norm()).squarefree_part() + # Note that this can only happen when d is even, w=d/2, and K + # contains (or the Galois closure of K contains?) the + # imaginary quadratic field F = Q(sqrt(a)) which is the + # splitting field of both fx12pol and fy12pol. We compute a + # and relativise K over F: - # See #19229: the name given here, which is not used, should - # not be the name of the generator of the base field. - F = QuadraticField(a, 'gal_rep_nf_sqrt_a') + a = fx12pol.discriminant().squarefree_part() - # Next, we turn K into relative number field over F. + # Construct a field isomorphic to K but a relative extension over QQ(sqrt(a)). - K = K.relativize(F.embeddings(K)[0], K.variable_name()+'0') - E = E.change_ring(K.structure()[1]) + # See #19229: the names given here, which are not used, should + # not be the name of the generator of the base field. - ## We try to find a nontrivial divisibility condition. ## + rootsa = K(a).sqrt(all=True) # otherwise if a is not a square the + # returned result is in the symbolic ring! + try: + roota = rootsa[0] + except IndexError: + raise RuntimeError("error in _semistable_reducible_primes: K={} does not contain sqrt({})".format(K,a)) + K_rel = K.relativize(roota, ['name1','name2']) + iso = K_rel.structure()[1] # an isomorphism from K to K_rel + E_rel = E.change_ring(iso) # same as E but over K_rel + ## We try again to find a nontrivial divisibility condition. ## + + div = 0 patience = 5 * K.absolute_degree() # Number of Frobenius elements to check before suspecting that E # has CM and computing the set of CM j-invariants of K to check. # TODO: Is this the best value for this parameter? - while True: - P = next(deg_one_primes) - - # the iterator tests this already - # if not P.is_principal(): - # continue - - try: - tr = E.change_ring(P.residue_field()).trace_of_frobenius() - except ArithmeticError: # Bad reduction at P. - continue - - x = P.gens_reduced()[0].norm(F) - div = (x**12).trace() - _tr12(tr, x.norm()) - - patience -= 1 - - if div != 0: - # We found our divisibility constraint. - - for prime in Integer(div).prime_factors(): - bad_primes.add(prime) - - # Turn K back into an absolute number field. + while div==0 and patience>0: + P = next(deg_one_primes) # a prime of K not K_rel + while E.has_bad_reduction(P): + P = next(deg_one_primes) + + if verbose: print("trying P = {}...".format(P)) + EmodP = E.reduction(P) + fpol = EmodP.frobenius_polynomial() + if verbose: print("...good reduction, frobenius poly = {}".format(fpol)) + x = iso(P.gens_reduced()[0]).relative_norm() + xpol = x.charpoly().adams_operator(12) + div2 = Integer(xpol.resultant(fpol.adams_operator(12)) // x.norm()**12) + if div2: + div = div2.isqrt() + assert div2==div**2 + if verbose: print("...div = {}".format(div)) + else: + if verbose: print("...div = 0, continuing") + patience -= 1 - E = E.change_ring(K.structure()[0]) - K = K.structure()[0].codomain() + if patience == 0: + # We suspect that E has CM, so we check: + if E.has_cm(): + raise ValueError("In _semistable_reducible_primes, the curve E should not have CM.") - break + assert div != 0 + # We found our divisibility constraint. - if patience == 0: - # We suspect that E has CM, so we check: - f = K.structure()[0] - if f(E.j_invariant()) in cm_j_invariants(f.codomain()): - raise ValueError("The curve E should not have CM.") + xprimes = div.prime_factors() + if verbose: print("...adding prime factors {} of {} to {}...".format(xprimes,div, sorted(bad_primes))) + bad_primes.update(xprimes) + if verbose: print("...done, bad_primes now {}".format(sorted(bad_primes))) L = sorted(bad_primes) return L @@ -1128,4 +1114,3 @@ def _possible_normalizers(E, SA): bad_primes = sorted(bad_primes) return bad_primes - diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 721dda184df..19ea18fe000 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -3171,7 +3171,7 @@ def numerical_approx(self, prec=53, algorithm=None): 8.4...e-31 + 6.0...e-31*I sage: E = EllipticCurve('37a'); P = E.heegner_point(-40); P Heegner point of discriminant -40 on elliptic curve of conductor 37 - sage: P.numerical_approx() # abs tol 1e-15 + sage: P.numerical_approx() # abs tol 1e-14 (-3.15940603400359e-16 + 1.41421356237309*I : 1.00000000000000 - 1.41421356237309*I : 1.00000000000000) A rank 2 curve, where all Heegner points of conductor 1 are 0:: @@ -6250,7 +6250,7 @@ def kolyvagin_point(self, D, c=ZZ(1), check=True): sage: E = EllipticCurve('37a1') sage: P = E.kolyvagin_point(-67); P Kolyvagin point of discriminant -67 on elliptic curve of conductor 37 - sage: P.numerical_approx() + sage: P.numerical_approx() # abs tol 1e-14 (6.00000000000000 : -15.0000000000000 : 1.00000000000000) sage: P.index() 6 diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index d214acc48c8..2b78e621507 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -25,7 +25,6 @@ - Ben Hutz (2015-11): iteration of subschemes - """ #***************************************************************************** @@ -1278,6 +1277,107 @@ def degree(self): """ return(self._polys[0].degree()) + def degree_sequence(self, iterates=2): + r""" + Return sequence of degrees of normalized iterates starting with + the degree of this map. + + INPUT: ``iterates`` -- positive integer (optional - default: 2) + + OUTPUT: list of integers + + EXAMPLES:: + + sage: P2. = ProjectiveSpace(QQ, 2) + sage: H = End(P2) + sage: f = H([Z^2, X*Y, Y^2]) + sage: f.degree_sequence(15) + [2, 3, 5, 8, 11, 17, 24, 31, 45, 56, 68, 91, 93, 184, 275] + + :: + + sage: F. = PolynomialRing(QQ) + sage: P2. = ProjectiveSpace(F, 2) + sage: H = End(P2) + sage: f = H([Y*Z, X*Y, Y^2 + t*X*Z]) + sage: f.degree_sequence(5) + [2, 3, 5, 8, 13] + + :: + + sage: P2. = ProjectiveSpace(QQ, 2) + sage: H = End(P2) + sage: f = H([X^2, Y^2, Z^2]) + sage: f.degree_sequence(10) + [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] + + :: + + sage: P2. = ProjectiveSpace(ZZ, 2) + sage: H = End(P2) + sage: f = H([X*Y, Y*Z+Z^2, Z^2]) + sage: f.degree_sequence(10) + [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + """ + if int(iterates) < 1: + raise TypeError("number of iterates must be a positive integer") + if not self.is_endomorphism(): + raise TypeError("map is not an endomorphism") + + if self.is_morphism(): + d = self.degree() + D = [d**t for t in range(1, iterates+1)] + else: + F = self + F.normalize_coordinates() + D = [F.degree()] + for n in range(2, iterates+1): + F = F*self + F.normalize_coordinates() + D.append(F.degree()) + return D + + def dynamical_degree(self, N=3, prec=53): + r""" + Return an approximation to the dynamical degree of this map. The + dynamical degree is defined as $\lim_{n \to \infty} \sqrt[n]{\deg(f^n)}$. + + INPUT: + + - ``N`` -- iterate to use for approximation (optional - default: 3) + + - ``prec`` -- real precision to use when computing root (optional - default: 53) + + OUTPUT: real number + + EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: H = End(P) + sage: f = H([x^2 + (x*y), y^2]) + sage: f.dynamical_degree() + 2.00000000000000 + + :: + + sage: P2. = ProjectiveSpace(ZZ, 2) + sage: H = End(P2) + sage: f = H([X*Y, Y*Z+Z^2, Z^2]) + sage: f.dynamical_degree(N=5, prec=100) + 1.4309690811052555010452244131 + """ + if int(N) < 1: + raise TypeError("number of iterates must be a positive integer") + if not self.is_endomorphism(): + raise TypeError("map is not an endomorphism") + + R = RealField(prec=prec) + if self.is_morphism(): + return R(self.degree()) + else: + D = self.nth_iterate_map(N, normalize=True).degree() + return R(D).nth_root(N) + def dehomogenize(self, n): r""" Returns the standard dehomogenization at the ``n[0]`` coordinate for the domain @@ -3204,7 +3304,7 @@ def critical_height(self, **kwds): ch += P.canonical_height(F, **kwds) return(ch) - def periodic_points(self, n, minimal=True, R=None, algorithm='variety'): + def periodic_points(self, n, minimal=True, R=None, algorithm='variety', return_scheme=False): r""" Computes the periodic points of period ``n`` of this map defined over the ring ``R`` or the base ring of the map. @@ -3218,7 +3318,9 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety'): but is slow as the defining equations of the variety get more complicated. - The map must be a projective morphism. + For rational map, where there are potentially infinitely many peiodic points + of a given period, you must use the ``return_scheme`` option. Note + that this scheme will include the indeterminacy locus. INPUT: @@ -3233,9 +3335,12 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety'): on the appropriate variety or ``cyclegraph`` to find the cycles from the cycle graph. Default: ``variety``. + - ``return_scheme`` - return a subscheme of the ambient space which defines the + ``n`` th periodic points. + OUTPUT: - - a list of periodic points of this map. + - a list of periodic points of this map or the subscheme defining the periodic points EXAMPLES:: @@ -3333,9 +3438,46 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety'): sage: H = End(P) sage: f = H([3*x^2+5*y^2,y^2]) sage: f.periodic_points(2, R=GF(3), minimal=False) + [(2 : 1)] + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: H = End(P) + sage: f = H([x^2, x*y, z^2]) + sage: f.periodic_points(1) + Traceback (most recent call last): + ... + TypeError: use return_scheme=True + + :: + + sage: R. = QQ[] + sage: K. = NumberField(x^2 - x + 3) + sage: P. = ProjectiveSpace(K,2) + sage: X = P.subscheme(2*x-y) + sage: H = End(X) + sage: f = H([x^2-y^2, 2*(x^2-y^2), y^2-z^2]) + sage: f.periodic_points(2) + [(-1/5*u - 1/5 : -2/5*u - 2/5 : 1), (1/5*u - 2/5 : 2/5*u - 4/5 : 1)] + + :: + + sage: P. = ProjectiveSpace(QQ,2) + sage: H = End(P) + sage: f = H([x^2-y^2, x^2-z^2, y^2-z^2]) + sage: f.periodic_points(1) + [(-1 : 0 : 1)] + sage: f.periodic_points(1, return_scheme=True) + Closed subscheme of Projective Space of dimension 2 over Rational Field + defined by: + -x^3 + x^2*y - y^3 + x*z^2, + -x*y^2 + x^2*z - y^2*z + x*z^2, + -y^3 + x^2*z + y*z^2 - z^3 + sage: f.periodic_points(2, minimal=True, return_scheme=True) Traceback (most recent call last): ... - NotImplementedError: must be a projective morphism + NotImplementedError: return_subscheme only implemented for minimal=False """ if n <= 0: raise ValueError("a positive integer period must be specified") @@ -3346,54 +3488,66 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety'): R = self.base_ring() else: f = self.change_ring(R) - if not f.is_morphism(): - # if not, the variety is not dimension 0 and - # can cannot construct the cyclegraph due to - #indeterminacies - raise NotImplementedError("must be a projective morphism") - PS = f.codomain() - if algorithm == 'variety': - if R in NumberFields() or R is QQbar or R in FiniteFields(): - N = PS.dimension_relative() + 1 - R = PS.coordinate_ring() - F = f.nth_iterate_map(n) - L = [F[i]*R.gen(j) - F[j]*R.gen(i) for i in range(0,N) for j in range(i+1, N)] - X = PS.subscheme(L) - points = [PS(Q) for Q in X.rational_points()] - - if not minimal: - return points + CR = f.coordinate_ring() + dom = f.domain() + PS = f.codomain().ambient_space() + N = PS.dimension_relative() + 1 + if algorithm == 'cyclegraph': + if R in FiniteFields(): + g = f.cyclegraph() + points = [] + for cycle in g.all_simple_cycles(): + m = len(cycle)-1 + if minimal: + if m == n: + points = points + cycle[:-1] + else: + if n % m == 0: + points = points + cycle[:-1] + return(points) else: - #we want only the points with minimal period n - #so we go through the list and remove any that - #have smaller period by checking the iterates - rem_indices = [] - for i in range(len(points)-1,-1,-1): - # iterate points to check if minimal - P = points[i] - for j in range(1,n): - P = f(P) - if P == points[i]: - points.pop(i) - break - return points - else: - raise NotImplementedError("ring must a number field or finite field") - elif algorithm == 'cyclegraph': - if R in FiniteFields(): - g = f.cyclegraph() - points = [] - for cycle in g.all_simple_cycles(): - m = len(cycle)-1 - if minimal: - if m == n: - points = points + cycle[:-1] + raise TypeError("ring must be finite to generate cyclegraph") + elif algorithm == 'variety': + F = f.nth_iterate_map(n) + L = [F[i]*CR.gen(j) - F[j]*CR.gen(i) for i in range(0,N) for j in range(i+1, N)] + L = [t for t in L if t != 0] + X = PS.subscheme(L + list(dom.defining_polynomials())) + if return_scheme: #this includes the indeterminancy locus points! + if minimal and n != 1: + raise NotImplementedError("return_subscheme only implemented for minimal=False") + return X + if X.dimension() == 0: + if R in NumberFields() or R is QQbar or R in FiniteFields(): + Z = f.indeterminacy_locus() + points = [dom(Q) for Q in X.rational_points()] + good_points = [] + for Q in points: + try: + Z(list(Q)) + except TypeError: + good_points.append(Q) + points = good_points + + if not minimal: + return points else: - if n % m == 0: - points = points + cycle[:-1] - return(points) - else: - raise TypeError("ring must be finite to generate cyclegraph") + #we want only the points with minimal period n + #so we go through the list and remove any that + #have smaller period by checking the iterates + rem_indices = [] + for i in range(len(points)-1,-1,-1): + # iterate points to check if minimal + P = points[i] + for j in range(1,n): + P = f(P) + if P == points[i]: + points.pop(i) + break + return points + else: + raise NotImplementedError("ring must a number field or finite field") + else: #a higher dimensional scheme + raise TypeError("use return_scheme=True") else: raise ValueError("algorithm must be either 'variety' or 'cyclegraph'") @@ -5452,9 +5606,9 @@ def indeterminacy_locus(self): sage: f.indeterminacy_locus() Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: - x, - y, - z + x^2, + y^2, + z^2 :: @@ -5481,16 +5635,25 @@ def indeterminacy_locus(self): x^3, x*y^2, x*z^2 + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: X = P.subscheme(x-y) + sage: H = End(X) + sage: f = H([x^2-4*y^2, y^2-z^2, 4*z^2-x^2]) + sage: Z = f.indeterminacy_locus(); Z + Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: + x - y, + x^2 - 4*y^2, + y^2 - z^2, + -x^2 + 4*z^2 + sage: Z.dimension() + -1 """ - from sage.schemes.projective.projective_space import is_ProjectiveSpace dom = self.domain() - if not is_ProjectiveSpace(dom): - raise NotImplementedError("not implemented for subschemes") - defPolys = self.defining_polynomials() - locus = dom.subscheme(defPolys) - if locus.dimension() < 0: - locus = dom.subscheme(dom.gens()) - return locus + AS = dom.ambient_space() + return AS.subscheme(list(dom.defining_polynomials()) + list(self.defining_polynomials())) def indeterminacy_points(self, F=None): r""" diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index 826a639718a..595068ca749 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -5,6 +5,10 @@ Disjoint-set data structure The main entry point is :func:`DisjointSet` which chooses the appropriate type to return. For more on the data structure, see :func:`DisjointSet`. +This module defines a class for mutable partitioning of a set, which +can not be used as a key of a dictionary, vertex of a graph etc. For +immutable partitioning see :class:`SetPartition`. + AUTHORS: - Sébastien Labbé (2008) - Initial version. diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index c61f7c57d99..3881b634288 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -497,3 +497,93 @@ def _latex_generator(self, m): return left + s + right return "%s_{%s}" % (prefix, s) +def standardize_names_index_set(names=None, index_set=None, ngens=None): + """ + Standardize the ``names`` and ``index_set`` for a Lie algebra. + + OUTPUT: + + A pair ``(names_std, index_set_std)``, where ``names_std`` is either + ``None`` or a tuple of strings, and where ``index_set_std`` is a finite + enumerated set. + The purpose of ``index_set_std`` is to index the generators of some object + (e.g., the basis of a module); the strings in ``names_std``, when they + exist, are used for printing these indices. The ``ngens`` + + If ``names`` contains exactly one name ``X`` and ``ngens`` is greater than + 1, then ``names_std`` are ``Xi`` for ``i`` in ``range(ngens)``. + + TESTS:: + + sage: from sage.structure.indexed_generators import standardize_names_index_set + sage: standardize_names_index_set('x,y') + (('x', 'y'), {'x', 'y'}) + sage: standardize_names_index_set(['x','y']) + (('x', 'y'), {'x', 'y'}) + sage: standardize_names_index_set(['x','y'], ['a','b']) + (('x', 'y'), {'a', 'b'}) + sage: standardize_names_index_set('x,y', ngens=2) + (('x', 'y'), {'x', 'y'}) + sage: standardize_names_index_set(index_set=['a','b'], ngens=2) + (None, {'a', 'b'}) + sage: standardize_names_index_set('x', ngens=3) + (('x0', 'x1', 'x2'), {'x0', 'x1', 'x2'}) + + sage: standardize_names_index_set() + Traceback (most recent call last): + ... + ValueError: the index_set, names, or number of generators must be specified + sage: standardize_names_index_set(['x'], ['a', 'b']) + Traceback (most recent call last): + ... + ValueError: the number of names must equal the size of the indexing set + sage: standardize_names_index_set('x,y', ['a']) + Traceback (most recent call last): + ... + ValueError: the number of names must equal the size of the indexing set + sage: standardize_names_index_set('x,y,z', ngens=2) + Traceback (most recent call last): + ... + ValueError: the number of names must equal the number of generators + sage: standardize_names_index_set(index_set=['a'], ngens=2) + Traceback (most recent call last): + ... + ValueError: the size of the indexing set must equal the number of generators + """ + if isinstance(names, str): + names = tuple(names.split(',')) + elif names is not None: + names = tuple(names) + + if names is not None and len(names) == 1 and ngens > 1: + letter = names[0] + names = tuple([letter + str(i) for i in range(ngens)]) + + if index_set is None: + if names is None: + # If neither is specified, we make range(ngens) the index set + if ngens is None: + raise ValueError("the index_set, names, or number of" + " generators must be specified") + index_set = tuple(range(ngens)) + else: + # If only the names are specified, then we make the indexing set + # be the names + index_set = tuple(names) + + if isinstance(index_set, (tuple, list)): + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + index_set = FiniteEnumeratedSet(index_set) + + if names is not None: + if len(names) != index_set.cardinality(): + raise ValueError("the number of names must equal" + " the size of the indexing set") + if ngens is not None and len(names) != ngens: + raise ValueError("the number of names must equal the number of generators") + elif ngens is not None and index_set.cardinality() != ngens: + raise ValueError("the size of the indexing set must equal" + " the number of generators") + + return (names, index_set) + diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 4b664926c8c..bc43e06e4e5 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -357,19 +357,6 @@ def domain(self): """ return self._domain - def __lt__(self, other): - """ - Perform float comparison with constant. - - EXAMPLES:: - - sage: cmp(pi, 0) - 1 - sage: cmp(pi, SR(0)) - 1 - """ - return self.__float__() < other - def expression(self): """ Returns an expression for this constant. diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index c1a1f3d838c..01319b5e687 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3532,7 +3532,7 @@ cdef class Expression(CommutativeRingElement): -1 sage: cmp(log(8), 3*log(2)) 0 - sage: RLF(1) < RLF(sqrt(2)) + sage: bool(RLF(1) < RLF(sqrt(2))) True sage: RealSet((0, pi),[pi, pi],(pi,4)) (0, 4) @@ -3540,6 +3540,17 @@ cdef class Expression(CommutativeRingElement): [0, 4) sage: RealSet((0, pi),[0, 3.5],(pi,4)) [0, 4) + + More sanity tests:: + + sage: bool(pi < pi) + False + sage: bool(e < e) + False + sage: bool(sqrt(2) < sqrt(2)) + False + sage: bool(pi < SR.zero()) + False """ return mixed_order(left, right) diff --git a/src/sage/tests/benchmark.py b/src/sage/tests/benchmark.py index 47ff60ed119..62cd3a642d2 100644 --- a/src/sage/tests/benchmark.py +++ b/src/sage/tests/benchmark.py @@ -16,6 +16,7 @@ """ from __future__ import print_function +from six.moves import range from sage.all import * # QQ, alarm, ModularSymbols, gp, pari, cputime, EllipticCurve import sage.libs.linbox.linbox as linbox @@ -1024,7 +1025,7 @@ def sage(self): """ R = self.__R n = self.__n - f = MatrixSpace(R,n)(range(n*n)) + f = MatrixSpace(R,n)(list(range(n*n))) t = cputime() g = f**2 return cputime(t) @@ -1076,7 +1077,7 @@ def gap(self): """ n = self.__n - m = gap(str([range(n*k,n*(k+1)) for k in range(n)])) + m = gap(str([list(range(n*k,n*(k+1))) for k in range(n)])) t = walltime() j = m*m return False, walltime(t) @@ -1292,7 +1293,7 @@ def sage(self): """ R = self.__R n = self.__n - f = MatrixSpace(R,n,2*n)(range(n*(2*n))) + f = MatrixSpace(R,n,2*n)(list(range(n*(2*n)))) t = cputime() g = f.kernel() return cputime(t) diff --git a/src/sage/version.py b/src/sage/version.py index 6743856ee05..c24735c795e 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.beta1' -date = '2017-04-06' +version = '8.0.beta2' +date = '2017-04-12'