diff --git a/VERSION.txt b/VERSION.txt index 962601e185e..8518a1669ae 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.0.beta5, Release Date: 2017-05-04 +SageMath version 8.0.beta6, Release Date: 2017-05-12 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 6816671ce21..e7d57ea79f9 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=339f2474b789d33051dde8f69d047ae723a36379 -md5=9bb01c255b39648bb19f62a3fa24a80d -cksum=2072910001 +sha1=6a2d4b05426a912b1589d9213760a6438105a502 +md5=4175d83db6c8a9a2a229bdbb383a923d +cksum=2310778507 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 037ba971962..3d4c7bfe8ef 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -219 +220 diff --git a/build/pkgs/normaliz/checksums.ini b/build/pkgs/normaliz/checksums.ini index e54ddf63dcc..afe7ac682f2 100644 --- a/build/pkgs/normaliz/checksums.ini +++ b/build/pkgs/normaliz/checksums.ini @@ -1,4 +1,4 @@ tarball=normaliz-VERSION.tar.gz -sha1=87bd2846a9132eb87ed6e9fa8cf875a0d6f5cc37 -md5=0eab32959e10b0c41b92c768ef562136 -cksum=1044635248 +sha1=81c89b5b2f8ff15d6939a3fd5822a288440f4d51 +md5=245b1c025ee45d0e2ffd3a77a585768e +cksum=3791680398 diff --git a/build/pkgs/normaliz/package-version.txt b/build/pkgs/normaliz/package-version.txt index 0aec50e6ede..e4604e3afd0 100644 --- a/build/pkgs/normaliz/package-version.txt +++ b/build/pkgs/normaliz/package-version.txt @@ -1 +1 @@ -3.1.4 +3.2.1 diff --git a/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch b/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch new file mode 100644 index 00000000000..2a4e9e35c40 --- /dev/null +++ b/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch @@ -0,0 +1,12 @@ +diff -druN src/source/libnormaliz/cone.cpp patches/source/libnormaliz/cone.cpp +--- src/source/libnormaliz/cone.cpp 2017-02-22 17:59:08.000000000 +0100 ++++ patches/source/libnormaliz/cone.cpp 2017-04-26 16:33:57.460366489 +0200 +@@ -3223,7 +3223,7 @@ + Deg1Elements=Matrix(0,dim); + ModuleGenerators=Matrix(0,dim); + +- Matrix Raw=ApproxCone.getDeg1ElementsMatrix(); ++ const Matrix& Raw=ApproxCone.getDeg1ElementsMatrix(); + Matrix Result(0,dim); + Matrix Eq=BasisChange.getEquations(); + Matrix Cong=BasisChange.getCongruences(); diff --git a/build/pkgs/normaliz/spkg-install b/build/pkgs/normaliz/spkg-install index 0e6526ed38d..a73ea271a5a 100755 --- a/build/pkgs/normaliz/spkg-install +++ b/build/pkgs/normaliz/spkg-install @@ -14,6 +14,3 @@ cd src $MAKE || die "Error building normaliz" $MAKE install || die "Error installing normaliz" -cd Singular || die "Normaliz distribution has no Singular subdirectory" -cp -pf normaliz.lib "$SAGE_LOCAL/share/singular/" || die "Error installing normaliz.lib for Singular." - diff --git a/build/pkgs/pynormaliz/checksums.ini b/build/pkgs/pynormaliz/checksums.ini index 8867a73bbf6..e4ca9b4c1aa 100644 --- a/build/pkgs/pynormaliz/checksums.ini +++ b/build/pkgs/pynormaliz/checksums.ini @@ -1,4 +1,4 @@ tarball=PyNormaliz-VERSION.tar.gz -sha1=e69046012a18f73cc820cf072d43f7d852020d49 -md5=27a5f7d3fa0d96766d4f811495b0f5ce -cksum=1643770354 +sha1=619d2b01d9ee20bfcf880cb25ea04b72cc2d2942 +md5=22eea4624caff219618640ed67003b9d +cksum=4251733870 diff --git a/build/pkgs/pynormaliz/package-version.txt b/build/pkgs/pynormaliz/package-version.txt index d3827e75a5c..c239c60cba2 100644 --- a/build/pkgs/pynormaliz/package-version.txt +++ b/build/pkgs/pynormaliz/package-version.txt @@ -1 +1 @@ -1.0 +1.5 diff --git a/build/pkgs/python3/patches/3.4.5-struct.patch b/build/pkgs/python3/patches/3.4.5-struct.patch index 3b677a72e90..6138b1359b4 100644 --- a/build/pkgs/python3/patches/3.4.5-struct.patch +++ b/build/pkgs/python3/patches/3.4.5-struct.patch @@ -3,6 +3,15 @@ as been fixed upstream for Python 3.7; see https://bugs.python.org/issue21124 diff -r b244bf74b638 Modules/_struct.c --- a/Modules/_struct.c Sun Oct 02 13:49:05 2016 +0300 +++ b/Modules/_struct.c Sun Oct 02 19:54:56 2016 +0900 +@@ -1627,7 +1627,7 @@ unpackiter_iternext(unpackiterobject *self) + } + + static PyTypeObject unpackiter_type = { +- PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ PyVarObject_HEAD_INIT(NULL, 0) + "unpack_iterator", /* tp_name */ + sizeof(unpackiterobject), /* tp_basicsize */ + 0, @@ -2301,6 +2301,9 @@ if (PyType_Ready(&PyStructType) < 0) return NULL; diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 4ee00d5aff2..0b69cae5c39 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.0.beta5, Release Date: 2017-05-04 │ +│ SageMath version 8.0.beta6, Release Date: 2017-05-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 3dd582b0ed1..caac449efca 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.0.beta5' -SAGE_RELEASE_DATE='2017-05-04' +SAGE_VERSION='8.0.beta6' +SAGE_RELEASE_DATE='2017-05-12' diff --git a/src/doc/en/a_tour_of_sage/index.rst b/src/doc/en/a_tour_of_sage/index.rst index ef3222ef4c1..d998c42b836 100644 --- a/src/doc/en/a_tour_of_sage/index.rst +++ b/src/doc/en/a_tour_of_sage/index.rst @@ -1,3 +1,5 @@ +.. _a-tour-of-sage: + ============== A Tour of Sage ============== diff --git a/src/doc/en/constructions/index.rst b/src/doc/en/constructions/index.rst index 6051fcafdcc..b6e660c245c 100644 --- a/src/doc/en/constructions/index.rst +++ b/src/doc/en/constructions/index.rst @@ -2,6 +2,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _constructions: + Welcome to the Sage Constructions documentation! ================================================ diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 8726af7f7ae..6647e3a06f0 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -1,3 +1,5 @@ +.. _developers-guide: + ====================================== Welcome to the Sage Developer's Guide! ====================================== diff --git a/src/doc/en/faq/index.rst b/src/doc/en/faq/index.rst index 5ccf5ddbbaa..31d5cb59040 100644 --- a/src/doc/en/faq/index.rst +++ b/src/doc/en/faq/index.rst @@ -3,6 +3,8 @@ .. your liking, but it should at least contain the root `toctree` .. directive. +.. _faq: + Welcome to the Sage FAQ! ======================== diff --git a/src/doc/en/installation/index.rst b/src/doc/en/installation/index.rst index ec1ce3d302c..06ebb3d7a26 100644 --- a/src/doc/en/installation/index.rst +++ b/src/doc/en/installation/index.rst @@ -1,3 +1,5 @@ +.. _installation-guide: + Welcome to the Sage Installation Guide ====================================== diff --git a/src/doc/en/prep/Advanced-2DPlotting.rst b/src/doc/en/prep/Advanced-2DPlotting.rst index 00967b9e683..5d2042684ca 100644 --- a/src/doc/en/prep/Advanced-2DPlotting.rst +++ b/src/doc/en/prep/Advanced-2DPlotting.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-advanced-2dplotting: + Tutorial for Advanced 2d Plotting ================================= diff --git a/src/doc/en/prep/Calculus.rst b/src/doc/en/prep/Calculus.rst index 81b0fed4346..53e90de0764 100644 --- a/src/doc/en/prep/Calculus.rst +++ b/src/doc/en/prep/Calculus.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-calculus: + Tutorial for Calculus ===================== diff --git a/src/doc/en/prep/Intro-Tutorial.rst b/src/doc/en/prep/Intro-Tutorial.rst index 82aef6ad728..2def3bef370 100644 --- a/src/doc/en/prep/Intro-Tutorial.rst +++ b/src/doc/en/prep/Intro-Tutorial.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-intro-tutorial: + Introductory Sage Tutorial ========================== diff --git a/src/doc/en/prep/Logging-On.rst b/src/doc/en/prep/Logging-On.rst index b9216cc69f4..24c356b3eba 100644 --- a/src/doc/en/prep/Logging-On.rst +++ b/src/doc/en/prep/Logging-On.rst @@ -2,6 +2,7 @@ .. linkall +.. _prep-logging-on: .. _logging-on: Logging on to a Sage Server and Creating a Worksheet diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index 595216d4411..cafca74b2d5 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-programming: + Sage Introductory Programming Tutorial ====================================== diff --git a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst index dfe894a4602..042b786420c 100644 --- a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst +++ b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-abstract-algebra: + Sage Quickstart for Abstract Algebra ==================================== diff --git a/src/doc/en/prep/Quickstarts/Differential-Equations.rst b/src/doc/en/prep/Quickstarts/Differential-Equations.rst index 539b21344fa..3c023424db8 100644 --- a/src/doc/en/prep/Quickstarts/Differential-Equations.rst +++ b/src/doc/en/prep/Quickstarts/Differential-Equations.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-differential-equations: + Sage Quickstart for Differential Equations ========================================== diff --git a/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst b/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst index aa7a308854a..9406595ddec 100644 --- a/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst +++ b/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-graphs-and-discrete: + Sage Quickstart for Graph Theory and Discrete Mathematics ========================================================= diff --git a/src/doc/en/prep/Quickstarts/Interact.rst b/src/doc/en/prep/Quickstarts/Interact.rst index 9898017ee22..666ad9023ce 100644 --- a/src/doc/en/prep/Quickstarts/Interact.rst +++ b/src/doc/en/prep/Quickstarts/Interact.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-interact: + Sage Interact Quickstart ======================== diff --git a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst index e14d7896b41..d9ad58c1f45 100644 --- a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst +++ b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-linear-algebra: + Sage Quickstart for Linear Algebra ================================== diff --git a/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst b/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst index dc534d0eef7..c5db7bfd4d1 100644 --- a/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst +++ b/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-multivariate-calculus: + Sage Quickstart for Multivariable Calculus ========================================== diff --git a/src/doc/en/prep/Quickstarts/NumAnalysis.rst b/src/doc/en/prep/Quickstarts/NumAnalysis.rst index 69e27e51b9b..70fc163fed8 100644 --- a/src/doc/en/prep/Quickstarts/NumAnalysis.rst +++ b/src/doc/en/prep/Quickstarts/NumAnalysis.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-numerical-analysis: + Sage Quickstart for Numerical Analysis ====================================== diff --git a/src/doc/en/prep/Quickstarts/Number-Theory.rst b/src/doc/en/prep/Quickstarts/Number-Theory.rst index d9596ee7e47..5b1f73d8138 100644 --- a/src/doc/en/prep/Quickstarts/Number-Theory.rst +++ b/src/doc/en/prep/Quickstarts/Number-Theory.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-number-theory: + Sage Quickstart for Number Theory ================================= diff --git a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst index 419d3b121eb..1192d8d84b9 100644 --- a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst +++ b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-statistics-and-distributions: + Sage Quickstart for Statistics ============================== diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index f6494f40da6..e8e74915cfd 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-symbolics-and-basic-plotting: + Tutorial for Symbolics and Plotting =================================== @@ -486,6 +488,6 @@ We close this tutorial with a cool plot that we define *implicitly* as a Graphics3d Object The next tutorial will use all that you have learned about Sage basics, -symbolics, and plotting in a specific mathematical venue \- the calculus -sequence! +symbolics, and plotting in a specific mathematical venue \- the +:ref:`calculus sequence `! diff --git a/src/doc/en/prep/index.rst b/src/doc/en/prep/index.rst index be762634ec1..33c3f3aaf26 100644 --- a/src/doc/en/prep/index.rst +++ b/src/doc/en/prep/index.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _prep-tutorials: + PREP Tutorials ============== diff --git a/src/doc/en/prep/quickstart.rst b/src/doc/en/prep/quickstart.rst index 336d2b7b92c..38167a8ca11 100644 --- a/src/doc/en/prep/quickstart.rst +++ b/src/doc/en/prep/quickstart.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _prep-quickstart-tutorials: + PREP Quickstart Tutorials ========================= diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index 1d4ed57a230..9e51d5b15b6 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -125,6 +125,7 @@ Individual Categories sage/categories/lie_algebras sage/categories/lie_algebras_with_basis sage/categories/lie_groups + sage/categories/loop_crystals sage/categories/l_trivial_semigroups sage/categories/magmas sage/categories/magmas_and_additive_magmas diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 8713d4f31ee..06a3214d6ef 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -65,13 +65,16 @@ Comprehensive Module list sage/combinat/crystals/highest_weight_crystals sage/combinat/crystals/induced_structure sage/combinat/crystals/infinity_crystals - sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/kirillov_reshetikhin sage/combinat/crystals/kyoto_path_model sage/combinat/crystals/letters sage/combinat/crystals/littelmann_path sage/combinat/crystals/monomial_crystals sage/combinat/crystals/multisegments + sage/combinat/crystals/mv_polytopes + sage/combinat/crystals/pbw_crystal + sage/combinat/crystals/pbw_datum + sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/spins sage/combinat/crystals/star_crystal sage/combinat/crystals/tensor_product @@ -213,6 +216,7 @@ Comprehensive Module list sage/combinat/root_system/all sage/combinat/root_system/ambient_space sage/combinat/root_system/associahedron + sage/combinat/root_system/braid_move_calculator sage/combinat/root_system/branching_rules sage/combinat/root_system/cartan_matrix sage/combinat/root_system/cartan_type diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 3607297d28e..9554e151a80 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -1,14 +1,13 @@ -******** -Contents -******** +.. _reference-manual: -Welcome to Sage's Reference Manual! +************************************ +Welcome to the Sage Reference Manual +************************************ -This manual is a thematic index of all of `Sage's `_ -features. It also contains many examples that illustrate their use, all of them -systematically tested with each release. - -Enjoy Sage! +This manual contains documentation for (almost) all of `Sage's +`_ features, each illustrated with examples +that are systematically tested with each release. A thematic index is +available below. User Interface ============== diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 9e4acb5c008..dc95b571607 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -322,6 +322,11 @@ REFERENCES: complexes and posets. I*. Trans. of Amer. Math. Soc. **348** No. 4. (1996) +.. [BZ01] \A. Berenstein, A. Zelevinsky + *Tensor product multiplicities, canonical bases + and totally positive varieties* + Invent. Math. **143** No. 1. (2002), 77-128. + .. _ref-C: **C** @@ -668,6 +673,10 @@ REFERENCES: curves". LMS Journal of Computation and Mathematics (2014), volume 17, issue 01, pp. 1-23. +.. [FOS2010] \G. Fourier, M. Okado, A. Schilling. *Perfectness of + Kirillov-Reshetikhin crystals for nonexceptional types*. + Contemp. Math. 506 (2010) 127-143 ( :arxiv:`0811.1604` ) + .. [FP1996] Komei Fukuda, Alain Prodon: Double Description Method Revisited, Combinatorics and Computer Science, volume 1120 of Lecture Notes in Computer Science, page @@ -940,6 +949,13 @@ REFERENCES: .. [Kal1980] \T. Kaliath, "Linear Systems", Prentice-Hall, 1980, 383--386. +.. [Kam2007] Joel Kamnitzer, + *The crystal structure on the set of Mirković-Vilonen polytopes*, + Adv. Math. **215** (2007), 66-93. + +.. [Kam2010] Joel Kamnitzer, *Mirković-Vilonen cycles and polytopes*, + Ann. Math. (2) **171** (2010), 731-777. + .. [Kan1958] \D. M. Kan, *A combinatorial definition of homotopy groups*, Ann. Math. (2) 67 (1958), 282-312. @@ -959,6 +975,10 @@ REFERENCES: \M. Grötschel, \L Lovász, *Handbook of combinatorics*, Vol. 1, Chapter 18, 1995 +.. [KKMMNN1992] S-J. Kang, M. Kashiwara, K. C. Misra, T. Miwa, T. Nakashima, + and A. Nakayashiki. *Affine crystals and vertex models*. + Int. J. Mod. Phys. A, **7** (suppl. 1A), (1992) pp. 449-484. + .. [KL2008] Chris Kurth and Ling Long, "Computations with finite index subgroups of `{\rm PSL}_2(\ZZ)` using Farey symbols", Advances in algebra and combinatorics, 225--242, World @@ -1500,6 +1520,11 @@ REFERENCES: manifolds and complexes into a cubic lattice", *Uspekhi Mat. Nauk* 47 (1992), 219-220. +.. [ST2011] \A. Schilling, P. Tingley. *Demazure crystals, + Kirillov-Reshetikhin crystals, and the energy function*. + Electronic Journal of Combinatorics. **19(2)**. 2012. + :arXiv:`1104.2359` + .. [Sta2007] Stanley, Richard: *Hyperplane Arrangements*, Geometric Combinatorics (E. Miller, V. Reiner, and B. Sturmfels, eds.), IAS/Park City Mathematics Series, vol. 13, diff --git a/src/doc/en/thematic_tutorials/index.rst b/src/doc/en/thematic_tutorials/index.rst index 8f73b34ffc1..bcae758a13e 100644 --- a/src/doc/en/thematic_tutorials/index.rst +++ b/src/doc/en/thematic_tutorials/index.rst @@ -2,6 +2,8 @@ .. Aug 21 20:15:55 2008. You can adapt this file completely to your .. liking, but it should at least contain the root `toctree` directive. +.. _thematic-tutorials: + Welcome to the Sage Thematic Tutorials! ======================================= diff --git a/src/doc/en/thematic_tutorials/lie/crystals.rst b/src/doc/en/thematic_tutorials/lie/crystals.rst index 77ba5c45e4b..8047c549a1f 100644 --- a/src/doc/en/thematic_tutorials/lie/crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/crystals.rst @@ -772,7 +772,7 @@ You can see how its done as follows:: sage: T = crystals.Tableaux("A4",shape=[3,2]) sage: v = T.highest_weight_vector().f(1).f(2).f(3).f(2).f(1).f(4).f(2).f(3); v [[1, 2, 5], [3, 4]] - sage: v._list + sage: list(v) [3, 1, 4, 2, 5] We've looked at the internal representation of `v`, where it is diff --git a/src/doc/en/tutorial/index.rst b/src/doc/en/tutorial/index.rst index 72b80ad2b5d..858cbb1f2c8 100644 --- a/src/doc/en/tutorial/index.rst +++ b/src/doc/en/tutorial/index.rst @@ -2,6 +2,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _tutorial: + Welcome to the Sage Tutorial! ============================= diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index 9476c44c3d9..bf04407ada0 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -1124,9 +1124,6 @@ def steenrod_basis_error_check(dim, p, **kwds): import sage.misc.misc as misc generic = kwds.get('generic', False if p==2 else True ) - # In this test function, we don't want to use caching. - # Hence, the uncached versions of steenrod_algebra_basis - # and of convert_to_milnor_matrix are used. if not generic: bases = ('adem','woody', 'woodz', 'wall', 'arnona', 'arnonc', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz', diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index a00d9647120..329e624809e 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -7,7 +7,7 @@ multiplication. - John H. Palmieri (2010-06-30: version 1.0) multiplication of Serre-Cartan basis elements using the Adem relations. - - Simon King (2011-10-25): Fix the use of cached functions. +- Simon King (2011-10-25): Fix the use of cached functions. .. rubric:: Milnor multiplication, `p=2` @@ -187,9 +187,8 @@ where `s_k \geq \epsilon_{k+1} + p s_{k+1}` for all `k`. As at the prime 2, these form a basis for the Steenrod algebra. -The main function for this is :func:`make_mono_admissible_` (and in -practice, one should use the cached version, -``make_mono_admissible``), which converts a product of Steenrod +The main function for this is :func:`make_mono_admissible`, +which converts a product of Steenrod squares or pth power operations and Bocksteins into a dictionary representing a sum of admissible monomials. """ @@ -707,12 +706,6 @@ def adem(a, b, c=0, p=2, generic=None): a dictionary representing the mod `p` Adem relations applied to `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`. - .. note:: - - Users should use :func:`adem` instead of this function (which - has a trailing underscore in its name): :func:`adem` - is the cached version of this one, and so will be faster. - The mod `p` Adem relations for the mod `p` Steenrod algebra are as follows: if `p=2`, then if `a < 2b`, @@ -876,13 +869,6 @@ def make_mono_admissible(mono, p=2, generic=None): tuples `(i_1, ..., i_{j-1}, NEW, i_{j+2}, ...)`, keeping track of the coefficients. - .. note:: - - Users should use :func:`make_mono_admissible` instead of this - function (which has a trailing underscore in its name): - :func:`make_mono_admissible` is the cached version of this - one, and so will be faster. - EXAMPLES:: sage: from sage.algebras.steenrod.steenrod_algebra_mult import make_mono_admissible diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index 08ad389ccda..5093ef189ce 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -237,7 +237,8 @@ def an_element(self): def weight_lattice_realization(self): """ - Returns the weight lattice realization used to express weights. + Return the weight lattice realization used to express weights + in ``self``. This default implementation uses the ambient space of the root system for (non relabelled) finite types and the @@ -245,6 +246,9 @@ def weight_lattice_realization(self): ambient spaces were partially implemented, and may be changed in the future. + For affine types, this returns the extended weight lattice + by default. + EXAMPLES:: sage: C = crystals.Letters(['A', 5]) @@ -253,10 +257,43 @@ def weight_lattice_realization(self): sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) sage: K.weight_lattice_realization() Weight lattice of the Root system of type ['A', 2, 1] + + TESTS: + + Check that crystals have the correct weight lattice realization:: + + sage: A = crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() + sage: A.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 2, 1] + + sage: B = crystals.AlcovePaths(['A',2,1],[1,0,0]) + sage: B.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 2, 1] + + sage: C = crystals.AlcovePaths("B3",[1,0,0]) + sage: C.weight_lattice_realization() + Ambient space of the Root system of type ['B', 3] + + sage: M = crystals.infinity.NakajimaMonomials(['A',3,2]) + sage: M.weight_lattice_realization() + Extended weight lattice of the Root system of type ['B', 2, 1]^* + sage: M = crystals.infinity.NakajimaMonomials(['A',2]) + sage: M.weight_lattice_realization() + Ambient space of the Root system of type ['A', 2] + sage: A = CartanMatrix([[2,-3],[-3,2]]) + sage: M = crystals.infinity.NakajimaMonomials(A) + sage: M.weight_lattice_realization() + Weight lattice of the Root system of type Dynkin diagram of rank 2 + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Y.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 3, 1] """ F = self.cartan_type().root_system() if self.cartan_type().is_finite() and F.ambient_space() is not None: return F.ambient_space() + if self.cartan_type().is_affine(): + return F.weight_lattice(extended=True) return F.weight_lattice() def cartan_type(self): @@ -391,9 +428,9 @@ def subcrystal(self, index_set=None, generators=None, max_depth=float("inf"), sage: len(S) 6 sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)])) - [[[1, 4]], [[2, 4]], [[1, 3]], [[2, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]], [[2, 3]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], max_depth=1)) - [[[1, 4]], [[2, 4]], [[1, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], direction='upper')) [[[1, 4]], [[1, 3]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], direction='lower')) @@ -592,7 +629,7 @@ def crystal_morphism(self, on_gens, codomain=None, [The crystal of tableaux of type ['A', 2] and shape(s) [[1]], The crystal of tableaux of type ['A', 2] and shape(s) [[1]], The crystal of tableaux of type ['A', 2] and shape(s) [[1]]] - Defn: [2, 1, 1] |--> [[[1]], [[2]], [[1]]] + Defn: [[1, 1], [2]] |--> [[[1]], [[2]], [[1]]] sage: b = B.module_generators[0] sage: b.pp() 1 1 @@ -634,7 +671,7 @@ def crystal_morphism(self, on_gens, codomain=None, ['D', 4] -> ['D', 4, 1] Virtual Crystal morphism: From: The crystal of tableaux of type ['D', 4] and shape(s) [[1, 1]] To: Kirillov-Reshetikhin crystal of type ['D', 4, 1] with (r,s)=(2,2) - Defn: [2, 1] |--> [[1], [2]] + Defn: [[1], [2]] |--> [[1], [2]] sage: b = B.module_generators[0] sage: psi(b) [[1], [2]] @@ -738,7 +775,7 @@ def crystal_morphism(self, on_gens, codomain=None, def digraph(self, subset=None, index_set=None): """ - Returns the DiGraph associated to ``self``. + Return the :class:`DiGraph` associated to ``self``. INPUT: @@ -826,12 +863,7 @@ def digraph(self, subset=None, index_set=None): .. TODO:: Add more tests. """ from sage.graphs.all import DiGraph - from sage.categories.highest_weight_crystals import HighestWeightCrystals d = {} - if self in HighestWeightCrystals: - f = lambda u_v_label: ({}) - else: - f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) # Parse optional arguments if subset is None: @@ -852,9 +884,8 @@ def digraph(self, subset=None, index_set=None): G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", - edge_labels = True, - color_by_label = self.cartan_type()._index_set_coloring, - edge_options = f) + edge_labels=True, + color_by_label=self.cartan_type()._index_set_coloring) return G def latex_file(self, filename): @@ -1139,7 +1170,7 @@ def tensor(self, *crystals, **options): The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]] sage: T.module_generators - [[2, 1, 1], [1, 2, 1]] + ([2, 1, 1], [1, 2, 1]) """ from sage.combinat.crystals.tensor_product import TensorProductOfCrystals return TensorProductOfCrystals(self, *crystals, **options) @@ -1655,9 +1686,9 @@ def subcrystal(self, index_set=None, max_depth=float("inf"), direction="both", sage: C = crystals.KirillovReshetikhin(['A',3,1], 1, 2) sage: elt = C(1,4) sage: list(elt.subcrystal(index_set=[1,3])) - [[[1, 4]], [[2, 4]], [[1, 3]], [[2, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]], [[2, 3]]] sage: list(elt.subcrystal(index_set=[1,3], max_depth=1)) - [[[1, 4]], [[2, 4]], [[1, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]]] sage: list(elt.subcrystal(index_set=[1,3], direction='upper')) [[[1, 4]], [[1, 3]]] sage: list(elt.subcrystal(index_set=[1,3], direction='lower')) @@ -1990,8 +2021,8 @@ def _repr_defn(self): sage: psi = H((None, b, b, None), generators=T.highest_weight_vectors()) sage: print(psi._repr_defn()) [[[1]], [[1]], [[1]]] |--> None - [[[2]], [[1]], [[1]]] |--> [2, 1, 1] - [[[1]], [[2]], [[1]]] |--> [2, 1, 1] + [[[2]], [[1]], [[1]]] |--> [[1, 1], [2]] + [[[1]], [[2]], [[1]]] |--> [[1, 1], [2]] [[[3]], [[2]], [[1]]] |--> None """ return '\n'.join(['{} |--> {}'.format(mg, im) @@ -2314,7 +2345,7 @@ class CrystalHomset(Homset): To: Direct sum of the crystals Family (The crystal of tableaux of type ['A', 1] and shape(s) [[2]], The crystal of tableaux of type ['A', 1] and shape(s) [[]]) - Defn: [[[1]], [[1]]] |--> [1, 1] + Defn: [[[1]], [[1]]] |--> [[1, 1]] [[[2]], [[1]]] |--> [] sage: psi.is_isomorphism() True @@ -2368,15 +2399,15 @@ class CrystalHomset(Homset): ['B', 3] -> ['D', 4] Virtual Crystal morphism: From: The crystal of tableaux of type ['B', 3] and shape(s) [[1]] To: The crystal of tableaux of type ['D', 4] and shape(s) [[2]] - Defn: [1] |--> [1, 1] + Defn: [[1]] |--> [[1, 1]] sage: for b in B: print("{} |--> {}".format(b, psi(b))) - [1] |--> [1, 1] - [2] |--> [2, 2] - [3] |--> [3, 3] - [0] |--> [3, -3] - [-3] |--> [-3, -3] - [-2] |--> [-2, -2] - [-1] |--> [-1, -1] + [[1]] |--> [[1, 1]] + [[2]] |--> [[2, 2]] + [[3]] |--> [[3, 3]] + [[0]] |--> [[3, -3]] + [[-3]] |--> [[-3, -3]] + [[-2]] |--> [[-2, -2]] + [[-1]] |--> [[-1, -1]] """ def __init__(self, X, Y, category=None): """ @@ -2416,7 +2447,7 @@ def _coerce_impl(self, x): sage: H = Hom(B, B) sage: H(H.an_element()) # indirect doctest ['B', 3] Crystal endomorphism of The crystal of tableaux of type ['B', 3] and shape(s) [[2, 1]] - Defn: [2, 1, 1] |--> None + Defn: [[1, 1], [2]] |--> None """ if not isinstance(x, CrystalMorphism): raise TypeError @@ -2502,7 +2533,7 @@ def _an_element_(self): ['A', 2] Crystal morphism: From: The crystal of tableaux of type ['A', 2] and shape(s) [[2, 1]] To: The infinity crystal of tableaux of type ['A', 2] - Defn: [2, 1, 1] |--> None + Defn: [[1, 1], [2]] |--> None """ return self.element_class(self, lambda x: None) diff --git a/src/sage/categories/finite_lattice_posets.py b/src/sage/categories/finite_lattice_posets.py index a70997e6eca..19330da28cc 100644 --- a/src/sage/categories/finite_lattice_posets.py +++ b/src/sage/categories/finite_lattice_posets.py @@ -119,6 +119,44 @@ def meet_irreducibles_poset(self): """ return self.subposet(self.meet_irreducibles()) + def irreducibles_poset(self): + """ + Return the poset of meet- or join-irreducibles of the lattice. + + A *join-irreducible* element of a lattice is an element with + exactly one lower cover. Dually a *meet-irreducible* element + has exactly one upper cover. + + This is the smallest poset with completion by cuts being + isomorphic to the lattice. As a special case this returns + one-element poset from one-element lattice. + + .. SEEALSO:: + + :meth:`~sage.combinat.posets.posets.FinitePoset.completion_by_cuts`. + + EXAMPLES:: + + sage: L = LatticePoset({1: [2, 3, 4], 2: [5, 6], 3: [5], + ....: 4: [6], 5: [9, 7], 6: [9, 8], 7: [10], + ....: 8: [10], 9: [10], 10: [11]}) + sage: L_ = L.irreducibles_poset() + sage: sorted(L_) + [2, 3, 4, 7, 8, 9, 10, 11] + sage: L_.completion_by_cuts().is_isomorphic(L) + True + + TESTS:: + + sage: LatticePoset().irreducibles_poset() + Finite poset containing 0 elements + sage: Posets.ChainPoset(1).irreducibles_poset() + Finite poset containing 1 elements + """ + if self.cardinality() == 1: + from sage.combinat.posets.posets import Poset + return Poset({self[0]: []}) + return self.subposet(self.join_irreducibles()+self.meet_irreducibles()) ########################################################################## # Lattice morphisms diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index 0a38a5691a3..b247ce0ea84 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -837,7 +837,7 @@ def _call_(self, x): [The T crystal of type ['A', 2] and weight Lambda[2], The crystal of tableaux of type ['A', 2] and shape(s) [[1]]] To: The crystal of tableaux of type ['A', 2] and shape(s) [[2, 1]] - Defn: [Lambda[2], [[3]]] |--> [2, 1, 3] + Defn: [Lambda[2], [[3]]] |--> [[1, 3], [2]] sage: psi(Bp.highest_weight_vector()) [[1, 1], [2]] """ diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py new file mode 100644 index 00000000000..fa4781b3ee6 --- /dev/null +++ b/src/sage/categories/loop_crystals.py @@ -0,0 +1,1195 @@ +r""" +Loop Crystals +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function, division, absolute_import + +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.categories.category_singleton import Category_singleton +from sage.categories.crystals import Crystals +from sage.categories.regular_crystals import RegularCrystals +from sage.categories.tensor import TensorProductsCategory +from sage.categories.map import Map +from sage.graphs.dot2tex_utils import have_dot2tex +from sage.functions.other import ceil +from sage.rings.all import ZZ + + +class LoopCrystals(Category_singleton): + r""" + The category of `U_q'(\mathfrak{g})`-crystals, where `\mathfrak{g}` + is of affine type. + + The category is called loop crystals as we can also consider them + as crystals corresponding to the loop algebra `\mathfrak{g}_0[t]`, + where `\mathfrak{g}_0` is the corresponding classical type. + + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: C = LoopCrystals() + sage: C + Category of loop crystals + sage: C.super_categories() + [Category of crystals] + sage: C.example() + Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(1,1) + + TESTS:: + + sage: TestSuite(C).run() + sage: B = FiniteCrystals().example() + sage: TestSuite(B).run() + """ + @cached_method + def super_categories(self): + r""" + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: LoopCrystals().super_categories() + [Category of crystals] + """ + return [Crystals()] + + def example(self, n = 3): + """ + Return an example of Kirillov-Reshetikhin crystals, as per + :meth:`Category.example`. + + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: B = LoopCrystals().example(); B + Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(1,1) + """ + from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal + return KirillovReshetikhinCrystal(['A', n, 1], 1, 1) + + class ParentMethods: + def weight_lattice_realization(self): + """ + Return the weight lattice realization used to express weights + of elements in ``self``. + + The default is to use the non-extended affine weight lattice. + + EXAMPLES:: + + sage: C = crystals.Letters(['A', 5]) + sage: C.weight_lattice_realization() + Ambient space of the Root system of type ['A', 5] + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.weight_lattice_realization() + Weight lattice of the Root system of type ['A', 2, 1] + """ + F = self.cartan_type().root_system() + return F.weight_lattice(extended=False) + + def digraph(self, subset=None, index_set=None): + r""" + Return the :class:`DiGraph` associated to ``self``. + + INPUT: + + - ``subset`` -- (optional) a subset of vertices for + which the digraph should be constructed + + - ``index_set`` -- (optional) the index set to draw arrows + + .. SEEALSO:: + + :meth:`sage.categories.crystals.Crystals.ParentMethods.digraph` + + EXAMPLES:: + + sage: C = crystals.KirillovReshetikhin(['D',4,1], 2, 1) + sage: G = C.digraph() + sage: G.latex_options() # optional - dot2tex + LaTeX options for Digraph on 29 vertices: + {...'edge_options': at 0x...>,...} + sage: view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window) + """ + G = Crystals().parent_class.digraph(self, subset, index_set) + if have_dot2tex(): + f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) + G.set_latex_options(edge_options=f) + return G + +# TODO: Should we make "regular" an axiom? +class RegularLoopCrystals(Category_singleton): + r""" + The category of regular `U_q'(\mathfrak{g})`-crystals, where + `\mathfrak{g}` is of affine type. + """ + @cached_method + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.loop_crystals import RegularLoopCrystals + sage: RegularLoopCrystals().super_categories() + [Category of regular crystals, + Category of loop crystals] + """ + return [RegularCrystals(), LoopCrystals()] + + class ElementMethods: + def classical_weight(self): + """ + Return the classical weight of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: hw = LS.classically_highest_weight_vectors() + sage: [(v.weight(), v.classical_weight()) for v in hw] + [(-2*Lambda[0] + 2*Lambda[1], (2, 0, 0)), + (-Lambda[0] + Lambda[2], (1, 1, 0))] + """ + CT = self.cartan_type().classical() + I0 = CT.index_set() + La = CT.root_system().ambient_space().fundamental_weights() + return sum(La[i] * (self.phi(i) - self.epsilon(i)) for i in I0) + +class KirillovReshetikhinCrystals(Category_singleton): + """ + Category of Kirillov-Reshetikhin crystals. + """ + @cached_method + def super_categories(self): + r""" + EXAMPLES:: + + sage: from sage.categories.loop_crystals import KirillovReshetikhinCrystals + sage: KirillovReshetikhinCrystals().super_categories() + [Category of finite regular loop crystals] + """ + return [RegularLoopCrystals().Finite()] + + class ParentMethods: + @abstract_method + def r(self): + r""" + Return the value `r` in ``self`` written as `B^{r,s}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,4) + sage: K.r() + 2 + """ + + @abstract_method + def s(self): + r""" + Return the value `s` in ``self`` written as `B^{r,s}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,4) + sage: K.s() + 4 + """ + + @abstract_method(optional=True) + def classical_decomposition(self): + """ + Return the classical decomposition of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,2) + sage: K.classical_decomposition() + The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] + """ + + @cached_method + def classically_highest_weight_vectors(self): + """ + Return the classically highest weight elements of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1],1,1) + sage: K.classically_highest_weight_vectors() + ([(1,)],) + """ + I0 = self.cartan_type().classical().index_set() + return tuple([x for x in self if x.is_highest_weight(I0)]) + + # TODO: This is duplicated in tensor product category + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,1) + sage: K.cardinality() + 27 + sage: K = crystals.KirillovReshetikhin(['C',6,1], 4,3) + sage: K.cardinality() + 4736732 + """ + CWLR = self.cartan_type().classical().root_system().ambient_space() + return sum(CWLR.weyl_dimension(mg.classical_weight()) + for mg in self.classically_highest_weight_vectors()) + + @cached_method + def maximal_vector(self): + r""" + Return the unique element of classical weight `s \Lambda_r` + in ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,2) + sage: K.maximal_vector() + [[1, 1]] + sage: K = crystals.KirillovReshetikhin(['E',6,1],1,1) + sage: K.maximal_vector() + [(1,)] + + sage: K = crystals.KirillovReshetikhin(['D',4,1],2,1) + sage: K.maximal_vector() + [[1], [2]] + """ + R = self.weight_lattice_realization() + Lambda = R.fundamental_weights() + r = self.r() + s = self.s() + weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() + + # First check the module generators as it is likely to be in here + for b in self.module_generators: + if b.weight() == weight: + return b + + # Otherwise check all of the elements + for b in self: + if b not in self.module_generators and b.weight() == weight: + return b + + assert False, "BUG: invalid Kirillov-Reshetikhin crystal" + + # TODO: Should this be in one of the super categories? + def affinization(self): + """ + Return the corresponding affinization crystal of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.affinization() + Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1, model='KR') + sage: K.affinization() + Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) + """ + from sage.combinat.crystals.affinization import AffinizationOfCrystal + return AffinizationOfCrystal(self) + + @cached_method + def R_matrix(self, K): + r""" + Return the combinatorial `R`-matrix of ``self`` to ``K``. + + The *combinatorial* `R`-*matrix* is the affine crystal + isomorphism `R : L \otimes K \to K \otimes L` which maps + `u_{L} \otimes u_K` to `u_K \otimes u_{L}`, where `u_K` + is the unique element in `K = B^{r,s}` of weight + `s\Lambda_r - s c \Lambda_0` (see :meth:`maximal_vector`). + + INPUT: + + - ``self`` -- a crystal `L` + - ``K`` -- a Kirillov-Reshetikhin crystal of the same type as `L` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: L = crystals.KirillovReshetikhin(['A',2,1],1,2) + sage: f = K.R_matrix(L) + sage: [[b,f(b)] for b in crystals.TensorProduct(K,L)] + [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]], + [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]], + [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]], + [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]], + [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]], + [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]], + [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]], + [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]], + [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]], + [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]], + [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]], + [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]], + [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]], + [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]], + [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]], + [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]], + [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]], + [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]] + + sage: K = crystals.KirillovReshetikhin(['D',4,1],1,1) + sage: L = crystals.KirillovReshetikhin(['D',4,1],2,1) + sage: f = K.R_matrix(L) + sage: T = crystals.TensorProduct(K,L) + sage: b = T( K(rows=[[1]]), L(rows=[]) ) + sage: f(b) + [[[2], [-2]], [[1]]] + + Alternatively, one can compute the combinatorial `R`-matrix + using the isomorphism method of digraphs:: + + sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) + sage: T1 = crystals.TensorProduct(K1,K2) + sage: T2 = crystals.TensorProduct(K2,K1) + sage: T1.digraph().is_isomorphic(T2.digraph(), edge_labels=True, certificate=True) #todo: not implemented (see #10904 and #10549) + (True, {[[[1]], [[2], [3]]]: [[[1], [3]], [[2]]], [[[3]], [[2], [3]]]: [[[2], [3]], [[3]]], + [[[3]], [[1], [3]]]: [[[1], [3]], [[3]]], [[[1]], [[1], [3]]]: [[[1], [3]], [[1]]], [[[1]], + [[1], [2]]]: [[[1], [2]], [[1]]], [[[2]], [[1], [2]]]: [[[1], [2]], [[2]]], [[[3]], + [[1], [2]]]: [[[2], [3]], [[1]]], [[[2]], [[1], [3]]]: [[[1], [2]], [[3]]], [[[2]], [[2], [3]]]: [[[2], [3]], [[2]]]}) + """ + from sage.combinat.crystals.tensor_product import TensorProductOfCrystals + T1 = TensorProductOfCrystals(self, K) + T2 = TensorProductOfCrystals(K, self) + gen1 = T1(self.maximal_vector(), K.maximal_vector()) + gen2 = T2(K.maximal_vector(), self.maximal_vector()) + return T1.crystal_morphism({gen1: gen2}, check=False) + + @cached_method + def local_energy_function(self, B): + r""" + Return the local energy function of ``self`` and ``B``. + + See + :class:`~sage.categories.loop_crystals.LocalEnergyFunction` + for a definition. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) + sage: Kp = crystals.KirillovReshetikhin(['A',6,2], 1,1) + sage: H = K.local_energy_function(Kp); H + Local energy function of + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) + tensor + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) + """ + return LocalEnergyFunction(self, B) + + @cached_method + def b_sharp(self): + r""" + Return the element `b^{\sharp}` of ``self``. + + Let `B` be a KR crystal. The element `b^{\sharp}` is the unique + element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with + `\ell = \min \{ \langle c, \varphi(b) \rangle \mid b \in B \}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) + sage: K.b_sharp() + [] + sage: K.b_sharp().Phi() + Lambda[0] + + sage: K = crystals.KirillovReshetikhin(['C',3,1], 1,3) + sage: K.b_sharp() + [[-1]] + sage: K.b_sharp().Phi() + 2*Lambda[0] + + sage: K = crystals.KirillovReshetikhin(['D',6,2], 2,2) + sage: K.b_sharp() # long time + [] + sage: K.b_sharp().Phi() # long time + 2*Lambda[0] + """ + ell = float('inf') + bsharp = None + for b in self: + phi = b.Phi() + if phi.support() == [0] and phi[0] < ell: + bsharp = b + ell = phi[0] + return bsharp + + def is_perfect(self, ell=None): + r""" + Check if ``self`` is a perfect crystal of level ``ell``. + + A crystal `\mathcal{B}` is perfect of level `\ell` if: + + #. `\mathcal{B}` is isomorphic to the crystal graph of a + finite-dimensional `U_q'(\mathfrak{g})`-module. + #. `\mathcal{B} \otimes \mathcal{B}` is connected. + #. There exists a `\lambda\in X`, such that + `\mathrm{wt}(\mathcal{B}) \subset \lambda + \sum_{i\in I} + \ZZ_{\le 0} \alpha_i` and there is a unique element in + `\mathcal{B}` of classical weight `\lambda`. + #. For all `b \in \mathcal{B}`, + `\mathrm{level}(\varepsilon (b)) \geq \ell`. + #. For all `\Lambda` dominant weights of level `\ell`, there + exist unique elements `b_{\Lambda}, b^{\Lambda} \in + \mathcal{B}`, such that `\varepsilon(b_{\Lambda}) = + \Lambda = \varphi(b^{\Lambda})`. + + Points (1)-(3) are known to hold. This method checks + points (4) and (5). + + If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, + then it was proven for non-exceptional types in [FOS2010]_ + that it is perfect if and only if `s/c_r` is an integer + (where `c_r` is a constant related to the type of the crystal). + + It is conjectured this is true for all affine types. + + INPUT: + + - ``ell`` -- (default: `s / c_r`) integer; the level + + REFERENCES: + + [FOS2010]_ + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.is_perfect() + True + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: K.is_perfect() + False + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K.is_perfect() + True + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,3) + sage: K.is_perfect() + True + + .. TODO:: + + Implement a version for tensor products of KR crystals. + """ + if ell is None: + ell = self.s() / self.cartan_type().c()[self.r()] + if ell not in ZZ: + return False + + if ell not in ZZ: + raise ValueError("perfectness not defined for non-integral levels") + + # [FOS2010]_ check + if self.cartan_type().classical().type() not in ['E','F','G']: + return ell == self.s() / self.cartan_type().c()[self.r()] + + # Check by definition + # TODO: This is duplicated from ProjectedLevelZeroLSPaths, combine the two methods. + # TODO: Similarly, don't duplicate in the tensor product category, maybe + # move this to the derived affine category? + MPhi = [] + for b in self: + p = b.Phi().level() + assert p == b.Epsilon().level() + if p < ell: + return False + if p == ell: + MPhi += [b] + weights = [] + I = self.index_set() + rank = len(I) + La = self.weight_lattice_realization().basis() + from sage.combinat.integer_vector import IntegerVectors + for n in range(1, ell+1): + for c in IntegerVectors(n, rank): + w = sum(c[i]*La[i] for i in I) + if w.level() == ell: + weights.append(w) + return sorted(b.Phi() for b in MPhi) == sorted(weights) + + def level(self): + r""" + Return the level of ``self`` when ``self`` is a perfect crystal. + + .. SEEALSO:: + + :meth:`~sage.categories.loop_crystals.KirillovReshetikhinCrystals.ParentMethods.is_perfect` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.level() + 1 + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K.level() + 1 + sage: K = crystals.KirillovReshetikhin(['D',4,1], 1, 3) + sage: K.level() + 3 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: K.level() + Traceback (most recent call last): + ... + ValueError: this crystal is not perfect + """ + if not self.is_perfect(): + raise ValueError("this crystal is not perfect") + return self.s() / self.cartan_type().c()[self.r()] + + def q_dimension(self, q=None, prec=None, use_product=False): + """ + Return the `q`-dimension of ``self``. + + The `q`-dimension of a KR crystal is defined as the `q`-dimension of + the underlying classical crystal. + + EXAMPLES:: + + sage: KRC = crystals.KirillovReshetikhin(['A',2,1], 2,2) + sage: KRC.q_dimension() + q^4 + q^3 + 2*q^2 + q + 1 + sage: KRC = crystals.KirillovReshetikhin(['D',4,1], 2,1) + sage: KRC.q_dimension() + q^10 + q^9 + 3*q^8 + 3*q^7 + 4*q^6 + 4*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 2 + """ + return self.classical_decomposition().q_dimension(q, prec, use_product) + + class ElementMethods: + def lusztig_involution(self): + r""" + Return the result of the classical Lusztig involution on ``self``. + + EXAMPLES:: + + sage: KRT = crystals.KirillovReshetikhin(['D',4,1], 2, 3, model='KR') + sage: mg = KRT.module_generators[1] + sage: mg.lusztig_involution() + [[-2, -2, 1], [-1, -1, 2]] + sage: elt = mg.f_string([2,1,3,2]); elt + [[3, -2, 1], [4, -1, 2]] + sage: elt.lusztig_involution() + [[-4, -2, 1], [-3, -1, 2]] + """ + Cl = self.parent().cartan_type().classical() + I = Cl.index_set() + aut = Cl.opposition_automorphism() + hw = self.to_highest_weight(I)[1] + hw.reverse() + return self.to_lowest_weight(I)[0].e_string(aut[i] for i in hw) + + @cached_method + def energy_function(self): + r""" + Return the energy function of ``self``. + + Let `B` be a KR crystal. Let `b^{\sharp}` denote the unique + element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with + `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. Let + `u_B` denote the maximal element of `B`. The *energy* of + `b \in B` is given by + + .. MATH:: + + D(b) = H(b \otimes b^{\sharp}) - H(u_B \otimes b^{\sharp}), + + where `H` is the :meth:`local energy function + `. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['D',4,1], 2,1) + sage: for x in K.classically_highest_weight_vectors(): + ....: x, x.energy_function() + ([], 1) + ([[1], [2]], 0) + + sage: K = crystals.KirillovReshetikhin(['D',4,3], 1,2) + sage: for x in K.classically_highest_weight_vectors(): + ....: x, x.energy_function() + ([], 2) + ([[1]], 1) + ([[1, 1]], 0) + """ + B = self.parent() + bsharp = B.b_sharp() + T = B.tensor(B) + H = B.local_energy_function(B) + return H(T(self, bsharp)) - H(T(B.maximal_vector(), bsharp)) + + class TensorProducts(TensorProductsCategory): + """ + The category of tensor products of Kirillov-Reshetikhin crystals. + """ + @cached_method + def extra_super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.loop_crystals import KirillovReshetikhinCrystals + sage: KirillovReshetikhinCrystals().TensorProducts().extra_super_categories() + [Category of finite regular loop crystals] + """ + return [RegularLoopCrystals().Finite()] + + class ParentMethods: + @cached_method + def maximal_vector(self): + """ + Return the maximal vector of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: T.maximal_vector() + [[[1]], [[1]], [[1]]] + """ + return self(*[K.maximal_vector() for K in self.crystals]) + + @cached_method + def classically_highest_weight_vectors(self): + r""" + Return the classically highest weight elements of ``self``. + + This works by using a backtracking algorithm since if + `b_2 \otimes b_1` is classically highest weight then `b_1` + is classically highest weight. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: T.classically_highest_weight_vectors() + ([[[1]], [[1]], [[1]]], + [[[2]], [[1]], [[1]]], + [[[1]], [[2]], [[1]]], + [[[3]], [[2]], [[1]]]) + """ + n = len(self.crystals) + I0 = self.cartan_type().classical().index_set() + it = [ iter(self.crystals[-1].classically_highest_weight_vectors()) ] + path = [] + ret = [] + while it: + try: + x = next(it[-1]) + except StopIteration: + it.pop() + if path: + path.pop(0) + continue + + b = self.element_class(self, [x] + path) + if not b.is_highest_weight(index_set=I0): + continue + path.insert(0, x) + if len(path) == n: + ret.append(b) + path.pop(0) + else: + it.append( iter(self.crystals[-len(path)-1]) ) + return tuple(ret) + + # TODO: This is duplicated in KR crystals category + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) + sage: RC.cardinality() + 100 + sage: len(RC.list()) + 100 + + sage: RC = RiggedConfigurations(['E', 7, 1], [[1,1]]) + sage: RC.cardinality() + 134 + sage: len(RC.list()) + 134 + + sage: RC = RiggedConfigurations(['B', 3, 1], [[2,2],[1,2]]) + sage: RC.cardinality() + 5130 + """ + CWLR = self.cartan_type().classical().root_system().ambient_space() + return sum(CWLR.weyl_dimension(mg.classical_weight()) + for mg in self.classically_highest_weight_vectors()) + + def one_dimensional_configuration_sum(self, q=None, group_components=True): + r""" + Compute the one-dimensional configuration sum of ``self``. + + INPUT: + + - ``q`` -- (default: ``None``) a variable or ``None``; + if ``None``, a variable `q` is set in the code + - ``group_components`` -- (default: ``True``) boolean; if + ``True``, then the terms are grouped by classical component + + The one-dimensional configuration sum is the sum of the + weights of all elements in the crystal weighted by the + energy function. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: T.one_dimensional_configuration_sum() + B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] + sage: R. = ZZ[] + sage: T.one_dimensional_configuration_sum(t, False) + B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + + (t+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: LS.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum() # long time + True + + TESTS:: + + sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) + sage: T = crystals.TensorProduct(K1,K2) + sage: T.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum(group_components=False) + True + + sage: RC = RiggedConfigurations(['A',3,1],[[1,1],[1,2]]) + sage: B = crystals.KirillovReshetikhin(['A',3,1],1,1) + sage: B1 = crystals.KirillovReshetikhin(['A',3,1],1,2) + sage: T = crystals.TensorProduct(B,B1) + sage: RC.fermionic_formula() == T.one_dimensional_configuration_sum() + True + """ + if q is None: + from sage.rings.all import QQ + q = QQ['q'].gens()[0] + P0 = self.weight_lattice_realization().classical() + B = P0.algebra(q.parent()) + if group_components: + G = self.digraph(index_set=self.cartan_type().classical().index_set()) + C = G.connected_components() + return B.sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) + for c in C) + return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self) + + class ElementMethods: + def energy_function(self, algorithm=None): + r""" + Return the energy function of ``self``. + + ALGORITHM: + + .. RUBRIC:: definition + + Let `T` be a tensor product of Kirillov-Reshetikhin + crystals. Let `R_i` and `H_i` be the combinatorial + `R`-matrix and local energy functions, respectively, acting + on the `i` and `i+1` factors. Let `D_B` be the energy + function of a single Kirillov-Reshetikhin crystal. The + *energy function* is given by + + .. MATH:: + + D = \sum_{j > i} H_i R_{i+1} R_{i+2} \cdots R_{j-1} + + \sum_j D_B R_1 R_2 \cdots R_{j-1}, + + where `D_B` acts on the rightmost factor. + + .. RUBRIC:: grading + + If ``self`` is an element of `T`, a tensor product of + perfect crystals of the same level, then use the affine + grading to determine the energy. Specifically, let `g` + denote the affine grading of ``self`` and `d` the affine + grading of the maximal vector in `T`. Then the energy + of ``self`` is given by `d - g`. + + For more details, see Theorem 7.5 in [ST2011]_. + + INPUT: + + - ``algorithm`` -- (default: ``None``) use one of the + following algorithms to determine the energy function: + + * ``'definition'`` - use the definition of the energy + function; + * ``'grading'`` - use the affine grading; + + if not specified, then this uses ``'grading'`` if all + factors are perfect of the same level and otherwise + this uses ``'definition'`` + + OUTPUT: an integer + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.energy_function() + [[[1]], [[1]], [[1]]] 0 + [[[2]], [[1]], [[1]]] 1 + [[[1]], [[2]], [[1]]] 2 + [[[3]], [[2]], [[1]]] 3 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: T = crystals.TensorProduct(K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.energy_function() + [[], []] 4 + [[[1, 1]], []] 3 + [[], [[1, 1]]] 1 + [[[1, 1]], [[1, 1]]] 0 + [[[1, 2]], [[1, 1]]] 1 + [[[2, 2]], [[1, 1]]] 2 + [[[-1, -1]], [[1, 1]]] 2 + [[[1, -1]], [[1, 1]]] 2 + [[[2, -1]], [[1, 1]]] 2 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: T = crystals.TensorProduct(K) + sage: t = T.module_generators[0] + sage: t.energy_function('grading') + Traceback (most recent call last): + ... + NotImplementedError: all crystals in the tensor product + need to be perfect of the same level + + TESTS:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2, 2) + sage: T = tensor([K, K2]) + sage: hw = T.classically_highest_weight_vectors() + sage: all(b.energy_function() == b.energy_function(algorithm='definition') + ....: for b in hw) + True + """ + C = self.parent().crystals[0] + ell = ceil(C.s()/C.cartan_type().c()[C.r()]) + is_perfect = all(ell == K.s()/K.cartan_type().c()[K.r()] + for K in self.parent().crystals) + if algorithm is None: + if is_perfect: + algorithm = 'grading' + else: + algorithm = 'definition' + + if algorithm == 'grading': + if not is_perfect: + raise NotImplementedError("all crystals in the tensor product need to be perfect of the same level") + d = self.parent().maximal_vector().affine_grading() + return d - self.affine_grading() + + if algorithm == 'definition': + # Setup + energy = ZZ.zero() + R_mats = [[K.R_matrix(Kp) for Kp in self.parent().crystals[i+1:]] + for i,K in enumerate(self.parent().crystals)] + H_funcs = [[K.local_energy_function(Kp) for Kp in self.parent().crystals[i+1:]] + for i,K in enumerate(self.parent().crystals)] + + for i,b in enumerate(self): + for j,R in enumerate(R_mats[i]): + H = H_funcs[i][j] + bp = self[i+j+1] + T = R.domain() + t = T(b, bp) + energy += H(t) + b = R(t)[1] + energy += b.energy_function() # D contribution + return energy + else: + raise ValueError("invalid algorithm") + + def affine_grading(self): + r""" + Return the affine grading of ``self``. + + The affine grading is calculated by finding a path + from ``self`` to a ground state path (using the helper method + :meth:`e_string_to_ground_state`) and counting the number + of affine Kashiwara operators `e_0` applied on the way. + + OUTPUT: an integer + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0] + sage: t.affine_grading() + 1 + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.affine_grading() + [[[1]], [[1]], [[1]]] 3 + [[[2]], [[1]], [[1]]] 2 + [[[1]], [[2]], [[1]]] 1 + [[[3]], [[2]], [[1]]] 0 + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.affine_grading() + [[[1]], [[1]], [[1]]] 2 + [[[2]], [[1]], [[1]]] 1 + [[[-1]], [[1]], [[1]]] 1 + [[[1]], [[2]], [[1]]] 1 + [[[-2]], [[2]], [[1]]] 0 + [[[1]], [[-1]], [[1]]] 0 + """ + return self.e_string_to_ground_state().count(0) + + @cached_method + def e_string_to_ground_state(self): + r""" + Return a string of integers in the index set + `(i_1, \ldots, i_k)` such that `e_{i_k} \cdots e_{i_1}` + of ``self`` is the ground state. + + This method calculates a path from ``self`` to a ground + state path using Demazure arrows as defined in Lemma 7.3 + in [ST2011]_. + + OUTPUT: a tuple of integers `(i_1, \ldots, i_k)` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0] + sage: t.e_string_to_ground_state() + (0, 2) + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0]; t + [[[1]], [[1]]] + sage: t.e_string_to_ground_state() + (0,) + sage: x = t.e(0) + sage: x.e_string_to_ground_state() + () + sage: y = t.f_string([1,2,1,1,0]); y + [[[2]], [[1]]] + sage: y.e_string_to_ground_state() + () + + TESTS: + + Check that :trac:`22882` is fixed:: + + sage: K = crystals.KirillovReshetikhin(CartanType(['A',6,2]).dual(), 1,1) + sage: T = tensor([K,K,K]) + sage: hw = [x for x in T if x.is_highest_weight([1,2,3])] + sage: gs = T(K(0), K(0), K(0)) + sage: all(elt.e_string(elt.e_string_to_ground_state()) == gs + ....: for elt in hw) + True + sage: all(elt.energy_function() == elt.energy_function('definition') + ....: for elt in hw) + True + """ + ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) + for K in self.parent().crystals) + if self.cartan_type().dual().type() == 'BC': + I = self.cartan_type().index_set() + for i in I[:-1]: + if self.epsilon(i) > 0: + return (i,) + (self.e(i)).e_string_to_ground_state() + if self.epsilon(I[-1]) > ell: + return (I[-1],) + (self.e(I[-1])).e_string_to_ground_state() + return () + + I = self.cartan_type().classical().index_set() + for i in I: + if self.epsilon(i) > 0: + return (i,) + self.e(i).e_string_to_ground_state() + if self.epsilon(0) > ell: + return (0,) + self.e(0).e_string_to_ground_state() + return () + + +##################################################################### +## Local energy function + +class LocalEnergyFunction(Map): + r""" + The local energy function. + + Let `B` and `B'` be Kirillov-Reshetikhin crystals with maximal + vectors `u_B` and `u_{B'}` respectively. The *local energy function* + `H : B \otimes B' \to \ZZ` is the function which satisfies + + .. MATH:: + + H(e_0(b \otimes b')) = H(b \otimes b') + \begin{cases} + 1 & \text{if } i = 0 \text{ and LL}, \\ + -1 & \text{if } i = 0 \text{ and RR}, \\ + 0 & \text{otherwise,} + \end{cases} + + where LL (resp. RR) denote `e_0` acts on the left (resp. right) + on both `b \otimes b'` and `R(b \otimes b')`, and + normalized by `H(u_B \otimes u_{B'}) = 0`. + + INPUT: + + - ``B`` -- a Kirillov-Reshetikhin crystal + - ``Bp`` -- a Kirillov-Reshetikhin crystal + - ``normalization`` -- (default: 0) the normalization value + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2,1) + sage: H = K.local_energy_function(K2) + sage: T = tensor([K, K2]) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: b, H(b) + ([[], [[1], [2]]], 1) + ([[[1, 1]], [[1], [2]]], 0) + ([[[2, -2]], [[1], [2]]], 1) + ([[[1, -2]], [[1], [2]]], 1) + + REFERENCES: + + [KKMMNN1992]_ + """ + def __init__(self, B, Bp, normalization=0): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',7,2], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['A',7,2], 2,1) + sage: H = K.local_energy_function(K2) + sage: TestSuite(H).run(skip=['_test_category', '_test_pickling']) + """ + self._B = B + self._Bp = Bp + self._R_matrix = self._B.R_matrix(self._Bp) + T = B.tensor(Bp) + self._known_values = {T(*[K.module_generator() for K in T.crystals]): + ZZ(normalization)} + self._I0 = T.cartan_type().classical().index_set() + from sage.categories.homset import Hom + Map.__init__(self, Hom(T, ZZ)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A', 6, 2], 2, 1) + sage: Kp = crystals.KirillovReshetikhin(['A', 6, 2], 1, 1) + sage: H = K.local_energy_function(Kp); H + Local energy function of + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) + tensor + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) + """ + return "Local energy function of {} tensor {}".format(self._B, self._Bp) + + def _call_(self, x): + """ + Return the local energy of ``x``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['B',4,1], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['B',4,1], 2,1) + sage: H = K.local_energy_function(K2) + sage: T = tensor([K, K2]) + sage: hw = [x for x in T if x.is_highest_weight([1,2])] + sage: H(hw[0]) + 1 + """ + # Setup variables + visited = {x: 0} + check0 = [x] + + # Helper function + def to_classical_hw(cur): + for i in self._I0: + b = cur.e(i) + if b is not None and b not in visited: + visited[b] = visited[cur] # No change + return b + return None # is classically HW or all have been visited + + cur = x + # Get the affine node (it might not be 0 if the type + # has been relabeled) + i0 = x.parent().cartan_type().special_node() + while cur not in self._known_values: + # We first go towards the classically highest weight since + # the maximal vector is classically highest weight + b = to_classical_hw(cur) + + # If classically HW, then try 0 arrows + while b is None: + b = check0.pop() + c = b.e(i0) + # If there is no 0 arrow or we have already seen c, move along + if c is None or c in visited: + b = None + continue + + bp = self._R_matrix(b) + cp = bp.e(i0) + if b[1] == c[1] and bp[1] == cp[1]: # LL case + visited[c] = visited[b] + 1 + elif b[0] == c[0] and bp[0] == cp[0]: # RR case + visited[c] = visited[b] - 1 + else: + visited[c] = visited[b] # Otherwise no change + b = c + + cur = b + check0.append(b) + + baseline = self._known_values[cur] - visited[cur] + for y in visited: + self._known_values[y] = baseline + visited[y] + + return self._known_values[x] + diff --git a/src/sage/coding/channels_catalog.py b/src/sage/coding/channels_catalog.py index e2fa3d82b4e..1ee638e949c 100644 --- a/src/sage/coding/channels_catalog.py +++ b/src/sage/coding/channels_catalog.py @@ -28,3 +28,5 @@ _lazy_import('sage.coding.channel_constructions', ['ErrorErasureChannel', 'QarySymmetricChannel', 'StaticErrorRateChannel']) +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 0f2c006ee8e..6a1bd4bdac4 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -11,6 +11,11 @@ sage: from sage.coding.codes_catalog import * +TESTS:: + + sage: import sage.coding.codes_catalog + sage: 'absolute_import' in dir(sage.coding.codes_catalog) + False """ #***************************************************************************** # Copyright (C) 2009 David Lucas @@ -28,12 +33,12 @@ # in the global namespace. from __future__ import absolute_import -from sage.misc.lazy_import import lazy_import as _lazy_import +from sage.misc.lazy_import import lazy_import from .linear_code import LinearCode from sage.coding.linear_code import LinearCode -_lazy_import('sage.coding.code_constructions', +lazy_import('sage.coding.code_constructions', ['BCHCode', 'BinaryGolayCode', 'CyclicCodeFromGeneratingPolynomial', 'CyclicCode', 'CyclicCodeFromCheckPolynomial', 'DuadicCodeEvenPair', 'DuadicCodeOddPair', 'ExtendedBinaryGolayCode', @@ -47,27 +52,29 @@ 'ReedSolomonCode', 'TernaryGolayCode', 'ToricCode', 'WalshCode']) -_lazy_import('sage.coding.bch', 'BCHCode') -_lazy_import('sage.coding.cyclic_code', 'CyclicCode') -_lazy_import('sage.coding.extended_code', 'ExtendedCode') -_lazy_import('sage.coding.golay_code', 'GolayCode') -_lazy_import('sage.coding.grs', 'GeneralizedReedSolomonCode') -_lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', +lazy_import('sage.coding.bch', 'BCHCode') +lazy_import('sage.coding.cyclic_code', 'CyclicCode') +lazy_import('sage.coding.extended_code', 'ExtendedCode') +lazy_import('sage.coding.golay_code', 'GolayCode') +lazy_import('sage.coding.grs', 'GeneralizedReedSolomonCode') +lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', 'RandomLinearCodeGuava']) -_lazy_import('sage.coding.hamming_code', 'HammingCode') -_lazy_import('sage.coding.parity_check_code', 'ParityCheckCode') -_lazy_import('sage.coding.punctured_code', 'PuncturedCode') -_lazy_import('sage.coding.reed_muller_code', ['BinaryReedMullerCode', +lazy_import('sage.coding.hamming_code', 'HammingCode') +lazy_import('sage.coding.parity_check_code', 'ParityCheckCode') +lazy_import('sage.coding.punctured_code', 'PuncturedCode') +lazy_import('sage.coding.reed_muller_code', ['BinaryReedMullerCode', 'ReedMullerCode']) -_lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') +lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') from . import decoders_catalog as decoders from . import encoders_catalog as encoders from . import bounds_catalog as bounds -_lazy_import('sage.coding','databases') +lazy_import('sage.coding','databases') + +from sage.misc.rest_index_of_methods import gen_rest_table_index +import sys +__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__], only_local_functions=False)) -del _lazy_import -from sage.misc.rest_index_of_methods import gen_rest_table_index as _gen_rest_table_index -import sys as _sys -__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=_gen_rest_table_index(_sys.modules[__name__], only_local_functions=False)) +# We don't want this to appear in tab completion +del absolute_import, lazy_import, sys, gen_rest_table_index diff --git a/src/sage/combinat/crystals/affine.py b/src/sage/combinat/crystals/affine.py index a90ceffd1c1..286784f5174 100644 --- a/src/sage/combinat/crystals/affine.py +++ b/src/sage/combinat/crystals/affine.py @@ -13,8 +13,7 @@ #**************************************************************************** from sage.misc.abstract_method import abstract_method -from sage.categories.regular_crystals import RegularCrystals -from sage.categories.finite_crystals import FiniteCrystals +from sage.categories.loop_crystals import RegularLoopCrystals from sage.structure.element import parent from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -82,7 +81,7 @@ def __classcall__(cls, cartan_type, *args, **options): ct = CartanType(cartan_type) return super(AffineCrystalFromClassical, cls).__classcall__(cls, ct, *args, **options) - def __init__(self, cartan_type, classical_crystal, category = None): + def __init__(self, cartan_type, classical_crystal, category=None): """ Input is an affine Cartan type ``cartan_type``, a classical crystal ``classical_crystal``, and automorphism and its inverse @@ -113,7 +112,7 @@ def __init__(self, cartan_type, classical_crystal, category = None): sage: TestSuite(A).run() """ if category is None: - category = (RegularCrystals(), FiniteCrystals()) + category = RegularLoopCrystals() self._cartan_type = cartan_type Parent.__init__(self, category = category) self.classical_crystal = classical_crystal; @@ -133,6 +132,20 @@ def _repr_(self): """ return "An affine crystal for type {}".format(self.cartan_type()) + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3],shape=[1]) + sage: pr = attrcall("promotion") + sage: pr_inverse = attrcall("promotion_inverse") + sage: A = crystals.AffineFromClassicalAndPromotion(['A',3,1],C,pr,pr_inverse,1) + sage: A.cardinality() == C.cardinality() + True + """ + return self.classical_crystal.cardinality() def __iter__(self): r""" @@ -566,7 +579,7 @@ class AffineCrystalFromClassicalAndPromotion(AffineCrystalFromClassical): [[[2]], [[1]], [[3]]] """ - def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_automorphism, dynkin_node): + def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_automorphism, dynkin_node, category=None): """ Input is an affine Cartan type ``cartan_type``, a classical crystal ``classical_crystal``, and promotion automorphism and its inverse @@ -591,7 +604,7 @@ def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_aut sage: TestSuite(A).run() """ - AffineCrystalFromClassical.__init__(self, cartan_type, classical_crystal) + AffineCrystalFromClassical.__init__(self, cartan_type, classical_crystal, category) self.p_automorphism = p_automorphism self.p_inverse_automorphism = p_inverse_automorphism self.dynkin_node = dynkin_node diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 8cced3900d1..e1dbce27e3b 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -92,8 +92,8 @@ def __init__(self, B): self._B = B self._cartan_type = B.cartan_type() Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) - mg_elt = lambda b: self.element_class(self, b, 0) - self.module_generators = tuple(map(mg_elt, B.module_generators)) + self.module_generators = tuple([self.element_class(self, b, 0) + for b in B.module_generators]) def _repr_(self): """ @@ -106,37 +106,6 @@ def _repr_(self): """ return "Affinization of {}".format(self._B) - def weight_lattice_realization(self): - """ - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: A = crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() - sage: A.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 2, 1] - """ - return self.cartan_type().root_system().weight_lattice(extended=True) - - # TODO: This should become unnecessary once we have a proper category for KR crystals - def digraph(self, subset=None, index_set=None): - """ - Return the DiGraph associated with ``self``. See - :meth:`~sage.categories.crystals.ParentMethods.digraph()` for more - information. - - EXAMPLES:: - - sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() - sage: S = A.subcrystal(max_depth=3) - sage: G = A.digraph(subset=S) - """ - G = super(AffinizationOfCrystal, self).digraph(subset, index_set) - from sage.graphs.dot2tex_utils import have_dot2tex - if have_dot2tex(): - G.set_latex_options(edge_options=lambda u_v_label: ({})) - return G - class Element(Element): """ An element in an affinization crystal. @@ -195,9 +164,8 @@ def __hash__(self): sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: mg = A.module_generators[0] - sage: hash(mg) - -6948036233304877976 # 64-bit - -1420700568 # 32-bit + sage: hash(mg) == hash(mg._b) ^^ hash(mg._m) + True """ return hash(self._b) ^ hash(self._m) diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index edbb4b99d06..3a766a82c72 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -30,6 +30,7 @@ from sage.structure.sage_object import richcmp from sage.categories.finite_crystals import FiniteCrystals from sage.categories.classical_crystals import ClassicalCrystals +from sage.categories.loop_crystals import LoopCrystals from sage.graphs.all import DiGraph from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem @@ -329,11 +330,11 @@ def __init__(self, starting_weight, highest_weight_crystal): if cartan_type.is_finite() and highest_weight_crystal: - Parent.__init__(self, category=ClassicalCrystals() ) + Parent.__init__(self, category=ClassicalCrystals()) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True elif cartan_type.is_finite() and not highest_weight_crystal: - Parent.__init__(self, category=FiniteCrystals() ) + Parent.__init__(self, category=LoopCrystals().Finite()) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True self._cartan_type = cartan_type.affine() @@ -476,31 +477,6 @@ def digraph_fast(self, depth=None): return super(CrystalOfAlcovePaths, self).digraph() return super(CrystalOfAlcovePaths, self).digraph(depth=depth) - def weight_lattice_realization(self): - r""" - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: B = crystals.AlcovePaths(['A',2,1],[1,0,0]) - sage: B.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 2, 1] - - sage: C = crystals.AlcovePaths("B3",[1,0,0]) - sage: C.weight_lattice_realization() - Ambient space of the Root system of type ['B', 3] - - sage: A = crystals.AlcovePaths(['A',2,1], [1,0], highest_weight_crystal=False) - sage: A.weight_lattice_realization() - Weight lattice of the Root system of type ['A', 2, 1] - """ - F = self.cartan_type().root_system() - if self.cartan_type().is_affine(): - return F.weight_lattice(extended=self._highest_weight_crystal) - if self.cartan_type().is_finite() and F.ambient_space() is not None: - return F.ambient_space() - return F.weight_lattice() - class CrystalOfAlcovePathsElement(ElementWrapper): """ Crystal of alcove paths element. diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index d6e9cab5e7d..1141ed64bfc 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -45,6 +45,11 @@ * :class:`DirectSum ` * :class:`TensorProduct ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals) + False """ from __future__ import absolute_import @@ -78,3 +83,5 @@ from . import catalog_infinity_crystals as infinity from . import catalog_elementary_crystals as elementary +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_elementary_crystals.py b/src/sage/combinat/crystals/catalog_elementary_crystals.py index ed330816b43..5e83fe5fdb8 100644 --- a/src/sage/combinat/crystals/catalog_elementary_crystals.py +++ b/src/sage/combinat/crystals/catalog_elementary_crystals.py @@ -8,6 +8,11 @@ or :class:`B ` * :class:`R ` * :class:`T ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.elementary) + False """ from __future__ import absolute_import @@ -17,3 +22,4 @@ from .elementary_crystals import ElementaryCrystal as B from .elementary_crystals import ComponentCrystal as Component +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_infinity_crystals.py b/src/sage/combinat/crystals/catalog_infinity_crystals.py index 16543788962..573c34fa017 100644 --- a/src/sage/combinat/crystals/catalog_infinity_crystals.py +++ b/src/sage/combinat/crystals/catalog_infinity_crystals.py @@ -9,12 +9,19 @@ ` * :class:`LSPaths ` * :class:`Multisegments ` +* :class:`MVPolytopes ` * :class:`NakajimaMonomials ` +* :class:`PBW ` * :class:`PolyhedralRealization ` * :class:`RiggedConfigurations ` * :class:`Star ` * :class:`Tableaux ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.infinity) + False """ from __future__ import absolute_import @@ -24,7 +31,10 @@ from sage.combinat.rigged_configurations.rc_infinity import InfinityCrystalOfRiggedConfigurations as RiggedConfigurations from .infinity_crystals import InfinityCrystalOfTableaux as Tableaux from sage.combinat.crystals.polyhedral_realization import InfinityCrystalAsPolyhedralRealization as PolyhedralRealization +from sage.combinat.crystals.pbw_crystal import PBWCrystal as PBW +from sage.combinat.crystals.mv_polytopes import MVPolytopes from sage.combinat.crystals.star_crystal import StarCrystal as Star from sage.combinat.crystals.littelmann_path import InfinityCrystalOfLSPaths as LSPaths from sage.combinat.crystals.alcove_path import InfinityCrystalOfAlcovePaths as AlcovePaths +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py b/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py index 3d5d99a95de..00dd6ed9223 100644 --- a/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py @@ -9,6 +9,11 @@ * :func:`LSPaths ` * :func:`RiggedConfigurations ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.kirillov_reshetikhin) + False """ from __future__ import absolute_import @@ -16,3 +21,6 @@ from .kirillov_reshetikhin import KirillovReshetikhinCrystalFromLSPaths as LSPaths from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux from sage.combinat.rigged_configurations.rigged_configurations import KirillovReshetikhinCrystal as RiggedConfigurations + +# remove from tab completion +del absolute_import diff --git a/src/sage/combinat/crystals/direct_sum.py b/src/sage/combinat/crystals/direct_sum.py index c0aef6c6479..59a9e2c49f6 100644 --- a/src/sage/combinat/crystals/direct_sum.py +++ b/src/sage/combinat/crystals/direct_sum.py @@ -53,7 +53,7 @@ class DirectSumOfCrystals(DisjointUnionEnumeratedSets): sage: [b.f(1) for b in B] [2, None, None, None, [[2], [3]], None] sage: B.module_generators - [1, [[1], [2]]] + (1, [[1], [2]]) :: @@ -61,7 +61,7 @@ class DirectSumOfCrystals(DisjointUnionEnumeratedSets): sage: B.list() [(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3)] sage: B.module_generators - [(0, 1), (1, 1)] + ((0, 1), (1, 1)) sage: b = B( tuple([0,C(1)]) ) sage: b (0, 1) @@ -105,10 +105,7 @@ def __classcall_private__(cls, crystals, facade=True, keepkey=False, category=No if not isinstance(facade, bool) or not isinstance(keepkey, bool): raise TypeError # Normalize the facade-keepkey by giving keepkey dominance - if keepkey: - facade = False - else: - facade = True + facade = not keepkey # We expand out direct sums of crystals ret = [] @@ -136,24 +133,21 @@ def __init__(self, crystals, facade, keepkey, category, **options): sage: isinstance(B, DirectSumOfCrystals) True """ - if facade: - Parent.__init__(self, facade=tuple(crystals), category=category) - else: - Parent.__init__(self, category=category) - DisjointUnionEnumeratedSets.__init__(self, crystals, keepkey=keepkey, facade=facade) + DisjointUnionEnumeratedSets.__init__(self, crystals, keepkey=keepkey, + facade=facade, category=category) self.rename("Direct sum of the crystals {}".format(crystals)) self._keepkey = keepkey self.crystals = crystals if len(crystals) == 0: - raise ValueError("The direct sum is empty") + raise ValueError("the direct sum is empty") else: assert(crystal.cartan_type() == crystals[0].cartan_type() for crystal in crystals) self._cartan_type = crystals[0].cartan_type() if keepkey: - self.module_generators = [ self(tuple([i,b])) for i in range(len(crystals)) - for b in crystals[i].module_generators ] + self.module_generators = tuple([ self((i,b)) for i,B in enumerate(crystals) + for b in B.module_generators ]) else: - self.module_generators = sum( (list(B.module_generators) for B in crystals), []) + self.module_generators = sum((tuple(B.module_generators) for B in crystals), ()) def weight_lattice_realization(self): r""" @@ -183,12 +177,11 @@ def weight_lattice_realization(self): class Element(ElementWrapper): r""" - A class for elements of direct sums of crystals + A class for elements of direct sums of crystals. """ - def e(self, i): r""" - Returns the action of `e_i` on self. + Return the action of `e_i` on ``self``. EXAMPLES:: @@ -206,7 +199,7 @@ def e(self, i): def f(self, i): r""" - Returns the action of `f_i` on self. + Return the action of `f_i` on ``self``. EXAMPLES:: @@ -224,7 +217,7 @@ def f(self, i): def weight(self): r""" - Returns the weight of self. + Return the weight of ``self``. EXAMPLES:: diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index 9f2d10eb0d1..a5153cafb5c 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -468,25 +468,13 @@ class RCrystal(UniqueRepresentation, Parent): sage: T = crystals.TensorProduct(R, B) sage: mg = T(R.highest_weight_vector(), B.highest_weight_vector()) sage: S = T.subcrystal(generators=[mg]) - sage: for x in S: x.weight() - (2, 1, 0) - (2, 0, 1) - (1, 2, 0) - (1, 1, 1) - (1, 1, 1) - (1, 0, 2) - (0, 2, 1) - (0, 1, 2) + sage: sorted([x.weight() for x in S], key=str) + [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 1, 1), + (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0)] sage: C = crystals.Tableaux("A2", shape=[2,1]) - sage: for x in C: x.weight() - (2, 1, 0) - (1, 2, 0) - (1, 1, 1) - (1, 0, 2) - (0, 1, 2) - (2, 0, 1) - (1, 1, 1) - (0, 2, 1) + sage: sorted([x.weight() for x in C], key=str) + [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 1, 1), + (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0)] sage: GT = T.digraph(subset=S) sage: GC = C.digraph() sage: GT.is_isomorphic(GC, edge_labels=True) diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index e0454309d1f..dee445ca766 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -787,18 +787,6 @@ def _repr_(self): """ return "Crystal of generalized Young walls of type {}".format(self._cartan_type) - def weight_lattice_realization(self): - r""" - Return the extended affine weight lattice of ``self``. - - EXAMPLES:: - - sage: Y = crystals.infinity.GeneralizedYoungWalls(3) - sage: Y.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 3, 1] - """ - return RootSystem(self._cartan_type).weight_lattice(extended=True) - ######################## ## Highest weight GYW ## diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index a0202182eea..84bedef47d8 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -37,7 +37,9 @@ from sage.combinat.partition import Partition from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.letters import CrystalOfLetters -from sage.combinat.crystals.tensor_product import CrystalOfWords, CrystalOfTableauxElement +from sage.combinat.crystals.tensor_product import CrystalOfWords +from sage.combinat.crystals.tensor_product_element import (CrystalOfTableauxElement, + InfinityCrystalOfTableauxElement, InfinityCrystalOfTableauxElementTypeD) class InfinityCrystalOfTableaux(CrystalOfWords): @@ -298,94 +300,10 @@ def _coerce_map_from_(self, P): return FromRCIsomorphism(Hom(P, self)) return super(InfinityCrystalOfTableaux, self)._coerce_map_from_(P) - class Element(CrystalOfTableauxElement): + class Element(InfinityCrystalOfTableauxElement): r""" Elements in `\mathcal{B}(\infty)` crystal of tableaux. """ - - def e(self,i): - r""" - Return the action of `\widetilde{e}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['B',3]) - sage: b = B(rows=[[1,1,1,1,1,1,1,2,0,-3,-1,-1,-1,-1],[2,2,2,2,-2,-2],[3,-3,-3]]) - sage: b.e(3).pp() - 1 1 1 1 1 1 1 2 0 -3 -1 -1 -1 -1 - 2 2 2 2 -2 -2 - 3 0 -3 - sage: b.e(1).pp() - 1 1 1 1 1 1 1 0 -3 -1 -1 -1 -1 - 2 2 2 2 -2 -2 - 3 -3 -3 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - ret = self.set_index(k, self[k].e(i)) - if k+i > len(self): - return ret - for j in reversed(range(1, i+1)): - if ret[k+i-j].value != j: - return ret - # We've found a column, so we need to remove it - for j in range(i): - ret._list.pop(k) - return ret - - def f(self, i): - r""" - Return the action of `\widetilde{f}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['C',4]) - sage: b = B.highest_weight_vector() - sage: b.f(1).pp() - 1 1 1 1 2 - 2 2 2 - 3 3 - 4 - sage: b.f(3).pp() - 1 1 1 1 1 - 2 2 2 2 - 3 3 4 - 4 - sage: b.f(3).f(4).pp() - 1 1 1 1 1 - 2 2 2 2 - 3 3 -4 - 4 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_minus(i) - if position == []: - return None - k = position[len(position)-1] - ret = self.set_index(k, self[k].f(i)) - if k+i > len(self): - return ret - for j in reversed(range(1,i+1)): - if self[k+i-j].value != j: - return ret - # We've found a full column, so we'll need to add a new column - for j in range(i): - ret._list.insert(k,self.parent().letters(j+1)) - return ret - def phi(self,i): r""" Return `\varphi_i` of ``self``. @@ -719,79 +637,9 @@ def module_generator(self): module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n-1)]) return self(list=[self.letters(x) for x in module_generator]) - class Element(InfinityCrystalOfTableaux.Element): + class Element(InfinityCrystalOfTableauxElementTypeD, InfinityCrystalOfTableaux.Element): r""" Elements in `\mathcal{B}(\infty)` crystal of tableaux for type `D_n`. """ - def e(self, i): - r""" - Return the action of `\widetilde{e}_i` on ``self``. - - INPUT: + pass - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['D',4]) - sage: b = B.highest_weight_vector().f_string([1,4,3,1,2]); b.pp() - 1 1 1 1 2 3 - 2 2 2 - 3 -3 - sage: b.e(2).pp() - 1 1 1 1 2 2 - 2 2 2 - 3 -3 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - ret = self.set_index(k, self[k].e(i)) - if i == self.cartan_type().rank(): - i -= 1 - if k+i > len(self): - return ret - for j in reversed(range(1, i+1)): - if ret[k+i-j].value != j: - return ret - # We've found a column, so we need to remove it - for j in range(i): - ret._list.pop(k) - return ret - - def f(self, i): - r""" - Return the action of `\widetilde{f}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['D',5]) - sage: b = B.highest_weight_vector().f_string([1,4,3,1,5]); b.pp() - 1 1 1 1 1 1 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 5 - sage: b.f(1).pp() - 1 1 1 1 1 1 2 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 5 - sage: b.f(5).pp() - 1 1 1 1 1 1 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 -4 - """ - ret = InfinityCrystalOfTableaux.Element.f(self, i) - if ret._list[0].value == -self.cartan_type().rank(): - # Exceptional case for f_n where we need to add a new column - for j in range(i-1): - ret._list.insert(0,self.parent().letters(j+1)) - return ret diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 8eac1105507..f66fe729df5 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -25,28 +25,27 @@ from six.moves import range from sage.misc.cachefunc import cached_method -from sage.misc.abstract_method import abstract_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.functional import is_even, is_odd -from sage.functions.other import floor, ceil +from sage.functions.other import floor from sage.combinat.combinat import CombinatorialObject from sage.structure.parent import Parent from sage.categories.crystals import CrystalMorphism -from sage.categories.regular_crystals import RegularCrystals -from sage.categories.finite_crystals import FiniteCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.categories.homset import Hom from sage.categories.map import Map from sage.rings.integer import Integer from sage.rings.all import QQ -from sage.combinat.crystals.affine import AffineCrystalFromClassical, \ - AffineCrystalFromClassicalElement, AffineCrystalFromClassicalAndPromotion, \ - AffineCrystalFromClassicalAndPromotionElement +from sage.combinat.crystals.affine import (AffineCrystalFromClassical, + AffineCrystalFromClassicalElement, + AffineCrystalFromClassicalAndPromotion, + AffineCrystalFromClassicalAndPromotionElement) from sage.combinat.crystals.highest_weight_crystals import HighestWeightCrystal from sage.combinat.crystals.littelmann_path import CrystalOfProjectedLevelZeroLSPaths from sage.combinat.crystals.direct_sum import DirectSumOfCrystals from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.combinat.crystals.tensor_product import CrystalOfTableaux, TensorProductOfCrystals +from sage.combinat.crystals.tensor_product import CrystalOfTableaux from sage.combinat.tableau import Tableau from sage.combinat.partition import Partition, Partitions from sage.combinat.integer_vector import IntegerVectors @@ -446,7 +445,7 @@ class KirillovReshetikhinGenericCrystal(AffineCrystalFromClassical): ``cartan_type``. """ - def __init__(self, cartan_type, r, s, dual = None): + def __init__(self, cartan_type, r, s, dual=None): r""" Initializes a generic Kirillov-Reshetikhin crystal. @@ -460,8 +459,8 @@ def __init__(self, cartan_type, r, s, dual = None): sage: K.s() 1 """ - # We need this here for the classical_decomposition() call - Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals())) + # We need this here for the classic al_decomposition() call + Parent.__init__(self, category=KirillovReshetikhinCrystals()) if dual is None: self._cartan_type = cartan_type else: @@ -469,7 +468,8 @@ def __init__(self, cartan_type, r, s, dual = None): self._r = r self._s = s self._dual = dual - AffineCrystalFromClassical.__init__(self, cartan_type, self.classical_decomposition()) + AffineCrystalFromClassical.__init__(self, cartan_type, self.classical_decomposition(), + KirillovReshetikhinCrystals()) def _repr_(self): """ @@ -487,7 +487,7 @@ def _element_constructor_(self, *args, **options): EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['A', 4, 1], 2, 1) - sage: K(columns=[[2,1]]) # indirect doctest + sage: K(columns=[[2,1]]) [[1], [2]] """ from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableauxElement @@ -496,7 +496,7 @@ def _element_constructor_(self, *args, **options): # Check to make sure it can be converted if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") + raise ValueError("the Kirillov-Reshetikhin tableau must have the same Cartan type and shape") to_hw = elt.to_classical_highest_weight() rows = [] @@ -509,21 +509,10 @@ def _element_constructor_(self, *args, **options): return hw_elt.f_string(f_str) return AffineCrystalFromClassical._element_constructor_(self, *args, **options) - @abstract_method - def classical_decomposition(self): - """ - Return the classical decomposition of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,2) - sage: K.classical_decomposition() - The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] - """ - def module_generator(self): r""" - Returns the unique module generator of classical weight `s \Lambda_r` of a Kirillov-Reshetikhin crystal `B^{r,s}` + Return the unique module generator of classical weight + `s \Lambda_r` of a Kirillov-Reshetikhin crystal `B^{r,s}` EXAMPLES:: @@ -543,11 +532,11 @@ def module_generator(self): r = self.r() s = self.s() weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() - return [ b for b in self.module_generators if b.weight() == weight][0] + return [b for b in self.module_generators if b.weight() == weight][0] def r(self): """ - Returns r of the underlying Kirillov-Reshetikhin crystal `B^{r,s}` + Return `r` of the underlying Kirillov-Reshetikhin crystal `B^{r,s}`. EXAMPLES:: @@ -559,7 +548,7 @@ def r(self): def s(self): """ - Returns s of the underlying Kirillov-Reshetikhin crystal `B^{r,s}` + Return `s` of the underlying Kirillov-Reshetikhin crystal `B^{r,s}`. EXAMPLES:: @@ -569,136 +558,20 @@ def s(self): """ return self._s - def is_perfect(self): - r""" - Returns True or False depending on whether ``self`` is a perfect crystal or not, respectively. - - If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, then it was proven in [FOS2010]_ - that it is perfect if and only if `s/c_r` is an integer (where `c_r` is a constant related to the - type of the crystal). - - REFERENCES: - - .. [FOS2010] \G. Fourier, M. Okado, A. Schilling. - Perfectness of Kirillov-Reshetikhin crystals for nonexceptional types - Contemp. Math. 506 (2010) 127-143 ( arXiv:0811.1604 [math.RT] ) - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.is_perfect() - True - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: K.is_perfect() - False - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K.is_perfect() - True + @cached_method + def classically_highest_weight_vectors(self): """ - x = self.s()/self.cartan_type().c()[self.r()] - return x - ceil(x) == 0 - - def level(self): - r""" - Returns the level of ``self`` assuming that it is a perfect crystal. - - If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, then it was proven in [FOS2010]_ - that its level is `s/c_r` which is an integer if ``self`` is perfect - (here `c_r` is a constant related to the type of the crystal). + Return the classically highest weight vectors of ``self``. EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.level() - 1 - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K.level() - 1 - sage: K = crystals.KirillovReshetikhin(['D',4,1], 1, 3) - sage: K.level() - 3 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: K.level() - Traceback (most recent call last): - ... - ValueError: this crystal is not perfect + sage: K = crystals.KirillovReshetikhin(['D', 4, 1], 2, 2) + sage: K.classically_highest_weight_vectors() + ([], [[1], [2]], [[1, 1], [2, 2]]) """ - if not self.is_perfect(): - raise ValueError("this crystal is not perfect") - return self.s()/self.cartan_type().c()[self.r()] - - @cached_method - def R_matrix(self, K): - r""" - INPUT: + return tuple([self.retract(mg) + for mg in self.classical_decomposition().module_generators]) - - ``self`` -- a crystal `L` - - ``K`` -- a Kirillov-Reshetikhin crystal of the same type as `L` - - Returns the *combinatorial `R`-matrix* from `L \otimes K \to K - \otimes L`, where the combinatorial `R`-matrix is the affine - crystal isomorphism which maps `u_{L} \otimes u_K` to `u_K - \otimes u_{L}`, where `u_K` is the unique element in `K = - B^{r,s}` of weight `s\Lambda_r - s c \Lambda_0` (see - module_generator). - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: L = crystals.KirillovReshetikhin(['A',2,1],1,2) - sage: f = K.R_matrix(L) - sage: [[b,f(b)] for b in crystals.TensorProduct(K,L)] - [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]], - [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]], - [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]], - [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]], - [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]], - [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]], - [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]], - [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]], - [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]], - [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]], - [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]], - [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]], - [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]], - [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]], - [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]], - [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]], - [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]], - [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]] - - sage: K = crystals.KirillovReshetikhin(['D',4,1],1,1) - sage: L = crystals.KirillovReshetikhin(['D',4,1],2,1) - sage: f = K.R_matrix(L) - sage: T = crystals.TensorProduct(K,L) - sage: b = T( K(rows=[[1]]), L(rows=[]) ) - sage: f(b) - [[[2], [-2]], [[1]]] - - Alternatively, one can compute the combinatorial `R`-matrix using the isomorphism method - of digraphs:: - - sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) - sage: T1 = crystals.TensorProduct(K1,K2) - sage: T2 = crystals.TensorProduct(K2,K1) - sage: T1.digraph().is_isomorphic(T2.digraph(), edge_labels = True, certificate = True) #todo: not implemented (see #10904 and #10549) - (True, {[[[1]], [[2], [3]]]: [[[1], [3]], [[2]]], [[[3]], [[2], [3]]]: [[[2], [3]], [[3]]], - [[[3]], [[1], [3]]]: [[[1], [3]], [[3]]], [[[1]], [[1], [3]]]: [[[1], [3]], [[1]]], [[[1]], - [[1], [2]]]: [[[1], [2]], [[1]]], [[[2]], [[1], [2]]]: [[[1], [2]], [[2]]], [[[3]], - [[1], [2]]]: [[[2], [3]], [[1]]], [[[2]], [[1], [3]]]: [[[1], [2]], [[3]]], [[[2]], [[2], [3]]]: [[[2], [3]], [[2]]]}) - """ - T1 = TensorProductOfCrystals(self, K) - T2 = TensorProductOfCrystals(K, self) - gen1 = T1( self.module_generator(), K.module_generator() ) - gen2 = T2( K.module_generator(), self.module_generator() ) - g = { gen1 : gen2 } - return T1.crystal_morphism(g, check=False) - - @cached_method def kirillov_reshetikhin_tableaux(self): """ Return the corresponding set of @@ -713,97 +586,6 @@ def kirillov_reshetikhin_tableaux(self): from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux return KirillovReshetikhinTableaux(self.cartan_type(), self._r, self._s) - def affinization(self): - """ - Return the corresponding affinization crystal of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.affinization() - Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) - """ - from sage.combinat.crystals.affinization import AffinizationOfCrystal - return AffinizationOfCrystal(self) - - def q_dimension(self, q=None, prec=None, use_product=False): - """ - Return the `q`-dimension of ``self``. - - The `q`-dimension of a KR crystal is defined as the `q`-dimension of - the underlying classical crystal. - - EXAMPLES:: - - sage: KRC = crystals.KirillovReshetikhin(['A',2,1], 2,2) - sage: KRC.q_dimension() - q^4 + q^3 + 2*q^2 + q + 1 - sage: KRC = crystals.KirillovReshetikhin(['D',4,1], 2,1) - sage: KRC.q_dimension() - q^10 + q^9 + 3*q^8 + 3*q^7 + 4*q^6 + 4*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 2 - """ - return self.classical_decomposition().q_dimension(q, prec, use_product) - - @cached_method - def local_energy_function(self, B): - r""" - Return the local energy function of ``self`` and ``B``. - - See - :class:`~sage.combinat.crystals.tensor_product.LocalEnergyFunction` - for a definition. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) - sage: Kp = crystals.KirillovReshetikhin(['A',6,2], 1,1) - sage: H = K.local_energy_function(Kp); H - Local energy function of - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) - tensor - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) - """ - from sage.combinat.crystals.tensor_product import LocalEnergyFunction - return LocalEnergyFunction(self, B) - - @cached_method - def b_sharp(self): - r""" - Return the element `b^{\sharp}` of ``self``. - - Let `B` be a KR crystal. The element `b^{\sharp}` is the unique - element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with - `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) - sage: K.b_sharp() - [] - sage: K.b_sharp().Phi() - Lambda[0] - - sage: K = crystals.KirillovReshetikhin(['C',3,1], 1,3) - sage: K.b_sharp() - [[-1]] - sage: K.b_sharp().Phi() - 2*Lambda[0] - - sage: K = crystals.KirillovReshetikhin(['D',6,2], 2,2) - sage: K.b_sharp() # long time - [] - sage: K.b_sharp().Phi() # long time - 2*Lambda[0] - """ - ell = float('inf') - bsharp = None - for b in self: - phi = b.Phi() - if phi.support() == [0] and phi[0] < ell: - bsharp = b - ell = phi[0] - return bsharp - class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement): """ Abstract class for all Kirillov-Reshetikhin crystal elements. @@ -895,62 +677,23 @@ def lusztig_involution(self): li = self.lift().lusztig_involution() return self.parent().retract(li) - @cached_method - def energy_function(self): - r""" - Return the energy function of ``self``. - - Let `B` be a KR crystal. Let `b^{\sharp}` denote the unique - element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with - `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. Let - `u_B` denote the maximal element of `B`. The *energy* of - `b \in B` is given by - - .. MATH:: - - D(b) = H(b \otimes b^{\sharp}) - H(u_B \otimes b^{\sharp}), - - where `H` is the :meth:`local energy function - `. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['D',4,1], 2,1) - sage: for x in K: - ....: if x.is_highest_weight([1,2,3,4]): - ....: x, x.energy_function() - ([], 1) - ([[1], [2]], 0) - - sage: K = crystals.KirillovReshetikhin(['D',4,3], 1,2) - sage: for x in K: - ....: if x.is_highest_weight([1,2]): - ....: x, x.energy_function() - ([], 2) - ([[1]], 1) - ([[1, 1]], 0) - """ - B = self.parent() - bsharp = B.b_sharp() - T = B.tensor(B) - H = B.local_energy_function(B) - return H(T(self, bsharp)) - H(T(B.module_generator(), bsharp)) - KirillovReshetikhinGenericCrystal.Element = KirillovReshetikhinGenericCrystalElement class KirillovReshetikhinCrystalFromPromotion(KirillovReshetikhinGenericCrystal, AffineCrystalFromClassicalAndPromotion): r""" - This generic class assumes that the Kirillov-Reshetikhin crystal is constructed - from a classical crystal 'classical_decomposition' and an automorphism 'promotion' and its inverse - which corresponds to a Dynkin diagram automorphism 'dynkin_diagram_automorphism'. + This generic class assumes that the Kirillov-Reshetikhin crystal is + constructed from a classical crystal using the + ``classical_decomposition`` and an automorphism ``promotion`` + and its inverse, which corresponds to a Dynkin diagram automorphism + ``dynkin_diagram_automorphism``. Each instance using this class needs to implement the methods: - - classical_decomposition - - promotion - - promotion_inverse - - dynkin_diagram_automorphism + - ``classical_decomposition`` + - ``promotion`` + - ``promotion_inverse`` + - ``dynkin_diagram_automorphism`` """ def __init__(self, cartan_type, r, s): r""" @@ -962,9 +705,12 @@ def __init__(self, cartan_type, r, s): sage: TestSuite(K).run() """ KirillovReshetikhinGenericCrystal.__init__(self, cartan_type, r, s) - AffineCrystalFromClassicalAndPromotion.__init__(self, cartan_type, self.classical_decomposition(), - self.promotion(), self.promotion_inverse(), - self.dynkin_diagram_automorphism(0)) + AffineCrystalFromClassicalAndPromotion.__init__(self, cartan_type, + self.classical_decomposition(), + self.promotion(), + self.promotion_inverse(), + self.dynkin_diagram_automorphism(0), + KirillovReshetikhinCrystals()) class KirillovReshetikhinCrystalFromPromotionElement(AffineCrystalFromClassicalAndPromotionElement, KirillovReshetikhinGenericCrystalElement): @@ -997,7 +743,8 @@ def classical_decomposition(self): sage: K.classical_decomposition() The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] """ - return CrystalOfTableaux(self.cartan_type().classical(), shape = [self.s() for i in range(1,self.r()+1)]) + return CrystalOfTableaux(self.cartan_type().classical(), + shape=[self.s()]*self.r()) @cached_method def promotion(self): @@ -1066,8 +813,9 @@ def dynkin_diagram_automorphism(self, i): class KR_type_vertical(KirillovReshetikhinCrystalFromPromotion): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `D_n^{(1)}` for `r\le n-2`, - `B_n^{(1)}` for `r0]) for r in b.to_tableau()]) outer = b.to_tableau().shape() @@ -1244,9 +1014,10 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): sage: K.from_pm_diagram_to_highest_weight_vector(pm) [[2], [-2]] """ - u = [b for b in self.classical_decomposition().module_generators if b.to_tableau().shape() == pm.outer_shape()][0] + u = [b for b in self.classical_decomposition().module_generators + if b.to_tableau().shape() == pm.outer_shape()][0] ct = self.cartan_type() - rank = ct.rank()-1 + rank = ct.rank() - 1 ct_type = ct.classical().type() assert ct_type in ['B', 'C', 'D'] ulist = [] @@ -1254,11 +1025,11 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): ulist += list(range(1, h + 1)) for h in pm.heights_of_minus(): if ct_type == 'D': - ulist += list(range(1,rank+1))+[rank-2-k for k in range(rank-1-h)] + ulist += list(range(1,rank+1)) + [rank-2-k for k in range(rank-1-h)] elif ct_type == 'B': - ulist += list(range(1,rank+1))+[rank-k for k in range(rank+1-h)] + ulist += list(range(1,rank+1)) + [rank-k for k in range(rank+1-h)] else: - ulist += list(range(1,rank+1))+[rank-1-k for k in range(rank-h)] + ulist += list(range(1,rank+1)) + [rank-1-k for k in range(rank-h)] for i in reversed(ulist): u = u.f(i) return u @@ -1331,19 +1102,22 @@ def classical_decomposition(self): """ La = self.cartan_type().classical().root_system().weight_lattice().fundamental_weights() if self.r() in [1,6]: - dw = [self.s()*La[self.r()]] + dw = [self.s() * La[self.r()]] elif self.r() == 2: - dw = sum( ([k*La[2]] for k in range(self.s()+1)), []) + dw = [k*La[2] for k in range(self.s()+1)] else: - raise ValueError - return DirectSumOfCrystals([HighestWeightCrystal(dominant_weight) for dominant_weight in dw], keepkey = False) + raise NotImplementedError + return DirectSumOfCrystals([HighestWeightCrystal(dominant_weight) + for dominant_weight in dw], + keepkey=False) def dynkin_diagram_automorphism(self, i): r""" - Specifies the Dynkin diagram automorphism underlying the promotion action on the crystal - elements. The automorphism needs to map node 0 to some other Dynkin node. + Specifies the Dynkin diagram automorphism underlying the promotion + action on the crystal elements. - Here we use the Dynkin diagram automorphism of order 3 which maps node 0 to node 1. + Here we use the Dynkin diagram automorphism of order 3 which maps + node 0 to node 1. EXAMPLES:: @@ -1356,41 +1130,45 @@ def dynkin_diagram_automorphism(self, i): def affine_weight(self, b): r""" - Returns the affine level zero weight corresponding to the element b of the classical - crystal underlying self. For the coefficients to calculate the level, see Kac pg. 48. + Return the affine level zero weight corresponding to the element + ``b`` of the classical crystal underlying ``self``. + + For the coefficients to calculate the level, see Table Aff 1 + in [Ka1990]_. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: [K.affine_weight(x.lift()) for x in K if all(x.epsilon(i) == 0 for i in [2,3,4,5])] + sage: [K.affine_weight(x.lift()) for x in K + ....: if all(x.epsilon(i) == 0 for i in [2,3,4,5])] [(0, 0, 0, 0, 0, 0, 0), - (-2, 0, 1, 0, 0, 0, 0), - (-1, -1, 0, 0, 0, 1, 0), - (0, 0, 0, 0, 0, 0, 0), - (0, 0, 0, 0, 0, 1, -2), - (0, -1, 1, 0, 0, 0, -1), - (-1, 0, 0, 1, 0, 0, -1), - (-1, -1, 0, 0, 1, 0, -1), - (0, 0, 0, 0, 0, 0, 0), - (0, -2, 0, 1, 0, 0, 0)] - """ - simple_roots = self.cartan_type().classical().root_system().ambient_space().simple_roots() - index_set = b.parent().index_set() - weight = [ Integer(b.weight().scalar( simple_roots[i] )) for i in index_set ] - E6_coeffs = [ 1, 2, 2, 3, 2, 1 ] - return tuple( [-sum([ weight[i-1] * E6_coeffs[i-1] for i in index_set ])] + weight ) - + (-2, 0, 1, 0, 0, 0, 0), + (-1, -1, 0, 0, 0, 1, 0), + (0, 0, 0, 0, 0, 0, 0), + (0, 0, 0, 0, 0, 1, -2), + (0, -1, 1, 0, 0, 0, -1), + (-1, 0, 0, 1, 0, 0, -1), + (-1, -1, 0, 0, 1, 0, -1), + (0, 0, 0, 0, 0, 0, 0), + (0, -2, 0, 1, 0, 0, 0)] + """ + cl = self.cartan_type().classical() + simple_roots = cl.root_system().ambient_space().simple_roots() + index_set = cl.index_set() + weight = [Integer(b.weight().scalar( simple_roots[i] )) for i in index_set] + E6_coeffs = [1, 2, 2, 3, 2, 1] + return tuple([-sum(weight[i] * coeff for i,coeff in enumerate(E6_coeffs))] + weight) @cached_method def hw_auxiliary(self): r""" - Returns the `{2,3,4,5}` highest weight elements of self. + Return the `{2,3,4,5}` highest weight elements of ``self``. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) sage: K.hw_auxiliary() - [[], [[(2, -1), (1,)]], + ([], [[(2, -1), (1,)]], [[(5, -3), (-1, 3)]], [[(6, -2), (-6, 2)]], [[(5, -2, -6), (-6, 2)]], @@ -1398,27 +1176,29 @@ def hw_auxiliary(self): [[(3, -1, -6), (1,)]], [[(4, -3, -6), (-1, 3)]], [[(1, -3), (-1, 3)]], - [[(-1,), (-1, 3)]]] + [[(-1,), (-1, 3)]]) """ - return [x for x in self.classical_decomposition() if all(x.epsilon(i) == 0 for i in [2,3,4,5])] + return tuple([x for x in self.classical_decomposition() + if all(x.epsilon(i) == 0 for i in [2,3,4,5])]) @cached_method def highest_weight_dict(self): r""" - Returns a dictionary between `{1,2,3,4,5}` highest weight elements, and a tuple of affine weights and its classical component. + Return a dictionary between `\{1,2,3,4,5\}`-highest weight elements, + and a tuple of affine weights and its classical component. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: K.highest_weight_dict() - {[[(2, -1), (1,)]]: ((-2, 0, 1, 0, 0, 0, 0), 1), - [[(3, -1, -6), (1,)]]: ((-1, 0, 0, 1, 0, 0, -1), 1), - [[(6, -2), (-6, 2)]]: ((0, 0, 0, 0, 0, 0, 0), 1), - [[(5, -2, -6), (-6, 2)]]: ((0, 0, 0, 0, 0, 1, -2), 1), - []: ((0, 0, 0, 0, 0, 0, 0), 0)} + sage: sorted(K.highest_weight_dict().items(), key=str) + [([[(2, -1), (1,)]], ((-2, 0, 1, 0, 0, 0, 0), 1)), + ([[(3, -1, -6), (1,)]], ((-1, 0, 0, 1, 0, 0, -1), 1)), + ([[(5, -2, -6), (-6, 2)]], ((0, 0, 0, 0, 0, 1, -2), 1)), + ([[(6, -2), (-6, 2)]], ((0, 0, 0, 0, 0, 0, 0), 1)), + ([], ((0, 0, 0, 0, 0, 0, 0), 0))] """ hw = [x for x in self.hw_auxiliary() if x.epsilon(1) == 0] - dic = dict( ( x, tuple( [self.affine_weight(x), len(x)] ) ) for x in hw ) + dic = {x: (self.affine_weight(x), len(x)) for x in hw} assert len(hw) == len(dic) return dic @@ -1426,7 +1206,7 @@ def highest_weight_dict(self): def highest_weight_dict_inv(self): r""" Return a dictionary between a tuple of affine weights and a classical - component, and `{2,3,4,5,6}` highest weight elements. + component, and `\{2,3,4,5,6\}`-highest weight elements. EXAMPLES:: @@ -1439,24 +1219,25 @@ def highest_weight_dict_inv(self): ((0, 0, 0, 0, 0, 0, 0), 1): [[(1, -3), (-1, 3)]]} """ hw = [x for x in self.hw_auxiliary() if x.epsilon(6) == 0] - dic = dict( ( tuple( [self.affine_weight(x), len(x)] ), x ) for x in hw ) + dic = {(self.affine_weight(x), len(x)): x for x in hw} assert len(hw) == len(dic) return dic def automorphism_on_affine_weight(self, weight): r""" - Acts with the Dynkin diagram automorphism on affine weights + Act with the Dynkin diagram automorphism on affine weights as outputted by the ``affine_weight`` method. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: [[x[0], K.automorphism_on_affine_weight(x[0])] for x in K.highest_weight_dict().values()] - [[(0, 0, 0, 0, 0, 1, -2), (-2, 0, 1, 0, 0, 0, 0)], - [(-1, 0, 0, 1, 0, 0, -1), (-1, -1, 0, 0, 0, 1, 0)], + sage: [[x[0], K.automorphism_on_affine_weight(x[0])] + ....: for x in K.highest_weight_dict().values()] + [[(-1, 0, 0, 1, 0, 0, -1), (-1, -1, 0, 0, 0, 1, 0)], + [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)], [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)], [(-2, 0, 1, 0, 0, 0, 0), (0, -2, 0, 1, 0, 0, 0)], - [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)]] + [(0, 0, 0, 0, 0, 1, -2), (-2, 0, 1, 0, 0, 0, 0)]] """ f = self.dynkin_diagram_automorphism return tuple( [weight[f(f(i))] for i in self.index_set()] ) @@ -1464,19 +1245,20 @@ def automorphism_on_affine_weight(self, weight): @cached_method def promotion_on_highest_weight_vectors(self): r""" - Gives a dictionary of the promotion map on `{1,2,3,4,5}` highest - weight elements to `{2,3,4,5,6}` elements in ``self``. + Return a dictionary of the promotion map on `\{1,2,3,4,5\}`-highest + weight elements to `\{2,3,4,5,6\}`-highest weight elements + in ``self``. EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) + sage: K = crystals.KirillovReshetikhin(['E',6,1], 2, 1) sage: dic = K.promotion_on_highest_weight_vectors() - sage: dic - {[[(2, -1), (1,)]]: [[(-1,), (-1, 3)]], - [[(3, -1, -6), (1,)]]: [[(5, -3), (-1, 3)]], - [[(6, -2), (-6, 2)]]: [], - [[(5, -2, -6), (-6, 2)]]: [[(2, -1), (1,)]], - []: [[(1, -3), (-1, 3)]]} + sage: sorted(dic.items(), key=str) + [([[(2, -1), (1,)]], [[(-1,), (-1, 3)]]), + ([[(3, -1, -6), (1,)]], [[(5, -3), (-1, 3)]]), + ([[(5, -2, -6), (-6, 2)]], [[(2, -1), (1,)]]), + ([[(6, -2), (-6, 2)]], []), + ([], [[(1, -3), (-1, 3)]])] """ dic = self.highest_weight_dict() dic_inv = self.highest_weight_dict_inv() @@ -1484,8 +1266,8 @@ def promotion_on_highest_weight_vectors(self): for (weight, i) in dic.values(): dic_weight[weight] = dic_weight.get(weight, []) + [i] map_index = lambda i_list: max(i_list[1]) + min(i_list[1]) - i_list[0] - map_element = lambda x : tuple([ self.automorphism_on_affine_weight(dic[x][0]), - map_index((dic[x][1], dic_weight[dic[x][0]])) ]) + map_element = lambda x: ( self.automorphism_on_affine_weight(dic[x][0]), + map_index((dic[x][1], dic_weight[dic[x][0]])) ) return {x: dic_inv[map_element(x)] for x in dic} @cached_method @@ -1496,12 +1278,12 @@ def promotion_on_highest_weight_vectors_function(self): EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['E',6,1], 2,1) + sage: K = crystals.KirillovReshetikhin(['E',6,1], 2, 1) sage: f = K.promotion_on_highest_weight_vectors_function() sage: f(K.module_generator().lift()) [[(-1,), (-1, 3)]] """ - return lambda x : self.promotion_on_highest_weight_vectors()[x] + return self.promotion_on_highest_weight_vectors().__getitem__ @cached_method def promotion(self): @@ -1523,7 +1305,7 @@ def promotion(self): T = self.classical_decomposition() ind = [1,2,3,4,5] return CrystalDiagramAutomorphism(T, self.promotion_on_highest_weight_vectors(), ind, - automorphism=self.dynkin_diagram_automorphism) + automorphism=self.dynkin_diagram_automorphism) @cached_method def promotion_inverse(self): @@ -1545,7 +1327,8 @@ def promotion_inverse(self): class KR_type_C(KirillovReshetikhinGenericCrystal): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `C_n^{(1)}` for `r """ - def e0(self): r""" - Gives `e_0` on self by mapping self to the ambient crystal, calculating `e_1 e_0` there and - pulling the element back. + Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `e_1 e_0` there and pulling the element back. EXAMPLES:: @@ -1730,8 +1522,8 @@ def e0(self): def f0(self): r""" - Gives `f_0` on self by mapping self to the ambient crystal, calculating `f_1 f_0` there and - pulling the element back. + Return `f_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `f_1 f_0` there and pulling the element back. EXAMPLES:: @@ -1781,11 +1573,11 @@ def phi0(self): class KR_type_A2(KirillovReshetikhinGenericCrystal): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `1\le r \le n` - in the realization with classical subalgebra `B_n`. The Cartan type in this case is inputted as - the dual of `A_{2n}^{(2)}`. + Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` + for `1 \leq r \leq n` in the realization with classical subalgebra `B_n`. + The Cartan type in this case is inputted as the dual of `A_{2n}^{(2)}`. - This is an alternative implementation to :class:`KR_type_box` which uses + This is an alternative implementation to :class:`KR_type_box` that uses the classical decomposition into type `C_n` crystals. EXAMPLES:: @@ -1799,8 +1591,9 @@ class KR_type_A2(KirillovReshetikhinGenericCrystal): [[1]] sage: b.e(0) - We can now check whether the two KR crystals of type `A_4^{(2)}` (namely the KR crystal and its dual - construction) are isomorphic up to relabelling of the edges:: + We can now check whether the two KR crystals of type `A_4^{(2)}` + (namely the KR crystal and its dual construction) are isomorphic + up to relabelling of the edges:: sage: C = CartanType(['A',4,2]) sage: K = crystals.KirillovReshetikhin(C,1,1) @@ -1815,13 +1608,15 @@ class KR_type_A2(KirillovReshetikhinGenericCrystal): def classical_decomposition(self): r""" - Specifies the classical crystal underlying the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}` - with `B_n` as classical subdiagram. + Return the classical crystal underlying the Kirillov-Reshetikhin + crystal of type `A_{2n}^{(2)}` with `B_n` as classical subdiagram. - It is given by `B^{r,s} \cong \bigoplus_\Lambda B(\Lambda)` where `B(\Lambda)` is a highest weight crystal of type - `B_n` of highest weight `\Lambda`. The sum is over all weights `\Lambda` obtained from - a rectangle of width `s` and height `r` by removing horizontal dominoes. Here we identify the fundamental - weight `\Lambda_i` with a column of height `i`. + It is given by `B^{r,s} \cong \bigoplus_{\Lambda} B(\Lambda)`, + where `B(\Lambda)` is a highest weight crystal of type `B_n` + of highest weight `\Lambda`. The sum is over all weights `\Lambda` + obtained from a rectangle of width `s` and height `r` by removing + horizontal dominoes. Here we identify the fundamental weight + `\Lambda_i` with a column of height `i`. EXAMPLES:: @@ -1835,8 +1630,11 @@ def classical_decomposition(self): def ambient_crystal(self): r""" - Returns the ambient crystal `B^{r,s}` of type `B_{n+1}^{(1)}` associated to the Kirillov-Reshetikhin - crystal of type `A_{2n}^{(2)}` dual. This ambient crystal is used to construct the zero arrows. + Return the ambient crystal `B^{r,s}` of type `B_{n+1}^{(1)}` + associated to the Kirillov-Reshetikhin crystal of type + `A_{2n}^{(2)}` dual. + + This ambient crystal is used to construct the zero arrows. EXAMPLES:: @@ -1850,8 +1648,8 @@ def ambient_crystal(self): @cached_method def ambient_dict_pm_diagrams(self): r""" - Gives a dictionary of all self-dual `\pm` diagrams for the ambient crystal. - Their key is their inner shape. + Return a dictionary of all self-dual `\pm` diagrams for the + ambient crystal whose keys are their inner shape. EXAMPLES:: @@ -1871,18 +1669,20 @@ def ambient_dict_pm_diagrams(self): ulist = [] s = self.s() r = self.r() - m = s//2 + m = s // 2 for i in range(m+1): for la in IntegerVectors(m-i, min_length=r, max_length=r): ulist.append(PMDiagram([[j,j] for j in la]+[[s-2*m+2*i]])) - return dict( (x.inner_shape(), x) for x in ulist ) + return {x.inner_shape(): x for x in ulist} @cached_method def ambient_highest_weight_dict(self): r""" - Gives a dictionary of all `{2,...,n+1}`-highest weight vectors in the ambient crystal. - Their key is the inner shape of their corresponding `\pm` diagram, or equivalently, their - `{2,...,n+1}` weight. + Return a dictionary of all `\{2,\ldots,n+1\}`-highest weight vectors + in the ambient crystal. + + The key is the inner shape of their corresponding `\pm` diagram, + or equivalently, their `\{2,\ldots,n+1\}` weight. EXAMPLES:: @@ -1893,13 +1693,14 @@ def ambient_highest_weight_dict(self): """ A = self.ambient_dict_pm_diagrams() ambient = self.ambient_crystal() - return dict( (key, ambient.retract(ambient.from_pm_diagram_to_highest_weight_vector(A[key]))) for key in A ) + return {key: ambient.retract(ambient.from_pm_diagram_to_highest_weight_vector(A[key])) + for key in A} @cached_method def highest_weight_dict(self): r""" - Gives a dictionary of the classical highest weight vectors of self. - Their key is their shape. + Return a dictionary of the classical highest weight vectors + of ``self`` whose keys are their shape. EXAMPLES:: @@ -1908,13 +1709,13 @@ def highest_weight_dict(self): sage: K.highest_weight_dict() {[]: [], [2]: [[1, 1]]} """ - return dict( (x.lift().to_tableau().shape(),x) for x in self.module_generators ) + return {x.lift().to_tableau().shape(): x for x in self.module_generators} @cached_method def to_ambient_crystal(self): r""" - Provides a map from the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}` to the - ambient crystal of type `B_{n+1}^{(1)}`. + Return a map from the Kirillov-Reshetikhin crystal of type + `A_{2n}^{(2)}` to the ambient crystal of type `B_{n+1}^{(1)}`. EXAMPLES:: @@ -1930,17 +1731,18 @@ def to_ambient_crystal(self): sage: K.to_ambient_crystal()(b).parent() Kirillov-Reshetikhin crystal of type ['B', 3, 1] with (r,s)=(2,2) """ - keys = self.highest_weight_dict() - pdict = dict( (self.highest_weight_dict()[key], self.ambient_highest_weight_dict()[key]) for key in keys ) + hwd = self.highest_weight_dict() + ahwd = self.ambient_highest_weight_dict() + pdict = {hwd[key]: ahwd[key] for key in hwd} classical = self.cartan_type().classical() - return self.crystal_morphism( pdict, index_set=classical.index_set(), - automorphism=lambda i: i+1, - cartan_type=classical, check=False ) + return self.crystal_morphism(pdict, index_set=classical.index_set(), + automorphism=lambda i: i+1, + cartan_type=classical, check=False) @cached_method def from_ambient_crystal(self): r""" - Provides a map from the ambient crystal of type `B_{n+1}^{(1)}` to + Return a map from the ambient crystal of type `B_{n+1}^{(1)}` to the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}`. Note that this map is only well-defined on type `A_{2n}^{(2)}` @@ -1954,17 +1756,17 @@ def from_ambient_crystal(self): sage: K.from_ambient_crystal()(b) [[1, 1]] """ - keys = self.highest_weight_dict() - pdict_inv = dict( (self.ambient_highest_weight_dict()[key], self.highest_weight_dict()[key]) - for key in keys ) + hwd = self.highest_weight_dict() + ahwd = self.ambient_highest_weight_dict() + pdict_inv = {ahwd[key]: hwd[key] for key in hwd} ind = [j+1 for j in self.cartan_type().classical().index_set()] - return AmbientRetractMap( self, self.ambient_crystal(), pdict_inv, index_set=ind, - automorphism=lambda i : i-1 ) + return AmbientRetractMap(self, self.ambient_crystal(), pdict_inv, index_set=ind, + automorphism=lambda i: i-1) class KR_type_A2Element(KirillovReshetikhinGenericCrystalElement): r""" - Class for the elements in the Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `r """ def e0(self): r""" - Gives `e_0` on self by mapping self to the ambient crystal, calculating `e_0` there and - pulling the element back. + Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `e_0` there and pulling the element back. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['A',4,2],1,1) + sage: K = crystals.KirillovReshetikhin(['A',4,2],1,1) sage: b = K(rows=[]) sage: b.e(0) # indirect doctest [[-1]] @@ -2254,12 +2063,12 @@ def e0(self): def f0(self): r""" - Gives `f_0` on self by mapping self to the ambient crystal, calculating `f_0` there and - pulling the element back. + Return `f_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `f_0` there and pulling the element back. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['A',4,2],1,1) + sage: K = crystals.KirillovReshetikhin(['A',4,2],1,1) sage: b = K(rows=[]) sage: b.f(0) # indirect doctest [[1]] @@ -2271,13 +2080,13 @@ def f0(self): def epsilon0(self): r""" - Calculate `\varepsilon_0` of ``self`` by mapping the element + Return `\varepsilon_0` of ``self`` by mapping the element to the ambient crystal and calculating `\varepsilon_0` there. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['A',4,2], 1,1) - sage: b=K(rows=[[1]]) + sage: b = K(rows=[[1]]) sage: b.epsilon(0) # indirect doctest 2 """ @@ -2286,13 +2095,13 @@ def epsilon0(self): def phi0(self): r""" - Calculate `\varphi_0` of ``self`` by mapping the element to + Return `\varphi_0` of ``self`` by mapping the element to the ambient crystal and calculating `\varphi_0` there. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['D',3,2], 1,1) - sage: b=K(rows=[[-1]]) + sage: b = K(rows=[[-1]]) sage: b.phi(0) # indirect doctest 2 """ @@ -2345,25 +2154,28 @@ def _element_constructor_(self, *args, **options): # Check to make sure it can be converted if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") + raise ValueError("the Kirillov-Reshetikhin tableau must have the same Cartan type and shape") to_hw = elt.to_classical_highest_weight() - wt = to_hw[0].classical_weight() / 2 + wt = to_hw[0].classical_weight() f_str = reversed(to_hw[1]) for x in self.module_generators: if x.classical_weight() == wt: return x.f_string(f_str) - raise ValueError("No matching highest weight element found") + raise ValueError("no matching highest weight element found") return KirillovReshetikhinGenericCrystal._element_constructor_(self, *args, **options) def classical_decomposition(self): r""" - Specifies the classical crystal underlying the Kirillov-Reshetikhin crystal `B^{n,s}` of type `B_n^{(1)}`. + Return the classical crystal underlying the Kirillov-Reshetikhin + crystal `B^{n,s}` of type `B_n^{(1)}`. - It is the same as for `r0]) for r in b.to_tableau()]) - inter = Partition([len([i for i in r if i>=0]) for r in b.to_tableau()]) + inter1 = Partition([len([i for i in r if i > 0]) for r in b.to_tableau()]) + inter = Partition([len([i for i in r if i >= 0]) for r in b.to_tableau()]) if inter != inter1: inner[n-1] += 2 inner = Partition(inner) @@ -2902,9 +2739,10 @@ def from_highest_weight_vector_to_pm_diagram(self, b): return PMDiagram([n, s, outer, inter, inner], from_shapes=True) def from_pm_diagram_to_highest_weight_vector(self, pm): - """ - This gives the bijection between a `\pm` diagram and an element b in the classical - decomposition of the KR crystal that is {2,3,..,n}-highest weight. + r""" + This gives the bijection between a `\pm` diagram and an element + ``b`` in the classical decomposition of the KR crystal that is + `\{2,3,\ldots,n\}`-highest weight. EXAMPLES:: @@ -2931,7 +2769,8 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): class KR_type_Dn_twistedElement(KirillovReshetikhinGenericCrystalElement): r""" - Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` of type `D_{n+1}^{(2)}`. + Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` + of type `D_{n+1}^{(2)}`. EXAMPLES:: @@ -2942,8 +2781,9 @@ class KR_type_Dn_twistedElement(KirillovReshetikhinGenericCrystalElement): def e0(self): r""" - Gives `e_0` on self by going to the `\pm`-diagram corresponding to the `{2,...,n}`-highest weight - vector in the component of `self`, then applying [Definition 6.2, 4], and pulling back from + Return `e_0` on ``self`` by going to the `\pm`-diagram corresponding + to the `\{2,\ldots,n\}`-highest weight vector in the component of + ``self``, then applying [Definition 6.2, 4], and pulling back from `\pm`-diagrams. EXAMPLES:: @@ -2977,13 +2817,14 @@ def e0(self): def f0(self): r""" - Gives `e_0` on self by going to the `\pm`-diagram corresponding to the `{2,...,n}`-highest weight - vector in the component of `self`, then applying [Definition 6.2, 4], and pulling back from + Return `e_0` on ``self`` by going to the `\pm`-diagram corresponding + to the `\{2,\ldots,n\}`-highest weight vector in the component of + ``self``, then applying [Definition 6.2, 4], and pulling back from `\pm`-diagrams. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['D',4,2],3,2) + sage: K = crystals.KirillovReshetikhin(['D',4,2],3,2) sage: b = K.module_generators[0] sage: b.f(0) # indirect doctest """ @@ -2993,7 +2834,7 @@ def f0(self): pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) [l1,l2] = pm.pm_diagram[n-1] l3 = pm.pm_diagram[n-2][0] - if l1+l2+l3==s and l2==0: + if l1+l2+l3 == s and l2 == 0: return None if l1+l2+l3=0 for i in starting_weight.coefficients()): - Parent.__init__( self, category = (RegularCrystals(), - HighestWeightCrystals(), - InfiniteEnumeratedSets()) ) + Parent.__init__( self, category=(RegularCrystals(), + HighestWeightCrystals(), + InfiniteEnumeratedSets()) ) elif starting_weight.parent().is_extended(): - Parent.__init__(self, category = (RegularCrystals(), InfiniteEnumeratedSets())) + Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) else: - Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals())) + cl = self._cartan_type.classical().index_set() + if sum(self.weight[i] for i in cl) == 1: + cat = KirillovReshetikhinCrystals() + else: + cat = RegularLoopCrystals().Finite() + Parent.__init__(self, category=cat) else: - Parent.__init__(self, category = ClassicalCrystals()) + Parent.__init__(self, category=ClassicalCrystals()) if starting_weight == starting_weight.parent().zero(): initial_element = self(()) @@ -627,12 +633,14 @@ class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths): INPUT: - - ``weight`` -- a dominant weight of the weight space of an affine Kac-Moody root system + - ``weight`` -- a dominant weight of the weight space of an affine + Kac-Moody root system - When ``weight`` is just a single fundamental weight `\Lambda_r`, this crystal is - isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also + When ``weight`` is just a single fundamental weight `\Lambda_r`, this + crystal is isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also :meth:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhinFromLSPaths`. - For general weights, it is isomorphic to a tensor product of single-column KR crystals. + For general weights, it is isomorphic to a tensor product of + single-column KR crystals. EXAMPLES:: @@ -696,10 +704,42 @@ def __classcall_private__(cls, weight): if weight.parent().is_extended(): raise ValueError("The weight should be in the non-extended weight lattice!") La = weight.parent().basis() - weight = weight - (weight.level())*La[0]/(La[0].level()) + weight = weight - weight.level() * La[0] / La[0].level() return super(CrystalOfLSPaths, cls).__classcall__(cls, weight, starting_weight_parent = weight.parent()) - def one_dimensional_configuration_sum(self, q = None, group_components = True): + @cached_method + def maximal_vector(self): + """ + Return the maximal vector of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]+La[2]) + sage: LS.maximal_vector() + (-3*Lambda[0] + 2*Lambda[1] + Lambda[2],) + """ + return self.module_generators[0] + + @cached_method + def classically_highest_weight_vectors(self): + r""" + Return the classically highest weight vectors of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: LS.classically_highest_weight_vectors() + ((-2*Lambda[0] + 2*Lambda[1],), + (-Lambda[0] + Lambda[1], -Lambda[1] + Lambda[2])) + """ + I0 = self.cartan_type().classical().index_set() + return tuple([x for x in self.list() if x.is_highest_weight(I0)]) + + def one_dimensional_configuration_sum(self, q=None, group_components=True): r""" Compute the one-dimensional configuration sum. @@ -710,10 +750,12 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): - ``group_components`` -- (default: ``True``) boolean; if ``True``, then the terms are grouped by classical component - The one-dimensional configuration sum is the sum of the weights of all elements in the crystal - weighted by the energy function. For untwisted types it uses the parabolic quantum Bruhat graph, see [LNSSS2013]_. - In the dual-of-untwisted case, the parabolic quantum Bruhat graph is defined by - exchanging the roles of roots and coroots (which is still conjectural at this point). + The one-dimensional configuration sum is the sum of the weights + of all elements in the crystal weighted by the energy function. + For untwisted types it uses the parabolic quantum Bruhat graph, + see [LNSSS2013]_. In the dual-of-untwisted case, the parabolic + quantum Bruhat graph is defined by exchanging the roles of roots + and coroots (which is still conjectural at this point). EXAMPLES:: @@ -722,11 +764,13 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) sage: LS.one_dimensional_configuration_sum() # long time B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] - + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] + + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] sage: R. = ZZ[] sage: LS.one_dimensional_configuration_sum(t, False) # long time - B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] + B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + + (t+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] TESTS:: @@ -775,7 +819,7 @@ def weight(x): def is_perfect(self, level=1): r""" - Checks whether the crystal ``self`` is perfect (of level ``level``). + Check whether the crystal ``self`` is perfect (of level ``level``). INPUT: @@ -783,15 +827,18 @@ def is_perfect(self, level=1): A crystal `\mathcal{B}` is perfect of level `\ell` if: - #. `\mathcal{B}` is isomorphic to the crystal graph of a finite-dimensional `U_q^{'}(\mathfrak{g})`-module. + #. `\mathcal{B}` is isomorphic to the crystal graph of a + finite-dimensional `U_q^{'}(\mathfrak{g})`-module. #. `\mathcal{B}\otimes \mathcal{B}` is connected. - #. There exists a `\lambda\in X`, such that `\mathrm{wt}(\mathcal{B}) \subset \lambda - + \sum_{i\in I} \mathbb{Z}_{\le 0} \alpha_i` and there is a unique element in `\mathcal{B}` of classical - weight `\lambda`. - #. `\forall b \in \mathcal{B}, \mathrm{level}(\varepsilon (b)) \geq \ell`. - #. `\forall \Lambda` dominant weights of level `\ell`, there exist unique elements - `b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`, - such that `\varepsilon ( b_{\Lambda}) = \Lambda = \varphi( b^{\Lambda})`. + #. There exists a `\lambda\in X`, such that + `\mathrm{wt}(\mathcal{B}) \subset \lambda + \sum_{i\in I} \ZZ_{\le 0} \alpha_i` + and there is a unique element in + `\mathcal{B}` of classical weight `\lambda`. + #. For all `b \in \mathcal{B}`, + `\mathrm{level}(\varepsilon (b)) \geq \ell`. + #. For all `\Lambda` dominant weights of level `\ell`, there exist + unique elements `b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`, such + that `\varepsilon (b_{\Lambda}) = \Lambda = \varphi(b^{\Lambda})`. Points (1)-(3) are known to hold. This method checks points (4) and (5). @@ -932,58 +979,70 @@ def energy_function(self): r""" Return the energy function of ``self``. - The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)` - requires a series of definitions; for simplicity the root system is assumed to be untwisted affine. + The energy function `D(\pi)` of the level zero LS path + `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)` requires a series + of definitions; for simplicity the root system is assumed to + be untwisted affine. - The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice. - It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors" - `x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the - stabilizer of `\lambda` in the finite Weyl group `W`. Precisely, + The LS path `\pi` is a piecewise linear map from the unit + interval `[0,1]` to the weight lattice. It is specified by + "times" `0 = \sigma_0 < \sigma_1 < \dotsm < \sigma_s = 1` and + "direction vectors" `x_u \lambda` where `x_u \in W / W_J` for + `1 \le u \le s`, and `W_J` is the stabilizer of `\lambda` in + the finite Weyl group `W`. Precisely, .. MATH:: - \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda + \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1}) + x_{u'} \lambda + (t-\sigma_{u-1}) x_{u} \lambda - for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`. + for `1 \le u \le s` and `\sigma_{u-1} \le t \le \sigma_{u}`. - For any `x,y\in W/W_J` let + For any `x,y \in W / W_J`, let .. MATH:: - d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow} + d: x = w_{0} \stackrel{\beta_{1}}{\leftarrow} w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots \stackrel{\beta_{n}}{\leftarrow} w_{n}=y - be a shortest directed path in the parabolic quantum Bruhat graph. Define + be a shortest directed path in the parabolic quantum + Bruhat graph. Define .. MATH:: - \mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\ \ell(w_{k-1})<\ell(w_k)}} - \beta_{k}^{\vee} + \mathrm{wt}(d) := \sum_{\substack{1 \le k \le n + \\ \ell(w_{k-1}) < \ell(w_k)}} + \beta_{k}^{\vee}. It can be shown that `\mathrm{wt}(d)` depends only on `x,y`; - call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by + call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` + is defined by .. MATH:: - D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle + D(\pi) = -\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda, + \mathrm{wt}(x_u,x_{u+1}) \rangle. For more information, see [LNSSS2013]_. REFERENCES: .. [LNSSS2013] \C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, - A uniform model for Kirillov-Reshetikhin crystals. Extended abstract. - DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} ) + *A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.* + DMTCS proc, to appear ( :arXiv:`1211.6019` ) .. NOTE:: - In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by - exchanging the roles of roots and coroots. Moreover, in the computation of the - pairing the short roots must be doubled (or tripled for type `G`). This factor - is determined by the translation factor of the corresponding root. - Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted. - Except for the untwisted cases, these formulas are currently still conjectural. + In the dual-of-untwisted case the parabolic quantum + Bruhat graph that is used is obtained by exchanging the + roles of roots and coroots. Moreover, in the computation + of the pairing the short roots must be doubled (or tripled + for type `G`). This factor is determined by the translation + factor of the corresponding root. Type `BC` is viewed as + untwisted type, whereas the dual of `BC` is viewed as twisted. + Except for the untwisted cases, these formulas are + currently still conjectural. EXAMPLES:: @@ -1017,7 +1076,8 @@ def energy_function(self): (Lambda[0] - Lambda[2], -Lambda[0] + Lambda[1]) 0 (Lambda[0] - Lambda[2], -Lambda[1] + Lambda[2]) 0 - The next test checks that the energy function is constant on classically connected components:: + The next test checks that the energy function is constant + on classically connected components:: sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 5889a681b2f..55dd94a9bc4 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -982,30 +982,6 @@ def cardinality(self): """ return Infinity - def weight_lattice_realization(self): - r""" - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',3,2]) - sage: M.weight_lattice_realization() - Extended weight lattice of the Root system of type ['B', 2, 1]^* - sage: M = crystals.infinity.NakajimaMonomials(['A',2]) - sage: M.weight_lattice_realization() - Ambient space of the Root system of type ['A', 2] - sage: A = CartanMatrix([[2,-3],[-3,2]]) - sage: M = crystals.infinity.NakajimaMonomials(A) - sage: M.weight_lattice_realization() - Weight lattice of the Root system of type Dynkin diagram of rank 2 - """ - F = self.cartan_type().root_system() - if self.cartan_type().is_finite() and F.ambient_space() is not None: - return F.ambient_space() - if self.cartan_type().is_affine(): - return F.weight_lattice(extended=True) - return F.weight_lattice() - def set_variables(self, letter): r""" Set the type of monomials to use for the element output. diff --git a/src/sage/combinat/crystals/mv_polytopes.py b/src/sage/combinat/crystals/mv_polytopes.py new file mode 100644 index 00000000000..7a0729f036b --- /dev/null +++ b/src/sage/combinat/crystals/mv_polytopes.py @@ -0,0 +1,469 @@ +# -*- coding: utf-8 -*- +r""" +Crystal Of Mirković-Vilonen (MV) Polytopes + +AUTHORS: + +- Dinakar Muthiah, Travis Scrimshaw (2015-05-11): initial version +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.crystals.pbw_crystal import PBWCrystalElement, PBWCrystal + +class MVPolytope(PBWCrystalElement): + """ + A Mirković-Vilonen (MV) polytope. + + EXAMPLES: + + We can create an animation showing how the MV polytope changes + under a string of crystal operators:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: u = MV.highest_weight_vector() + sage: L = RootSystem(['C',2,1]).ambient_space() + sage: s = [1,2,1,2,2,2,1,1,1,1,2,1,2,2,1,2] + sage: BB = [[-9, 2], [-10, 2]] + sage: p = L.plot(reflection_hyperplanes=False, bounding_box=BB) # long time + sage: frames = [p + L.plot_mv_polytope(u.f_string(s[:i]), # long time + ....: circle_size=0.1, + ....: wireframe='green', + ....: fill='purple', + ....: bounding_box=BB) + ....: for i in range(len(s))] + sage: for f in frames: # long time + ....: f.axes(False) + sage: animate(frames).show(delay=60) # optional -- ImageMagick # long time + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['E', 6]) + sage: b = MV.module_generators[0].f_string([1,2,6,4,3,2,5,2]) + sage: b + MV polytope with Lusztig datum (0, 1, ..., 1, 0, 0, 0, 0, 0, 0, 3, 1) + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + return "MV polytope with Lusztig datum {}".format(pbw_datum.lusztig_datum) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-1, 1) -- (-1, 1) -- (-2, 0) -- (-2, -2); + \draw (0, 0) -- (0, -2) -- (-1, -3) -- (-1, -3) -- (-2, -2); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (-2, -2) circle (0.1); + \end{tikzpicture} + sage: MV = crystals.infinity.MVPolytopes(['D',4]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: latex(b) + \text{\texttt{MV{ }polytope{ }...}} + + TESTS:: + + sage: MV = crystals.infinity.MVPolytopes(['A',2]) + sage: u = MV.highest_weight_vector() + sage: b = u.f_string([1,2,2,1]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (3/2, -989/1142) -- (3/2, -2967/1142) -- (0, -1978/571); + \draw (0, 0) -- (-3/2, -989/1142) -- (-3/2, -2967/1142) -- (0, -1978/571); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (0, -1978/571) circle (0.1); + \end{tikzpicture} + """ + latex_options = self.parent()._latex_options + P = latex_options['P'] + plot_options = P.plot_parse_options(projection=latex_options["projection"]) + proj = plot_options.projection + if proj(P.zero()).parent().dimension() != 2: + from sage.misc.latex import latex + return latex(repr(self)) + + # We need this to use tikz + from sage.graphs.graph_latex import setup_latex_preamble + setup_latex_preamble() + + pbw_data = self._pbw_datum.parent + W = pbw_data.weyl_group + w0 = W.long_element() + al = P.simple_roots() + ret = "\\begin{tikzpicture}\n" + + final = None + for red in w0.reduced_words(): + ret += "\\draw " + cur = proj(P.zero()) + red = tuple(red) + ret += str(cur) + roots = [proj(P.sum(c*al[a] for a,c in root)) + for root in pbw_data._root_list_from(red)] + datum = pbw_data.convert_to_new_long_word(self._pbw_datum, red) + for i in reversed(range(len(datum.lusztig_datum))): + cur -= roots[i] * datum.lusztig_datum[i] + ret += " -- " + str(cur) + final = cur + ret += ";\n" + + if latex_options["mark_endpoints"]: + circle_size = latex_options["circle_size"] + ret += "\\draw[fill=black] {} circle ({});\n".format(proj(P.zero()), circle_size) + ret += "\\draw[fill=black] {} circle ({});\n".format(final, circle_size) + ret += "\\end{tikzpicture}" + return ret + + def _polytope_vertices(self, P): + """ + Return a list of the vertices of ``self`` in ``P``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 3]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: sorted(b._polytope_vertices(MV.weight_lattice_realization()), key=list) + [(0, 0, 0), (2, 0, -2), (0, 2, -2)] + + sage: MV = crystals.infinity.MVPolytopes(['D', 4]) + sage: b = MV.module_generators[0].f_string([1,2,3,4]) + sage: P = RootSystem(['D',4]).weight_lattice() + sage: sorted(b._polytope_vertices(P), key=list) # long time + [0, + -Lambda[1] + Lambda[3] + Lambda[4], + Lambda[1] - Lambda[2] + Lambda[3] + Lambda[4], + -2*Lambda[2] + 2*Lambda[3] + 2*Lambda[4], + -Lambda[2] + 2*Lambda[3], + -Lambda[2] + 2*Lambda[4]] + """ + pbw_data = self._pbw_datum.parent + W = pbw_data.weyl_group + w0 = W.long_element() + al = P.simple_roots() + + vertices = set([P.zero()]) + for red in w0.reduced_words(): + cur = P.zero() + red = tuple(red) + roots = [P.sum(c*al[a] for a,c in root) + for root in pbw_data._root_list_from(red)] + datum = pbw_data.convert_to_new_long_word(self._pbw_datum, red) + for i,c in enumerate(datum.lusztig_datum): + cur = cur + roots[i] * c + vertices.add(cur) + return list(vertices) + + def polytope(self, P=None): + """ + Return a polytope of ``self``. + + INPUT: + + - ``P`` -- (optional) a space to realize the polytope; default is + the weight lattice realization of the crystal + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 3]) + sage: b = MV.module_generators[0].f_string([3,2,3,2,1]) + sage: P = b.polytope(); P + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 6 vertices + sage: P.vertices() + (A vertex at (0, 0, 0), + A vertex at (0, 1, -1), + A vertex at (0, 1, 1), + A vertex at (1, -1, 0), + A vertex at (1, 1, -2), + A vertex at (1, 1, 2)) + """ + if P is None: + P = self.parent().weight_lattice_realization() + + from sage.geometry.polyhedron.constructor import Polyhedron + return Polyhedron([v.to_vector() for v in self._polytope_vertices(P)]) + + def plot(self, P=None, **options): + """ + Plot ``self``. + + INPUT: + + - ``P`` -- (optional) a space to realize the polytope; default is + the weight lattice realization of the crystal + + .. SEEALSO:: + + :meth:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot_mv_polytope` + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: b = MV.highest_weight_vector().f_string([1,2,1,2,2,2,1,1,1,1,2,1]) + sage: b.plot() + Graphics object consisting of 12 graphics primitives + + Here is the above example placed inside the ambient space + of type `C_2`: + + .. PLOT:: + :width: 300 px + + MV = crystals.infinity.MVPolytopes(['C', 2]) + b = MV.highest_weight_vector().f_string([1,2,1,2,2,2,1,1,1,1,2,1]) + L = RootSystem(['C', 2, 1]).ambient_space() + p = L.plot(reflection_hyperplanes=False, bounding_box=[[-8,2], [-8,2]]) + p += b.plot() + p.axes(False) + sphinx_plot(p) + """ + if P is None: + P = self.parent().weight_lattice_realization() + return P.plot_mv_polytope(self, **options) + +class MVPolytopes(PBWCrystal): + r""" + The crystal of Mirković-Vilonen (MV) polytopes. + + Let `W` denote the corresponding Weyl group and `P_{\RR} = \RR \otimes P`. + Let `\Gamma = \{ w \Lambda_i \mid w \in W, i \in I \}`. Consider + `M = (M_{\gamma} \in \ZZ)_{\gamma \in \Gamma}` that satisfy the + *tropical Plücker relations* (see Proposition 7.1 of [BZ01]_). + The *MV polytope* is defined as + + .. MATH:: + + P(M) = \{ \alpha \in P_{\RR} \mid + \langle \alpha, \gamma \rangle \geq M_{\gamma} + \text{ for all } \gamma \in \Gamma \}. + + The vertices `\{\mu_w\}_{w \in W}` are given by + + .. MATH:: + + \langle \mu_w, \gamma \rangle = M_{\gamma} + + and are known as the GGMS datum of the MV polytope. + + Each path from `\mu_e` to `\mu_{w_0}` corresponds to a reduced + expression `\mathbf{i} = (i_1, \ldots, i_m)` for `w_0` and the + corresponding edge lengths `(n_k)_{k=1}^m` from the Lusztig datum + with respect to `\mathbf{i}`. Explicitly, we have + + .. MATH:: + + \begin{aligned} + n_k & = -M_{w_{k-1} \Lambda_{i_k}} - M_{w_k \Lambda_{i_k}} + - \sum_{j \neq i} a_{ji} M_{w_k \Lambda_j}, + \\ \mu_{w_k} - \mu_{w_{k-1}} & = n_k w_{k-1} \alpha_{i_k}, + \end{aligned} + + where `w_k = s_{i_1} \cdots s_{i_k}` and `(a_{ji})` is the Cartan matrix. + + MV polytopes have a crystal structure that corresponds to the + crystal structure, which is isomorphic to `\mathcal{B}(\infty)` + with `\mu_{w_0} = 0`, on + :class:`PBW data `. + Specifically, we have `f_j P(M)` as being the unique MV polytope + given by shifting `\mu_e` by `-\alpha_j` and fixing the vertices + `\mu_w` when `s_j w < w` (in Bruhat order) and the weight is given by + `\mu_e`. Furthermore, the `*`-involution is given by negating `P(M)`. + + INPUT: + + - ``cartan_type`` -- a Cartan type + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['B', 3]) + sage: hw = MV.highest_weight_vector() + sage: x = hw.f_string([1,2,2,3,3,1,3,3,2,3,2,1,3,1,2,3,1,2,1,3,2]); x + MV polytope with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + Elements are expressed in terms of Lusztig datum for a fixed + reduced expression of `w_0`:: + + sage: MV.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: MV.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: x + MV polytope with Lusztig datum (3, 1, 1, 0, 1, 0, 1, 3, 4) + sage: MV.set_default_long_word([1, 3, 2, 3, 1, 2, 3, 1, 2]) + + We can construct elements by giving it Lusztig data (with respect + to the default long word reduced expression):: + + sage: MV([1,1,1,3,1,0,0,1,1]) + MV polytope with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + We can also construct elements by passing in a reduced expression + for a long word:: + + sage: x = MV([1,1,1,3,1,0,0,1,1], [3,2,1,3,2,3,2,1,2]); x + MV polytope with Lusztig datum (1, 1, 1, 0, 1, 0, 5, 1, 1) + sage: x.to_highest_weight()[1] + [1, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 2, 1, 3] + + The highest weight crystal `B(\lambda) \subseteq B(\infty)` is + characterized by the MV polytopes that sit inside of `W \lambda` + (translating `\mu_{w_0} \mapsto \lambda`):: + + sage: MV = crystals.infinity.MVPolytopes(['A',2]) + sage: La = MV.weight_lattice_realization().fundamental_weights() + sage: R = crystals.elementary.R(La[1]+La[2]) + sage: T = tensor([R, MV]) + sage: x = T(R.module_generators[0], MV.highest_weight_vector()) + sage: lw = x.to_lowest_weight()[0]; lw + [(2, 1, 0), MV polytope with Lusztig datum (1, 1, 1)] + sage: lw[1].polytope().vertices() + (A vertex at (0, 0, 0), + A vertex at (0, 1, -1), + A vertex at (1, -1, 0), + A vertex at (1, 1, -2), + A vertex at (2, -1, -1), + A vertex at (2, 0, -2)) + + .. PLOT:: + :width: 300 px + + MV = crystals.infinity.MVPolytopes(['A',2]) + x = MV.module_generators[0].f_string([1,2,2,1]) + L = RootSystem(['A',2,1]).ambient_space() + p = L.plot(bounding_box=[[-2,2],[-4,2]]) + x.plot() + p.axes(False) + sphinx_plot(x.plot()) + + REFERENCES: + + - [Kam2007]_ + - [Kam2010]_ + """ + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['B', 2]) + sage: TestSuite(MV).run() + """ + PBWCrystal.__init__(self, cartan_type) + self._latex_options = {"projection": True, + "mark_endpoints": True, + "P": self.weight_lattice_realization(), + "circle_size": 0.1} + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: crystals.infinity.MVPolytopes(['F', 4]) + MV polytopes of type ['F', 4] + """ + return "MV polytopes of type {}".format(self._cartan_type) + + def set_latex_options(self, **kwds): + r""" + Set the latex options for the elements of ``self``. + + INPUT: + + - ``projection`` -- the projection; set to ``True`` to use the + default projection of the specified weight lattice realization + (initial: ``True``) + - ``P`` -- the weight lattice realization to use (initial: the + weight lattice realization of ``self``) + - ``mark_endpoints`` -- whether to mark the endpoints (initial: ``True``) + - ``circle_size`` -- the size of the endpoint circles (initial: 0.1) + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: P = RootSystem(['C', 2]).weight_lattice() + sage: b = MV.highest_weight_vector().f_string([1,2,1,2]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-1, 1) -- (-1, 1) -- (-2, 0) -- (-2, -2); + \draw (0, 0) -- (0, -2) -- (-1, -3) -- (-1, -3) -- (-2, -2); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (-2, -2) circle (0.1); + \end{tikzpicture} + sage: MV.set_latex_options(P=P, circle_size=float(0.2)) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-2, 1) -- (-2, 1) -- (-2, 0) -- (0, -2); + \draw (0, 0) -- (2, -2) -- (2, -3) -- (2, -3) -- (0, -2); + \draw[fill=black] (0, 0) circle (0.2); + \draw[fill=black] (0, -2) circle (0.2); + \end{tikzpicture} + sage: MV.set_latex_options(mark_endpoints=False) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-2, 1) -- (-2, 1) -- (-2, 0) -- (0, -2); + \draw (0, 0) -- (2, -2) -- (2, -3) -- (2, -3) -- (0, -2); + \end{tikzpicture} + sage: MV.set_latex_options(P=MV.weight_lattice_realization(), + ....: circle_size=0.2, + ....: mark_endpoints=True) + """ + if "projection" in kwds: + self._latex_options["projection"] = True + del kwds["projection"] + + if 'P' in kwds: + self._latex_options['P'] = kwds['P'] + del kwds['P'] + + if "mark_endpoints" in kwds: + self._latex_options["mark_endpoints"] = kwds["mark_endpoints"] + del kwds["mark_endpoints"] + + if "circle_size" in kwds: + self._latex_options["circle_size"] = kwds["circle_size"] + del kwds["circle_size"] + + if kwds: + raise ValueError("invalid latex option") + + def latex_options(self): + """ + Return the latex options of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['F', 4]) + sage: MV.latex_options() + {'P': Ambient space of the Root system of type ['F', 4], + 'circle_size': 0.1, + 'mark_endpoints': True, + 'projection': True} + """ + from copy import copy + return copy(self._latex_options) + + Element = MVPolytope + diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py new file mode 100644 index 00000000000..4edb3576587 --- /dev/null +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -0,0 +1,509 @@ +# -*- coding: utf-8 -*- +r""" +`\mathcal{B}(\infty)` Crystal Of PBW Monomials. + +AUTHORS: + +- Dinakar Muthiah (2015-05-11): initial version + +.. SEEALSO:: + + For infromation on PBW datum, see + :ref:`sage.combinat.crystals.pbw_datum`. +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.structure.element import Element +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.sage_object import richcmp +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + +class PBWCrystalElement(Element): + """ + A crystal element in the PBW model. + """ + def __init__(self, parent, lusztig_datum, long_word=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['F', 4]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + sage: TestSuite(b).run() + """ + Element.__init__(self, parent) + if long_word is None: + long_word = parent._default_word + self._pbw_datum = PBWDatum(parent._pbw_datum_parent, long_word, lusztig_datum) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 4]) + sage: u = B.highest_weight_vector() + sage: u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + PBW monomial with Lusztig datum + (0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 2) + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + return "PBW monomial with Lusztig datum {}".format(pbw_datum.lusztig_datum) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['F', 4]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + sage: latex(b) + f_{\alpha_{4}}^{2} + f_{\alpha_{3}} + f_{\alpha_{1} + \alpha_{2} + 2\alpha_{3}} + f_{\alpha_{1} + \alpha_{2}} + f_{\alpha_{2}}^{2} + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + lusztig_datum = list(pbw_datum.lusztig_datum) + al = self.parent()._pbw_datum_parent._root_list_from(self.parent()._default_word) + from sage.misc.latex import latex + ret_str = ' '.join("f_{%s}%s"%(latex(al[i]), "^{%s}"%latex(exp) if exp > 1 else "") + for i, exp in enumerate(lusztig_datum) if exp) + if ret_str == '': + return '1' + return ret_str + + def lusztig_datum(self, word=None): + """ + Return the Lusztig datum of ``self`` with respect to the reduced + expression of the long word ``word``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: b.lusztig_datum() + (6, 0, 10) + sage: b.lusztig_datum(word=[2,1,2]) + (4, 6, 0) + """ + if word is None: + word = self.parent()._default_word + else: + self.parent()._check_is_long_word(word) + word = tuple(word) + pbw_datum = self._pbw_datum.convert_to_new_long_word(word) + return tuple(pbw_datum.lusztig_datum) + + def __eq__(self, other): + """ + Check equality of ``self`` with ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: b == bp + True + """ + if other not in self.parent(): + return False + other_long_word = other._pbw_datum.long_word + other_lusztig_datum = other._pbw_datum.lusztig_datum + equiv_pbw_datum = self._pbw_datum.convert_to_new_long_word(other_long_word) + return equiv_pbw_datum.lusztig_datum == other_lusztig_datum + + def __ne__(self, other): + """ + Check inequality of ``self`` with ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: b != bp + False + """ + return not (self == other) + + # Necessary for displaying subcrystals + def _richcmp_(self, other, op): + """ + Return comparison of ``self`` and ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2]) + sage: w = [1, 2, 1] + sage: (b < bp) == (b.lusztig_datum(w) < bp.lusztig_datum(w)) + True + sage: (b > bp) == (b.lusztig_datum(w) > bp.lusztig_datum(w)) + True + """ + i = self.parent().index_set()[0] + word = self.parent()._pbw_datum_parent._long_word_begin_with(i) + lusztig_datum = tuple(self._pbw_datum.convert_to_new_long_word(word).lusztig_datum) + other_lusztig_datum = tuple(other._pbw_datum.convert_to_new_long_word(word).lusztig_datum) + return richcmp(lusztig_datum, other_lusztig_datum, op) + + @cached_method + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: hash(b) == hash(bp) + True + """ + i = self.parent().index_set()[0] + word = self.parent()._pbw_datum_parent._long_word_begin_with(i) + pbw_datum = self._pbw_datum.convert_to_new_long_word(word) + return hash(tuple(pbw_datum.lusztig_datum)) + + def e(self, i): + """ + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 3]) + sage: b = B.highest_weight_vector() + sage: c = b.f_string([2,1,3,2,1,3,2,2]); c + PBW monomial with Lusztig datum (0, 1, 0, 1, 0, 0, 0, 1, 2) + sage: c.e(2) + PBW monomial with Lusztig datum (0, 1, 0, 1, 0, 0, 0, 1, 1) + sage: c.e_string([2,2,1,3,2,1,3,2]) == b + True + """ + equiv_pbw_datum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + new_long_word = equiv_pbw_datum.long_word + new_lusztig_datum = list(equiv_pbw_datum.lusztig_datum) + if new_lusztig_datum[0] == 0: + return None + new_lusztig_datum[0] -= 1 + return type(self)(self.parent(), tuple(new_lusztig_datum), new_long_word) + + def f(self, i): + """ + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW("D4") + sage: b = B.highest_weight_vector() + sage: c = b.f_string([1,2,3,1,2,3,4]); c + PBW monomial with Lusztig datum (0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0) + sage: c == b.f_string([1,2,4,1,2,3,3]) + True + """ + equiv_PBWDatum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + new_long_word = equiv_PBWDatum.long_word + new_lusztig_datum = list(equiv_PBWDatum.lusztig_datum) + new_lusztig_datum[0] += 1 + return type(self)(self.parent(), tuple(new_lusztig_datum), new_long_word) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(["A2"]) + sage: s = B((3,0,0), (1,2,1)) + sage: s.epsilon(1) + 3 + sage: s.epsilon(2) + 0 + """ + equiv_pbw_datum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + return equiv_pbw_datum.lusztig_datum[0] + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: s = B((3,0,0), (1,2,1)) + sage: s.phi(1) + -3 + sage: s.phi(2) + 3 + """ + WLR = self.parent().weight_lattice_realization() + h = WLR.simple_coroots() + return self.epsilon(i) + self.weight().scalar(h[i]) + + def weight(self): + """ + Return weight of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: s = B((2,2,2), (1,2,1)) + sage: s.weight() + (-4, 0, 4) + """ + WLR = self.parent().weight_lattice_realization() + al = WLR.simple_roots() + return WLR.sum(c*al[i] for i,c in self._pbw_datum.weight()) + + def star(self): + r""" + Return the starred crystal element corresponding + to ``self``. + + Let `b` be an element of ``self`` with Lusztig datum + `(b_1, \ldots, b_N)` with respect to `w_0 = s_{i_1} \cdots s_{i_N}`. + Then `b^*` is the element with Lusztig datum `(b_N, \ldots, b_1)` + with respect to `w_0 = s_{i_N^*} \cdots s_{i_1^*}`, where + `i_j^* = \omega(i_j)` with `\omega` being the :meth:`automorphism + ` + given by the action of `w_0` on the simple roots. + + EXAMPLES:: + + sage: P = crystals.infinity.PBW(['A', 2]) + sage: P((1,2,3), (1,2,1)).star() == P((3,2,1), (2,1,2)) + True + + sage: B = crystals.infinity.PBW(['E', 6]) + sage: b = B.highest_weight_vector() + sage: c = b.f_string([1,2,6,3,4,2,5,2,3,4,1,6]) + sage: c == c.star().star() + True + + TESTS:: + + sage: from itertools import product + sage: def test_star(PBW, depth): + ....: S = crystals.infinity.Star(PBW) + ....: for f_str in product(*([PBW.index_set()]*depth)): + ....: x = PBW.highest_weight_vector().f_string(f_str).star() + ....: y = S.highest_weight_vector().f_string(f_str) + ....: assert x.lusztig_datum() == y.value.lusztig_datum() + sage: P = crystals.infinity.PBW(['A', 2]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['A', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['B', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['C', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['D', 4]) + sage: test_star(P, 5) # long time + sage: P = crystals.infinity.PBW(['D', 5]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['E', 6]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['F', 4]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['G', 2]) + sage: test_star(P, 5) + """ + starred_pbw_datum = self._pbw_datum.star() + return type(self)(self.parent(), starred_pbw_datum.lusztig_datum, + starred_pbw_datum.long_word) + + +class PBWCrystal(Parent, UniqueRepresentation): + r""" + Crystal of `\mathcal{B}(\infty)` given by PBW monomials. + + A model of the crystal `\mathcal{B}(\infty)` whose elements are + PBW datum up to equivalence by the tropical Plücker relations. + The crystal structure on Lusztig data `x = (x_1, \ldots, x_m)` + for the reduced word `s_{i_1} \cdots s_{i_m} = w_0` is given as + follows. Suppose `i_1 = j`, then `f_j x = (x_1 + 1, x_2, \ldots, x_m)`. + If `i_1 \neq j`, then we use the tropical Plücker relations to + change the reduced expression such that `i_1' = j` and then we + change back to the original word. + + EXAMPLES:: + + sage: PBW = crystals.infinity.PBW(['B', 3]) + sage: hw = PBW.highest_weight_vector() + sage: x = hw.f_string([1,2,2,3,3,1,3,3,2,3,2,1,3,1,2,3,1,2,1,3,2]); x + PBW monomial with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + Elements are expressed in terms of Lusztig datum for a fixed + reduced expression of `w_0`:: + + sage: PBW.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: PBW.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: x + PBW monomial with Lusztig datum (3, 1, 1, 0, 1, 0, 1, 3, 4) + sage: PBW.set_default_long_word([1, 3, 2, 3, 1, 2, 3, 1, 2]) + + We can construct elements by giving it Lusztig data (with respect + to the default long word):: + + sage: PBW([1,1,1,3,1,0,0,1,1]) + PBW monomial with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + We can also construct elements by passing in a reduced expression + for a long word:: + + sage: x = PBW([1,1,1,3,1,0,0,1,1], [3,2,1,3,2,3,2,1,2]); x + PBW monomial with Lusztig datum (1, 1, 1, 0, 1, 0, 5, 1, 1) + sage: x.to_highest_weight()[1] + [1, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 2, 1, 3] + """ + @staticmethod + def __classcall__(cls, cartan_type): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: B1 = crystals.infinity.PBW(['A', 2]) + sage: B2 = crystals.infinity.PBW("A2") + sage: B3 = crystals.infinity.PBW(CartanType("A2")) + sage: B1 is B2 and B2 is B3 + True + """ + cartan_type = CartanType(cartan_type) + if not cartan_type.is_finite(): + raise NotImplementedError("only implemented for finite types") + return super(PBWCrystal, cls).__classcall__(cls, cartan_type) + + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 2]) + sage: TestSuite(B).run() + """ + self._cartan_type = cartan_type + self._pbw_datum_parent = PBWData(self._cartan_type) + category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) + Parent.__init__(self, category=category) + + # There must be a better way to do the following + i = self._cartan_type.index_set()[0] + self._default_word = self._pbw_datum_parent._long_word_begin_with(i) + zero_lusztig_datum = [0]*len(self._default_word) + self.module_generators = (self.element_class(self, + zero_lusztig_datum, + self._default_word),) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: crystals.infinity.PBW(['C', 3]) + Crystal of PBW data of type ['C', 3] + """ + return "Crystal of PBW data of type {}".format(self._cartan_type) + + def default_long_word(self): + """ + Return the default long word used to express elements of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['E', 6]) + sage: B.default_long_word() + [1, 3, 4, 5, 6, 2, 4, 5, 3, 4, 1, 3, 2, 4, 5, 6, 2, 4, + 5, 3, 4, 1, 3, 2, 4, 5, 3, 4, 1, 3, 2, 4, 1, 3, 2, 1] + """ + return list(self._default_word) + + def _check_is_long_word(self, word): + """ + Check if ``word`` is a reduced expression of the long of the + Coxeter group of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 3]) + sage: B._check_is_long_word([1,2,1,3,2,1]) + sage: B._check_is_long_word([1,3,2,3,2,1]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + sage: B._check_is_long_word([1,2,1,3,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + sage: B._check_is_long_word([1,2,1,3,2,1,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + """ + W = self._pbw_datum_parent.weyl_group + if (len(word) != len(self._default_word) + or W.from_reduced_word(word) != W.long_element()): + raise ValueError("not a reduced word of the long element") + + def set_default_long_word(self, word): + """ + Set the default long word used to express elements of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['C', 3]) + sage: B.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: x = B.highest_weight_vector().f_string([2,1,3,2,3,1,2,3,3,1]) + sage: x + PBW monomial with Lusztig datum (1, 2, 2, 0, 0, 0, 0, 0, 1) + sage: B.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: B.default_long_word() + [2, 1, 3, 2, 1, 3, 2, 3, 1] + sage: x + PBW monomial with Lusztig datum (2, 0, 0, 0, 0, 0, 1, 3, 2) + + TESTS:: + + sage: B = crystals.infinity.PBW(['A', 3]) + sage: B._check_is_long_word([1,2,1,3,2,1,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + """ + self._check_is_long_word(word) + self._default_word = tuple(word) + + Element = PBWCrystalElement + diff --git a/src/sage/combinat/crystals/pbw_datum.pxd b/src/sage/combinat/crystals/pbw_datum.pxd new file mode 100644 index 00000000000..039c197ea90 --- /dev/null +++ b/src/sage/combinat/crystals/pbw_datum.pxd @@ -0,0 +1,4 @@ +cpdef tuple compute_new_lusztig_datum(list enhanced_braid_chain, initial_lusztig_datum) +cpdef tuple tropical_plucker_relation(tuple a, lusztig_datum) +cpdef list enhance_braid_move_chain(braid_move_chain, cartan_type) + diff --git a/src/sage/combinat/crystals/pbw_datum.pyx b/src/sage/combinat/crystals/pbw_datum.pyx new file mode 100644 index 00000000000..5eb504ad1db --- /dev/null +++ b/src/sage/combinat/crystals/pbw_datum.pyx @@ -0,0 +1,484 @@ +# -*- coding: utf-8 -*- +r""" +PBW Data + +This contains helper classes and functions which encode PBW data +in finite type. + +AUTHORS: + +- Dinakar Muthiah (2015-05): initial version +- Travis Scrimshaw (2016-06): simplfied code and converted to Cython +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +#from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.cachefunc import cached_method +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.coxeter_group import CoxeterGroup +from sage.combinat.root_system.root_system import RootSystem +from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + +cimport cython + +class PBWDatum(object): + """ + Helper class which represents a PBW datum. + """ + def __init__(self, parent, long_word, lusztig_datum): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L = PBWDatum(P, (1,2,1), (1,4,7)) + sage: TestSuite(L).run(skip="_test_pickling") + """ + self.parent = parent + self.long_word = tuple(long_word) + self.lusztig_datum = tuple(lusztig_datum) + + def __repr__(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: PBWDatum(P, (1,2,1), (1,4,7)) + PBW Datum element of type ['A', 2] with long word (1, 2, 1) + and Lusztig datum (1, 4, 7) + """ + return_str = "PBW Datum element of type {cartan_type} with ".format( + cartan_type=self.parent.cartan_type) + return_str += "long word {long_word} and Lusztig datum {lusztig_datum}".format( + long_word=self.long_word, + lusztig_datum=self.lusztig_datum) + return return_str + + def __eq__(self, other_PBWDatum): + """ + Check equality. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,4,7)) + sage: L2 = PBWDatum(P, (1,2,1), (1,4,7)) + sage: L1 == L2 + True + """ + return (self.parent == other_PBWDatum.parent and + self.long_word == other_PBWDatum.long_word and + self.lusztig_datum == other_PBWDatum.lusztig_datum) + + def is_equivalent_to(self, other_pbw_datum): + r""" + Return whether ``self`` is equivalent to ``other_pbw_datum``. + modulo the tropical Plücker relations. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,0,1)) + sage: L2 = PBWDatum(P, (2,1,2), (0,1,0)) + sage: L1.is_equivalent_to(L2) + True + sage: L1 == L2 + False + """ + other_long_word = other_pbw_datum.long_word + other_lusztig_datum = other_pbw_datum.lusztig_datum + equiv_pbw_datum = self.convert_to_new_long_word(other_long_word) + return equiv_pbw_datum.lusztig_datum == other_lusztig_datum + + def convert_to_long_word_with_first_letter(self, i): + r""" + Return a new PBWDatum equivalent to ``self`` + whose long word begins with ``i``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A3") + sage: datum = PBWDatum(P, (1,2,1,3,2,1), (1,0,1,4,2,3)) + sage: datum.convert_to_long_word_with_first_letter(1) + PBW Datum element of type ['A', 3] with long word (1, 2, 3, 1, 2, 1) + and Lusztig datum (1, 0, 4, 1, 2, 3) + sage: datum.convert_to_long_word_with_first_letter(2) + PBW Datum element of type ['A', 3] with long word (2, 1, 2, 3, 2, 1) + and Lusztig datum (0, 1, 0, 4, 2, 3) + sage: datum.convert_to_long_word_with_first_letter(3) + PBW Datum element of type ['A', 3] with long word (3, 1, 2, 3, 1, 2) + and Lusztig datum (8, 1, 0, 4, 1, 2) + """ + return self.convert_to_new_long_word(self.parent._long_word_begin_with(i)) + + def convert_to_new_long_word(self, new_long_word): + r""" + Return a new PBWDatum equivalent to ``self`` + whose long word is ``new_long_word``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: datum = PBWDatum(P, (1,2,1), (1,0,1)) + sage: new_datum = datum.convert_to_new_long_word((2,1,2)) + sage: new_datum.long_word + (2, 1, 2) + sage: new_datum.lusztig_datum + (0, 1, 0) + """ + return self.parent.convert_to_new_long_word(self, new_long_word) + + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L = PBWDatum(P, (1,2,1), (1,1,1)) + sage: L.weight() + -2*alpha[1] - 2*alpha[2] + """ + root_list = self.parent._root_list_from(tuple(self.long_word)) + R = self.parent.root_lattice + return R.linear_combination((root_list[i], -coeff) + for i, coeff in enumerate(self.lusztig_datum)) + + def star(self): + """ + Return the starred version of ``self``, i.e., + with reversed `long_word` and `lusztig_datum` + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,2,3)) + sage: L1.star() == PBWDatum(P, (2,1,2), (3,2,1)) + True + """ + aut = self.parent.cartan_type.opposition_automorphism() + reversed_long_word = [aut[i] for i in reversed(self.long_word)] + reversed_lusztig_datum = reversed(self.lusztig_datum) + return PBWDatum(self.parent, reversed_long_word, reversed_lusztig_datum) + + +class PBWData(object): # UniqueRepresentation? + """ + Helper class for the set of PBW data. + """ + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["A",2]) + sage: TestSuite(P).run(skip="_test_pickling") + """ + self.cartan_type = CartanType(cartan_type) + self.root_system = RootSystem(self.cartan_type) + self.root_lattice = self.root_system.root_lattice() + self.weyl_group = self.root_lattice.weyl_group() + self._braid_move_calc = BraidMoveCalculator(self.weyl_group) + + def convert_to_new_long_word(self, pbw_datum, new_long_word): + """ + Convert the PBW datum ``pbw_datum`` from its long word to + ``new_long_word``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: datum = PBWDatum(P, (1,2,1), (1,0,1)) + sage: new_datum = P.convert_to_new_long_word(datum,(2,1,2)) + sage: new_datum + PBW Datum element of type ['A', 2] with long word (2, 1, 2) + and Lusztig datum (0, 1, 0) + sage: new_datum.long_word + (2, 1, 2) + sage: new_datum.lusztig_datum + (0, 1, 0) + """ + assert pbw_datum.parent is self + chain = self._braid_move_calc.chain_of_reduced_words(pbw_datum.long_word, + new_long_word) + cdef list enhanced_braid_chain = enhance_braid_move_chain(chain, self.cartan_type) + new_lusztig_datum = compute_new_lusztig_datum(enhanced_braid_chain, + pbw_datum.lusztig_datum) + return PBWDatum(self, new_long_word, new_lusztig_datum) + + @cached_method + def _root_list_from(self, reduced_word): + """ + Return the list of positive roots in the order determined by + ``reduced_word``. + + .. WARNING:: + + No error checking is done to verify that ``reduced_word`` + is reduced. + + INPUT: + + - ``reduced_word`` -- a tuple corresponding to a reduced word + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["A",2]) + sage: P._root_list_from((1,2,1)) + [alpha[1], alpha[1] + alpha[2], alpha[2]] + """ + al = self.root_lattice.simple_roots() + cur = [] + for i in reversed(reduced_word): + cur = [al[i]] + [x.simple_reflection(i) for x in cur] + return cur + + @cached_method + def _long_word_begin_with(self, i): + """ + Return a reduced expression of the long word which begins with ``i``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["C",3]) + sage: P._long_word_begin_with(1) + (1, 3, 2, 3, 1, 2, 3, 1, 2) + sage: P._long_word_begin_with(2) + (2, 3, 2, 3, 1, 2, 3, 2, 1) + sage: P._long_word_begin_with(3) + (3, 2, 3, 1, 2, 3, 1, 2, 1) + """ + si = self.weyl_group.simple_reflection(i) + w0 = self.weyl_group.long_element() + return tuple([i] + (si * w0).reduced_word()) + +#enhanced_braid_chain is an ugly data structure. +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef tuple compute_new_lusztig_datum(list enhanced_braid_chain, initial_lusztig_datum): + """ + Return the lusztig datum obtained by applying tropical Plücker + relations along ``enhanced_braid_chain`` starting with + ``initial_lusztig_datum``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: from sage.combinat.crystals.pbw_datum import compute_new_lusztig_datum + sage: ct = CartanType(['A', 2]) + sage: W = CoxeterGroup(ct) + sage: B = BraidMoveCalculator(W) + sage: chain = B.chain_of_reduced_words((1,2,1),(2,1,2)) + sage: enhanced_braid_chain = enhance_braid_move_chain(chain, ct) + sage: compute_new_lusztig_datum(enhanced_braid_chain,(1,0,1)) + (0, 1, 0) + + TESTS:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: from sage.combinat.crystals.pbw_datum import compute_new_lusztig_datum + sage: ct = CartanType(['A', 2]) + sage: W = CoxeterGroup(ct) + sage: B = BraidMoveCalculator(W) + sage: chain = B.chain_of_reduced_words((1,2,1), (2,1,2)) + sage: enhanced_braid_chain = enhance_braid_move_chain(chain, ct) + sage: compute_new_lusztig_datum(enhanced_braid_chain,(1,0,1)) == (0,1,0) + True + """ + cdef tuple interval_of_change + # Does not currently check that len(initial_lusztig_datum) is appropriate + cdef list new_lusztig_datum = list(initial_lusztig_datum) #shallow copy + cdef int i + for i in range(1, len(enhanced_braid_chain)): + interval_of_change, type_data = enhanced_braid_chain[i] + a,b = interval_of_change + old_interval_datum = new_lusztig_datum[a:b] + new_interval_datum = tropical_plucker_relation(type_data, old_interval_datum) + new_lusztig_datum[a:b] = new_interval_datum + return tuple(new_lusztig_datum) + +# The tropical plucker relations +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef tuple tropical_plucker_relation(tuple a, lusztig_datum): + r""" + Apply the tropical Plücker relation of type ``a`` to ``lusztig_datum``. + + The relations are obtained by tropicalizing the relations in + Proposition 7.1 of [BZ01]_. + + INPUT: + + - ``a`` -- a pair ``(x, y)`` of the off-diagonal entries of a + `2 \times 2` Cartan matrix + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import tropical_plucker_relation + sage: tropical_plucker_relation((0,0), (2,3)) + (3, 2) + sage: tropical_plucker_relation((-1,-1), (1,2,3)) + (4, 1, 2) + sage: tropical_plucker_relation((-1,-2), (1,2,3,4)) + (8, 1, 2, 3) + sage: tropical_plucker_relation((-2,-1), (1,2,3,4)) + (6, 1, 2, 3) + """ + if a == (0, 0): # A1xA1 + t1, t2 = lusztig_datum + return (t2, t1) + elif a == (-1, -1): # A2 + t1,t2,t3 = lusztig_datum + return (t2+t3-min(t1,t3), + min(t1,t3), + t1+t2-min(t1,t3)) + elif a == (-1, -2): # B2 + t1,t2,t3,t4 = lusztig_datum + pi1 = min(t1+t2,min(t1,t3)+t4) + pi2 = min(2*t1+t2,2*min(t1,t3)+t4) + return (t2+2*t3+t4-pi2, + pi2-pi1, + 2*pi1-pi2, + t1+t2+t3-pi1) + elif a == (-1, -3): # G2 + t1,t2,t3,t4,t5,t6 = lusztig_datum + pi1 = min(t1+t2+2*t3+t4, + t1+t2+2*min(t3,t5)+t6, + min(t1,t3)+t4+2*t5+t6) + pi2 = min(2*t1+2*t2+3*t3+t4, + 2*t1+2*t2+3*min(t3,t5)+t6, + 2*min(t1,t3)+2*t4+3*t5+t6, + t1+t2+t4+2*t5+t6+min(t1+t3,2*t3,t3+t5,t1+t5)) + pi3 = min(3*t1+2*t2+3*t3+t4, + 3*t1+2*t2+3*min(t3,t5)+t6, + 3*min(t1,t3)+2*t4+3*t5+t6, + 2*t1+t2+t4+2*t5+t6+min(t1+t3,2*t3,t3+t5,t1+t5)) + pi4 = min(2*t1+2*t2+3*t3+t4+min(t1+t2+3*t3+t4, + t1+t2+3*min(t3,t5)+t6, + min(t1+t3,2*t3,t3+t5,t1+t5)+t4+2*t5+t6), + 2*t6+3*min(t1+t2+2*min(t3,t5),min(t1,t3)+t4+2*t5)) + return (t2+3*t3+2*t4+3*t5+t6-pi3, + pi3-pi2, + 3*pi2-pi3-pi4, + pi4-pi1-pi2, + 3*pi1-pi4, + t1+t2+2*t3+t4+t5-pi1) + else: # (-1,-2) and (-1,-3) + reversed_lusztig_datum = tuple(reversed(lusztig_datum)) + return tuple(reversed(tropical_plucker_relation((a[1], a[0]), + reversed_lusztig_datum))) + +# Maybe we need to be more specific, and pass not the Cartan type, but the root lattice? +# TODO: Move to PBW_data? +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef list enhance_braid_move_chain(braid_move_chain, cartan_type): + r""" + Return a list of tuples that records the data of the long words in + ``braid_move_chain`` plus the data of the intervals where the braid moves + occur and the data of the off-diagonal entries of the `2 \times 2` Cartan + submatrices of each braid move. + + INPUT: + + - ``braid_move_chain`` -- a chain of reduced words in the Weyl group + of ``cartan_type`` + - ``cartan_type`` -- a finite Cartan type + + OUTPUT: + + A list of 2-tuples + ``(interval_of_change, cartan_sub_matrix)`` where + + - ``interval_of_change`` is the (half-open) interval of indices where + the braid move occurs; this is `None` for the first tuple + - ``cartan_sub_matrix`` is the off-diagonal entries of the `2 \times 2` + submatrix of the cartan matrix corresponding to the braid move; + this is `None` for the first tuple + + For a matrix:: + + [2 a] + [b 2] + + the ``cartan_sub_matrix`` is the pair ``(a, b)``. + + TESTS:: + + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: braid_chain = [(1, 2, 1, 3, 2, 1), + ....: (1, 2, 3, 1, 2, 1), + ....: (1, 2, 3, 2, 1, 2), + ....: (1, 3, 2, 3, 1, 2), + ....: (3, 1, 2, 3, 1, 2), + ....: (3, 1, 2, 1, 3, 2), + ....: (3, 2, 1, 2, 3, 2), + ....: (3, 2, 1, 3, 2, 3)] + sage: enhanced_chain = enhance_braid_move_chain(braid_chain, CartanType(["A",5])) + sage: enhanced_chain[0] + (None, None) + sage: enhanced_chain[7] + ((3, 6), (-1, -1)) + """ + cdef int i, j + cdef int k, pos, first, last + cdef tuple interval_of_change, cartan_sub_matrix + cdef list output_list = [] + output_list.append( (None, None) ) + cdef tuple previous_word = (braid_move_chain[0]) + cdef tuple current_word + cartan_matrix = cartan_type.cartan_matrix() + cdef int ell = len(previous_word) + # TODO - Optimize this by avoiding calls to here? + # This likely could be done when performing chain_of_reduced_words + # Things in here get called the most (about 50x more than enhance_braid_move_chain) + for pos in range(1, len(braid_move_chain)): + # This gets the smallest continguous half-open interval [a, b) + # that contains the indices where current_word and previous_word differ. + current_word = (braid_move_chain[pos]) + for k in range(ell): + i = previous_word[k] + j = current_word[k] + if i != j: + i -= 1 # -1 for indexing + j -= 1 # -1 for indexing + first = k + break + for k in range(ell-1, k-1, -1): + if previous_word[k] != current_word[k]: + last = k + 1 + break + + cartan_sub_matrix = (cartan_matrix[i,j], cartan_matrix[j,i]) + output_list.append( ((first, last), cartan_sub_matrix) ) + previous_word = current_word + return output_list + diff --git a/src/sage/combinat/crystals/polyhedral_realization.py b/src/sage/combinat/crystals/polyhedral_realization.py index 4707a0b7a1b..27775dd9926 100644 --- a/src/sage/combinat/crystals/polyhedral_realization.py +++ b/src/sage/combinat/crystals/polyhedral_realization.py @@ -266,7 +266,7 @@ def phi(self, i): sage: [elt.phi(i) for i in B.index_set()] [1, 1, 0] """ - P = self[-1].parent().weight_lattice_realization() + P = self.parent().weight_lattice_realization() h = P.simple_coroots() omega = P(self.weight()).scalar(h[i]) return self.epsilon(i) + omega @@ -297,7 +297,7 @@ def e(self, i): if pos is None or pos <= nf: return None - l = self._list[:] + l = list(self) l[-pos] = crystal if pos <= 2*nf and all(b._m == 0 for b in l[-2*nf:-nf]): return self.__class__(self.parent(), l[:-nf]) @@ -327,10 +327,10 @@ def f(self, i): nf = len(self.parent()._factors) if pos <= nf: - l = self._list[:] + l = list(self) l[-pos] = l[-pos].f(i) return self.__class__(self.parent(), l + self.parent()._tp) - return self.set_index(-pos, crystal) + return self._set_index(-pos, crystal) def truncate(self, k=None): r""" @@ -359,13 +359,13 @@ def truncate(self, k=None): [-1, -2, -1, 0, 0, 0, 0, 0, 0, 0] """ if k is None: - k = len(self._list) + k = len(self) P = self.parent().finite_tensor_product(k) - if k <= len(self._list): - l = self._list[:k] + if k <= len(self): + l = self[:k] else: - l = self._list[:] + l = list(self) N = len(self.parent()._tp) while len(l) < k: i = len(l) % N diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index 15f43642ad1..476fa40907c 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -82,10 +82,10 @@ class Subcrystal(UniqueRepresentation, Parent): 8 sage: list(T) [[[1, 1], [3]], - [[1, 2], [3]], [[1, 1], [2]], - [[1, 2], [2]], + [[1, 2], [3]], [[2, 2], [3]], + [[1, 2], [2]], [[2, 3], [3]], [[1, 3], [2]], [[1, 3], [3]]] @@ -321,10 +321,10 @@ def _richcmp_(self, other, op): [[1, 2]](0), [[1, -2]](0), [[2, 2]](0), - [[2, -1]](1), - [[-1, -1]](1), [](1), + [[2, -1]](1), [[-2, -1]](1), + [[-1, -1]](1), [[-1, -1]](2)] For != operator:: diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 4bd5194455e..cfd7856860e 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -32,26 +32,23 @@ from __future__ import absolute_import import operator -from sage.misc.latex import latex -from sage.misc.cachefunc import cached_method, cached_in_parent_method +from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent -from sage.structure.element import parent +from sage.structure.unique_representation import UniqueRepresentation from sage.structure.global_options import GlobalOptions from sage.categories.category import Category from sage.categories.cartesian_product import cartesian_product from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.regular_crystals import RegularCrystals from sage.categories.sets_cat import Sets -from sage.categories.map import Map from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.combinat import CombinatorialElement from sage.combinat.partition import Partition -from sage.combinat.tableau import Tableau from .letters import CrystalOfLetters from .spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus +from sage.combinat.crystals.tensor_product_element import (TensorProductOfCrystalsElement, + TensorProductOfRegularCrystalsElement, CrystalOfTableauxElement) from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model -from sage.rings.all import ZZ ############################################################################## # Until trunc gets implemented in sage.function.other @@ -78,222 +75,6 @@ def trunc(i): # Support classes ############################################################################## -from sage.structure.unique_representation import UniqueRepresentation - -class TestParent(UniqueRepresentation, Parent): - """ - A parent for tests. - """ - def _repr_(self): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import TestParent - sage: TestParent() - A parent for tests - """ - return "A parent for tests" - -class ImmutableListWithParent(CombinatorialElement): - r""" - A class for lists having a parent - - Specification: any subclass ``C`` should implement ``__init__`` which - accepts the following form ``C(parent, list = list)`` - - EXAMPLES: - - We create an immutable list whose parent is the class list:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l._list - [1, 2, 3] - sage: l.parent() - A parent for tests - sage: l.sibling([2,1]) == ImmutableListWithParent(TestParent(), [2,1]) - True - sage: l.reversed() - [3, 2, 1] - sage: l.set_index(1,4) - [1, 4, 3] - - TESTS:: - - sage: TestSuite(l).run(skip = "_test_category") - """ - def _repr_(self): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l._repr_() - '[1, 2, 3]' - """ - return repr(self._list) - - def __eq__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l == l - True - sage: l == m - False - sage: m == n - False - """ - return self.__class__ is other.__class__ and \ - self.parent() == other.parent() and \ - self._list == other._list - - def __ne__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l != l - False - sage: l != m - True - sage: m != n - True - """ - return not self == other - - def __lt__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l < l - False - sage: l < m - False - sage: m < n - True - """ - if parent(self) is not parent(other): - return NotImplemented - return self._list.__lt__(other._list) - - def __le__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l <= l - True - sage: l <= m - True - sage: m <= n - True - """ - return self == other or self.__lt__(other) - - def __gt__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l > l - False - sage: l > m - False - sage: m > n - False - """ - if parent(self) is not parent(other): - return NotImplemented - return other.__lt__(self) - - def __ge__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l >= l - True - sage: l >= m - True - sage: m >= n - False - """ - return self == other or self.__gt__(other) - - def sibling(self, l): - """ - Returns an :class:`ImmutableListWithParent` object whose list is - ``l`` and whose parent is the same as the parent of ``self``. - - Note that the implementation of this function makes an assumption - about the constructor for subclasses. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = l.sibling([2,3,4]); m - [2, 3, 4] - sage: m.parent() - A parent for tests - """ - return self.__class__(self.parent(), list=l) - - def reversed(self): - """ - Returns the sibling of ``self`` which is obtained by reversing the - elements of`` self``. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l.reversed() - [3, 2, 1] - """ - return self.sibling([ i for i in reversed(self._list)]) - - def set_index(self, k, value): - """ - Returns the sibling of ``self`` obtained by setting the - `k^{th}` entry of self to value. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l.set_index(0,2) - [2, 2, 3] - sage: l.set_index(1,4) - [1, 4, 3] - sage: _.parent() - A parent for tests - """ - l = [i for i in self._list] - l[k] = value - return self.sibling(l) - class CrystalOfWords(UniqueRepresentation, Parent): """ Auxiliary class to provide a call method to create tensor product elements. @@ -319,64 +100,8 @@ def _element_constructor_(self, *crystalElements): """ return self.element_class(self, list(crystalElements)) - def one_dimensional_configuration_sum(self, q=None, group_components=True): - r""" - Computes the one-dimensional configuration sum. - - INPUT: - - - ``q`` -- (default: ``None``) a variable or ``None``; if ``None``, - a variable `q` is set in the code - - ``group_components`` -- (default: ``True``) boolean; if ``True``, - then the terms are grouped by classical component - - The one-dimensional configuration sum is the sum of the weights of all - elements in the crystal weighted by the energy function. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: T.one_dimensional_configuration_sum() - B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + (q+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] - sage: R. = ZZ[] - sage: T.one_dimensional_configuration_sum(t, False) - B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] - - sage: R = RootSystem(['A',2,1]) - sage: La = R.weight_space().basis() - sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) - sage: LS.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum() # long time - True - - TESTS:: - - sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) - sage: T = crystals.TensorProduct(K1,K2) - sage: T.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum(group_components=False) - True - - sage: RC = RiggedConfigurations(['A',3,1],[[1,1],[1,2]]) - sage: B = crystals.KirillovReshetikhin(['A',3,1],1,1) - sage: B1 = crystals.KirillovReshetikhin(['A',3,1],1,2) - sage: T = crystals.TensorProduct(B,B1) - sage: RC.fermionic_formula() == T.one_dimensional_configuration_sum() - True - """ - if q is None: - from sage.rings.all import QQ - q = QQ['q'].gens()[0] - P0 = self.weight_lattice_realization().classical() - B = P0.algebra(q.parent()) - if group_components: - G = self.digraph(index_set = self.cartan_type().classical().index_set()) - C = G.connected_components() - return sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C) - return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self) - + class Element(TensorProductOfCrystalsElement): + pass class TensorProductOfCrystals(CrystalOfWords): r""" @@ -518,7 +243,8 @@ class TensorProductOfCrystals(CrystalOfWords): It has `8` elements:: sage: T.list() - [[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]] + [[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], + [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]] One can also check the Cartan type of the crystal:: @@ -536,7 +262,7 @@ class TensorProductOfCrystals(CrystalOfWords): 24 sage: TestSuite(T).run() sage: T.module_generators - [[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]] + ([[[1], [2]], [[1]]], [[[2], [3]], [[1]]]) sage: [x.weight() for x in T.module_generators] [(2, 1, 0, 0), (1, 1, 1, 0)] @@ -766,7 +492,7 @@ def __init__(self, crystals, generators, cartan_type): Parent.__init__(self, category = category) self.crystals = crystals self._cartan_type = cartan_type - self.module_generators = [ self(*x) for x in generators ] + self.module_generators = tuple([self(*x) for x in generators]) def _repr_(self): """ @@ -890,803 +616,19 @@ def weight_lattice_realization(self): return cm.common_parent(*[crystal.weight_lattice_realization() for crystal in self.crystals]) -class TensorProductOfCrystalsElement(ImmutableListWithParent): - r""" - A class for elements of tensor products of crystals. - """ - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',3]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)) - [1, 2] - """ - if self.parent().options.convention == "Kashiwara": - return repr(list(reversed(self._list))) - return repr(self._list) - - def _latex_(self): - r""" - Return latex code for ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(["A",2]) - sage: D = crystals.Tableaux(["A",2], shape=[2]) - sage: E = crystals.TensorProduct(C,D) - sage: latex(E.module_generators[0]) - 1 \otimes {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} - \lr{1}&\lr{1}\\\cline{1-2} - \end{array}$} - } - """ - return ' \otimes '.join(latex(c) for c in self) - - def _ascii_art_(self): - """ - Return an ASCII art representation of ``self``. - - EXAMPLES:: - - sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) - sage: ascii_art(KT.module_generators[0]) - 1 1 1 - 2 2 2 # 1 # 1 1 - 3 3 3 2 - -4 -4 -4 - """ - from sage.typeset.ascii_art import ascii_art, AsciiArt - s = ascii_art(self[0]) - s._baseline = s._h // 2 - ret = s - for tableau in self[1:]: - s = ascii_art(tableau) - s._baseline = s._h // 2 - ret += AsciiArt([" # "]) + s - return ret - - def __lt__(self, other): - """ - Non elements of the crystal are incomparable with elements of the crystal - (or should it return NotImplemented?). - - Comparison of two elements of this crystal: - - - different length: incomparable - - otherwise lexicographically, considering ``self[i]`` and ``other[i]`` - as incomparable if ``self[i] < other[i]`` returns NotImplemented - """ - if parent(self) is not parent(other): - return False - if len(self) != len(other): - return False - for i in range(len(self)): - if (self[i] < other[i]): - return True - if (other[i] < self[i]): - return False - return False - - def _repr_diagram(self): - r""" - Return a string representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A',3], shape=[3,1]) - sage: D = crystals.Tableaux(['A',3], shape=[1]) - sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) - sage: T = crystals.TensorProduct(C,D,E) - sage: print(T.module_generators[0]._repr_diagram()) - 1 1 1 (X) 1 (X) 1 1 - 2 2 2 - 3 3 - """ - pplist = [] - max_widths = [] - num_cols = len(self) - for c in self: - try: - pplist.append(c._repr_diagram().split('\n')) - except AttributeError: - pplist.append(c._repr_().split('\n')) - max_widths.append(max(map(len, pplist[-1]))) - num_rows = max(map(len, pplist)) - ret = "" - for i in range(num_rows): - if i > 0: - ret += '\n' - for j in range(num_cols): - if j > 0: - if i == 0: - ret += ' (X) ' - else: - ret += ' ' - if i < len(pplist[j]): - ret += pplist[j][i] - ret += ' '*(max_widths[j] - len(pplist[j][i])) - else: - ret += ' '*max_widths[j] - return ret - - def pp(self): - """ - Pretty print ``self``. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A',3], shape=[3,1]) - sage: D = crystals.Tableaux(['A',3], shape=[1]) - sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) - sage: T = crystals.TensorProduct(C,D,E) - sage: T.module_generators[0].pp() - 1 1 1 (X) 1 (X) 1 1 - 2 2 2 - 3 3 - """ - print(self._repr_diagram()) - - def weight(self): - r""" - Return the weight of ``self``. - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("A3") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([2,1,3]) - sage: b2 = B.highest_weight_vector().f(1) - sage: t = T(b2, b1) - sage: t - [[[1, 1, 1, 2], [2, 2], [3]], [[1, 1, 1, 1, 2], [2, 2, 4], [3]]] - sage: t.weight() - (-2, 1, 0, 1) - """ - return sum(self[i].weight() for i in range(len(self))) - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("G2") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f(2) - sage: b2 = B.highest_weight_vector().f_string([2,2,1]) - sage: t = T(b2, b1) - sage: [t.epsilon(i) for i in B.index_set()] - [0, 3] - """ - return max(self._sig(i, k) for k in range(1, len(self)+1)) - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: B = crystals.GeneralizedYoungWalls(2,La[0]+La[1]) - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([1,0]) - sage: b2 = B.highest_weight_vector().f_string([0,1]) - sage: t = T(b2, b1) - sage: [t.phi(i) for i in B.index_set()] - [1, 1, 4] - - TESTS: - - Check that :trac:`15462` is fixed:: - - sage: B = crystals.Tableaux(['A',2], shape=[2,1]) - sage: La = RootSystem(['A',2]).ambient_space().fundamental_weights() - sage: T = crystals.TensorProduct(crystals.elementary.T(['A',2], La[1]+La[2]), B) - sage: t = T.an_element() - sage: t.phi(1) - 2 - sage: t.phi(2) - 2 - """ - P = self[-1].parent().weight_lattice_realization() - h = P.simple_coroots() - omega = P(self.weight()).scalar(h[i]) - return max([omega + self._sig(i, k) for k in range(1, len(self)+1)]) - - @cached_in_parent_method - def _sig(self,i,k): - r""" - Return `a_i(k)` of ``self``. - - The value `a_i(k)` of a crystal `b = b_N \otimes \cdots \otimes b_1` - is defined as: - - .. MATH:: - - a_i(k) = \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle h_i, - \mathrm{wt}(b_j) \rangle - - where `\mathrm{wt}` is the :meth:`weight` of `b_j`. - - INPUT: - - - ``i`` -- An element of the index set - - - ``k`` -- The (1-based) index of the tensor factor of ``self`` - - EXAMPLES:: - - sage: B = crystals.infinity.GeneralizedYoungWalls(3) - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([0,3,1]) - sage: b2 = B.highest_weight_vector().f_string([3,2,1,0,2,3]) - sage: t = T(b1, b2) - sage: [[t._sig(i,k) for k in range(1,len(t)+1)] for i in B.index_set()] - [[0, -1], [0, 0], [0, 1], [1, 2]] - - TESTS: - - Check that :trac:`18469` is fixed:: - - sage: E1 = crystals.elementary.B(['A',2], 1) - sage: E2 = crystals.elementary.B(['A',2], 2) - sage: T = crystals.TensorProduct(E1, E2) - sage: x = T(E1.module_generators[0], E2.module_generators[0]); x - [0, 0] - sage: [[x._sig(i,k) for k in range(1,3)] for i in T.index_set()] - [[-inf, 0], [0, -inf]] - sage: x.f(1) - [-1, 0] - sage: x.e(1) - [1, 0] - """ - if k == 1: - return self[-1].epsilon(i) - ep = self[-k].epsilon(i) - if ep == float("-inf"): - return ep - - P = self[-1].parent().weight_lattice_realization() - h = P.simple_coroots() - wt = sum(P(self[-j].weight()) for j in range(1, k)) - return ep - P(wt).scalar(h[i]) - - def e(self,i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("D4") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([1,4,3]) - sage: b2 = B.highest_weight_vector().f_string([2,2,3,1,4]) - sage: t = T(b2, b1) - sage: t.e(1) - [[[1, 1, 1, 1, 1], [2, 2, 3, -3], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - sage: t.e(2) - sage: t.e(3) - [[[1, 1, 1, 1, 1, 2], [2, 2, 3, -4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - sage: t.e(4) - [[[1, 1, 1, 1, 1, 2], [2, 2, 3, 4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - """ - N = len(self) + 1 - for k in range(1, N): - if all(self._sig(i,k) > self._sig(i,j) for j in range(1, k)) and \ - all(self._sig(i,k) >= self._sig(i,j) for j in range(k+1, N)): - crystal = self[-k].e(i) - if crystal is None: - return None - return self.set_index(-k, crystal) - return None - - def f(self,i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: La = RootSystem(['A',3,1]).weight_lattice(extended=True).fundamental_weights() - sage: B = crystals.GeneralizedYoungWalls(3,La[0]) - sage: T = crystals.TensorProduct(B,B,B) - sage: b1 = B.highest_weight_vector().f_string([0,3]) - sage: b2 = B.highest_weight_vector().f_string([0]) - sage: b3 = B.highest_weight_vector() - sage: t = T(b3, b2, b1) - sage: t.f(0) - [[[0]], [[0]], [[0, 3]]] - sage: t.f(1) - [[], [[0]], [[0, 3], [1]]] - sage: t.f(2) - [[], [[0]], [[0, 3, 2]]] - sage: t.f(3) - [[], [[0, 3]], [[0, 3]]] - """ - N = len(self) + 1 - for k in range(1, N): - if all(self._sig(i,k) >= self._sig(i,j) for j in range(1, k)) and \ - all(self._sig(i,k) > self._sig(i,j) for j in range(k+1, N)): - crystal = self[-k].f(i) - if crystal is None: - return None - return self.set_index(-k, crystal) - return None - -class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): - """ - Element class for a tensor product of regular crystals. - - TESTS:: - - sage: C = crystals.Letters(['A',2]) - sage: T = crystals.TensorProduct(C, C) - sage: elt = T(C(1), C(2)) - sage: from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement - sage: isinstance(elt, TensorProductOfRegularCrystalsElement) - True - """ - def e(self, i): - """ - Return the action of `e_i` on ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)).e(1) == T(C(1),C(1)) - True - sage: T(C(2),C(1)).e(1) is None - True - sage: T(C(2),C(2)).e(1) == T(C(1),C(2)) - True - """ - if i not in self.index_set(): - raise ValueError("i must be in the index set") - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - return self.set_index(k, self[k].e(i)) - - def weight(self): - """ - Return the weight of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',3]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)).weight() - (1, 1, 0, 0) - sage: T = crystals.Tableaux(['D',4],shape=[]) - sage: T.list()[0].weight() - (0, 0, 0, 0) - """ - return sum((self[j].weight() for j in range(len(self))), self.parent().weight_lattice_realization().zero()) - - def f(self, i): - """ - Return the action of `f_i` on ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).f(1) - [1, 2] - sage: T(C(1),C(2)).f(1) - [2, 2] - sage: T(C(2),C(1)).f(1) is None - True - """ - if i not in self.index_set(): - raise ValueError("i must be in the index set") - position = self.positions_of_unmatched_minus(i) - if position == []: - return None - k = position[len(position)-1] - return self.set_index(k, self[k].f(i)) - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).phi(1) - 2 - sage: T(C(1),C(2)).phi(1) - 1 - sage: T(C(2),C(1)).phi(1) - 0 - """ - self = self.reversed() - height = 0 - for j in range(len(self)): - plus = self[j].epsilon(i) - minus = self[j].phi(i) - if height-plus < 0: - height = minus - else: - height = height - plus + minus - return height - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).epsilon(1) - 0 - sage: T(C(1),C(2)).epsilon(1) - 1 - sage: T(C(2),C(1)).epsilon(1) - 0 - """ - height = 0 - for j in range(len(self)): - minus = self[j].phi(i) - plus = self[j].epsilon(i) - if height-minus < 0: - height = plus - else: - height = height - minus + plus - return height - - def positions_of_unmatched_minus(self, i, dual=False, reverse=False): - """ - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(2),C(1)).positions_of_unmatched_minus(1) - [] - sage: T(C(1),C(2)).positions_of_unmatched_minus(1) - [0] - """ - unmatched_plus = [] - height = 0 - if reverse: - self = self.reversed() - if not dual: - for j in range(len(self)): - minus = self[j].phi(i) - plus = self[j].epsilon(i) - if height-minus < 0: - unmatched_plus.append(j) - height = plus - else: - height = height - minus + plus - else: - for j in range(len(self)): - plus = self[j].epsilon(i) - minus = self[j].phi(i) - if height-plus < 0: - unmatched_plus.append(j) - height = minus - else: - height = height - plus + minus - return unmatched_plus - - def positions_of_unmatched_plus(self, i): - """ - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(2),C(1)).positions_of_unmatched_plus(1) - [] - sage: T(C(1),C(2)).positions_of_unmatched_plus(1) - [1] - """ - l = self.positions_of_unmatched_minus(i, dual=True, reverse=True) - l.reverse() - return [len(self)-1-l[j] for j in range(len(l))] - - def energy_function(self, algorithm=None): - r""" - Return the energy function of ``self``. - - ALGORITHM: - - .. RUBRIC:: definition - - Let `T` be a tensor product of Kirillov-Reshetikhin - crystals. Let `R_i` and `H_i` be the combinatorial - `R`-matrix and local energy functions, respectively, acting - on the `i` and `i+1` factors. Let `D_B` be the energy - function of a single Kirillov-Reshetikhin crystal. The - *energy function* is given by - - .. MATH:: - - D = \sum_{j > i} H_i R_{i+1} R_{i+2} \cdots R_{j-1} - + \sum_j D_B R_1 R_2 \cdots R_{j-1}, - - where `D_B` acts on the rightmost factor. - - .. RUBRIC:: grading - - If ``self`` is an element of `T`, a tensor product of - perfect crystals of the same level, then use the affine - grading to determine the energy. Specifically, let `g` - denote the affine grading of ``self`` and `d` the affine - grading of the maximal vector in `T`. Then the energy - of ``self`` is given by `d - g`. - - For more details, see Theorem 7.5 in [SchillingTingley2011]_. - - INPUT: - - - ``algorithm`` -- (default: ``None``) use one of the - following algorithms to determine the energy function: - - * ``'definition'`` - use the definition of the energy - function; - * ``'grading'`` - use the affine grading; - - if not specified, then this uses ``'grading'`` if all - factors are perfect of the same level and otherwise - this uses ``'definition'`` - - OUTPUT: an integer - - REFERENCES: - - .. [SchillingTingley2011] \A. Schilling, P. Tingley. - *Demazure crystals, Kirillov-Reshetikhin crystals, and - the energy function*. - Electronic Journal of Combinatorics. **19(2)**. 2012. - :arXiv:`1104.2359` - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = sorted([x for x in T if x.is_highest_weight([1,2])]) - sage: for b in hw: - ....: print("{} {}".format(b, b.energy_function())) - [[[1]], [[1]], [[1]]] 0 - [[[1]], [[2]], [[1]]] 2 - [[[2]], [[1]], [[1]]] 1 - [[[3]], [[2]], [[1]]] 3 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: T = crystals.TensorProduct(K,K) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.energy_function())) - [[], []] 4 - [[], [[1, 1]]] 1 - [[[1, 1]], []] 3 - [[[1, 1]], [[1, 1]]] 0 - [[[1, 2]], [[1, 1]]] 1 - [[[2, 2]], [[1, 1]]] 2 - [[[-1, -1]], [[1, 1]]] 2 - [[[1, -1]], [[1, 1]]] 2 - [[[2, -1]], [[1, 1]]] 2 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: T = crystals.TensorProduct(K) - sage: t = T.module_generators[0] - sage: t.energy_function('grading') - Traceback (most recent call last): - ... - NotImplementedError: all crystals in the tensor product need to be perfect of the same level - - TESTS:: - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2, 2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: all(b.energy_function() == b.energy_function(algorithm='definition') - ....: for b in hw) - True - """ - C = self.parent().crystals[0] - ell = ceil(C.s()/C.cartan_type().c()[C.r()]) - is_perfect = all(ell == K.s()/K.cartan_type().c()[K.r()] - for K in self.parent().crystals) - if algorithm is None: - if is_perfect: - algorithm = 'grading' - else: - algorithm = 'definition' - - if algorithm == 'grading': - if not is_perfect: - raise NotImplementedError("all crystals in the tensor product need to be perfect of the same level") - t = self.parent()(*[K.module_generator() for K in self.parent().crystals]) - d = t.affine_grading() - return d - self.affine_grading() - - if algorithm == 'definition': - # Setup - energy = ZZ.zero() - R_mats = [[K.R_matrix(Kp) for Kp in self.parent().crystals[i+1:]] - for i,K in enumerate(self.parent().crystals)] - H_funcs = [[K.local_energy_function(Kp) for Kp in self.parent().crystals[i+1:]] - for i,K in enumerate(self.parent().crystals)] - - for i,b in enumerate(self): - for j,R in enumerate(R_mats[i]): - H = H_funcs[i][j] - bp = self[i+j+1] - T = R.domain() - t = T(b, bp) - energy += H(t) - b = R(t)[1] - energy += b.energy_function() # D contribution - return energy - else: - raise ValueError("invalid algorithm") - - def affine_grading(self): - r""" - Returns the affine grading of `self`. - - The affine grading is only defined when ``self`` is an element of a - tensor product of affine Kirillov-Reshetikhin crystals. It is - calculated by finding a path from ``self`` to a ground state path - using the helper method :meth:`e_string_to_ground_state` and counting - the number of affine Kashiwara operators `e_0` applied on the way. - - INPUT: - - - ``self`` -- an element of a tensor product of Kirillov-Reshetikhin - crystals - - OUTPUT: an integer - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0] - sage: t.affine_grading() - 1 - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.affine_grading())) - [[[1]], [[1]], [[1]]] 3 - [[[1]], [[2]], [[1]]] 1 - [[[2]], [[1]], [[1]]] 2 - [[[3]], [[2]], [[1]]] 0 - - sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.affine_grading())) - [[[1]], [[1]], [[1]]] 2 - [[[1]], [[2]], [[1]]] 1 - [[[1]], [[-1]], [[1]]] 0 - [[[2]], [[1]], [[1]]] 1 - [[[-2]], [[2]], [[1]]] 0 - [[[-1]], [[1]], [[1]]] 1 - """ - return self.e_string_to_ground_state().count(0) - - @cached_method - def e_string_to_ground_state(self): - r""" - Returns a string of integers in the index set `(i_1,\ldots,i_k)` such - that `e_{i_k} \cdots e_{i_1}` of ``self`` is the ground state. - - This method is only defined when ``self`` is an element of a tensor - product of affine Kirillov-Reshetikhin crystals. It calculates a path - from ``self`` to a ground state path using Demazure arrows as defined - in Lemma 7.3 in [SchillingTingley2011]_. - - INPUT: - - - ``self`` -- an element of a tensor product of Kirillov-Reshetikhin - crystals - - OUTPUT: a tuple of integers `(i_1,\ldots,i_k)` - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0] - sage: t.e_string_to_ground_state() - (0, 2) - - sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0]; t - [[[1]], [[1]]] - sage: t.e_string_to_ground_state() - (0,) - sage: x=t.e(0) - sage: x.e_string_to_ground_state() - () - sage: y=t.f_string([1,2,1,1,0]); y - [[[2]], [[1]]] - sage: y.e_string_to_ground_state() - () - - TESTS: - - Check that :trac:`22882` is fixed:: - - sage: K = crystals.KirillovReshetikhin(CartanType(['A',6,2]).dual(), 1,1) - sage: T = tensor([K,K,K]) - sage: hw = [x for x in T if x.is_highest_weight([1,2,3])] - sage: gs = T(K(0), K(0), K(0)) - sage: all(elt.e_string(elt.e_string_to_ground_state()) == gs - ....: for elt in hw) - True - sage: all(elt.energy_function() == elt.energy_function('definition') - ....: for elt in hw) - True - """ - from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux - if self.parent().crystals[0].__module__ != 'sage.combinat.crystals.kirillov_reshetikhin' and \ - not isinstance(self.parent().crystals[0], KirillovReshetikhinTableaux): - raise ValueError("All crystals in the tensor product need to be Kirillov-Reshetikhin crystals") - ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals) - if self.cartan_type().dual().type() == 'BC': - I = self.cartan_type().index_set() - for i in I[:-1]: - if self.epsilon(i) > 0: - return (i,) + (self.e(i)).e_string_to_ground_state() - if self.epsilon(I[-1]) > ell: - return (I[-1],) + (self.e(I[-1])).e_string_to_ground_state() - return () - - I = self.cartan_type().classical().index_set() - for i in I: - if self.epsilon(i) > 0: - return (i,) + (self.e(i)).e_string_to_ground_state() - if self.epsilon(0) > ell: - return (0,) + (self.e(0)).e_string_to_ground_state() - return () - -CrystalOfWords.Element = TensorProductOfCrystalsElement - class FullTensorProductOfRegularCrystals(FullTensorProductOfCrystals): """ Full tensor product of regular crystals. """ - Element = TensorProductOfRegularCrystalsElement + class Element(TensorProductOfRegularCrystalsElement): + pass class TensorProductOfRegularCrystalsWithGenerators(TensorProductOfCrystalsWithGenerators): """ Tensor product of regular crystals with a generating set. """ - Element = TensorProductOfRegularCrystalsElement + class Element(TensorProductOfRegularCrystalsElement): + pass ######################################################### ## Crystal of tableaux @@ -1753,7 +695,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T = crystals.Tableaux(['A',2], shape = [3,2]) sage: T.module_generators[0] [[1, 1, 1], [2, 2]] - sage: T.module_generators[0]._list + sage: list(T.module_generators[0]) [2, 1, 2, 1, 1] To create a tableau, one can use:: @@ -1790,7 +732,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T.cardinality() 48 sage: T.module_generators - [[+++, [[1]]]] + ([+++, [[1]]],) sage: TestSuite(T).run() sage: T = crystals.Tableaux(['D',3],shape=[3/2,1/2,-1/2]); T @@ -1798,7 +740,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T.cardinality() 20 sage: T.module_generators - [[++-, [[1]]]] + ([++-, [[1]]],) sage: TestSuite(T).run() TESTS: @@ -1829,18 +771,18 @@ class CrystalOfTableaux(CrystalOfWords): sage: T = crystals.Tableaux(['A',3], shape = [2,2]) sage: C = T.letters - sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(rows = [[1,2],[3,4]])) == [C(3),C(1),C(4),C(2)] True - sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(columns = [[3,1],[4,2]])) == [C(3),C(1),C(4),C(2)] True For compatibility with :func:`~sage.combinat.crystals.tensor_product.TensorProductOfCrystals` we need to accept as input the internal list or sequence of elements:: - sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(list = [3,1,4,2])) == [C(3),C(1),C(4),C(2)] True - sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(3,1,4,2)) == [C(3),C(1),C(4),C(2)] True The next example checks whether a given tableau is in fact a valid @@ -1898,13 +840,13 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): elif all(shape[-1]<0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: - raise ValueError("In type D spins should all be positive or negative") + raise ValueError("in type D spins should all be positive or negative") else: if any( i < 0 for shape in spin_shapes for i in shape): raise ValueError("shapes should all be partitions") S = CrystalOfSpins(cartan_type) - B = CrystalOfTableaux(cartan_type, shapes = shapes) - T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators]) + B = CrystalOfTableaux(cartan_type, shapes=shapes) + T = TensorProductOfCrystals(S, B, generators=[[S.module_generators[0],x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes))) T.shapes = spin_shapes return T @@ -1972,12 +914,12 @@ def module_generator(self, shape): type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True - shape = shape[:-1]+(-shape[type[1]-1],) + shape = shape[:-1] + (-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns - module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))]) + module_generator = flatten([[val-i for i in range(val)] for val in p]) if invert: module_generator = [(-x if x == type[1] else x) for x in module_generator] return self(list=[self.letters(x) for x in module_generator]) @@ -1997,420 +939,11 @@ def _element_constructor_(self, *args, **options): """ return self.element_class(self, *args, **options) - - -class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): - """ - Element in a crystal of tableaux. - """ - def __init__(self, parent, *args, **options): - """ - There are several ways to input tableaux, by rows, - by columns, as the list of column elements, or as a sequence of numbers - in column reading. - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t - [[1, 2], [3, 4]] - sage: TestSuite(t).run() - - sage: t = T(columns=[[3,1],[4,2]]) - sage: t - [[1, 2], [3, 4]] - sage: TestSuite(t).run() - - sage: t = T(list=[3,1,4,2]) - sage: t - [[1, 2], [3, 4]] - - sage: t = T(3,1,4,2) - sage: t - [[1, 2], [3, 4]] - - Currently inputting the empty tableau as an empty sequence is - broken due to a bug in the generic __call__ method (see :trac:`8648`). - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape=[]) - sage: t = T() - sage: t._list - [0] - - TESTS: - - Integer types that are not a Sage ``Integer`` (such as a Python ``int`` - and typically arise from compiled code) were not converted into a - letter. This caused certain functions to fail. This is fixed in - :trac:`13204`:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(list=[int(3),1,4,2]) - sage: type(t[0]) - - sage: t = T(list=[3,int(1),4,2]) - sage: type(t[1]) - - sage: C = crystals.KirillovReshetikhin(['A',int(3),1], 1,1) - sage: C[0].e(0) - [[4]] - """ - if len(args) == 1: - if isinstance(args[0], Tableau): - options['rows'] = args[0] - if 'list' in options: - the_list = options['list'] - elif 'rows' in options: - rows = options['rows'] -# the_list=Tableau(rows).to_word_by_column() - rows = Tableau(rows).conjugate() - the_list = [] - for col in rows: - the_list += reversed(col) - elif 'columns' in options: - columns = options['columns'] - the_list = [] - for col in columns: - the_list += col - else: - the_list = [i for i in args] - TensorProductOfRegularCrystalsElement.__init__(self, parent, [parent.letters(_) for _ in the_list]) - - def _repr_(self): - """ - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t._repr_() - '[[1, 2], [3, 4]]' - """ - return repr(self.to_tableau()) - - def _repr_diagram(self): - """ - Return a string representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A', 4], shape=[4,2,1]) - sage: elt = C(rows=[[1,1,1,2], [2,3], [4]]) - sage: print(elt._repr_diagram()) - 1 1 1 2 - 2 3 - 4 - """ - return self.to_tableau()._repr_diagram() - - def pp(self): - """ - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t.pp() - 1 2 - 3 4 - """ - return self.to_tableau().pp() - - def _ascii_art_(self): - """ - Return an ascii art version of ``self``. - - EXAMPLES: - - We check that :trac:`16486` is fixed:: - - sage: T = crystals.Tableaux(['B',6], shape=[1]*5) - sage: ascii_art(T.module_generators[0]) - 1 - 2 - 3 - 4 - 5 - sage: T = crystals.Tableaux(['D',4], shape=[2,1]) - sage: t = T.module_generators[0].f_string([1,2,3,4,2,2,3,4]) - sage: ascii_art(t) - 1 -2 - -3 - """ - return self.to_tableau()._ascii_art_() - - def _latex_(self): - r""" - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [4,2]) - sage: t = T(rows=[[1,1,2,3],[2,3]]) - sage: latex(t) # indirect doctest - {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} - \lr{1}&\lr{1}&\lr{2}&\lr{3}\\\cline{1-4} - \lr{2}&\lr{3}\\\cline{1-2} - \end{array}$} - } - """ - from sage.combinat.output import tex_from_array - # Modified version of to_tableau() to have the entries be letters - # rather than their values - if self._list == []: - return "{\\emptyset}" - - tab = [ [self[0]] ] - for i in range(1,len(self)): - if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): - tab.append([self[i]]) - else: - l = len(tab)-1 - tab[l].append(self[i]) - for x in tab: - x.reverse() - T = Tableau(tab).conjugate() - return tex_from_array([[letter._latex_() for letter in row] for row in T]) - - @cached_method - def to_tableau(self): - """ - Returns the Tableau object corresponding to self. - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t - [[1, 2], [3, 4]] - sage: type(t) - - sage: type(t[0][0]) - <... 'int'> - sage: T = crystals.Tableaux(['D',3], shape = [1,1]) - sage: t=T(rows=[[-3],[3]]).to_tableau(); t - [[-3], [3]] - sage: t=T(rows=[[3],[-3]]).to_tableau(); t - [[3], [-3]] - sage: T = crystals.Tableaux(['B',2], shape = [1,1]) - sage: t = T(rows=[[0],[0]]).to_tableau(); t - [[0], [0]] - """ - if self._list == []: - return Tableau([]) - tab = [ [self[0].value] ] - for i in range(1,len(self)): - if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): - tab.append([self[i].value]) - else: - l = len(tab)-1 - tab[l].append(self[i].value) - for x in tab: - x.reverse() - return Tableau(tab).conjugate() - - def promotion(self): - """ - Promotion for type A crystals of tableaux of rectangular shape - - Returns the result of applying promotion on this tableau. - - This method only makes sense in type A with rectangular shapes. - - EXAMPLES:: - - sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) - sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) - sage: t - [[1, 1, 1], [2, 2, 3], [3, 4, 4]] - sage: t.promotion() - [[1, 1, 2], [2, 2, 3], [3, 4, 4]] - sage: t.promotion().parent() - The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] - """ - crystal = self.parent() - cartan_type = crystal.cartan_type() - assert cartan_type.type() == 'A' - return crystal(self.to_tableau().promotion(cartan_type.rank())) - - def promotion_inverse(self): - """ - Inverse promotion for type A crystals of tableaux of rectangular shape - - Returns the result of applying inverse promotion on this tableau. - - This method only makes sense in type A with rectangular shapes. - - EXAMPLES:: - - sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) - sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) - sage: t - [[1, 1, 1], [2, 2, 3], [3, 4, 4]] - sage: t.promotion_inverse() - [[1, 1, 2], [2, 3, 3], [4, 4, 4]] - sage: t.promotion_inverse().parent() - The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] - """ - crystal = self.parent() - cartan_type = crystal.cartan_type() - assert cartan_type.type() == 'A' - return crystal(self.to_tableau().promotion_inverse(cartan_type.rank())) - -CrystalOfTableaux.Element = CrystalOfTableauxElement - -##################################################################### -## Local energy function - -class LocalEnergyFunction(Map): - r""" - The local energy function. - - Let `B` and `B'` be Kirillov-Reshetikhin crystals with maximal - vectors `u_B` and `u_{B'}` respectively. The *local energy function* - `H : B \otimes B' \to \ZZ` is the function which satisfies - - .. MATH:: - - H(e_0(b \otimes b')) = H(b \otimes b') + \begin{cases} - 1 & \text{if } i = 0 \text{ and LL}, \\ - -1 & \text{if } i = 0 \text{ and RR}, \\ - 0 & \text{otherwise,} - \end{cases} - - where LL (resp. RR) denote `e_0` acts on the left (resp. right) - on both `b \otimes b'` and `R(b \otimes b')`, and - normalized by `H(u_B \otimes u_{B'}) = 0`. - - INPUT: - - - ``B`` -- a Kirillov-Reshetikhin crystal - - ``Bp`` -- a Kirillov-Reshetikhin crystal - - ``normalization`` -- (default: 0) the normalization value - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2,1) - sage: H = K.local_energy_function(K2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: for b in hw: - ....: b, H(b) - ([[], [[1], [2]]], 1) - ([[[1, 1]], [[1], [2]]], 0) - ([[[2, -2]], [[1], [2]]], 1) - ([[[1, -2]], [[1], [2]]], 1) - - REFERENCES: - - .. [KKMMNN92] S-J. Kang, M. Kashiwara, K. C. Misra, T. Miwa, - T. Nakashima, and A. Nakayashiki. - *Affine crystals and vertex models*. - Int. J. Mod. Phys. A, **7** (suppl. 1A), (1992) pp. 449-484. - """ - def __init__(self, B, Bp, normalization=0): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',7,2], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['A',7,2], 2,1) - sage: H = K.local_energy_function(K2) - sage: TestSuite(H).run(skip=['_test_category', '_test_pickling']) - """ - self._B = B - self._Bp = Bp - self._R_matrix = self._B.R_matrix(self._Bp) - T = B.tensor(Bp) - self._known_values = {T(*[K.module_generator() for K in T.crystals]): - ZZ(normalization)} - self._I0 = T.cartan_type().classical().index_set() - from sage.categories.homset import Hom - Map.__init__(self, Hom(T, ZZ)) - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A', 6, 2], 2, 1) - sage: Kp = crystals.KirillovReshetikhin(['A', 6, 2], 1, 1) - sage: H = K.local_energy_function(Kp); H - Local energy function of - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) - tensor - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) - """ - return "Local energy function of {} tensor {}".format(self._B, self._Bp) - - def _call_(self, x): - """ - Return the local energy of ``x``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['B',4,1], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['B',4,1], 2,1) - sage: H = K.local_energy_function(K2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: H(hw[0]) - 1 - """ - # Setup variables - visited = {x: 0} - check0 = [x] - - # Helper function - def to_classical_hw(cur): - for i in self._I0: - b = cur.e(i) - if b is not None and b not in visited: - visited[b] = visited[cur] # No change - return b - return None # is classically HW or all have been visited - - cur = x - # Get the affine node (it might not be 0 if the type - # has been relabeled) - i0 = x.parent().cartan_type().special_node() - while cur not in self._known_values: - # We first go towards the classically highest weight since - # the maximal vector is classically highest weight - b = to_classical_hw(cur) - - # If classically HW, then try 0 arrows - while b is None: - b = check0.pop() - c = b.e(i0) - # If there is no 0 arrow or we have already seen c, move along - if c is None or c in visited: - b = None - continue - - bp = self._R_matrix(b) - cp = bp.e(i0) - if b[1] == c[1] and bp[1] == cp[1]: # LL case - visited[c] = visited[b] + 1 - elif b[0] == c[0] and bp[0] == cp[0]: # RR case - visited[c] = visited[b] - 1 - else: - visited[c] = visited[b] # Otherwise no change - b = c - - cur = b - check0.append(b) - - baseline = self._known_values[cur] - visited[cur] - for y in visited: - self._known_values[y] = baseline + visited[y] - - return self._known_values[x] - + class Element(CrystalOfTableauxElement): + pass # deprecations from trac:18555 from sage.misc.superseded import deprecated_function_alias TensorProductOfCrystals.global_options=deprecated_function_alias(18555, TensorProductOfCrystals.options) TensorProductOfCrystalsOptions=deprecated_function_alias(18555, TensorProductOfCrystals.options) + diff --git a/src/sage/combinat/crystals/tensor_product_element.pxd b/src/sage/combinat/crystals/tensor_product_element.pxd new file mode 100644 index 00000000000..df44135819f --- /dev/null +++ b/src/sage/combinat/crystals/tensor_product_element.pxd @@ -0,0 +1,20 @@ +from sage.structure.list_clone cimport ClonableArray + +cdef class ImmutableListWithParent(ClonableArray): + cpdef _set_index(self, k, value) + +cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): + pass + +cdef class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): + cpdef position_of_last_unmatched_minus(self, i) + cpdef position_of_first_unmatched_plus(self, i) + +cdef class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): + pass + +cdef class InfinityCrystalOfTableauxElement(CrystalOfTableauxElement): + pass + +cdef class InfinityCrystalOfTableauxElementTypeD(InfinityCrystalOfTableauxElement): + pass diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx new file mode 100644 index 00000000000..71cfda0b418 --- /dev/null +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -0,0 +1,1118 @@ +""" +Tensor Products of Crystal Elements + +AUTHORS: + +- Anne Schilling, Nicolas Thiery (2007): Initial version +- Ben Salisbury, Travis Scrimshaw (2013): Refactored tensor products to handle + non-regular crystals and created new subclass to take advantage of + the regularity +- Travis Scrimshaw (2017): Cythonized element classes +""" +#***************************************************************************** +# Copyright (C) 2007 Anne Schilling +# Nicolas Thiery +# 2017 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** +from __future__ import print_function, absolute_import + +from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE +from sage.structure.parent cimport Parent + +from sage.misc.cachefunc import cached_method, cached_in_parent_method +from sage.functions.other import ceil +from sage.combinat.tableau import Tableau +from sage.rings.all import ZZ + +############################################################################## +# Support classes +############################################################################## + +cdef class ImmutableListWithParent(ClonableArray): + r""" + A class for lists having a parent + + Specification: any subclass ``C`` should implement ``__init__`` which + accepts the following form ``C(parent, list=list)`` + """ + def __init__(self, Parent parent, list): + """ + Initialize ``self``. + + TESTS:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: TestSuite(b).run() + """ + ClonableArray.__init__(self, parent, list, check=False) + + cpdef long _hash_(self) except? -1: + """ + Return the hash of ``self``. + + TESTS:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: b._hash_() == hash(b) + True + """ + return hash(tuple(self._list)) + + def __setstate__(self, state): + """ + For unpickling old pickles. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',2], shape=[2,1]) + sage: b = T.module_generators[0] + sage: b.__setstate__([T, {'_list': list(b)}]) + """ + self._parent = state[0] + self._list = state[1]['_list'] + self._is_immutable = True + self._hash = 0 + + def reversed(self): + """ + Return a copy of ``self`` but in the reversed order. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: list(b) + [2, 1, 1] + sage: list(b.reversed()) + doctest:warning + ... + DeprecationWarning: reversed() is deprecated; use reversed(self) instead + See http://trac.sagemath.org/22642 for details. + [1, 1, 2] + """ + from sage.misc.superseded import deprecation + deprecation(22642, 'reversed() is deprecated; use reversed(self) instead') + return type(self)(self._parent, list=list(reversed(self._list))) + + def set_index(self, k, value): + """ + Return a sibling of ``self`` obtained by setting the + `k^{th}` entry of self to value. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[3]).module_generators[0] + sage: list(b.set_index(0, 2)) + doctest:warning + ... + DeprecationWarning: set_index is deprecated; use _set_index instead + See http://trac.sagemath.org/22642 for details. + [2, 1, 1] + """ + from sage.misc.superseded import deprecation + deprecation(22642, 'set_index is deprecated; use _set_index instead') + return self._set_index(int(k), value) + + cpdef _set_index(self, k, value): + r""" + Return a sibling of ``self`` obtained by setting the + `k^{th}` entry of self to value. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[3]).module_generators[0] + sage: list(b._set_index(0, 2)) + [2, 1, 1] + sage: list(b._set_index(1, 4)) + [1, 4, 1] + """ + cdef list l = list(self._list) # Make a (shallow) copy + l[k] = value + return type(self)(self._parent, list=l) + +############################################################################## +# Primary classes +############################################################################## + +cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): + r""" + A class for elements of tensor products of crystals. + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',3]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)) + [1, 2] + """ + if self._parent.options.convention == "Kashiwara": + return repr(list(reversed(self._list))) + return repr(self._list) + + def _latex_(self): + r""" + Return latex code for ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(["A",2]) + sage: D = crystals.Tableaux(["A",2], shape=[2]) + sage: E = crystals.TensorProduct(C,D) + sage: latex(E.module_generators[0]) + 1 \otimes {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} + \lr{1}&\lr{1}\\\cline{1-2} + \end{array}$} + } + """ + from sage.misc.latex import latex + if self._parent.options.convention == "Kashiwara": + return ' \otimes '.join(latex(c) for c in reversed(self)) + return ' \otimes '.join(latex(c) for c in self) + + def _ascii_art_(self): + """ + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) + sage: ascii_art(KT.module_generators[0]) + 1 1 1 + 2 2 2 # 1 # 1 1 + 3 3 3 2 + -4 -4 -4 + """ + if self._parent.options.convention == "Kashiwara": + lst = list(reversed(self)) + else: + lst = self + from sage.typeset.ascii_art import ascii_art, AsciiArt + s = ascii_art(lst[0]) + s._baseline = s._h // 2 + ret = s + for tableau in lst[1:]: + s = ascii_art(tableau) + s._baseline = s._h // 2 + ret += AsciiArt([" # "]) + s + return ret + + def _repr_diagram(self): + r""" + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3], shape=[3,1]) + sage: D = crystals.Tableaux(['A',3], shape=[1]) + sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) + sage: T = crystals.TensorProduct(C,D,E) + sage: print(T.module_generators[0]._repr_diagram()) + 1 1 1 (X) 1 (X) 1 1 + 2 2 2 + 3 3 + """ + pplist = [] + max_widths = [] + num_cols = len(self._list) + for c in self: + try: + pplist.append(c._repr_diagram().split('\n')) + except AttributeError: + pplist.append(c._repr_().split('\n')) + max_widths.append(max(map(len, pplist[-1]))) + num_rows = max(map(len, pplist)) + ret = "" + for i in range(num_rows): + if i > 0: + ret += '\n' + for j in range(num_cols): + if j > 0: + if i == 0: + ret += ' (X) ' + else: + ret += ' ' + if i < len(pplist[j]): + ret += pplist[j][i] + ret += ' '*(max_widths[j] - len(pplist[j][i])) + else: + ret += ' '*max_widths[j] + return ret + + def pp(self): + """ + Pretty print ``self``. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3], shape=[3,1]) + sage: D = crystals.Tableaux(['A',3], shape=[1]) + sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) + sage: T = crystals.TensorProduct(C,D,E) + sage: T.module_generators[0].pp() + 1 1 1 (X) 1 (X) 1 1 + 2 2 2 + 3 3 + """ + print(self._repr_diagram()) + + def weight(self): + r""" + Return the weight of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("A3") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([2,1,3]) + sage: b2 = B.highest_weight_vector().f(1) + sage: t = T(b2, b1) + sage: t + [[[1, 1, 1, 2], [2, 2], [3]], [[1, 1, 1, 1, 2], [2, 2, 4], [3]]] + sage: t.weight() + (-2, 1, 0, 1) + + :: + + sage: C = crystals.Letters(['A',3]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)).weight() + (1, 1, 0, 0) + sage: T = crystals.Tableaux(['D',4],shape=[]) + sage: T.list()[0].weight() + (0, 0, 0, 0) + """ + WLR = self._parent.weight_lattice_realization() + return WLR(sum(elt.weight() for elt in self)) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("G2") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f(2) + sage: b2 = B.highest_weight_vector().f_string([2,2,1]) + sage: t = T(b2, b1) + sage: [t.epsilon(i) for i in B.index_set()] + [0, 3] + """ + return max(self._sig(i, k) for k in range(1, len(self._list)+1)) + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: B = crystals.GeneralizedYoungWalls(2,La[0]+La[1]) + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([1,0]) + sage: b2 = B.highest_weight_vector().f_string([0,1]) + sage: t = T(b2, b1) + sage: [t.phi(i) for i in B.index_set()] + [1, 1, 4] + + TESTS: + + Check that :trac:`15462` is fixed:: + + sage: B = crystals.Tableaux(['A',2], shape=[2,1]) + sage: La = RootSystem(['A',2]).ambient_space().fundamental_weights() + sage: T = crystals.TensorProduct(crystals.elementary.T(['A',2], La[1]+La[2]), B) + sage: t = T.an_element() + sage: t.phi(1) + 2 + sage: t.phi(2) + 2 + """ + P = self._list[-1].parent().weight_lattice_realization() + h = P.simple_coroots() + omega = P(self.weight()).scalar(h[i]) + return max(omega + self._sig(i, k) for k in range(1, len(self._list)+1)) + + @cached_in_parent_method + def _sig(self, i, k): + r""" + Return `a_i(k)` of ``self``. + + The value `a_i(k)` of a crystal `b = b_N \otimes \cdots \otimes b_1` + is defined as: + + .. MATH:: + + a_i(k) = \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle h_i, + \mathrm{wt}(b_j) \rangle + + where `\mathrm{wt}` is the :meth:`weight` of `b_j`. + + INPUT: + + - ``i`` -- an element of the index set + + - ``k`` -- the (1-based) index of the tensor factor of ``self`` + + EXAMPLES:: + + sage: B = crystals.infinity.GeneralizedYoungWalls(3) + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([0,3,1]) + sage: b2 = B.highest_weight_vector().f_string([3,2,1,0,2,3]) + sage: t = T(b1, b2) + sage: [[t._sig(i,k) for k in range(1,len(t)+1)] for i in B.index_set()] + [[0, -1], [0, 0], [0, 1], [1, 2]] + + TESTS: + + Check that :trac:`18469` is fixed:: + + sage: E1 = crystals.elementary.B(['A',2], 1) + sage: E2 = crystals.elementary.B(['A',2], 2) + sage: T = crystals.TensorProduct(E1, E2) + sage: x = T(E1.module_generators[0], E2.module_generators[0]); x + [0, 0] + sage: [[x._sig(i,k) for k in range(1,3)] for i in T.index_set()] + [[-inf, 0], [0, -inf]] + sage: x.f(1) + [-1, 0] + sage: x.e(1) + [1, 0] + """ + if k == 1: + return self._list[-1].epsilon(i) + ep = self._list[-k].epsilon(i) + if ep == float("-inf"): + return ep + + P = self._list[-1].parent().weight_lattice_realization() + h = P.simple_coroots() + wt = P.sum(P(self._list[-j].weight()) for j in range(1, k)) + return ep - wt.scalar(h[i]) + + def e(self, i): + r""" + Return the action of `e_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("D4") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([1,4,3]) + sage: b2 = B.highest_weight_vector().f_string([2,2,3,1,4]) + sage: t = T(b2, b1) + sage: t.e(1) + [[[1, 1, 1, 1, 1], [2, 2, 3, -3], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + sage: t.e(2) + sage: t.e(3) + [[[1, 1, 1, 1, 1, 2], [2, 2, 3, -4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + sage: t.e(4) + [[[1, 1, 1, 1, 1, 2], [2, 2, 3, 4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + """ + N = len(self._list) + 1 + for k in range(1, N): + if all(self._sig(i,k) > self._sig(i,j) for j in range(1, k)) and \ + all(self._sig(i,k) >= self._sig(i,j) for j in range(k+1, N)): + crystal = self._list[-k].e(i) + if crystal is None: + return None + return self._set_index(-k, crystal) + return None + + def f(self, i): + r""" + Return the action of `f_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: La = RootSystem(['A',3,1]).weight_lattice(extended=True).fundamental_weights() + sage: B = crystals.GeneralizedYoungWalls(3,La[0]) + sage: T = crystals.TensorProduct(B,B,B) + sage: b1 = B.highest_weight_vector().f_string([0,3]) + sage: b2 = B.highest_weight_vector().f_string([0]) + sage: b3 = B.highest_weight_vector() + sage: t = T(b3, b2, b1) + sage: t.f(0) + [[[0]], [[0]], [[0, 3]]] + sage: t.f(1) + [[], [[0]], [[0, 3], [1]]] + sage: t.f(2) + [[], [[0]], [[0, 3, 2]]] + sage: t.f(3) + [[], [[0, 3]], [[0, 3]]] + """ + N = len(self._list) + 1 + for k in range(1, N): + if all(self._sig(i,k) >= self._sig(i,j) for j in range(1, k)) and \ + all(self._sig(i,k) > self._sig(i,j) for j in range(k+1, N)): + crystal = self._list[-k].f(i) + if crystal is None: + return None + return self._set_index(-k, crystal) + return None + +cdef class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): + """ + Element class for a tensor product of regular crystals. + + TESTS:: + + sage: C = crystals.Letters(['A',2]) + sage: T = crystals.TensorProduct(C, C) + sage: elt = T(C(1), C(2)) + sage: from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement + sage: isinstance(elt, TensorProductOfRegularCrystalsElement) + True + """ + def e(self, i): + """ + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)).e(1) == T(C(1),C(1)) + True + sage: T(C(2),C(1)).e(1) is None + True + sage: T(C(2),C(2)).e(1) == T(C(1),C(2)) + True + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + return self._set_index(k, self._list[k].e(i)) + + def f(self, i): + """ + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).f(1) + [1, 2] + sage: T(C(1),C(2)).f(1) + [2, 2] + sage: T(C(2),C(1)).f(1) is None + True + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + k = self.position_of_last_unmatched_minus(i) + if k is None: + return None + return self._set_index(k, self._list[k].f(i)) + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).phi(1) + 2 + sage: T(C(1),C(2)).phi(1) + 1 + sage: T(C(2),C(1)).phi(1) + 0 + """ + height = 0 + for elt in reversed(self._list): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - plus < 0: + height = minus + else: + height = height - plus + minus + return height + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).epsilon(1) + 0 + sage: T(C(1),C(2)).epsilon(1) + 1 + sage: T(C(2),C(1)).epsilon(1) + 0 + """ + height = 0 + for elt in self: + minus = elt.phi(i) + plus = elt.epsilon(i) + if height - minus < 0: + height = plus + else: + height = height - minus + plus + return height + + cpdef position_of_last_unmatched_minus(self, i): + """ + Return the position of the last unmatched `-` or ``None`` if + there is no unmatched `-`. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).position_of_last_unmatched_minus(1) + sage: T(C(1),C(2)).position_of_last_unmatched_minus(1) + 0 + """ + unmatched_minus = None + height = 0 + cdef int j + for j,elt in enumerate(self): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - minus < 0: + unmatched_minus = j + height = plus + else: + height = height - minus + plus + return unmatched_minus + + cpdef position_of_first_unmatched_plus(self, i): + """ + Return the position of the first unmatched `+` or ``None`` if + there is no unmatched `+`. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).position_of_first_unmatched_plus(1) + sage: T(C(1),C(2)).position_of_first_unmatched_plus(1) + 1 + """ + unmatched_plus = None + height = 0 + cdef int N = len(self._list) - 1 + cdef int j + for j, elt in enumerate(reversed(self._list)): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - plus < 0: + unmatched_plus = N - j + height = minus + else: + height = height - plus + minus + return unmatched_plus + + # Legacy function + def positions_of_unmatched_minus(self, i, dual=False, reverse=False): + """ + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).positions_of_unmatched_minus(1) + [] + sage: T(C(1),C(2)).positions_of_unmatched_minus(1) + [0] + """ + cdef list unmatched_plus = [] + cdef int j + height = 0 + if reverse: + self = type(self)(self._parent, list(reversed(self._list))) + if not dual: + for j,elt in enumerate(self): + minus = elt.phi(i) + plus = elt.epsilon(i) + if height-minus < 0: + unmatched_plus.append(j) + height = plus + else: + height = height - minus + plus + else: + for j,elt in enumerate(self): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height-plus < 0: + unmatched_plus.append(j) + height = minus + else: + height = height - plus + minus + return unmatched_plus + + # Legacy function + def positions_of_unmatched_plus(self, i): + """ + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).positions_of_unmatched_plus(1) + [] + sage: T(C(1),C(2)).positions_of_unmatched_plus(1) + [1] + """ + cdef list L = self.positions_of_unmatched_minus(i, dual=True, reverse=True) + L.reverse() + cdef int N = len(self._list) - 1 + return [N - val for val in L] + +cdef class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): + """ + Element in a crystal of tableaux. + """ + def __init__(self, parent, *args, **options): + """ + There are several ways to input tableaux, by rows, by columns, + by columns, as the list of column elements, or as a sequence + of numbers in column reading. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t + [[1, 2], [3, 4]] + sage: TestSuite(t).run() + + sage: t = T(columns=[[3,1],[4,2]]) + sage: t + [[1, 2], [3, 4]] + sage: TestSuite(t).run() + + sage: t = T(list=[3,1,4,2]) + sage: t + [[1, 2], [3, 4]] + + sage: t = T(3,1,4,2) + sage: t + [[1, 2], [3, 4]] + + Currently inputting the empty tableau as an empty sequence is + broken due to a bug in the generic __call__ method (see :trac:`8648`). + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape=[]) + sage: t = T() + sage: list(t) + [0] + + TESTS: + + Integer types that are not a Sage ``Integer`` (such as a Python ``int`` + and typically arise from compiled code) were not converted into a + letter. This caused certain functions to fail. This is fixed in + :trac:`13204`:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(list=[int(3),1,4,2]) + sage: type(t[0]) + + sage: t = T(list=[3,int(1),4,2]) + sage: type(t[1]) + + sage: C = crystals.KirillovReshetikhin(['A',int(3),1], 1,1) + sage: C[0].e(0) + [[4]] + """ + if len(args) == 1: + if isinstance(args[0], Tableau): + options['rows'] = args[0] + if 'list' in options: + the_list = options['list'] + elif 'rows' in options: + rows = options['rows'] +# the_list=Tableau(rows).to_word_by_column() + rows = Tableau(rows).conjugate() + the_list = [] + for col in rows: + the_list += reversed(col) + elif 'columns' in options: + columns = options['columns'] + the_list = [] + for col in columns: + the_list += col + else: + the_list = [i for i in args] + TensorProductOfRegularCrystalsElement.__init__(self, parent, [parent.letters(_) for _ in the_list]) + + def _repr_(self): + """ + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t._repr_() + '[[1, 2], [3, 4]]' + """ + return repr(self.to_tableau()) + + def _repr_diagram(self): + """ + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A', 4], shape=[4,2,1]) + sage: elt = C(rows=[[1,1,1,2], [2,3], [4]]) + sage: print(elt._repr_diagram()) + 1 1 1 2 + 2 3 + 4 + """ + return self.to_tableau()._repr_diagram() + + def pp(self): + """ + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t.pp() + 1 2 + 3 4 + """ + return self.to_tableau().pp() + + def _ascii_art_(self): + """ + Return an ascii art version of ``self``. + + EXAMPLES: + + We check that :trac:`16486` is fixed:: + + sage: T = crystals.Tableaux(['B',6], shape=[1]*5) + sage: ascii_art(T.module_generators[0]) + 1 + 2 + 3 + 4 + 5 + sage: T = crystals.Tableaux(['D',4], shape=[2,1]) + sage: t = T.module_generators[0].f_string([1,2,3,4,2,2,3,4]) + sage: ascii_art(t) + 1 -2 + -3 + """ + return self.to_tableau()._ascii_art_() + + def _latex_(self): + r""" + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [4,2]) + sage: t = T(rows=[[1,1,2,3],[2,3]]) + sage: latex(t) # indirect doctest + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} + \lr{1}&\lr{1}&\lr{2}&\lr{3}\\\cline{1-4} + \lr{2}&\lr{3}\\\cline{1-2} + \end{array}$} + } + """ + from sage.combinat.output import tex_from_array + # Modified version of to_tableau() to have the entries be letters + # rather than their values + if not self._list: + return "{\\emptyset}" + + tab = [ [self[0]] ] + for i in range(1,len(self)): + if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): + tab.append([self[i]]) + else: + l = len(tab)-1 + tab[l].append(self[i]) + for x in tab: + x.reverse() + T = Tableau(tab).conjugate() + return tex_from_array([[letter._latex_() for letter in row] for row in T]) + + @cached_method + def to_tableau(self): + """ + Return the :class:`Tableau` object corresponding to ``self``. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t + [[1, 2], [3, 4]] + sage: type(t) + + sage: type(t[0][0]) + <... 'int'> + sage: T = crystals.Tableaux(['D',3], shape = [1,1]) + sage: t=T(rows=[[-3],[3]]).to_tableau(); t + [[-3], [3]] + sage: t=T(rows=[[3],[-3]]).to_tableau(); t + [[3], [-3]] + sage: T = crystals.Tableaux(['B',2], shape = [1,1]) + sage: t = T(rows=[[0],[0]]).to_tableau(); t + [[0], [0]] + """ + if not self._list: + return Tableau([]) + cdef list lst = self._list + cdef list tab = [ [lst[0].value] ] + cdef int i + for i in range(1,len(self)): + if lst[i-1] < lst[i] or (lst[i-1].value != 0 and lst[i-1] == lst[i]): + tab.append([lst[i].value]) + else: + tab[len(tab)-1].append(lst[i].value) + for x in tab: + x.reverse() + return Tableau(tab).conjugate() + + def promotion(self): + """ + Return the result of applying promotion on ``self``. + + Promotion for type A crystals of tableaux of rectangular shape. + This method only makes sense in type A with rectangular shapes. + + EXAMPLES:: + + sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) + sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) + sage: t + [[1, 1, 1], [2, 2, 3], [3, 4, 4]] + sage: t.promotion() + [[1, 1, 2], [2, 2, 3], [3, 4, 4]] + sage: t.promotion().parent() + The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] + """ + crystal = self._parent + cartan_type = crystal.cartan_type() + assert cartan_type.type() == 'A' + return crystal(self.to_tableau().promotion(cartan_type.rank())) + + def promotion_inverse(self): + """ + Return the result of applying inverse promotion on ``self``. + + Inverse promotion for type A crystals of tableaux of rectangular shape. + This method only makes sense in type A with rectangular shapes. + + EXAMPLES:: + + sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) + sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) + sage: t + [[1, 1, 1], [2, 2, 3], [3, 4, 4]] + sage: t.promotion_inverse() + [[1, 1, 2], [2, 3, 3], [4, 4, 4]] + sage: t.promotion_inverse().parent() + The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] + """ + crystal = self._parent + cartan_type = crystal.cartan_type() + assert cartan_type.type() == 'A' + return crystal(self.to_tableau().promotion_inverse(cartan_type.rank())) + +cdef class InfinityCrystalOfTableauxElement(CrystalOfTableauxElement): + def e(self,i): + r""" + Return the action of `\widetilde{e}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['B',3]) + sage: b = B(rows=[[1,1,1,1,1,1,1,2,0,-3,-1,-1,-1,-1],[2,2,2,2,-2,-2],[3,-3,-3]]) + sage: b.e(3).pp() + 1 1 1 1 1 1 1 2 0 -3 -1 -1 -1 -1 + 2 2 2 2 -2 -2 + 3 0 -3 + sage: b.e(1).pp() + 1 1 1 1 1 1 1 0 -3 -1 -1 -1 -1 + 2 2 2 2 -2 -2 + 3 -3 -3 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElement ret + ret = (self._set_index(k, self._list[k].e(i))) + if k+i > len(self._list): + return ret + for j in reversed(range(1, i+1)): + if ret._list[k+i-j].value != j: + return ret + # We've found a column, so we need to remove it + for j in range(i): + ret._list.pop(k) + return ret + + def f(self, i): + r""" + Return the action of `\widetilde{f}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['C',4]) + sage: b = B.highest_weight_vector() + sage: b.f(1).pp() + 1 1 1 1 2 + 2 2 2 + 3 3 + 4 + sage: b.f(3).pp() + 1 1 1 1 1 + 2 2 2 2 + 3 3 4 + 4 + sage: b.f(3).f(4).pp() + 1 1 1 1 1 + 2 2 2 2 + 3 3 -4 + 4 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_last_unmatched_minus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElement ret + ret = (self._set_index(k, self._list[k].f(i))) + if k+i > len(self._list): + return ret + for j in reversed(range(1,i+1)): + if self._list[k+i-j].value != j: + return ret + # We've found a full column, so we'll need to add a new column + for j in range(i): + ret._list.insert(k, self._parent.letters(j+1)) + return ret + +cdef class InfinityCrystalOfTableauxElementTypeD(InfinityCrystalOfTableauxElement): + def e(self, i): + r""" + Return the action of `\widetilde{e}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['D',4]) + sage: b = B.highest_weight_vector().f_string([1,4,3,1,2]); b.pp() + 1 1 1 1 2 3 + 2 2 2 + 3 -3 + sage: b.e(2).pp() + 1 1 1 1 2 2 + 2 2 2 + 3 -3 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElementTypeD ret + ret = (self._set_index(k, self._list[k].e(i))) + if i == self.cartan_type().rank(): + i -= 1 + if k+i > len(self._list): + return ret + for j in reversed(range(1, i+1)): + if ret._list[k+i-j].value != j: + return ret + # We've found a column, so we need to remove it + for j in range(i): + ret._list.pop(k) + return ret + + def f(self, i): + r""" + Return the action of `\widetilde{f}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['D',5]) + sage: b = B.highest_weight_vector().f_string([1,4,3,1,5]); b.pp() + 1 1 1 1 1 1 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 5 + sage: b.f(1).pp() + 1 1 1 1 1 1 2 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 5 + sage: b.f(5).pp() + 1 1 1 1 1 1 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 -4 + """ + cdef InfinityCrystalOfTableauxElementTypeD ret + ret = (InfinityCrystalOfTableauxElement.f(self, i)) + if ret._list[0].value == -self._parent.cartan_type().rank(): + # Exceptional case for f_n where we need to add a new column + for j in range(i-1): + ret._list.insert(0, self._parent.letters(j+1)) + return ret + +# for unpickling +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.combinat.crystals.tensor_product', 'ImmutableListWithParent', ImmutableListWithParent) + diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index c3f7d71bb63..796d4f0f45b 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -71,6 +71,11 @@ .. [1] La Jolla Covering Repository, http://www.ccrwest.org/cover.html + +TESTS:: + + sage: 'absolute_import' in dir(designs) or 'deprecated_callable_import' in dir(designs) + False """ from __future__ import absolute_import from sage.combinat.designs.block_design import (BlockDesign, @@ -123,4 +128,4 @@ ("This function will soon be removed. Use the designs.orthogonal_arrays.* functions instead")) # We don't want this to appear in designs. -del deprecated_callable_import +del deprecated_callable_import, absolute_import diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 1454a4b71f9..abf59b5a822 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -618,7 +618,8 @@ def RandomLattice(n, p, properties=None): * ``None``, no restrictions for lattices to create * ``'planar'``, the lattice has an upward planar drawing * ``'dismantlable'`` (implicated by ``'planar'``) - * ``'distributive'`` + * ``'distributive'`` (implicated by ``'stone'``) + * ``'stone'`` OUTPUT: @@ -708,7 +709,7 @@ def RandomLattice(n, p, properties=None): else: properties = set(properties) - known_properties = set(['planar', 'dismantlable', 'distributive']) + known_properties = set(['planar', 'dismantlable', 'distributive', 'stone']) errors = properties.difference(known_properties) if errors: raise ValueError("unknown value %s for 'properties'" % errors.pop()) @@ -717,13 +718,17 @@ def RandomLattice(n, p, properties=None): # Change this, if property='complemented' is added return Posets.ChainPoset(n) - # Handling properties. Every planar lattice is also dismantlable. + # Handling properties: planar => dismantlable, stone => distributive if 'planar' in properties: properties.discard('dismantlable') + if 'stone' in properties: + properties.discard('distributive') # Test property combinations that are not implemented. if 'distributive' in properties and len(properties) > 1: raise NotImplementedError("combining 'distributive' with other properties is not implemented") + if 'stone' in properties and len(properties) > 1: + raise NotImplementedError("combining 'stone' with other properties is not implemented") if properties == set(['planar']): D = _random_planar_lattice(n) @@ -735,6 +740,11 @@ def RandomLattice(n, p, properties=None): D.relabel([i-1 for i in Permutations(n).random_element()]) return LatticePoset(D) + if properties == set(['stone']): + D = _random_stone_lattice(n) + D.relabel([i-1 for i in Permutations(n).random_element()]) + return LatticePoset(D) + if properties == set(['distributive']): tmp = Poset(_random_distributive_lattice(n)).order_ideals_lattice(as_ideals=False) D = copy(tmp._hasse_diagram) @@ -1535,6 +1545,10 @@ def _random_distributive_lattice(n): from sage.combinat.posets.hasse_diagram import HasseDiagram from copy import copy from sage.combinat.subset import Subsets + from sage.graphs.digraph_generators import digraphs + + if n < 4: + return digraphs.Path(n-1) H = HasseDiagram({0: []}) while sum(1 for _ in H.antichains_iterator()) < n: @@ -1559,4 +1573,53 @@ def _random_distributive_lattice(n): H = HasseDiagram(D) return D +def _random_stone_lattice(n): + """ + Return a random Stone lattice on `n` elements. + + INPUT: + + - ``n`` -- number of elements, a non-negative integer + + OUTPUT: + + A random lattice (as a digraph) of `n` elements. + + EXAMPLES:: + + sage: g = sage.combinat.posets.poset_examples._random_stone_lattice(10) + sage: LatticePoset(g).is_stone() + True + + ALGORITHM: + + Randomly split `n` to some factors. For every factor `p` generate + a random distributive lattice on `p-1` elements and add a new new bottom + element to it. Compute the cartesian product of those lattices. + """ + from sage.arith.misc import factor + from sage.combinat.partition import Partitions + from sage.misc.misc_c import prod + from copy import copy + + factors = sum([[f[0]]*f[1] for f in factor(n)], []) + sage.misc.prandom.shuffle(factors) + + part_lengths = list(Partitions(len(factors)).random_element()) + parts = [] + while part_lengths: + x = part_lengths.pop() + parts.append(prod(factors[:x])) + factors = factors[x:] + + result = DiGraph(1) + for p in parts: + g = _random_distributive_lattice(p-1) + g = copy(Poset(g).order_ideals_lattice(as_ideals=False)._hasse_diagram) + g.add_edge('bottom', 0) + result = result.cartesian_product(g) + result.relabel() + + return result + posets = Posets diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index ce3477e9bc8..48350ae59a9 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6992,6 +6992,10 @@ def completion_by_cuts(self): - a finite lattice + .. SEEALSO:: + + :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.irreducibles_poset` + EXAMPLES:: sage: P = Posets.PentagonPoset() diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 21dab1dead1..20bf132e306 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -123,7 +123,9 @@ def run(self, verbose=False): self.ret_rig_con[-1] = RiggedPartition(self.ret_rig_con[-1]._list, self.ret_rig_con[-1].rigging, self.ret_rig_con[-1].vacancy_numbers) - bij = KRTToRCBijectionTypeA2Odd(KRT.module_generators[0]) # Placeholder element + # Placeholder element + elt = KRT(*[C.module_generators[0] for C in KRT.crystals]) + bij = KRTToRCBijectionTypeA2Odd(elt) bij.ret_rig_con = KRT.rigged_configurations()(*self.ret_rig_con, use_vacancy_numbers=True) bij.cur_path = self.cur_path bij.cur_dims = self.cur_dims diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 121fba5b5c2..eedd0ad9d54 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -73,12 +73,14 @@ from sage.misc.latex import latex from sage.arith.all import binomial from sage.rings.integer import Integer +from sage.rings.all import ZZ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation from sage.structure.sage_object import richcmp_not_equal, richcmp from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.modules.free_module import FreeModule from sage.combinat.root_system.cartan_type import CartanType @@ -116,11 +118,11 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh \end{tikzpicture} """ draw_point = lambda point: '(%.3f, %.3f)'%(point[0],point[1]) - if len(tree_node.children) == 0: + if not tree_node.children: r = '' node_name = node_prefix + str(node_id) r = "\\node (%s) at %s"%(node_name, draw_point(start)) - if(node_label): + if node_label: r += "{$%s$};\n"%tree_node._latex_() else: r += "{};\n" @@ -178,7 +180,7 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh rpos[1] = pos[1] point_str = '' node_str = "\\node%s (%s) at %s"%(style_node, node_name, draw_point(pos)) - if(node_label): + if node_label: node_str += "{$%s$};\n"%tree_node._latex_() else: node_str += "{};\n" @@ -221,8 +223,8 @@ def __init__(self, parent_obj, node_weight, dominant_root, parent_node=None): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: parent = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: parent @@ -250,8 +252,8 @@ def depth(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: n.depth @@ -303,11 +305,11 @@ def multiplicity(self): sage: KT = KleberTree(['A',3,1], [[3,2],[2,1],[1,1],[1,1]]) sage: for x in KT: x, x.multiplicity() (Kleber tree node with weight [2, 1, 2] and upwards edge root [0, 0, 0], 1) + (Kleber tree node with weight [3, 0, 1] and upwards edge root [0, 1, 1], 1) (Kleber tree node with weight [0, 2, 2] and upwards edge root [1, 0, 0], 1) (Kleber tree node with weight [1, 0, 3] and upwards edge root [1, 1, 0], 2) (Kleber tree node with weight [1, 1, 1] and upwards edge root [1, 1, 1], 4) (Kleber tree node with weight [0, 0, 2] and upwards edge root [2, 2, 1], 2) - (Kleber tree node with weight [3, 0, 1] and upwards edge root [0, 1, 1], 1) (Kleber tree node with weight [2, 0, 0] and upwards edge root [0, 1, 1], 2) (Kleber tree node with weight [0, 0, 2] and upwards edge root [1, 1, 0], 1) (Kleber tree node with weight [0, 1, 0] and upwards edge root [1, 1, 1], 2) @@ -355,8 +357,8 @@ def __hash__(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: hash(n) @@ -373,8 +375,8 @@ def _richcmp_(self, rhs, op): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: n2 = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero(), n) @@ -407,8 +409,8 @@ def _repr_(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: node = KT(WS.sum_of_terms([(1,2), (2,1), (3,1)]), R.sum_of_terms([(1,3), (3,3)])); node Kleber tree node with weight [2, 1, 1] and upwards edge root [3, 0, 3] @@ -431,8 +433,8 @@ def _latex_(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 3, 1], [[3,2], [1,1]]) sage: node = KT(WS.sum_of_terms([(1,4), (3,1)]), R.zero()) sage: latex(node) @@ -592,7 +594,7 @@ def __classcall_private__(cls, cartan_type, B, classical=None): raise ValueError("use VirtualKleberTree for non-simply-laced types") # Standardize B input into a tuple of tuples - B = tuple(map(tuple, B)) + B = tuple([tuple(rs) for rs in B]) if classical is None: classical = cartan_type.classical() @@ -619,7 +621,7 @@ def __init__(self, cartan_type, B, classical_ct): self._cartan_type = cartan_type self.B = B self._classical_ct = classical_ct - self._build_tree(B) + self._build_tree() self._latex_options = dict(edge_labels=True, use_vector_notation=False, hspace=2.5, vspace=min(-2.5, -0.75*self._classical_ct.rank())) @@ -647,7 +649,7 @@ def latex_options(self, **options): sage: sorted(KT.latex_options().items()) [('edge_labels', True), ('hspace', 2.5), ('use_vector_notation', True), ('vspace', -4)] """ - if len(options) == 0: + if not options: from copy import copy return copy(self._latex_options) for k in options: @@ -675,7 +677,7 @@ def _latex_(self): _draw_tree(self.root, **self._latex_options) \ + "\\end{tikzpicture}" - def _build_tree(self, B): + def _build_tree(self): """ Build the Kleber tree. @@ -686,9 +688,10 @@ def _build_tree(self, B): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: KT = KleberTree(['A',3,1], [[2,2]]) # indirect doctest """ + P = self._classical_ct.root_system().weight_lattice() # Create an empty node at first step - self.root = KleberTreeNode(self, self._classical_ct.root_system().weight_space().zero(), - self._classical_ct.root_system().root_space().zero()) + self.root = KleberTreeNode(self, P.zero(), + self._classical_ct.root_system().root_lattice().zero()) full_list = [self.root] # The list of tree nodes n = self._classical_ct.rank() @@ -699,18 +702,18 @@ def _build_tree(self, B): for i in range(n): L.append([0]) - for r,s in B: + for r,s in self.B: while len(L[0]) < s: # Add more columns if needed for row in L: row.append(0) L[I.index(r)][s - 1] += 1 # The -1 is for indexing # Perform a special case of the algorithm for the root node - weight_basis = self._classical_ct.root_system().weight_space().basis() + weight_basis = P.basis() for a in range(n): self.root.weight += sum(L[a]) * weight_basis[I[a]] new_children = [] - for new_child in self._children_root_iter(): + for new_child in self._children_iter(self.root): if not self._prune(new_child, 1): new_children.append(new_child) self.root.children.append(new_child) @@ -719,6 +722,12 @@ def _build_tree(self, B): depth = 1 growth = True + # self._has_normaliz is set by _children_iter + if self._classical_ct.rank() >= 7 or self._has_normaliz: + child_itr = self._children_iter + else: + child_itr = self._children_iter_vector + while growth: growth = False depth += 1 @@ -733,17 +742,17 @@ def _build_tree(self, B): x.weight += L[a][i] * weight_basis[I[a]] if x in leaves: - for new_child in self._children_iter(x): + for new_child in child_itr(x): if not self._prune(new_child, depth): new_children.append(new_child) else: for x in leaves: - for new_child in self._children_iter(x): + for new_child in child_itr(x): if not self._prune(new_child, depth): new_children.append(new_child) # Connect the new children into the tree - if len(new_children) > 0: + if new_children: growth = True for new_child in new_children: new_child.parent_node.children.append(new_child) @@ -751,16 +760,23 @@ def _build_tree(self, B): self._set = full_list - def _children_root_iter(self): - """ - Iterate over the children of the root node. + def _children_iter(self, node): + r""" + Iterate over the children of ``node``. Helper iterator to iterate over all children, by generating and/or - computing them, of the Kleber tree root. + computing them, of the Kleber tree node. + + We compute the children by computing integral points (expressed as + simple roots) in the polytope given by the intersection of the + negative root cone and shifted positive weight cone. More precisely, + we rewrite the condition `\lambda - \mu \in Q^+`, for `\mu \in P^+`, + as `\lambda - Q^+ = \mu \in P^+`. + + INPUT: - Right now we are just assuming that if a linear combination of positive - roots keeps us in the Weyl chamber, then a shorter linear combination - does as well. + - ``node`` -- the current node in the tree whose children we want + to generate TESTS:: @@ -770,65 +786,72 @@ def _children_root_iter(self): Kleber tree node with weight [2, 0, 0] and upwards edge root [0, 0, 0] Kleber tree node with weight [0, 1, 1] and upwards edge root [1, 0, 0] Kleber tree node with weight [0, 0, 0] and upwards edge root [2, 1, 1] - """ - pos_roots = list(self._classical_ct.root_system().root_space().positive_roots()) - WS = self._classical_ct.root_system().weight_space() - num_pos_roots = len(pos_roots) - roots_visited = [] - - for root in pos_roots: - # If we've already tried this root - if root in roots_visited: - continue - - # If not, then try it - - roots_visited.append(root) - - new_weight = self.root.weight - WS(root) - - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, root, self.root) - root_stack = [root] - index_stack = [0] - - # Now try all of its children - while len(root_stack) > 0: - # If we've tried all of the roots, then back up - if index_stack[-1] == num_pos_roots: - root_stack.pop() - index_stack.pop() - continue - - new_root = root_stack[-1] + pos_roots[index_stack[-1]] - index_stack[-1] += 1 - - # If we've already tried this root, move on to the next one - if new_root in roots_visited: - continue - - roots_visited.append(new_root) - new_weight = self.root.weight - WS(new_root) - - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, new_root, self.root) - root_stack.append(new_root) - index_stack.append(0) - - def _children_iter(self, node): + sage: KT = KleberTree(['D', 4, 1], [[2,2]]) + sage: KT[1] + Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] + sage: for x in KT: x + Kleber tree node with weight [0, 2, 0, 0] and upwards edge root [0, 0, 0, 0] + Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] + Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] + sage: for x in KT._children_iter(KT[1]): x + Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] """ - Iterate over all children nodes. + n = self._classical_ct.rank() + I = self._classical_ct.index_set() + Q = self._classical_ct.root_system().root_lattice() + P = self._classical_ct.root_system().weight_lattice() + + # Construct the polytope by inequalities + from sage.geometry.polyhedron.constructor import Polyhedron + # Construct the shifted weight cone + root_weight = node.weight.to_vector() + ieqs = [[root_weight[i]] + list(col) + for i,col in enumerate(self._classical_ct.cartan_matrix())] + # Construct the negative weight cone + for i in range(n): + v = [0] * (n+1) + v[i+1] = -1 + ieqs.append(v) + ieqs.append([-1]*(n+1)) # For avoiding the origin + # Construct the bounds for the non-root nodes + if node != self.root: + for i,c in enumerate(node.up_root.to_vector()): + v = [0] * (n+1) + v[0] = c + v[i+1] = 1 + ieqs.append(v) + + try: + poly = Polyhedron(ieqs=ieqs, backend='normaliz') + self._has_normaliz = True + except ImportError: + poly = Polyhedron(ieqs=ieqs) + self._has_normaliz = False + + # Build the nodes from the polytope + # Sort for a consistent ordering (it is typically a small list) + for pt in sorted(poly.integral_points(), reverse=True): + up_root = Q._from_dict({I[i]: -val for i,val in enumerate(pt) if val != 0}, + remove_zeros=False) + wt = node.weight + sum(val * P.simple_root(I[i]) for i,val in enumerate(pt)) + yield KleberTreeNode(self, wt, up_root, node) + + def _children_iter_vector(self, node): + r""" + Iterate over the children of ``node``. - This is a helper iterator to iterate over all children, by generating - and/or computing them, of a given Kleber tree node this isn't the root. + Helper iterator to iterate over all children, by generating and/or + computing them, of the Kleber tree node. This implementation + iterates over all possible uproot vectors. - We perform the dominance iteration by using the condition that that - new root must be smaller than the previous root. + .. SEEALSO:: + + :meth:`_children_iter` INPUT: - - ``node`` -- The current node in the tree whose children we want + - ``node`` -- the current node in the tree whose children we want to generate TESTS:: @@ -837,28 +860,29 @@ def _children_iter(self, node): sage: KT = KleberTree(['D', 4, 1], [[2,2]]) sage: KT[1] Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] - sage: for x in KT: x - Kleber tree node with weight [0, 2, 0, 0] and upwards edge root [0, 0, 0, 0] - Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] - Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] sage: for x in KT._children_iter(KT[1]): x Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] """ - RS = self._classical_ct.root_system().root_space() - WS = self._classical_ct.root_system().weight_space() + Q = self._classical_ct.root_system().root_lattice() + P = self._classical_ct.root_system().weight_lattice() I = self._classical_ct.index_set() + wt = node.weight.to_vector() + # Everything is dense at this point; moreover, ranks are relatively small + CM = self._classical_ct.cartan_matrix().dense_matrix() + F = FreeModule(ZZ, self._classical_ct.rank()) L = [range(val + 1) for val in node.up_root.to_vector()] it = itertools.product(*L) next(it) # First element is the zero element for root in it: - # Convert the list to an honest root in the root space - converted_root = RS.sum_of_terms([[I[i], val] for i, val in enumerate(root)]) + # Convert the list to the weight lattice + converted_root = CM * F(root) - new_weight = node.weight - WS(converted_root) - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, converted_root, node) + if all(wt[i] >= val for i,val in enumerate(converted_root)): + wd = {I[i]: wt[i] - val for i, val in enumerate(converted_root)} + rd = {I[i]: val for i, val in enumerate(root)} + yield KleberTreeNode(self, P._from_dict(wd), Q._from_dict(rd), node) def _prune(self, new_child, depth): r""" @@ -1015,8 +1039,8 @@ def _element_constructor_(self, node_weight, dominant_root, parent_node=None): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: root = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()); root # indirect doctest Kleber tree node with weight [5, 2] and upwards edge root [0, 0] @@ -1172,8 +1196,8 @@ def _prune(self, new_child, depth): sage: from sage.combinat.rigged_configurations.kleber_tree import VirtualKleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = VirtualKleberTree(['C',2,1], [[1,2],[1,1],[2,1]]) sage: x = KT(WS.sum_of_terms([(1,1), (2,1), (3,3)]), R.sum_of_terms([(1,2),(2,2),(3,1)]), KT.root) sage: KT._prune(x, 1) @@ -1368,8 +1392,8 @@ def _prune(self, new_child, depth): sage: from sage.combinat.rigged_configurations.kleber_tree import VirtualKleberTree sage: RS = RootSystem(['A', 5]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = VirtualKleberTree(['A',6,2], [[2,2]]) sage: x = KT(WS.sum_of_terms([(2,1), (4,1)]), R.sum_of_terms([(1,1),(2,2),(3,2),(4,2),(5,1)]), KT.root) sage: KT._prune(x, 1) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 7d9ec53d8fc..d17a30d98ef 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -51,8 +51,7 @@ from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper -from sage.categories.finite_crystals import FiniteCrystals -from sage.categories.regular_crystals import RegularCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.combinat.crystals.letters import CrystalOfLetters, EmptyLetter from sage.combinat.root_system.cartan_type import CartanType @@ -283,7 +282,7 @@ def __init__(self, cartan_type, r, s): EXAMPLES:: sage: KRT = crystals.KirillovReshetikhin(['A', 4, 1], 2, 2, model='KR') - sage: TestSuite(KRT).run() # long time + sage: TestSuite(KRT).run() sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 2, 2, model='KR') sage: TestSuite(KRT).run() # long time sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 4, 1, model='KR'); KRT @@ -294,7 +293,7 @@ def __init__(self, cartan_type, r, s): self._s = s self._cartan_type = cartan_type - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals()) self.letters = CrystalOfLetters(cartan_type.classical()) self.module_generators = self._build_module_generators() @@ -463,7 +462,7 @@ def _element_constructor_(self, *lst, **options): # Check to make sure it can be converted if lst[0].cartan_type() != self.cartan_type() \ or lst[0].parent().r() != self._r or lst[0].parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") + raise ValueError("the Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") return self.from_kirillov_reshetikhin_crystal(lst[0]) return self.element_class(self, list(lst), **options) @@ -508,19 +507,6 @@ def kirillov_reshetikhin_crystal(self): """ return KashiwaraNakashimaTableaux(self._cartan_type, self._r, self._s) - def affinization(self): - """ - Return the corresponding affinization crystal of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1, model='KR') - sage: K.affinization() - Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) - """ - from sage.combinat.crystals.affinization import AffinizationOfCrystal - return AffinizationOfCrystal(self) - def classical_decomposition(self): """ Return the classical crystal decomposition of ``self``. @@ -1050,9 +1036,9 @@ def from_kirillov_reshetikhin_crystal(self, krc): f_str = reversed(to_hw[1]) wt = to_hw[0].weight() for x in self.module_generators: - if x.classical_weight() / 2 == wt: + if x.classical_weight() == wt: return x.f_string(f_str) - raise ValueError("No matching highest weight element found") + raise ValueError("no matching highest weight element found") class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): r""" @@ -1326,8 +1312,7 @@ def classical_weight(self): WLR = F.weight_lattice() else: WLR = F.ambient_space() - weight = lambda x: x.weight() - return sum((weight(self[j]) for j in range(len(self))), WLR.zero()) + return sum((self[j].weight() for j in range(len(self))), WLR.zero()) def e(self, i): """ @@ -1408,28 +1393,6 @@ def phi(self, i): return self.to_kirillov_reshetikhin_crystal().phi0() return TensorProductOfRegularCrystalsElement.phi(self, i) - def lusztig_involution(self): - r""" - Return the result of the classical Lusztig involution on ``self``. - - EXAMPLES:: - - sage: KRT = crystals.KirillovReshetikhin(['D',4,1], 2, 3, model='KR') - sage: mg = KRT.module_generators[1] - sage: mg.lusztig_involution() - [[-2, -2, 1], [-1, -1, 2]] - sage: elt = mg.f_string([2,1,3,2]); elt - [[3, -2, 1], [4, -1, 2]] - sage: elt.lusztig_involution() - [[-4, -2, 1], [-3, -1, 2]] - """ - Cl = self.parent().cartan_type().classical() - I = Cl.index_set() - aut = Cl.opposition_automorphism() - hw = self.to_highest_weight(I)[1] - hw.reverse() - return self.to_lowest_weight(I)[0].e_string(aut[i] for i in hw) - def left_split(self): r""" Return the image of ``self`` under the left column splitting map. @@ -1651,6 +1614,26 @@ def left_split(self): rf = TP.crystals[1](*(self[h:])) return TP(lf, rf) + # FIXME: This is a copy of the above classical weight, and cached_method + # overwrites this method if it is called via super. + @cached_method + def classical_weight(self): + r""" + Return the classical weight of ``self``. + + EXAMPLES:: + + sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 4, 1, model='KR') + sage: KRT.module_generators[0].classical_weight() + (1/2, 1/2, 1/2, 1/2) + """ + F = self.cartan_type().classical().root_system() + if F.ambient_space() is None: + WLR = F.weight_lattice() + else: + WLR = F.ambient_space() + return sum((self[j].weight() for j in range(len(self))), WLR.zero()) / 2 + KRTableauxBn.Element = KRTableauxSpinElement KRTableauxSpin.Element = KRTableauxSpinElement @@ -1800,7 +1783,7 @@ def __init__(self, cartan_type, r, s): self._r = r self._s = s self._cartan_type = cartan_type - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals()) self.letters = CrystalOfLetters(cartan_type.classical()) @lazy_attribute diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 2f9004a908d..61dd6842f56 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -31,8 +31,7 @@ from sage.combinat.misc import IterableFunctionCall import sage.combinat.tableau as tableau from sage.rings.all import QQ -from sage.categories.finite_crystals import FiniteCrystals -from sage.categories.regular_crystals import RegularCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.rigged_configurations.kleber_tree import KleberTree, VirtualKleberTree from sage.combinat.rigged_configurations.rigged_configuration_element import ( @@ -345,6 +344,8 @@ def __classcall_private__(cls, cartan_type, B): # Standardize B input into a tuple of tuples B = tuple(tuple(factor) for factor in B) + if not B: + raise ValueError("must contain at least one factor") if cartan_type.type() == 'BC': # Type `A_{2n}^{(2)}` return RCTypeA2Even(cartan_type, B) @@ -382,7 +383,7 @@ def __init__(self, cartan_type, B): self._rc_index = cl.index_set() # We store the Cartan matrix for the vacancy number calculations for speed self._cartan_matrix = cl.cartan_matrix() - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals().TensorProducts()) # add options to class options=GlobalOptions('RiggedConfigurations', @@ -666,6 +667,19 @@ def _blocks_to_values(self, blocks): values[-1].extend(block) return values + def classically_highest_weight_vectors(self): + """ + Return the classically highest weight elements of ``self``. + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) + sage: ascii_art(RC.classically_highest_weight_vectors()) + ( ) + ( (/) (/) (/) (/) ) + """ + return self.module_generators + def _element_constructor_(self, *lst, **options): """ Construct a ``RiggedConfigurationElement``. @@ -828,31 +842,6 @@ def tensor_product_of_kirillov_reshetikhin_tableaux(self): from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux return TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, self.dims) - def cardinality(self): - """ - Return the cardinality of ``self``. - - EXAMPLES:: - - sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) - sage: RC.cardinality() - 100 - sage: len(RC.list()) - 100 - - sage: RC = RiggedConfigurations(['E', 7, 1], [[1,1]]) - sage: RC.cardinality() - 134 - sage: len(RC.list()) - 134 - - sage: RC = RiggedConfigurations(['B', 3, 1], [[2,2],[1,2]]) - sage: RC.cardinality() - 5130 - """ - CWLR = self.cartan_type().classical().root_system().ambient_space() - return sum(CWLR.weyl_dimension(mg.classical_weight()) for mg in self.module_generators) - @cached_method def tensor_product_of_kirillov_reshetikhin_crystals(self): """ diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 5b4ac01976a..76932ed595d 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -112,8 +112,8 @@ def __getitem__(self, i): [[1], [2], [3]] (X) [[1], [2]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() - for x in self.tp_krt.rigged_configurations().module_generators] + self._cache = tuple([x.to_tensor_product_of_kirillov_reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators]) return self._cache[i] def __iter__(self): @@ -130,8 +130,8 @@ def __iter__(self): [[1], [-1]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() - for x in self.tp_krt.rigged_configurations().module_generators] + self._cache = tuple([x.to_tensor_product_of_kirillov_reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators]) for x in self._cache: yield x @@ -151,7 +151,8 @@ def __repr__(self): @cached_method def cardinality(self): """ - Return the cardinality of ``self`` which is the number of highest weight elements. + Return the cardinality of ``self``, which is the number of + highest weight elements. EXAMPLES:: diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index 5ae9b4841dd..95cb5c10883 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -56,6 +56,7 @@ - :ref:`sage.combinat.root_system.weyl_group` - :ref:`sage.combinat.root_system.extended_affine_weyl_group` - :ref:`sage.combinat.root_system.fundamental_group` +- :ref:`sage.combinat.root_system.braid_move_calculator` .. SEEALSO:: diff --git a/src/sage/combinat/root_system/braid_move_calculator.py b/src/sage/combinat/root_system/braid_move_calculator.py new file mode 100644 index 00000000000..f2315d2a424 --- /dev/null +++ b/src/sage/combinat/root_system/braid_move_calculator.py @@ -0,0 +1,139 @@ +""" +Braid Move Calculator + +AUTHORS: + +- Dinakar Muthiah (2014-06-03): initial version +""" + +#***************************************************************************** +# Copyright (C) 2014 Dinakar Muthiah +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method + +class BraidMoveCalculator(object): + """ + Helper class to compute braid moves. + """ + def __init__(self, coxeter_group): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: TestSuite(B).run(skip="_test_pickling") + """ + self.coxeter_matrix = coxeter_group.coxeter_matrix() + + def _apply_put_in_front_recur_step(self, k, input_word, coxeter_matrix_entry): + """ + Recurrence step for :meth:`put_in_front`. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: B.put_in_front(2, (3, 2, 3, 1, 2, 3, 1, 2, 1)) # indirect doctest + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 3, 1, 2, 1, 3, 2, 1), + (3, 2, 3, 2, 1, 2, 3, 2, 1), + (2, 3, 2, 3, 1, 2, 3, 2, 1)) + """ + i = input_word[0] + def partial_braid_word(length, swap=False, i=i, k=k): + if swap: + i,k = k,i + running_braid_word = [i,k]*(length//2) + if length % 2 == 1: + running_braid_word.append(i) + return tuple(running_braid_word) + + current_last_word = input_word + current_first_letter = k + output_word_list = [current_last_word] + for counter in range(1, coxeter_matrix_entry): + current_word_list = self.put_in_front(current_first_letter, current_last_word[1:]) + output_word_list += [partial_braid_word(counter) + word + for word in current_word_list[1:]] + if current_first_letter == k: + current_first_letter = i + else: + current_first_letter = k + current_last_word = current_word_list[-1] + if i != k: + output_word_list += [partial_braid_word(coxeter_matrix_entry, swap=True) + + current_last_word[1:]] + return tuple(output_word_list) + + def put_in_front(self, k, input_word): + """ + Return a list of reduced words starting with ``input_word`` + and ending with a reduced word whose first letter is ``k``. + There still remains an issue with 0 indices. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: B.put_in_front(2, (3, 2, 3, 1, 2, 3, 1, 2, 1)) + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 3, 1, 2, 1, 3, 2, 1), + (3, 2, 3, 2, 1, 2, 3, 2, 1), + (2, 3, 2, 3, 1, 2, 3, 2, 1)) + sage: B.put_in_front(1, (3, 2, 3, 1, 2, 3, 1, 2, 1)) + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 1, 3, 2, 3, 1, 2, 1), + (3, 2, 1, 3, 2, 3, 2, 1, 2), + (3, 2, 1, 2, 3, 2, 3, 1, 2), + (3, 1, 2, 1, 3, 2, 3, 1, 2), + (1, 3, 2, 1, 3, 2, 3, 1, 2)) + sage: B.put_in_front(1, (1, 3, 2, 3, 2, 1, 2, 3, 2)) + ((1, 3, 2, 3, 2, 1, 2, 3, 2),) + """ + i = input_word[0] + if i == 0 or k == 0: # Is this for affine types? - Travis + raise NotImplementedError + entry = self.coxeter_matrix[i, k] + return self._apply_put_in_front_recur_step(k, input_word, entry) + + @cached_method + def chain_of_reduced_words(self, start_word, end_word): + """ + Compute the chain of reduced words from ``stard_word`` + to ``end_word``. + + INPUT: + + - ``start_word``, ``end_word`` -- two reduced expressions + for the long word + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['A',5]) + sage: B = BraidMoveCalculator(W) + sage: B.chain_of_reduced_words((1,2,1,3,2,1,4,3,2,1,5,4,3,2,1), # not tested + ....: (5,4,5,3,4,5,2,3,4,5,1,2,3,4,5)) + """ + if start_word == end_word: + return (start_word,) + k = end_word[0] + first_word_list = self.put_in_front(k, start_word) + first_last_word = first_word_list[-1] + return (first_word_list[:-1] + + tuple([ (k,) + word for word in + self.chain_of_reduced_words(first_last_word[1:], + end_word[1:]) ])) + diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 3cd701a1064..b4b1429e127 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -2050,6 +2050,7 @@ def plot(self, - :meth:`plot_alcoves` - :meth:`plot_alcove_walk` - :meth:`plot_ls_paths` + - :meth:`plot_mv_polytope` - :meth:`plot_crystal` """ plot_options = self.plot_parse_options(**options) @@ -3026,6 +3027,78 @@ def plot_ls_paths(self, paths, plot_labels=None, colored_labels=True, **options) G += plot_options.text(b, prev + prev.normalized()*plot_labels) return G + def plot_mv_polytope(self, mv_polytope, mark_endpoints=True, + circle_size=0.06, circle_thickness=1.6, + wireframe='blue', fill='green', alpha=1, + **options): + r""" + Plot an MV polytope. + + INPUT: + + - ``mv_polytope`` -- an MV polytope + - ``mark_endpoints`` -- (default: ``True``) mark the endpoints + of the MV polytope + - ``circle_size`` -- (default: 0.06) the size of the circles + - ``circle_thickness`` -- (default: 1.6) the thinkness of the + extra rings of circles + - ``wireframe`` -- (default: ``'blue'``) color to draw the + wireframe of the polytope with + - ``fill`` -- (default: ``'green'``) color to fill the polytope with + - ``alpha`` -- (default: 1) the alpha value (opacity) of the fill + - ``**options`` -- plotting options + + .. SEEALSO:: + + - :meth:`plot` for a description of the plotting options + - :ref:`sage.combinat.root_system.plot` for a tutorial + on root system plotting + + EXAMPLES:: + + sage: B = crystals.infinity.MVPolytopes(['C',2]) + sage: L = RootSystem(['C',2]).ambient_space() + sage: p = B.highest_weight_vector().f_string([1,2,1,2]) + sage: L.plot_fundamental_weights() + L.plot_mv_polytope(p) + Graphics object consisting of 14 graphics primitives + + This also works in 3 dimensions:: + + sage: B = crystals.infinity.MVPolytopes(['A',3]) + sage: L = RootSystem(['A',3]).ambient_space() + sage: p = B.highest_weight_vector().f_string([2,1,3,2]) + sage: L.plot_mv_polytope(p) + Graphics3d Object + """ + from sage.geometry.polyhedron.all import Polyhedron + plot_options = self.plot_parse_options(**options) + + # Setup the shift for plotting + pbw_data = mv_polytope._pbw_datum.parent + al = self.simple_roots() + red = tuple(mv_polytope._pbw_datum.long_word) + roots = [self.sum(c*al[a] for a,c in root) + for root in pbw_data._root_list_from(red)] + datum = mv_polytope._pbw_datum.lusztig_datum + end_pt = self.sum(roots[i] * c for i,c in enumerate(datum)) + shift = plot_options.projection(end_pt) + + vertices = [plot_options.projection(vertex) - shift + for vertex in mv_polytope._polytope_vertices(self)] + p = Polyhedron(vertices=vertices).plot(wireframe=wireframe, + fill=fill, alpha=alpha) + if mark_endpoints: + from sage.plot.circle import circle + + p += circle(plot_options.projection(self.zero()), + circle_size, fill=True, + thickness=circle_thickness, color=wireframe) + + p += circle(-shift, + circle_size, fill=True, + thickness=circle_thickness, color=wireframe) + return p + def plot_crystal(self, crystal, plot_labels=True, label_color='black', edge_labels=False, diff --git a/src/sage/game_theory/catalog.py b/src/sage/game_theory/catalog.py index aaefe760fba..9e47ab0c4bc 100644 --- a/src/sage/game_theory/catalog.py +++ b/src/sage/game_theory/catalog.py @@ -1,6 +1,13 @@ r""" Catalog Of Games + +TESTS:: + + sage: 'absolute_import' in dir(game_theory) + False """ from __future__ import absolute_import from . import catalog_normal_form_games as normal_form_games + +del absolute_import diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index 785f9f54ed3..ed055ebf51d 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -886,6 +886,16 @@ cdef class Inequality_int: Traceback (most recent call last): ... OverflowError: ... + + TESTS: + + Check that :trac:`21993` is fixed:: + + sage: Inequality_int([18560500, -89466500], 108027, [178933, 37121]) + Traceback (most recent call last): + ... + OverflowError: ... + """ cdef int A[INEQ_INT_MAX_DIM] cdef int b @@ -928,8 +938,8 @@ cdef class Inequality_int: if self.dim > 0: self.coeff_next = self.A[1] # finally, make sure that there cannot be any overflow during the enumeration - self._to_int(ZZ(b) + sum( ZZ(A[i]) * ZZ(max_abs_coordinates[i]) - for i in range(self.dim) )) + self._to_int(abs(ZZ(b)) + sum( abs(ZZ(A[i])) * ZZ(max_abs_coordinates[i]) + for i in range(self.dim) )) def __repr__(self): """ diff --git a/src/sage/geometry/polyhedron/backend_field.py b/src/sage/geometry/polyhedron/backend_field.py index ca49decf889..7aac890ba42 100644 --- a/src/sage/geometry/polyhedron/backend_field.py +++ b/src/sage/geometry/polyhedron/backend_field.py @@ -210,9 +210,9 @@ def _init_Vrepresentation_backend(self, Vrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() - (A vertex at (0, 0.7071067811865475?), + (A vertex at (0.?e-15, 0.707106781186548?), A vertex at (1.414213562373095?, 0), - A vertex at (4, 0.372677996249965?)) + A vertex at (4.000000000000000?, 0.372677996249965?)) """ self._Vrepresentation = [] parent = self.parent() @@ -237,9 +237,9 @@ def _init_Hrepresentation_backend(self, Hrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() - (A vertex at (0, 0.7071067811865475?), + (A vertex at (0.?e-15, 0.707106781186548?), A vertex at (1.414213562373095?, 0), - A vertex at (4, 0.372677996249965?)) + A vertex at (4.000000000000000?, 0.372677996249965?)) """ self._Hrepresentation = [] parent = self.parent() diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 6c29099d45f..c5099ca8793 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -494,11 +494,11 @@ def integral_points(self, threshold=10000): be a very bad idea (note this is a rational (non-lattice) polytope, so the other backends use the bounding box method):: - sage: P = Polyhedron(vertices=((0, 0), (1789345,37121))) + 1/1000*polytopes.hypercube(2) + sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2) sage: P = Polyhedron(vertices=P.vertices_list(), # optional - pynormaliz ....: backend='normaliz') sage: len(P.integral_points()) # optional - pynormaliz - 3654 + 434 Finally, the 3-d reflexive polytope number 4078:: @@ -551,6 +551,20 @@ def integral_points(self, threshold=10000): sage: P = Polyhedron([[]], backend='normaliz') # optional - pynormaliz sage: P.integral_points() # optional - pynormaliz ((),) + + A polytope with no integral points (:trac:`22938`):: + + sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2], + ....: [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1], + ....: [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0], + ....: [1, 0, 0, 1]] + sage: P = Polyhedron(ieqs=ieqs, backend='normaliz') # optional - pynormaliz + sage: P.bounding_box() # optional - pynormaliz + ((-3/4, -1/2, -1/4), (-1/2, -1/4, 0)) + sage: P.bounding_box(integral_hull=True) # optional - pynormaliz + (None, None) + sage: P.integral_points() # optional - pynormaliz + () """ import PyNormaliz if not self.is_compact(): @@ -567,6 +581,8 @@ def integral_points(self, threshold=10000): # for small bounding boxes, it is faster to naively iterate over the points of the box if threshold > 1: box_min, box_max = self.bounding_box(integral_hull=True) + if box_min is None: + return () box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max)) if box_points node_1 [color = "blue"];\n}' + REFERENCES: .. [dotspec] http://www.graphviz.org/doc/info/lang.html @@ -19541,7 +19574,7 @@ def graphviz_string(self, **options): color_by_edge = {} for color in options['edge_colors'].keys(): for edge in options['edge_colors'][color]: - assert isinstance(edge, tuple) and len(edge) >= 2 and len(edge) <= 3,\ + assert isinstance(edge, (list, tuple)) and len(edge) >= 2 and len(edge) <= 3,\ "%s is not a valid format for edge"%(edge) u = edge[0] v = edge[1] diff --git a/src/sage/groups/affine_gps/catalog.py b/src/sage/groups/affine_gps/catalog.py index 3ff92fe86a5..d78a1bdb2c4 100644 --- a/src/sage/groups/affine_gps/catalog.py +++ b/src/sage/groups/affine_gps/catalog.py @@ -1,6 +1,11 @@ r""" Type ``groups.affine.`` to access examples of groups implemented as affine groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.affine) + False """ from __future__ import absolute_import @@ -15,3 +20,6 @@ from .affine_group import AffineGroup as Affine from .euclidean_group import EuclideanGroup as Euclidean + +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/groups/finitely_presented_catalog.py b/src/sage/groups/finitely_presented_catalog.py index acb1609e01a..dc934864556 100644 --- a/src/sage/groups/finitely_presented_catalog.py +++ b/src/sage/groups/finitely_presented_catalog.py @@ -2,6 +2,11 @@ Type ``groups.presentation.`` to access examples of groups implemented as finite presentations (quotients of free groups). + +TESTS:: + + sage: 'absolute_import' in dir(groups.presentation) + False """ from __future__ import absolute_import @@ -24,4 +29,5 @@ from .finitely_presented_named import AlternatingPresentation as Alternating from .finitely_presented_named import BinaryDihedralPresentation as BinaryDihedral - +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/groups/matrix_gps/catalog.py b/src/sage/groups/matrix_gps/catalog.py index 525cb0248af..9febc470765 100644 --- a/src/sage/groups/matrix_gps/catalog.py +++ b/src/sage/groups/matrix_gps/catalog.py @@ -3,6 +3,11 @@ Type ``groups.matrix.`` to access examples of groups implemented as permutation groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.matrix) + False """ from __future__ import absolute_import @@ -19,3 +24,5 @@ from .all import QuaternionMatrixGroupGF3 as QuaternionGF3 from sage.groups.matrix_gps.binary_dihedral import BinaryDihedralGroup as BinaryDihedral +# We don't want this to appear tab completion +del absolute_import diff --git a/src/sage/groups/perm_gps/permutation_groups_catalog.py b/src/sage/groups/perm_gps/permutation_groups_catalog.py index b9ad040de7e..5c015376b49 100644 --- a/src/sage/groups/perm_gps/permutation_groups_catalog.py +++ b/src/sage/groups/perm_gps/permutation_groups_catalog.py @@ -3,6 +3,11 @@ Type ``groups.permutation.`` to access examples of groups implemented as permutation groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.permutation) + False """ from __future__ import absolute_import @@ -29,3 +34,6 @@ from .permgroup_named import (PGL, PSL, PSp,PSU,PGU,) from .permgroup_named import TransitiveGroup as Transitive from .cubegroup import CubeGroup as RubiksCube + +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index 7d71d01acb9..bd5f04b703a 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -14,13 +14,17 @@ from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.category_object import CategoryObject +from sage.structure.parent import Parent +from sage.structure.element import ModuleElement, parent +from sage.structure.sage_object import richcmp from sage.categories.category_types import ChainComplexes from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule -from sage.homology.chain_complex import ChainComplex +from sage.homology.chain_complex import ChainComplex, Chain_class -class HochschildComplex(UniqueRepresentation, CategoryObject): +from sage.misc.superseded import deprecated_function_alias + +class HochschildComplex(UniqueRepresentation, Parent): r""" The Hochschild complex. @@ -94,14 +98,12 @@ def __init__(self, A, M): sage: H in ChainComplexes(QQ) True - Some methods required by the category are not implemented:: - - sage: TestSuite(H).run() # known bug (#21386) + sage: TestSuite(H).run() """ self._A = A self._M = M - CategoryObject.__init__(self, base=A.base_ring(), - category=ChainComplexes(A.base_ring())) + Parent.__init__(self, base=A.base_ring(), + category=ChainComplexes(A.base_ring())) def _repr_(self): """ @@ -162,21 +164,21 @@ def coefficients(self): """ return self._M - def free_module(self, d): + def module(self, d): """ - Return the free module in degree ``d``. + Return the module in degree ``d``. EXAMPLES:: sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: T = SGA.trivial_representation() sage: H = SGA.hochschild_complex(T) - sage: H.free_module(0) + sage: H.module(0) Trivial representation of Standard permutations of 3 over Rational Field - sage: H.free_module(1) + sage: H.module(1) Trivial representation of Standard permutations of 3 over Rational Field # Symmetric group algebra of order 3 over Rational Field - sage: H.free_module(2) + sage: H.module(2) Trivial representation of Standard permutations of 3 over Rational Field # Symmetric group algebra of order 3 over Rational Field # Symmetric group algebra of order 3 over Rational Field @@ -185,6 +187,8 @@ def free_module(self, d): raise ValueError("only defined for non-negative degree") return tensor([self._M] + [self._A]*d) + free_module = deprecated_function_alias(21386, module) + @cached_method def trivial_module(self): """ @@ -238,8 +242,8 @@ def boundary(self, d): ....: phi = H.boundary(n) ....: psi = H.boundary(n+1) ....: comp = phi * psi - ....: zero = H.free_module(n-1).zero() - ....: return all(comp(b) == zero for b in H.free_module(n+1).basis()) + ....: zero = H.module(n-1).zero() + ....: return all(comp(b) == zero for b in H.module(n+1).basis()) sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: H = SGA.hochschild_complex(SGA) @@ -264,9 +268,9 @@ def boundary(self, d): if d == 0: t = self.trivial_module() zero = t.zero() - return self.free_module(0).module_morphism(lambda x: zero, codomain=t) - Fd = self.free_module(d-1) - Fd1 = self.free_module(d) + return self.module(0).module_morphism(lambda x: zero, codomain=t) + Fd = self.module(d-1) + Fd1 = self.module(d) mone = -one def on_basis(k): p = self._M.monomial(k[0]) * self._A.monomial(k[1]) @@ -318,8 +322,8 @@ def coboundary(self, d): ....: phi = H.coboundary(n) ....: psi = H.coboundary(n+1) ....: comp = psi * phi - ....: zero = H.free_module(n+1).zero() - ....: return all(comp(b) == zero for b in H.free_module(n-1).basis()) + ....: zero = H.module(n+1).zero() + ....: return all(comp(b) == zero for b in H.module(n-1).basis()) sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: H = SGA.hochschild_complex(SGA) @@ -452,3 +456,299 @@ def cohomology(self, d): unitriangular=True) return ker.quotient_module(im_retract) + def _element_constructor_(self, vectors): + """ + Construct an element of ``self`` from ``vectors``. + + TESTS:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H(0) + Trivial chain + sage: H(2) + Chain(0: 2) + sage: H(x+2*y) + Chain(0: x + 2*y) + sage: H({0: H.module(0).an_element()}) + Chain(0: 2 + 2*x + 3*y) + sage: H({2: H.module(2).an_element()}) + Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y) + sage: H({0:x-y, 2: H.module(2).an_element()}) + Chain with 2 nonzero terms over Rational Field + sage: H([2]) + Traceback (most recent call last): + ... + ValueError: cannot construct an element from [2] + """ + if not vectors: # special case: the zero chain + return self.element_class(self, {}) + # special case: an element of the defining module + if self._M.has_coerce_map_from(parent(vectors)): + vectors = self._M(vectors) + if parent(vectors) is self._M: + mc = vectors.monomial_coefficients(copy=False) + vec = self.module(0)._from_dict({(k,): mc[k] for k in mc}) + return self.element_class(self, {0: vec}) + if isinstance(vectors, (Chain_class, self.element_class)): + vectors = vectors._vec + data = dict() + if not isinstance(vectors, dict): + raise ValueError("cannot construct an element from {}".format(vectors)) + # Special handling for the 0 free module + # FIXME: Allow coercions between the 0 free module and the defining module + if 0 in vectors: + vec = vectors.pop(0) + if parent(vec) is self._M: + mc = vec.monomial_coefficients(copy=False) + data[0] = self.module(0)._from_dict({(k,): mc[k] for k in mc}) + else: + data[0] = self.module(0)(vec) + for degree in vectors: + vec = self.module(degree)(vectors[degree]) + if not vec: + continue + data[degree] = vec + return self.element_class(self, data) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: v = H.an_element() + sage: [v.vector(i) for i in range(6)] + [2*F[1] + 2*F[x] + 3*F[y], + 2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y], + 2*F[1] # F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[1] # F[x] + + 3*F[1] # F[1] # F[1] # F[y], + 0, + 0] + """ + return self.element_class(self, {d: self.module(d).an_element() + for d in range(4)}) + + class Element(ModuleElement): + """ + A chain of the Hochschild complex. + + INPUT: + + Can be one of the following: + + - A dictionary whose keys are the degree and whose `d`-th + value is an element in the degree `d` module. + - An element in the coefficient module `M`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H(T.an_element()) + Chain(0: 2*B['v']) + sage: H({0: T.an_element()}) + Chain(0: 2*B['v']) + sage: H({1: H.module(1).an_element()}) + Chain(1: 2*B['v'] # [1, 2, 3] + 2*B['v'] # [1, 3, 2] + 3*B['v'] # [2, 1, 3]) + sage: H({0: H.module(0).an_element(), 3: H.module(3).an_element()}) + Chain with 2 nonzero terms over Rational Field + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: H(x + 2*y^2) + Chain(0: F[x] + 2*F[y^2]) + sage: H({0: x*y - x}) + Chain(0: -F[x] + F[x*y]) + sage: H(2) + Chain(0: 2*F[1]) + sage: H({0: x-y, 2: H.module(2).basis().an_element()}) + Chain with 2 nonzero terms over Integer Ring + """ + def __init__(self, parent, vectors): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x-y, 2: H.module(2).basis().an_element()}) + sage: TestSuite(a).run() + """ + self._vec = vectors + ModuleElement.__init__(self, parent) + + def vector(self, degree): + """ + Return the free module element in ``degree``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x-y, 2: H.module(2).basis().an_element()}) + sage: [a.vector(i) for i in range(3)] + [F[x] - F[y], 0, F[1] # F[1] # F[1]] + """ + try: + return self._vec[degree] + except KeyError: + return self.parent().module(degree).zero() + + def _repr_(self): + """ + Print representation. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H(0) + Trivial chain + sage: H(x+2*y) + Chain(0: x + 2*y) + sage: H({2: H.module(2).an_element()}) + Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y) + sage: H({0:x-y, 2: H.module(2).an_element()}) + Chain with 2 nonzero terms over Rational Field + """ + n = len(self._vec) + if n == 0: + return 'Trivial chain' + + if n == 1: + deg, vec = self._vec.items()[0] + return 'Chain({0}: {1})'.format(deg, vec) + + return 'Chain with {0} nonzero terms over {1}'.format(n, + self.parent().base_ring()) + + def _ascii_art_(self): + """ + Return an ascii art representation. + + Note that arrows go to the left so that composition of + differentials is the usual matrix multiplication. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: ascii_art(a) + d_0 d_1 d_2 d_3 + 0 <---- F - F <---- 1 # 1 <---- 1 # 1 # 1 <---- 0 + x y + """ + from sage.typeset.ascii_art import AsciiArt, ascii_art + + if not self._vec: # 0 chain + return AsciiArt(['0']) + + def arrow_art(d): + d_str = [' d_{0} '.format(d)] + arrow = ' <' + '-'*(len(d_str[0])-3) + ' ' + d_str.append(arrow) + return AsciiArt(d_str, baseline=0) + + result = AsciiArt(['0']) + max_deg = max(self._vec) + for deg in range(min(self._vec), max_deg+1): + A = ascii_art(self.vector(deg)) + A._baseline = A.height() // 2 + result += arrow_art(deg) + A + return result + arrow_art(max_deg+1) + AsciiArt(['0']) + + def _add_(self, other): + """ + Module addition + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: [a.vector(i) for i in range(3)] + [F[x] - F[y], F[1] # F[1], F[1] # F[1] # F[1]] + sage: [H.an_element().vector(i) for i in range(3)] + [2*F[1] + 2*F[x] + 3*F[y], + 2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]] + + sage: v = a + H.an_element() + sage: [v.vector(i) for i in range(3)] + [2*F[1] + 3*F[x] + 2*F[y], + 3*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 3*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]] + """ + vectors = dict(self._vec) # Make a (shallow) copy + for d in other._vec: + if d in vectors: + vectors[d] += other._vec[d] + if not vectors[d]: + del vectors[d] + else: + vectors[d] = other._vec + parent = self.parent() + return parent.element_class(parent, vectors) + + def _lmul_(self, scalar): + """ + Scalar multiplication + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: v = 3*a + sage: [v.vector(i) for i in range(3)] + [3*F[x] - 3*F[y], 3*F[1] # F[1], 3*F[1] # F[1] # F[1]] + """ + if scalar == 0: + return self.zero() + vectors = dict() + for d in self._vec: + vec = scalar * self._vec[d] + if vec: + vectors[d] = vec + return self.__class__(self.parent(), vectors) + + def _richcmp_(self, other, op): + """ + Rich comparison of ``self`` to ``other``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: a == 3*a + False + sage: a + a == 2*a + True + sage: a == H.zero() + False + + sage: a != 3*a + True + sage: a + a != 2*a + False + sage: a != H.zero() + True + """ + return richcmp(self._vec, other._vec, op) + diff --git a/src/sage/libs/pynac/pynac.pxd b/src/sage/libs/pynac/pynac.pxd index 17c9a355a22..20907bbdf84 100644 --- a/src/sage/libs/pynac/pynac.pxd +++ b/src/sage/libs/pynac/pynac.pxd @@ -421,7 +421,7 @@ cdef extern from "sage/libs/pynac/wrap.h": unsigned g_register_new "GiNaC::function::register_new" (GFunctionOpt opt) unsigned find_function "GiNaC::function::find_function" (char* name, - unsigned nargs) except +ValueError + unsigned nargs) except + bint has_symbol "GiNaC::has_symbol" (GEx ex) bint has_symbol_or_function "GiNaC::has_symbol_or_function" (GEx ex) diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 68eff58c2cf..733dc6c2bfe 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -1791,19 +1791,40 @@ cdef py_atan2(x, y): 2.284887025407... sage: atan2(2.1000000000000000000000000000000000000, -1.20000000000000000000000000000000) 2.089942441041419571002776071... + + Check that :trac:`22877` is fixed:: + + sage: atan2(CC(I), CC(I+1)) + 0.553574358897045 + 0.402359478108525*I + sage: atan2(CBF(I), CBF(I+1)) + [0.55357435889705 +/- 5.75e-15] + [0.40235947810852 +/- 6.01e-15]*I """ from sage.symbolic.constants import pi, NaN + from sage.rings.real_arb import RealBallField + from sage.rings.real_mpfr import RealField_class P = coercion_model.common_parent(x, y) + is_real = False if P is ZZ: P = RR + if (P is float + or parent(P) is RealField_class + or isinstance(P, RealBallField)): + is_real = True if y != 0: - if x > 0: - res = py_atan(abs(y/x)) - elif x < 0: - res = P(pi) - py_atan(abs(y/x)) + try: + is_real = is_real or (x.is_real() and y.is_real()) + except AttributeError: + is_real = False + if is_real: + if x > 0: + res = py_atan(abs(y/x)) + elif x < 0: + res = P(pi) - py_atan(abs(y/x)) + else: + res = P(pi)/2 + return res if y > 0 else -res else: - res = P(pi)/2 - return res if y > 0 else -res + return -I*py_log((x + I*y)/py_sqrt(x**2 + y**2)) else: if x > 0: return P(0) diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 4922aa87dce..8590272dff2 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -670,12 +670,15 @@ def hecke_series_degree_bound(p,N,k,m): # Returns matrix A modulo p^m from Step 6 of Algorithm 2. -def higher_level_UpGj(p,N,klist,m,modformsring,bound): +def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): r""" - Returns a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` - parameterised by the weights k in ``klist``. The matrix `A_k` is the finite - square matrix which occurs on input p,k,N and m in Step 6 of Algorithm 2 in - [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined + Return a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` + parameterised by the weights k in ``klist``. + + The matrix `A_k` is the finite square matrix which occurs on input + p, k, N and m in Step 6 of Algorithm 2 in [Lau2011]_. + + Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices.) @@ -685,12 +688,15 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): - ``N`` -- integer at least 2 and not divisible by p (level). - ``klist`` -- list of integers all congruent modulo (p-1) (the weights). - ``m`` -- positive integer. - - ``modformsring`` -- True or False. + - ``modformsring`` -- ``True`` or ``False``. - ``bound`` -- (even) positive integer. + - ``extra_data`` -- (default: ``False``) boolean. OUTPUT: - - list of square matrices. + - list of square matrices. If ``extra_data`` is ``True``, return also + extra intermediate data, namely the matrix `E` in [Lau2011]_ and + the integers ``elldash`` and ``mdash``. EXAMPLES:: @@ -704,7 +710,8 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): [ 0 7 20 0 20 0] [ 0 1 24 0 20 0] ] - + sage: len(higher_level_UpGj(5,3,[4],2,true,6,extra_data=True)) + 4 """ t = cputime() # Step 1 @@ -715,11 +722,12 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): elldashp = elldash*p mdash = m + ceil(n/(p+1)) - verbose("done step 1",t) + verbose("done step 1", t) t = cputime() # Steps 2 and 3 - e,Ep1 = higher_level_katz_exp(p,N,k0,m,mdash,elldash,elldashp,modformsring,bound) + e, Ep1 = higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, + modformsring, bound) ell = dimension(transpose(e)[0].parent()) S = e[0,0].parent() @@ -753,14 +761,14 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. - A = matrix(S,ell,ell) + A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell) verbose("elldash is %s" % elldash) - for i in range(0,ell): + for i in range(ell): Ti = T[i] - for j in range(0,ell): - ej = Ti.parent()([e[j][l] for l in range(0,elldash)]) + for j in range(ell): + ej = Ti.parent()([e[j][l] for l in range(elldash)]) ejleadpos = ej.nonzero_positions()[0] lj = ZZ(ej[ejleadpos]) A[i,j] = S(ZZ(Ti[j])/lj) @@ -769,7 +777,10 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A)) verbose("done step 6", t) - return Alist + if extra_data: + return Alist, e, elldash, mdash + else: + return Alist # *** LEVEL 1 CODE *** @@ -915,12 +926,15 @@ def katz_expansions(k0,p,ellp,mdash,n): # *** MAIN FUNCTION FOR LEVEL 1 *** -def level1_UpGj(p,klist,m): +def level1_UpGj(p, klist, m, extra_data=False): r""" - Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` - parameterised by the weights k in ``klist``. The matrix `A_k` is the finite - square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in - [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined + Return a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` + parameterised by the weights k in ``klist``. + + The matrix `A_k` is the finite square matrix which occurs on input + p, k and m in Step 6 of Algorithm 1 in [Lau2011]_. + + Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices. @@ -929,10 +943,13 @@ def level1_UpGj(p,klist,m): - ``p`` -- prime at least 5. - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights). - ``m`` -- positive integer. + - ``extra_data`` -- (default: ``False``) boolean OUTPUT: - - list of square matrices. + - list of square matrices. If ``extra_data`` is ``True``, return also + extra intermediate data, namely the matrix `E` in [Lau2011]_ and + the integers ``elldash`` and ``mdash``. EXAMPLES:: @@ -945,6 +962,9 @@ def level1_UpGj(p,klist,m): [ 0 1995 4802 0 0] [ 0 9212 14406 0 0] ] + sage: len(level1_UpGj(7,[100],5,extra_data=True)) + 4 + """ # Step 1 t = cputime() @@ -1012,7 +1032,10 @@ def level1_UpGj(p,klist,m): Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A)) verbose("done step 6", t) - return Alist + if extra_data: + return Alist, e, ell, mdash + else: + return Alist # *** CODE FOR GENERAL LEVEL *** diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index f4342adc54a..1dfcfd74b93 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -201,14 +201,28 @@ def __init__(self, n, names=None): #self._assign_names(names) Monoid_class.__init__(self,names) - def __cmp__(self, other): + def __eq__(self, other): + """ + Test for equality. + """ + if self is other: + return True if not isinstance(other, FreeMonoid_class): - return -1 - c = cmp(self.__ngens, other.__ngens) - if c: return c - if self.variable_names() == other.variable_names(): - return 0 - return 1 + return False + if self.__ngens != other.__ngens: + return False + try: + if self.variable_names() != other.variable_names(): + return False + except ValueError: + pass + return True + + def __ne__(self, other): + """ + Test for unequality. + """ + return not (self == other) def _repr_(self): return "Free monoid on %s generators %s"%(self.__ngens,self.gens()) diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index 1173827469d..a4d46bb38e9 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -26,6 +26,8 @@ from sage.rings.integer import Integer from sage.rings.all import RealField from .free_monoid_element import FreeMonoidElement +from sage.structure.sage_object import richcmp + def is_StringMonoidElement(x): r""" @@ -100,12 +102,12 @@ def __init__(self, S, x, check=True): else: raise TypeError("Argument x (= %s) is of the wrong type." % x) - def __cmp__(left, right): + def _richcmp_(left, right, op): """ Compare two free monoid elements with the same parents. The ordering is the one on the underlying sorted list of - (monomial,coefficients) pairs. + (monomial, coefficients) pairs. EXAMPLES:: @@ -116,7 +118,7 @@ def __cmp__(left, right): sage: S("01") < S("10") True """ - return cmp(left._element_list, right._element_list) + return richcmp(left._element_list, right._element_list, op) def _repr_(self): """ diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index f872cd0c3f9..c66c9d01529 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -267,8 +267,8 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): TESTS: - A PolynomialSequence can exist with elements in a infinite field of - characteristic 2 that is not (see :trac:`19452`):: + A PolynomialSequence can exist with elements in an infinite field of + characteristic 2 (see :trac:`19452`):: sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence sage: F = GF(2) @@ -277,7 +277,6 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): sage: PolynomialSequence([0], R) [0] """ - from sage.matrix.matrix import is_Matrix from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 39631f0cccf..525dd2462b2 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -636,7 +636,7 @@ cdef class NCPolynomialRing_plural(Ring): """ return False - def is_field(self): + def is_field(self, *args, **kwargs): """ Return ``False``. @@ -646,6 +646,14 @@ cdef class NCPolynomialRing_plural(Ring): sage: P = A.g_algebra(relations={y*x:-x*y}, order = 'lex') sage: P.is_field() False + + TESTS: + + Make the method accept additional parameters, such as the flag ``proof``. + See :trac:`22910`:: + + sage: P.is_field(proof=False) + False """ return False diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 28fa62bc430..71db491b116 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -823,8 +823,27 @@ def is_field(self, proof = True): sage: S = R.quotient(x^2 - 2) sage: S.is_field() True + + If proof is ``True``, requires the ``is_irreducible`` method of the + modulus to be implemented:: + + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2+x+1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2+x+1) + sage: F2.is_field() + Traceback (most recent call last): + ... + NotImplementedError + sage: F2.is_field(proof = False) + False """ - return self.base_ring().is_field(proof) and self.modulus().is_irreducible() + if proof: + return self.base_ring().is_field(True) and self.modulus().is_irreducible() + try: + return self.base_ring().is_field(False) and self.modulus().is_irreducible() + except NotImplementedError: + return False def krull_dimension(self): return self.base_ring().krull_dimension() diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 65bcc819c86..2621249f1b5 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -510,7 +510,10 @@ import sage.rings.ring from sage.misc.fast_methods import Singleton from sage.misc.cachefunc import cached_method -from sage.structure.sage_object import SageObject +from sage.structure.sage_object import (SageObject, richcmp, + rich_to_bool, richcmp_not_equal, + op_EQ, op_NE, op_LE, op_LT, + op_GE, op_GT) from sage.rings.real_mpfr import RR from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement, RealIntervalField_class from sage.rings.complex_field import ComplexField @@ -2054,6 +2057,79 @@ def mk_algebraic(x): QQxy_x = QQxy.gen(0) QQxy_y = QQxy.gen(1) + +def cmp_elements_with_same_minpoly(a, b, p): + r""" + Compare the algebraic elements ``a`` and ``b`` knowing that they have the + same minimal polynomial ``p``. + + This is an helper function for comparison of algebraic elements (i.e. the + methods :meth:`AlgebraicNumber._richcmp_` and + :meth:`AlgebraicReal._richcmp_`). + + INPUT: + + - ``a`` and ``b`` -- elements of the algebraic or the real algebraic field + with same minimal polynomial + + - ``p`` -- the minimal polynomial + + OUTPUT: + + `-1`, `0`, `1`, `None` depending on whether `a < b`, `a = b` or `a > b` or + the function did not succeed with the given precision of `a` and `b`. + + EXAMPLES:: + + sage: from sage.rings.qqbar import cmp_elements_with_same_minpoly + sage: x = polygen(ZZ) + sage: p = x^2 - 2 + sage: a = AA.polynomial_root(p, RIF(1,2)) + sage: b = AA.polynomial_root(p, RIF(-2,-1)) + sage: cmp_elements_with_same_minpoly(a, b, p) + 1 + sage: cmp_elements_with_same_minpoly(-a, b, p) + 0 + """ + ar = a._value.real() + br = b._value.real() + if not ar.overlaps(br): + return -1 if richcmp_not_equal(ar, br, op_LT) else 1 + + ai = a._value.imag() + bi = b._value.imag() + + if a.parent() is AA or b.parent() is AA: + ring = AA + else: + ring = QQbar + roots = p.roots(ring, False) + + real = ar.union(br) + imag = ai.union(bi) + roots = [r for r in roots if r._value.real().overlaps(real) + and r._value.imag().abs().overlaps(imag)] + if len(roots) == 1: + # There is only a single (real) root matching both descriptors + # so they both must be that root and therefore equal. + return 0 + if (len(roots) == 2 and + not roots[0]._value.imag().contains_zero()): + # There is a complex conjugate pair of roots matching both + # descriptors, so compare by imaginary value. + while ai.contains_zero(): + a._more_precision() + ai = a._value.imag() + while bi.contains_zero(): + b._more_precision() + bi = b._value.imag() + if ai.overlaps(bi): + return 0 + return -1 if richcmp_not_equal(ai, bi, op_LT) else 1 + + return None + + class AlgebraicGeneratorRelation(SageObject): """ A simple class for maintaining relations in the lattice of algebraic @@ -2172,7 +2248,13 @@ def __cmp__(self, other): sage: gen.__cmp__(qq_generator) 1 """ - return cmp(self._index, other._index) + si = self._index + oi = other._index + if si < oi: + return -1 + if si > oi: + return 1 + return 0 def is_complex(self): r""" @@ -3022,6 +3104,146 @@ def __hash__(self): else: return hash((self + QQbar_hash_offset).interval_exact(CIF)) + def __bool__(self): + """ + Check whether ``self`` is nonzero. + + This is fast if interval arithmetic proves it and in many other cases. + Though, it might be slow in very particular cases where the number is + actually zero or very close to zero. + + EXAMPLES:: + + sage: bool(QQbar.zeta(2) + 1) + False + sage: bool(QQbar.zeta(7) / (2^500)) + True + + sage: bool(QQbar(I).imag()) + True + sage: bool(QQbar(I).real()) + False + + The following is very fast, even though the number is really small:: + + sage: a1 = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: a2 = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: bool(a1 + a2) + True + sage: bool(a1 - a2) + False + + sage: a = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: b = QQbar(2).sqrt() - 6882627592338442563/4866752642924153522 + sage: c = QQbar(3).sqrt() - 142437039878091970439/82236063316189858921 + sage: d = (59/2)**(1000/7) + sage: e = (a + b + c) * (a + b - c) * (a - b) * (a - b - c) / d + sage: bool(e) + True + sage: bool(e.abs() < 2**-500) + True + + An identity between roots of unity:: + + sage: z3 = QQbar.zeta(3) + sage: z4 = QQbar.zeta(4) + sage: z5 = QQbar.zeta(5) + sage: p1 = (z3 + z4 + z5)**2 + sage: p2 = (z3 - z4 - z5)**2 + sage: p3 = (z3 - z4 + z5)**2 + sage: p4 = (z3 + z4 - z5)**2 + sage: bool(p1 - p2 + p3 - p4 - 8 * QQbar.zeta(15)**8) + False + + Test some non-trivial zeros:: + + sage: x = polygen(ZZ) + sage: a = (AA(2).sqrt() + AA(3).sqrt() + AA(5).sqrt())^2 + sage: b = 10 + 2*max((x^4 - 62*x^2 - 240*x - 239).roots(AA, False)) + sage: bool(a - b) + False + + sage: d = sum(AA(k)**(1/k) for k in [2..100]) + sage: bool(d * (a - b)) + False + sage: bool((a - b) * d) + False + sage: bool(d * (a - b) * d) + False + sage: bool((a - b) / d) + False + + sage: d = sum(QQbar(-k)**(1/k) for k in [2..100]) + sage: bool(d * (a - b)) + False + sage: bool((a - b) * d) + False + sage: bool(d * (a - b) * d) + False + sage: bool((a - b) / d) + False + """ + # case 0: trivial tests + if not self._value.contains_zero(): + return True + elif self._value.is_zero(): + if not isinstance(self._descr, ANRational): + self._set_descr(ANRational(QQ.zero())) + return False + + # case 1: cheap tests + sd = self._descr + if isinstance(sd, ANExtensionElement): + # The ANExtensionElement returns an ANRational + # instead, if the number is zero. + return True + elif isinstance(sd, ANRational): + return bool(sd._value) + elif isinstance(sd, ANUnaryExpr) and sd._op != 'real' and sd._op != 'imag': + ans = bool(sd._arg) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + elif isinstance(sd, ANBinaryExpr) and sd._op is operator.mul: + ans = bool(sd._left) and bool(sd._right) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + elif isinstance(sd, ANBinaryExpr) and sd._op is operator.div: + ans = bool(sd._left) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + + # case 2: try more precision + if self._value.prec() < 128: + self._more_precision() + if not self._value.contains_zero(): + return True + + # case 3: try with minpoly in case of x+y or x-y + if isinstance(sd, ANBinaryExpr): + op = sd._op + left = sd._left + right = sd._right if op is operator.sub else -sd._right + + lp = left.minpoly() + rp = right.minpoly() + if lp != rp: + return True + + c = cmp_elements_with_same_minpoly(left, right, left.minpoly()) + if c is not None: + if c == 0: + self._set_descr(ANRational(QQ.zero())) + return bool(c) + + # Sigh... + self.exactify() + return bool(self) + + __nonzero__ = __bool__ + def is_square(self): """ Return whether or not this number is square. @@ -3613,11 +3835,14 @@ def radical_expression(self): roots = candidates interval_field = interval_field.to_prec(interval_field.prec()*2) + class AlgebraicNumber(AlgebraicNumber_base): r""" The class for algebraic numbers (complex numbers which are the roots of a polynomial with integer coefficients). Much of its functionality is inherited from ``AlgebraicNumber_base``. + + .. automethod:: _richcmp_ """ def __init__(self, x): r""" @@ -3643,8 +3868,8 @@ def __reduce__(self): """ return (AlgebraicNumber, (self._descr, )) - def __cmp__(self, other): - """ + def _richcmp_(self, other, op): + r""" Compare two algebraic numbers, lexicographically. (That is, first compare the real components; if the real components are equal, compare the imaginary components.) @@ -3673,8 +3898,8 @@ def __cmp__(self, other): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-215 - 0.7598602580415435?*I, - 0.?e-229 + 0.7598602580415435?*I, + 0.?e-79 - 0.7598602580415435?*I, + 0.?e-79 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] @@ -3707,63 +3932,8 @@ def __cmp__(self, other): 1.000000000000000? + 3.390564396412898?*I, 1.000000000000000? + 3.850538755978243?*I, 1.000000000000000? + 4.016778562562223?*I] - """ - # case 0: same object - if self is other: return 0 - # case 1: real parts are clearly distinct - ri1 = self._value.real() - ri2 = other._value.real() - if not ri1.overlaps(ri2): - return cmp(ri1, ri2) - - # case 2: possibly equal or conjugate values - # (this case happen a lot when sorting the roots of a real polynomial) - if is_RealIntervalFieldElement(self._value): - ci1 = ri1.parent().zero() - else: - ci1 = self._value.imag().abs() - if is_RealIntervalFieldElement(other._value): - ci2 = ri2.parent().zero() - else: - ci2 = other._value.imag().abs() - if ci1.overlaps(ci2) and self.minpoly() == other.minpoly(): - ri = ri1.union(ri2) - ci = ci1.union(ci2) - roots = self.minpoly().roots(QQbar, False) - roots = [r for r in roots if r._value.real().overlaps(ri) - and r._value.imag().abs().overlaps(ci)] - if len(roots) == 1: - # There is only a single (real) root matching both descriptors - # so they both must be that root and therefore equal. - return 0 - if (len(roots) == 2 and - not roots[0]._value.imag().contains_zero()): - # There is a complex conjugate pair of roots matching both - # descriptors, so compare by imaginary value. - ii1 = self._value.imag() - while ii1.contains_zero(): - self._more_precision() - ii1 = self._value.imag() - ii2 = other._value.imag() - while ii2.contains_zero(): - other._more_precision() - ii2 = other._value.imag() - if ii1.overlaps(ii2): - return 0 - return cmp(ii1, ii2) - - # case 3: try hard to compare real parts and imaginary parts - rcmp = cmp(self.real(), other.real()) - if rcmp != 0: - return rcmp - return cmp(self.imag(), other.imag()) - - def __eq__(self, other): - """ - Test two algebraic numbers for equality. - - EXAMPLES:: + TESTS:: sage: QQbar.zeta(6) == QQbar(1/2 + I*sqrt(3)/2) True @@ -3775,25 +3945,6 @@ def __eq__(self, other): False sage: GF(7)(2) in QQbar False - """ - if not isinstance(other, AlgebraicNumber): - try: - self, other = canonical_coercion(self, other) - return self == other - except TypeError: - return False - if self is other: return True - if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): - return not self - if isinstance(self._descr, ANRational) and self._descr._value.is_zero(): - return not other - return not self._sub_(other) - - def __ne__(self, other): - r""" - Test two algebraic numbers for inequality. - - EXAMPLES:: sage: QQbar.zeta(6) != QQbar(1/2 + I*sqrt(3)/2) False @@ -3803,39 +3954,59 @@ def __ne__(self, other): False sage: QQbar(2) != GF(7)(2) True - """ - return not self == other - def __bool__(self): - """ - Check whether self is equal is nonzero. This is fast if - interval arithmetic proves that self is nonzero, but may be - slow if the number actually is very close to zero. - - EXAMPLES:: - - sage: bool(QQbar.zeta(2) + 1) - False - sage: bool(QQbar.zeta(7) / (2^500)) + sage: QQbar.zeta(3).real() == -1/2 True """ - val = self._value - d = self._descr - if not val.contains_zero() or isinstance(d, ANExtensionElement): - return True - elif isinstance(d, ANRational): - return bool(d._value) + # note: we can assume that self is not other here + sd = self._descr + od = other._descr + + if isinstance(sd, ANRational) and isinstance(od, ANRational): + return richcmp(sd._value, od._value, op) + + if op == op_EQ or op == op_NE: + # some cheap and quite common tests where we can decide + # equality or difference + if not (self._value.real().overlaps(other._value.real()) and + self._value.imag().overlaps(other._value.imag())): + return op == op_NE + if isinstance(sd, ANRational) and not sd._value: + return bool(other) == (op == op_NE) + elif isinstance(od, ANRational) and not od._value: + return bool(self) == (op == op_NE) + elif (isinstance(sd, ANExtensionElement) and + isinstance(od, ANExtensionElement) and + sd._generator is od._generator): + return sd._value == od._value if op == op_EQ else sd._value != od._value + + # case 0: real parts are clearly distinct + ri1 = self._value.real() + ri2 = other._value.real() + if not ri1.overlaps(ri2): + return richcmp_not_equal(ri1, ri2, op) - while self._value.prec() < 128: - self._more_precision() - if not self._value.contains_zero(): - return True + # case 1: rationals + sd = self._descr + od = other._descr + if isinstance(sd, ANRational) and isinstance(od, ANRational): + return richcmp(sd._value, od._value, op) - # Sigh... - self.exactify() - return self.__bool__() + # case 2: possibly equal or conjugate values + # (this case happen a lot when sorting the roots of a real polynomial) + ci1 = self._value.imag().abs() + ci2 = other._value.imag().abs() + if ci1.overlaps(ci2) and self.minpoly() == other.minpoly(): + c = cmp_elements_with_same_minpoly(self, other, self.minpoly()) + if c is not None: + return rich_to_bool(op, c) - __nonzero__ = __bool__ + # case 3: try hard to compare real parts and imaginary parts + srp = self.real() + orp = other.real() + if srp != orp: + return richcmp_not_equal(srp, orp, op) + return richcmp(self.imag(), other.imag(), op) def __pow__(self, e): r""" ``self**p`` returns the `p`'th power of self (where `p` can @@ -3879,7 +4050,7 @@ def __pow__(self, e): sage: QQbar.zeta(7)^6 0.6234898018587335? - 0.7818314824680299?*I sage: (QQbar.zeta(7)^6)^(1/3) * QQbar.zeta(21) - 1.000000000000000? + 0.?e-18*I + 1.000000000000000? + 0.?e-17*I TESTS: @@ -4311,6 +4482,11 @@ def rational_argument(self): return self._descr.rational_argument(self) class AlgebraicReal(AlgebraicNumber_base): + r""" + A real algebraic number. + + .. automethod:: _richcmp_ + """ def __init__(self, x): """ Create an algebraic real from x, possibly taking the real part of x. @@ -4388,26 +4564,68 @@ def __reduce__(self): """ return (AlgebraicReal, (self._descr, )) - def __cmp__(self, other): + def _richcmp_(self, other, op): """ Compare two algebraic reals. EXAMPLES:: - sage: cmp(AA(golden_ratio), AA(sqrt(5))) - -1 - sage: cmp(AA(golden_ratio), AA((sqrt(5)+1)/2)) - 0 - sage: cmp(AA(7), AA(50/7)) - -1 + sage: AA(2).sqrt() < AA(3).sqrt() + True + sage: ((5+AA(5).sqrt())/2).sqrt() == 2*QQbar.zeta(5).imag() + True + sage: AA(3).sqrt() + AA(2).sqrt() < 3 + False + + TESTS:: + + sage: AA(golden_ratio) < AA(sqrt(5)) + True + sage: AA(golden_ratio) == AA((sqrt(5)+1)/2) + True + sage: AA(7) >= AA(50/7) + False """ - if self is other: return 0 - if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): - return self.sign() - elif isinstance(self._descr, ANRational) and self._descr._value.is_zero(): - return -other.sign() - else: - return self._sub_(other).sign() + # note: we can assume that self is not other here + sd = self._descr + od = other._descr + + if type(sd) is ANRational and type(od) is ANRational: + return richcmp(sd._value, od._value, op) + + if op == op_EQ or op == op_NE: + # some cheap and quite common tests where we can decide equality or difference + if not self._value.real().overlaps(other._value.real()): + return op == op_NE + if type(sd) is ANRational and not sd._value: + return bool(other) == (op == op_NE) + elif type(od) is ANRational and not od._value: + return bool(self) == (op == op_NE) + elif (type(sd) is ANExtensionElement and + type(od) is ANExtensionElement and + sd._generator is od._generator): + return sd._value == od._value if op == op_EQ else sd._value != od._value + elif self.minpoly() != other.minpoly(): + return op == op_NE + + # case 0: real parts are clearly distinct + if not self._value.overlaps(other._value): + return richcmp(self._value, other._value, op) + + # case 1: rationals + sd = self._descr + od = other._descr + if type(sd) is ANRational and type(od) is ANRational: + return richcmp(sd._value, od._value, op) + + if self._value.prec() < 128: + self._more_precision() + if other._value.prec() < 128: + other._more_precision() + if not self._value.overlaps(other._value): + return richcmp(self._value, other._value, op) + + return rich_to_bool(op, (self-other).sign()) def __pow__(self, e): """ @@ -4719,28 +4937,82 @@ def sign(self): -1 sage: (AA(2).sqrt() - AA(2).sqrt()).sign() 0 + + sage: a = AA(2).sqrt() + AA(3).sqrt() - 58114382797550084497/18470915334626475921 + sage: a.sign() + 1 + sage: b = AA(2).sqrt() + AA(3).sqrt() - 2602510228533039296408/827174681630786895911 + sage: b.sign() + -1 + + sage: c = AA(5)**(1/3) - 1437624125539676934786/840727688792155114277 + sage: c.sign() + 1 + + sage: (((a+b)*(a+c)*(b+c))**9 / (a*b*c)).sign() + 1 + sage: (a-b).sign() + 1 + sage: (b-a).sign() + -1 + sage: (a*b).sign() + -1 + sage: ((a*b).abs() + a).sign() + 1 + sage: (a*b - b*a).sign() + 0 """ - if self._value.lower() > 0: - return 1 - elif self._value.upper() < 0: - return -1 - elif isinstance(self._descr, ANRational): - return self._descr._value.sign() + if not self._value.contains_zero(): + return self._value.unique_sign() + + sd = self._descr + if isinstance(self._descr, ANRational): + return sd._value.sign() elif isinstance(self._descr, ANExtensionElement): # All field elements are irrational by construction # (the ANExtensionElement constructor will return an ANRational # instead, if the number is actually rational). # An irrational number must eventually be different from 0 - self._more_precision() - return self.sign() - elif self._value.prec() < 128: + while self._value.contains_zero(): + self._more_precision() + return self._value.unique_sign() + elif type(sd) is ANBinaryExpr: + ls = sd._left.sign() + rs = sd._right.sign() + if sd._op is operator.mul or sd._op is operator.div: + return sd._left.sign() * sd._right.sign() + elif sd._op is operator.add: + if ls == rs: + return ls + else: + if ls == -rs: + return ls + elif not ls: + self._set_descr((-sd._right)._descr) + return -rs + elif not rs: + self._set_descr(sd._left._descr) + return ls + elif type(sd) is ANUnaryExpr: + if sd._op == 'abs': + c = 1 if bool(sd._arg) else 0 + if not c: + self._set_descr(ANRational(QQ.zero())) + return c + elif sd._op == '-': + return -(sd._arg.sign()) + elif sd._op == '~': + return sd._arg.sign() + + if self._value.prec() < 128: # OK, we'll try adding precision one more time self._more_precision() - return self.sign() - else: - # Sigh... - self.exactify() - return self.sign() + if not self._value.contains_zero(): + return self._value.unique_sign() + + # Sigh... + self.exactify() + return self.sign() def _interval_fast(self, prec): r""" diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 22ac471cc12..383935431f4 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -994,8 +994,17 @@ cdef class Ring(ParentWithGens): Traceback (most recent call last): ... NotImplementedError + + Forward the proof flag to ``is_field``, see :trac:`22910`:: + + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2+x+1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2+x+1) + sage: F2.is_integral_domain(False) + False """ - if self.is_field(): + if self.is_field(proof): return True if self.is_zero(): diff --git a/src/sage/sandpiles/all.py b/src/sage/sandpiles/all.py index 4e746e78966..ea997043368 100644 --- a/src/sage/sandpiles/all.py +++ b/src/sage/sandpiles/all.py @@ -1,13 +1,6 @@ from __future__ import absolute_import from sage.misc.lazy_import import lazy_import -from .sandpile import Sandpile, SandpileDivisor, SandpileConfig, firing_graph, parallel_firing_graph, wilmes_algorithm, random_digraph, random_DAG, triangle_sandpile +from .sandpile import Sandpile, SandpileDivisor, SandpileConfig, firing_graph, parallel_firing_graph, wilmes_algorithm, random_DAG, triangle_sandpile lazy_import('sage.sandpiles.examples', 'sandpiles') - -lazy_import('sage.sandpiles.sandpile', 'sandlib', deprecation=(18618,'sandlib() will soon be removed. Use sandpile() instead.')) -lazy_import('sage.sandpiles.sandpile', 'grid_sandpile', deprecation=(18618,'grid_sandpile() will soon be removed. Use sandpile.Grid() instead.')) -lazy_import('sage.sandpiles.sandpile', 'complete_sandpile', deprecation=(18618,'complete_sandpile() will soon be removed. Use sandpile.Complete() instead.')) -lazy_import('sage.sandpiles.sandpile', 'firing_vector', deprecation=(18618,'firing_vector() will soon be removed. Use SandpileDivisor.is_linearly_equivalent() instead.')) - -lazy_import('sage.sandpiles.sandpile', ['admissible_partitions','partition_sandpile','min_cycles','glue_graphs','aztec_sandpile','triangle_sandpile'], deprecation=18618) diff --git a/src/sage/sandpiles/examples.py b/src/sage/sandpiles/examples.py index 3e8c158898b..eb38b711700 100644 --- a/src/sage/sandpiles/examples.py +++ b/src/sage/sandpiles/examples.py @@ -65,11 +65,11 @@ def __call__(self): EXAMPLES:: sage: sandpiles() - Try sandpile.FOO() where FOO is in the list: + Try sandpiles.FOO() where FOO is in the list: Complete, Cycle, Diamond, Fan, Grid, House, Wheel """ - print('Try sandpile.FOO() where FOO is in the list:\n') + print('Try sandpiles.FOO() where FOO is in the list:\n') print(" " + ", ".join([str(i) for i in dir(sandpiles) if i[0] != '_'])) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ea1762268a2..d49850317cf 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -28,7 +28,7 @@ DEPRECATED -SandpileDivisor.linear_system, SandpileDivisor.r_of_D, sandlib method, complete_sandpile, grid_sandpile, triangle_sandpile, aztec_sandpile, random_digraph, random_tree, glue_graphs, admissible_partitions, firing_vector, min_cycles. +SandpileDivisor.linear_system, SandpileDivisor.r_of_D. MINOR CHANGES @@ -6203,10 +6203,8 @@ def sandlib(selector=None): EXAMPLES:: + sage: from sage.sandpiles.sandpile import sandlib sage: sandlib() - doctest:...: DeprecationWarning: sandlib() will soon be removed. Use sandpile() instead. - See http://trac.sagemath.org/18618 for details. - Sandpiles in the sandlib: kite : generic undirected graphs with 5 vertices generic : generic digraph with 6 vertices @@ -6302,10 +6300,8 @@ def triangle_sandpile(n): EXAMPLES:: + sage: from sage.sandpiles.sandpile import triangle_sandpile sage: T = triangle_sandpile(5) - doctest:...: DeprecationWarning: - Importing triangle_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. sage: T.group_order() 135418115000 """ @@ -6349,10 +6345,8 @@ def aztec_sandpile(n): EXAMPLES:: + sage: from sage.sandpiles.sandpile import aztec_sandpile sage: aztec_sandpile(2) - doctest:...: DeprecationWarning: - Importing aztec_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. {'sink': {(-3/2, -1/2): 2, (-3/2, 1/2): 2, (-1/2, -3/2): 2, @@ -6415,60 +6409,6 @@ def aztec_sandpile(n): aztec_sandpile['sink'][vert] = out_degree return aztec_sandpile -def random_digraph(num_verts, p=0.5, directed=True, weight_max=1): - """ - A random weighted digraph with a directed spanning tree rooted at `0`. If - ``directed = False``, the only difference is that if `(i,j,w)` is an edge with - tail `i`, head `j`, and weight `w`, then `(j,i,w)` appears also. The result - is returned as a Sage digraph. - - INPUT: - - - ``num_verts`` -- number of vertices - - - ``p`` -- (default: 0.5) probability edges occur - - - ``directed`` -- (default: ``True``) if directed - - - ``weight_max`` -- (default: 1) integer maximum for random weights - - OUTPUT: - - random graph - - EXAMPLES:: - - sage: g = random_digraph(6,0.2,True,3) - doctest:...: DeprecationWarning: random_digraph will be removed soon. Use any of the Random* methods - from graphs() and from digraphs() instead. - See http://trac.sagemath.org/18618 for details. - sage: S = Sandpile(g,0) - sage: S.show(edge_labels = True) - - TESTS: - - Check that we can construct a random digraph with the - default arguments (:trac:`12181`):: - - sage: random_digraph(5) - Digraph on 5 vertices - """ - deprecation(18618,'random_digraph will be removed soon. Use any of the Random* methods from graphs() and from digraphs() instead.') - a = digraphs.RandomDirectedGN(num_verts) - b = graphs.RandomGNP(num_verts,p) - a.add_edges(b.edges()) - if directed: - c = graphs.RandomGNP(num_verts,p) - # reverse the edges of c and add them in - a.add_edges([(j,i,None) for i,j,k in c.edges()]) - else: - a.add_edges([(j,i,None) for i,j,k in a.edges()]) - a.add_edges([(j,i,None) for i,j,k in b.edges()]) - # now handle the weights - for i,j,k in a.edge_iterator(): - a.set_edge_label(i,j,ZZ.random_element(weight_max)+1) - return a - def random_DAG(num_verts, p=0.5, weight_max=1): r""" A random directed acyclic graph with ``num_verts`` vertices. @@ -6547,16 +6487,12 @@ def glue_graphs(g, h, glue_g, glue_h): EXAMPLES:: + sage: from sage.sandpiles.sandpile import glue_graphs sage: x = {0: {}, 1: {0: 1}, 2: {0: 1, 1: 1}, 3: {0: 1, 1: 1, 2: 1}} sage: y = {0: {}, 1: {0: 2}, 2: {1: 2}, 3: {0: 1, 2: 1}} sage: glue_x = {1: 1, 3: 2} sage: glue_y = {0: 1, 1: 2, 3: 1} - sage: z = glue_graphs(x,y,glue_x,glue_y) - doctest:...: DeprecationWarning: - Importing glue_graphs from here is deprecated. If you need to use it, - please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. - sage: z + sage: z = glue_graphs(x,y,glue_x,glue_y); z {0: {}, 'x0': {0: 1, 'x1': 1, 'x3': 2, 'y1': 2, 'y3': 1}, 'x1': {'x0': 1}, @@ -6709,11 +6645,10 @@ def admissible_partitions(S, k): EXAMPLES:: + sage: from sage.sandpiles.sandpile import admissible_partitions + sage: from sage.sandpiles.sandpile import partition_sandpile sage: S = sandpiles.Cycle(4) sage: P = [admissible_partitions(S, i) for i in [2,3,4]] - doctest:...: DeprecationWarning: - Importing admissible_partitions from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. sage: P [[{{0}, {1, 2, 3}}, {{0, 2, 3}, {1}}, @@ -6728,9 +6663,6 @@ def admissible_partitions(S, k): [{{0}, {1}, {2}, {3}}]] sage: for p in P: ....: sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p]) - doctest:...: DeprecationWarning: - Importing partition_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. 6 8 3 @@ -6771,6 +6703,7 @@ def partition_sandpile(S, p): EXAMPLES:: + sage: from sage.sandpiles.sandpile import admissible_partitions, partition_sandpile sage: S = sandpiles.Cycle(4) sage: P = [admissible_partitions(S, i) for i in [2,3,4]] sage: for p in P: @@ -6799,50 +6732,6 @@ def partition_sandpile(S, p): if S.sink() in i: return Sandpile(g,i) -def firing_vector(S, D, E): - r""" - If `D` and `E` are linearly equivalent divisors, find the firing vector - taking `D` to `E`. - - INPUT: - - - ``S`` -- Sandpile - - - ``D``, ``E`` -- tuples (representing linearly equivalent divisors) - - OUTPUT: - - tuple (representing a firing vector from ``D`` to ``E``) - - EXAMPLES:: - - sage: S = sandpiles.Complete(4) - sage: D = SandpileDivisor(S, {0: 0, 1: 0, 2: 8, 3: 0}) - sage: E = SandpileDivisor(S, {0: 2, 1: 2, 2: 2, 3: 2}) - sage: v = firing_vector(S, D, E) - doctest:...: DeprecationWarning: firing_vector() will soon be removed. Use SandpileDivisor.is_linearly_equivalent() instead. - See http://trac.sagemath.org/18618 for details. - doctest:...: DeprecationWarning: May 25, 2015: Replaced by SandpileDivisor.is_linearly_equivalent. - See http://trac.sagemath.org/18618 for details. - sage: v - (0, 0, 2, 0) - - The divisors must be linearly equivalent:: - - sage: vector(D.values()) - S.laplacian()*vector(v) == vector(E.values()) - True - sage: firing_vector(S, D, S.zero_div()) - Error. Are the divisors linearly equivalent? - """ - deprecation(18618,'May 25, 2015: Replaced by SandpileDivisor.is_linearly_equivalent.') - try: - v = vector(D.values()) - w = vector(E.values()) - return tuple(S.laplacian().solve_left(v-w)) - except ValueError: - print("Error. Are the divisors linearly equivalent?") - return - def min_cycles(G, v): r""" Minimal length cycles in the digraph `G` starting at vertex `v`. @@ -6859,11 +6748,9 @@ def min_cycles(G, v): EXAMPLES:: + sage: from sage.sandpiles.sandpile import min_cycles, sandlib sage: T = sandlib('gor') sage: [min_cycles(T, i) for i in T.vertices()] - doctest:...: DeprecationWarning: - Importing min_cycles from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. [[], [[1, 3]], [[2, 3, 1], [2, 3]], [[3, 1], [3, 2]]] """ pr = G.neighbors_in(v) diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index e51fc13758a..eb6ae832954 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1051,7 +1051,7 @@ def ei(self): sage: abs(x1.real())+abs(x2.real())<1e-14 True sage: x1.imag(),x2.imag(),x3 - (-1.122462048309373?, 1.122462048309373?, -1) + (-1.122462048309373?, 1.122462048309373?, -1.000000000000000?) :: diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 2b78e621507..0b3a76726fd 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -3597,7 +3597,7 @@ def multiplier_spectra(self, n, formal=True, embedding=None): sage: f = H([x^2 - w/4*y^2, y^2]) sage: f.multiplier_spectra(2, False, embedding=K.embeddings(QQbar)[0]) [0, - 5.931851652578137? + 0.?e-49*I, + 5.931851652578137? + 0.?e-47*I, 0.0681483474218635? - 1.930649271699173?*I, 0.0681483474218635? + 1.930649271699173?*I] diff --git a/src/sage/stats/basic_stats.py b/src/sage/stats/basic_stats.py index 7bfd64f195b..3c532a62c75 100644 --- a/src/sage/stats/basic_stats.py +++ b/src/sage/stats/basic_stats.py @@ -84,22 +84,27 @@ def mean(v): return s/ZZ(len(v)) return s/len(v) + def mode(v): """ - Return the mode of `v`. The mode is the sorted list of the most - frequently occuring elements in `v`. If `n` is the most times - that any element occurs in `v`, then the mode is the sorted list - of elements of `v` that occur `n` times. + Return the mode of `v`. + + The mode is the list of the most frequently occuring + elements in `v`. If `n` is the most times that any element occurs + in `v`, then the mode is the list of elements of `v` that + occur `n` times. The list is sorted if possible. + + .. NOTE:: - NOTE: The elements of `v` must be hashable and comparable. + The elements of `v` must be hashable. INPUT: - - `v` -- a list + - `v` -- a list OUTPUT: - - a list + - a list (sorted if possible) EXAMPLES:: @@ -110,20 +115,28 @@ def mode(v): 3 sage: mode([]) [] + sage: mode([1,2,3,4,5]) [1, 2, 3, 4, 5] sage: mode([3,1,2,1,2,3]) [1, 2, 3] - sage: mode(['sage', 4, I, 3/5, 'sage', pi]) + sage: mode([0, 2, 7, 7, 13, 20, 2, 13]) + [2, 7, 13] + + sage: mode(['sage', 'four', 'I', 'three', 'sage', 'pi']) ['sage'] + sage: class MyClass: ....: def mode(self): ....: return [1] sage: stats.mode(MyClass()) [1] """ - if hasattr(v, 'mode'): return v.mode() - from operator import itemgetter + if hasattr(v, 'mode'): + return v.mode() + + if not v: + return v freq = {} for i in v: @@ -132,8 +145,12 @@ def mode(v): else: freq[i] = 1 - s = sorted(freq.items(), key=itemgetter(1), reverse=True) - return [i[0] for i in s if i[1]==s[0][1]] + n = max(freq.values()) + try: + return sorted(u for u, f in freq.items() if f == n) + except TypeError: + return [u for u, f in freq.items() if f == n] + def std(v, bias=False): """ diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index e6c6d8384eb..858207dee0d 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -9481,8 +9481,6 @@ cdef class Expression(CommutativeRingElement): else: return self - - def simplify_real(self): r""" Simplify the given expression over the real numbers. This allows @@ -9791,7 +9789,7 @@ cdef class Expression(CommutativeRingElement): TESTS: - Check that the problem with applying `full_simplify()` to gamma + Check that the problem with applying ``full_simplify()`` to gamma functions (:trac:`9240`) has been fixed:: sage: gamma(1/3) diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index c37de13a637..976960b7b02 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -806,7 +806,7 @@ cdef class GinacFunction(BuiltinFunction): # get serial try: self._serial = find_function(fname, self._nargs) - except ValueError as err: + except RuntimeError as err: raise ValueError("cannot find GiNaC function with name %s and %s arguments" % (fname, self._nargs)) global sfunction_serial_dict @@ -1061,7 +1061,7 @@ cdef class BuiltinFunction(Function): # search ginac registry for name and nargs try: serial = find_function(self._name, self._nargs) - except ValueError as err: + except RuntimeError as err: pass # if match, get operator from function table diff --git a/src/sage/version.py b/src/sage/version.py index bc64af91cba..7ce1258f0fd 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.0.beta5' -date = '2017-05-04' +version = '8.0.beta6' +date = '2017-05-12' diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index daa7dc6a556..70c3ef405ce 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -1208,7 +1208,8 @@ def get_builder(name): print("of documents, or 'sage --docbuild --help' for more help.") sys.exit(1) -def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): + +def format_columns(lst, align=u'<', cols=None, indent=4, pad=3, width=80): """ Utility function that formats a list as a simple table and returns a Unicode string representation. The number of columns is @@ -1223,13 +1224,14 @@ def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): if cols is None: import math cols = math.trunc((width - indent) / size) - s = " " * indent + s = u" " * indent for i in range(len(lst)): if i != 0 and i % cols == 0: - s += "\n" + " " * indent - s += "{0:{1}{2}}".format(lst[i], align, size) - s += "\n" - return unicode(s) + s += u"\n" + u" " * indent + s += u"{0:{1}{2}}".format(lst[i], align, size) + s += u"\n" + return s + def help_usage(s=u"", compact=False): """