diff --git a/VERSION.txt b/VERSION.txt index b412f32b96b..870a599bb16 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.9.beta5, Release Date: 2019-08-03 +SageMath version 8.9.beta6, Release Date: 2019-08-10 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index fb2a1d7aaa3..d2dcaa9fe06 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=997b7b8745c4b48954089e3607c0deb2f3de5fa1 -md5=7f2255d9146518296793f7f6183568c2 -cksum=2571648127 +sha1=e9fc4a6b98663c0f625707d699638017b65688ae +md5=fe7bb18c282553543c98688e78dab7d0 +cksum=3711812649 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 9a93e6005af..1d807d366da 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -73d2ad18c0e4fdd99f9cfbae99f726cdd70486f2 +442411009b7c99f34315b76fe35ad092aa3a6962 diff --git a/build/pkgs/dot2tex/SPKG.txt b/build/pkgs/dot2tex/SPKG.txt index 488a2bb6de2..4cbf60564e5 100644 --- a/build/pkgs/dot2tex/SPKG.txt +++ b/build/pkgs/dot2tex/SPKG.txt @@ -22,6 +22,12 @@ example via the graphviz spkg). preview, a LaTeX package for extracting parts of a document. +Self-tests dependencies: + * graphviz + * texlive-latex-base + * texlive-pictures + * texlive-pstricks + == Patches == * remove_test_semicolon.patch: diff --git a/build/pkgs/gap_packages/dependencies b/build/pkgs/gap_packages/dependencies index 8ef5447e135..21b057d2e47 100644 --- a/build/pkgs/gap_packages/dependencies +++ b/build/pkgs/gap_packages/dependencies @@ -1,4 +1,4 @@ -gap | $(SAGERUNTIME) +gap libsemigroups planarity | $(SAGERUNTIME) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/gap_packages/package-version.txt b/build/pkgs/gap_packages/package-version.txt index 156f6abfa33..5cd045ae73e 100644 --- a/build/pkgs/gap_packages/package-version.txt +++ b/build/pkgs/gap_packages/package-version.txt @@ -1 +1 @@ -4.10.2.p0 +4.10.2.p1 diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index 09a4d2bf32f..10b444b5c65 100644 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -19,6 +19,7 @@ sdh_install \ crystcat \ design-* \ gbnp \ + genss-* \ Hap* \ HAPcryst \ hecke-* \ @@ -64,7 +65,7 @@ install_compiled_pkg() # # These packages have an old ./configure that take the GAP_ROOT as a positional # argument -for pkg in cohomolo-* crypting-* grape-* guava-* +for pkg in cohomolo-* crypting-* grape-* guava-* orb-* do echo "Building GAP package $pkg" cd "$PKG_SRC_DIR/$pkg" @@ -77,10 +78,23 @@ done # These packages have a new-style autoconf ./configure # that takes --with-gaproot -for pkg in nq-* io-* +############################################################################# +########## add extra parameters for packages' configures here ############### +# +# ng : none +# io : none +# semigroups needs to use external libsemigroups +# digraphs needs to use external planarity +pararr=( " " " " "--with-external-planarity" "--with-external-libsemigroups" ) +############################################################################## + +parind=0 +for pkg in nq-* io-* digraphs-* semigroups-* do + echo "Building GAP package $pkg" cd "$PKG_SRC_DIR/$pkg" - sdh_configure --with-gaproot="$GAP_ROOT" + sdh_configure --with-gaproot="$GAP_ROOT" ${pararr[$parind]} + ((parind+=1)) sdh_make -j1 install_compiled_pkg "$pkg" cd "$PKG_SRC_DIR" diff --git a/build/pkgs/libsemigroups/SPKG.txt b/build/pkgs/libsemigroups/SPKG.txt new file mode 100644 index 00000000000..3d4c9478a9b --- /dev/null +++ b/build/pkgs/libsemigroups/SPKG.txt @@ -0,0 +1,15 @@ += libffi = + +== Description == + +C++ library for semigroups and monoids; +used in GAP's package Semigroups. + +== License == + +GPL-3.0 + +== Upstream Contact == + +http://james-d-mitchell.github.io/libsemigroups +https://github.com/james-d-mitchell/libsemigroups diff --git a/build/pkgs/libsemigroups/checksums.ini b/build/pkgs/libsemigroups/checksums.ini new file mode 100644 index 00000000000..ba335c74d1f --- /dev/null +++ b/build/pkgs/libsemigroups/checksums.ini @@ -0,0 +1,4 @@ +tarball=libsemigroups-VERSION.tar.gz +sha1=d1be401d098fc36b096f8b5a89a556892a471385 +md5=788856c7cf162474580e48094ff5de29 +cksum=2423299159 diff --git a/build/pkgs/libsemigroups/dependencies b/build/pkgs/libsemigroups/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/libsemigroups/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/libsemigroups/package-version.txt b/build/pkgs/libsemigroups/package-version.txt new file mode 100644 index 00000000000..2228cad41f3 --- /dev/null +++ b/build/pkgs/libsemigroups/package-version.txt @@ -0,0 +1 @@ +0.6.7 diff --git a/build/pkgs/libsemigroups/spkg-check b/build/pkgs/libsemigroups/spkg-check new file mode 100644 index 00000000000..27cd9419538 --- /dev/null +++ b/build/pkgs/libsemigroups/spkg-check @@ -0,0 +1,2 @@ +cd src +$MAKE check diff --git a/build/pkgs/libsemigroups/spkg-configure.m4 b/build/pkgs/libsemigroups/spkg-configure.m4 new file mode 100644 index 00000000000..307659b4664 --- /dev/null +++ b/build/pkgs/libsemigroups/spkg-configure.m4 @@ -0,0 +1,4 @@ +SAGE_SPKG_CONFIGURE([libsemigroups], [ + dnl checking with pkg-config + PKG_CHECK_MODULES([LIBSEMIGROUPS], [libsemigroups >= 0.6.7], [], [sage_spkg_install_libsemigroups=yes]) +]) diff --git a/build/pkgs/libsemigroups/spkg-install b/build/pkgs/libsemigroups/spkg-install new file mode 100644 index 00000000000..2aaf0e99043 --- /dev/null +++ b/build/pkgs/libsemigroups/spkg-install @@ -0,0 +1,4 @@ +cd src +sdh_configure +sdh_make +sdh_make_install diff --git a/build/pkgs/libsemigroups/type b/build/pkgs/libsemigroups/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/libsemigroups/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/lrcalc/spkg-configure.m4 b/build/pkgs/lrcalc/spkg-configure.m4 new file mode 100644 index 00000000000..f2f8b74f8f9 --- /dev/null +++ b/build/pkgs/lrcalc/spkg-configure.m4 @@ -0,0 +1,9 @@ +SAGE_SPKG_CONFIGURE([lrcalc], [ + AC_CHECK_HEADERS([lrcalc/schublib.h], [ + AC_SEARCH_LIBS([mult_poly_schubert], [lrcalc], [], [sage_spkg_install_lrcalc=yes]) + ], [sage_spkg_install_lrcalc=yes], + [[#ifndef _HASHTAB_H + #include + #include + #endif]]) +]) diff --git a/build/pkgs/mpfi/spkg-configure.m4 b/build/pkgs/mpfi/spkg-configure.m4 new file mode 100644 index 00000000000..5c4f3c51393 --- /dev/null +++ b/build/pkgs/mpfi/spkg-configure.m4 @@ -0,0 +1,38 @@ +SAGE_SPKG_CONFIGURE([mpfi], [ + AC_REQUIRE([SAGE_SPKG_CONFIGURE_MPFR]) + AC_MSG_CHECKING([installing mpfr? ]) + if test x$sage_spkg_install_mpfr = xyes; then + AC_MSG_RESULT([yes; install mpfi as well]) + sage_spkg_install_mpfi=yes + else + AC_MSG_RESULT([no]) + fi + + m4_pushdef(SAGE_MPFI_VERSION_MAJOR, [1]) + m4_pushdef(SAGE_MPFI_VERSION_MINOR, [5]) + + if test x$sage_spkg_install_mpfi != xyes; then + AC_CHECK_HEADER([mpfi.h], [], [sage_spkg_install_mpfi=yes]) + AC_SEARCH_LIBS([mpfi_diam_abs], [mpfi], [ + AC_LANG_PUSH(C) + AC_MSG_CHECKING([MPFI version >= ]SAGE_MPFI_VERSION_MAJOR[.]SAGE_MPFI_VERSION_MINOR) + AC_RUN_IFELSE([ + AC_LANG_PROGRAM( + [[#include + #include + ]], [[ + printf("%s\n", MPFI_VERSION_STRING); + if (MPFI_VERSION_MAJOR >]] SAGE_MPFI_VERSION_MAJOR[[) return 0; + else if (MPFI_VERSION_MAJOR ==]] SAGE_MPFI_VERSION_MAJOR[[ && + MPFI_VERSION_MINOR >=]] SAGE_MPFI_VERSION_MINOR[[) return 0; + else return 1; + ]])], [AC_MSG_RESULT([.. yes])], [ + AC_MSG_RESULT([.. no]) + sage_spkg_install_mpfi=yes]) + AC_LANG_POP(C)], [sage_spkg_install_mpfi=yes]) + fi + + m4_popdef([SAGE_MPFI_VERSION_MAJOR]) + m4_popdef([SAGE_MPFI_VERSION_MINOR]) +]) + diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 88cecd1af8d..44f80cca151 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.9.beta5' -SAGE_RELEASE_DATE='2019-08-03' -SAGE_VERSION_BANNER='SageMath version 8.9.beta5, Release Date: 2019-08-03' +SAGE_VERSION='8.9.beta6' +SAGE_RELEASE_DATE='2019-08-10' +SAGE_VERSION_BANNER='SageMath version 8.9.beta6, Release Date: 2019-08-10' diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 4d98616fc6b..125314b01fd 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -156,7 +156,8 @@ In any case, you always need at least a C/C++ compiler to build the GCC package and its prerequisites before the compilers it provides can be used. Note that you can always override this behavior through the configure -options `--without-system-gcc` and `--with-system-gcc`, see :ref:`section_compilers`. +options ``--without-system-gcc`` and ``--with-system-gcc``, see +:ref:`section_compilers`. Other notes ^^^^^^^^^^^ diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 3e63704b9fd..f9fdd077bcd 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -3,54 +3,51 @@ Coding Theory ============= -Basic Coding Theory objects ---------------------------- +Coding theory is the mathematical theory for algebraic and combinatorial codes +used for forward error correction in communications theory. Sage provides an +extensive library of objects and algorithms in coding theory. +Basic objects in coding theory are channels, linear codes, encoders, and +decoders. The following modules provide the base classes defining them. .. toctree:: :maxdepth: 1 sage/coding/linear_code - sage/coding/channel_constructions - sage/coding/decoder + sage/coding/channel sage/coding/encoder + sage/coding/decoder -Catalogs --------- +Catalogs for available constructions of the basic objects and for bounds on +the parameters of linear codes are provided. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/channels_catalog sage/coding/codes_catalog sage/coding/decoders_catalog sage/coding/encoders_catalog sage/coding/bounds_catalog - sage/coding/databases - sage/coding/two_weight_db -Code constructions ------------------- +Families of Codes +----------------- -.. toctree:: - :maxdepth: 1 - - sage/coding/linear_code - -The named code families below are represented in Sage by their own classes, -allowing specialised implementations of e.g. decoding or computation of properties: +Famous families of codes, listed below, are represented in Sage by their own +classes. For some of them, implementations of special decoding algorithms or +computations for structural invariants are available. .. toctree:: :maxdepth: 2 - sage/coding/grs + sage/coding/parity_check_code sage/coding/hamming_code + sage/coding/cyclic_code + sage/coding/bch_code sage/coding/golay_code - sage/coding/parity_check_code sage/coding/reed_muller_code - sage/coding/cyclic_code - sage/coding/bch - sage/coding/goppa + sage/coding/grs_code + sage/coding/goppa_code In contrast, for some code families Sage can only construct their generator matrix and has no other a priori knowledge on them: @@ -67,8 +64,8 @@ Derived Code Constructions -------------------------- Sage supports the following derived code constructions. If the constituent code -is from a special code family, the derived codes inherit e.g. decoding or -minimum distance capabilities: +is from a special code family, the derived codes inherit structural properties +like decoding radius or minimum distance: .. toctree:: :maxdepth: 2 @@ -80,9 +77,27 @@ minimum distance capabilities: Other derived constructions that simply produce the modified generator matrix can be found among the methods of a constructed code. -Methods and Operations related to Linear Codes ----------------------------------------------- +Decoding +-------- + +Information-set decoding for linear codes: + +.. toctree:: + :maxdepth: 1 + + sage/coding/information_set_decoder +Guruswami-Sudan interpolation-based list decoding for Reed-Solomon codes: + +.. toctree:: + :maxdepth: 1 + + sage/coding/guruswami_sudan/gs_decoder + sage/coding/guruswami_sudan/interpolation + sage/coding/guruswami_sudan/utils + +Automorphism Groups of Linear Codes +----------------------------------- .. toctree:: :maxdepth: 2 @@ -90,27 +105,39 @@ Methods and Operations related to Linear Codes sage/coding/codecan/codecan sage/coding/codecan/autgroup_can_label -Source coding -------------- +Bounds for Parameters of Linear Codes +------------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/coding/code_bounds + sage/coding/delsarte_bounds + +Databases for Coding Theory +--------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/coding/databases + sage/coding/two_weight_db + +Miscellaneous Modules +--------------------- + +There is at least one module in Sage for source coding in communications theory: .. toctree:: :maxdepth: 1 sage/coding/source_coding/huffman -Other modules -------------- +Finally an experimental module used for code constructions: .. toctree:: :maxdepth: 1 sage/coding/relative_finite_field_extension - sage/coding/guruswami_sudan/gs_decoder - sage/coding/guruswami_sudan/interpolation - sage/coding/guruswami_sudan/utils - sage/coding/information_set_decoder - sage/coding/code_bounds - sage/coding/delsarte_bounds - .. include:: ../footer.txt diff --git a/src/doc/en/thematic_tutorials/coding_theory.rst b/src/doc/en/thematic_tutorials/coding_theory.rst index c745e2439cf..2a4fe5c55be 100644 --- a/src/doc/en/thematic_tutorials/coding_theory.rst +++ b/src/doc/en/thematic_tutorials/coding_theory.rst @@ -132,7 +132,7 @@ Sage can build. For the rest of this section, we will illustrate specific functionalities of these code families by manipulating -:class:`sage.coding.grs.GeneralizedReedSolomonCode`. +:class:`sage.coding.grs_code.GeneralizedReedSolomonCode`. So, for starters, we want to create a Generalized Reed-Solomon (GRS) code. @@ -170,8 +170,8 @@ previous section:: It is also possible to ask for the evaluation points and the column multipliers by calling -:meth:`sage.coding.grs.GeneralizedReedSolomonCode.evaluation_points` and -:meth:`sage.coding.grs.GeneralizedReedSolomonCode.column_multipliers`. +:meth:`sage.coding.grs_code.GeneralizedReedSolomonCode.evaluation_points` and +:meth:`sage.coding.grs_code.GeneralizedReedSolomonCode.column_multipliers`. Now, if you know some theory for GRS codes, you know that it's especially easy to compute their minimum distance, which is: @@ -230,7 +230,7 @@ So, we might want to put some errors in it, and try to correct these errors afterwards. We can obviously do it by changing the values at some random positions of our codeword, but we propose here something more general: communication channels. -:class:`sage.coding.channel_constructions.Channel` objects are meant +:class:`sage.coding.channel.Channel` objects are meant as abstractions for communication channels and for manipulation of data representation. In this case, we want to emulate a communication channel which adds some, but not too many, errors to a transmitted word:: @@ -316,7 +316,7 @@ we can now ask for specific encoder and decoder:: sage: Evect Evaluation vector-style encoder for [40, 12, 29] Generalized Reed-Solomon Code over GF(59) sage: type(Evect) - + sage: msg = random_vector(GF(59), C.dimension()) #random sage: c = Evect.encode(msg) sage: NN = C.decoder("NearestNeighbor") @@ -329,7 +329,7 @@ Calling:: is actually a short-hand for constructing the encoder manually, by calling the constructor for -:class:`sage.coding.grs.EncoderGRSEvaluationVector` yourself. +:class:`sage.coding.grs_code.EncoderGRSEvaluationVector` yourself. If you don't supply ``encoder_name`` to :meth:`sage.coding.linear_code.AbstractLinearCode.encoder` you get the default encoder for the code. @@ -468,7 +468,7 @@ introduce a second Channel. in Sage. Consider again the -:meth:`sage.coding.channel_constructions.ChannelStaticErrorRate` from before. +:meth:`sage.coding.channel.ChannelStaticErrorRate` from before. This is a channel that places errors in the transmitted vector but within controlled boundaries. We can describe these boundaries in two ways: @@ -492,7 +492,7 @@ We can describe these boundaries in two ways: Static error rate channel creating between 1 and 14 errors, of input and output space Vector space of dimension 40 over Finite Field of size 59 We already know that a channel has a -:meth:`sage.coding.channel_constructions.Channel.transmit` method which will +:meth:`sage.coding.channel.Channel.transmit` method which will perform transmission over the channel; in this case it will return the transmitted word with some errors in it. This method will always check if the provided word belongs to @@ -501,9 +501,9 @@ In a case one is absolutely certain that one's word is in the input space, one might want to avoid this check, which is time consuming - especially if one is simulating millions of transmissions. For this usage there is -:meth:`sage.coding.channel_constructions.Channel.transmit_unsafe` which does +:meth:`sage.coding.channel.Channel.transmit_unsafe` which does the same as -:meth:`sage.coding.channel_constructions.Channel.transmit` +:meth:`sage.coding.channel.Channel.transmit` but without checking the input, as illustrated thereafter:: sage: c = C.random_element() @@ -514,7 +514,7 @@ but without checking the input, as illustrated thereafter:: False Note there exists a useful shortcut for -:meth:`sage.coding.channel_constructions.Channel.transmit` :: +:meth:`sage.coding.channel.Channel.transmit` :: sage: r = Chan(c) sage: r in C @@ -536,14 +536,14 @@ The first parameter is the input space of the channel. The next two are (respectively) the number of errors and the number or erasures. Each of these can be tuples too, just as it was with -:class:`sage.coding.channel_constructions.StaticErrorRateChannel`. +:class:`sage.coding.channel.StaticErrorRateChannel`. As opposed to this channel though, the output of -:class:`sage.coding.channel_constructions.ErrorErasureChannel` +:class:`sage.coding.channel.ErrorErasureChannel` is not the same as its input space, i.e. the ambient space of C. Rather, it will return two vectors: the first is the transmitted word with the errors added and erased positions set to 0. The second one is the erasure vector whose erased positions contain ones. -This is reflected in :meth:`sage.coding.channel_constructions.output_space`:: +This is reflected in :meth:`sage.coding.channel.output_space`:: sage: C = codes.random_linear_code(GF(7), 10, 5) sage: Chan.output_space() diff --git a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst index e3756af5d28..13f1a83a2b6 100644 --- a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst +++ b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst @@ -429,7 +429,7 @@ We will implement a very naive channel which works only for words over :math:`\GF{2}` and flips as many bits as requested by the user. As channels are not directly related to code families, but more to -vectors and words, we have a specific file, ``channel_constructions.py`` +vectors and words, we have a specific file, ``channel.py`` to store them. So we will just add our new class in this file. @@ -447,7 +447,7 @@ Plus, in our case, as this channel only works for vectors over :math:`\GF{2}`, the input and output spaces are the same. Let us write the constructor of our new channel class:: - sage: from sage.coding.channel_constructions import Channel + sage: from sage.coding.channel import Channel sage: class BinaryStaticErrorRateChannel(Channel): ....: def __init__(self, space, number_errors): ....: if space.base_ring() is not GF(2): @@ -457,7 +457,7 @@ Let us write the constructor of our new channel class:: ....: super(BinaryStaticErrorRateChannel, self).__init__(space, space) ....: self._number_errors = number_errors -Remember to inherit from :class:`sage.coding.channel_constructions.Channel`! +Remember to inherit from :class:`sage.coding.channel.Channel`! We also want to override representation methods ``_repr_`` and ``_latex_``:: @@ -503,7 +503,7 @@ That is it, we now have our new channel class ready to use! Summary of the implementation for channels ------------------------------------------ -1. Inherit from :class:`sage.coding.channel_constructions.Channel`. +1. Inherit from :class:`sage.coding.channel.Channel`. 2. Add this line in the class' constructor: .. CODE-BLOCK:: python @@ -557,7 +557,7 @@ Here it means the following: .. CODE-BLOCK:: python - from sage.coding.channel_constructions import BinaryStaticErrorRateChannel + from sage.coding.channel import BinaryStaticErrorRateChannel VII. Complete code of this tutorial =================================== @@ -683,7 +683,7 @@ derive from the one that follows. BinaryRepetitionCode._registered_decoders["MajorityVoteDecoder"] = BinaryRepetitionCodeMajorityVoteDecoder BinaryRepetitionCodeMajorityVoteDecoder._decoder_type = {"hard-decision", "unique"} -``channel_constructions.py`` (continued): +``channel.py`` (continued): .. CODE-BLOCK:: python @@ -741,4 +741,4 @@ derive from the one that follows. .. CODE-BLOCK:: python - from sage.coding.channel_constructions import (ErrorErasureChannel, StaticErrorRateChannel, BinaryStaticErrorRateChannel) + from sage.coding.channel import (ErrorErasureChannel, StaticErrorRateChannel, BinaryStaticErrorRateChannel) diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index e61a1dbeee0..ed0d33a88f4 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -1663,9 +1663,9 @@ def crystal_graph(self): True """ G = self._libgap.CrystalGraph() - vertices = [CrystalGraphVertex(self, repr(p)) for p in G[bytes('points')]] + vertices = [CrystalGraphVertex(self, repr(p)) for p in G[b'points']] edges = [[vertices[e[0][0]-1], vertices[e[0][1]-1], e[1]] - for e in G[bytes('edges')].sage()] + for e in G[b'edges'].sage()] G = DiGraph([vertices, edges], format='vertices_and_edges') from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): @@ -2094,9 +2094,10 @@ def crystal_graph(self, use_ambient=True): B = self.basis() d = {repr(B[k]._libgap): '<{!r}>'.format(self._ambient_basis_map[k]) for k in self._ambient_basis_map} - vertices = [CrystalGraphVertex(self, d[repr(p)[1:-1]]) for p in G[bytes('points')]] + vertices = [CrystalGraphVertex(self, d[repr(p)[1:-1]]) + for p in G[b'points']] edges = [[vertices[e[0][0]-1], vertices[e[0][1]-1], e[1]] - for e in G[bytes('edges')].sage()] + for e in G[b'edges'].sage()] G = DiGraph([vertices, edges], format='vertices_and_edges') from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): diff --git a/src/sage/coding/bch.py b/src/sage/coding/bch_code.py similarity index 96% rename from src/sage/coding/bch.py rename to src/sage/coding/bch_code.py index 8ccb3087e00..a07be8a20ce 100644 --- a/src/sage/coding/bch.py +++ b/src/sage/coding/bch_code.py @@ -1,16 +1,16 @@ r""" -BCH Code +BCH code -Let `F = GF(q)` and `\Phi` be the splitting field of `x^{n} - 1` over `F`, -with `n` a positive integer. Let also `\alpha` be an element of multiplicative -order `n` in `\Phi`. Finally, let `b, \delta, \ell` be integers such that -`0 \le b \le n`, `1 \le \delta \le n` and `\alpha^\ell` generates the -multiplicative group `\Phi^{\times}`. +Let `F = GF(q)` and `\Phi` be the splitting field of `x^{n} - 1` over `F`, with +`n` a positive integer. Let also `\alpha` be an element of multiplicative order +`n` in `\Phi`. Finally, let `b, \delta, \ell` be integers such that `0 \le b +\le n`, `1 \le \delta \le n` and `\alpha^\ell` generates the multiplicative +group `\Phi^{\times}`. A BCH code over `F` with designed distance `\delta` is a cyclic code whose codewords `c(x) \in F[x]` satisfy `c(\alpha^{a}) = 0`, for all integers `a` in -the arithmetic sequence -`b, b + \ell, b + 2 \times \ell, \dots, b + (\delta - 2) \times \ell`. +the arithmetic sequence `b, b + \ell, b + 2 \times \ell, \dots, b + (\delta - +2) \times \ell`. TESTS: @@ -23,11 +23,13 @@ sage: Fqm. = GF(16) sage: Fq. = GF(4) sage: RelativeFiniteFieldExtension(Fqm, Fq) - doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. + doctest:...: FutureWarning: This class/method/function is marked as + experimental. It, its functionality or its interface might change without a + formal deprecation. See http://trac.sagemath.org/20284 for details. - Relative field extension between Finite Field in aa of size 2^4 and Finite Field in a of size 2^2 + Relative field extension between Finite Field in aa of size 2^4 and Finite + Field in a of size 2^2 """ - # ***************************************************************************** # Copyright (C) 2016 David Lucas # 2017 Julien Lavauzelle @@ -38,17 +40,17 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # ***************************************************************************** +from copy import copy -from .cyclic_code import CyclicCode -from .grs import GeneralizedReedSolomonCode -from .decoder import Decoder from sage.modules.free_module_element import vector from sage.misc.misc_c import prod from sage.categories.fields import Fields from sage.arith.all import gcd from sage.rings.all import Zmod -from copy import copy +from .cyclic_code import CyclicCode +from .grs_code import GeneralizedReedSolomonCode +from .decoder import Decoder class BCHCode(CyclicCode): r""" @@ -272,7 +274,7 @@ def bch_to_grs(self): class BCHUnderlyingGRSDecoder(Decoder): r""" A decoder which decodes through the underlying - :class:`sage.coding.grs.GeneralizedReedSolomonCode` code of the provided + :class:`sage.coding.grs_code.GeneralizedReedSolomonCode` code of the provided BCH code. INPUT: @@ -448,7 +450,7 @@ def decode_to_code(self, y): (a, a + 1, 1, a + 1, 1, a, a + 1, a + 1, 0, 1, a + 1, 1, 1, 1, a) sage: D.decode_to_code(y) in C True - + We check that it still works when, while list-decoding, the GRS decoder output some words which do not lie in the BCH code:: diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index ede70b82377..536ae116227 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -736,6 +736,17 @@ cdef class BinaryCode: """ def __cinit__(self, arg1, arg2=None): + """ + Initialize. + + TESTS:: + + sage: import sage.coding.binary_code + sage: from sage.coding.binary_code import * + sage: M = Matrix(GF(2), [[1,1,1,1]]) + sage: B = BinaryCode(M) + sage: TestSuite(B).run() + """ cdef int nrows, i, j, size cdef int nwords, other_nwords, parity, combination cdef codeword word, glue_word @@ -1259,6 +1270,16 @@ cdef class OrbitPartition: """ def __cinit__(self, int nrows, int ncols): + """ + Initialize. + + TESTS:: + + sage: import sage.coding.binary_code + sage: from sage.coding.binary_code import * + sage: O = OrbitPartition(4, 8) + sage: TestSuite(O).run(skip='_test_pickling') + """ cdef int col cdef int nwords, word nwords = (1 << nrows) @@ -1558,6 +1579,16 @@ cdef class PartitionStack: group computation. """ def __cinit__(self, arg1, arg2=None): + """ + Initialize. + + TESTS:: + + sage: import sage.coding.binary_code + sage: from sage.coding.binary_code import * + sage: P = PartitionStack(2, 6) + sage: TestSuite(P).run(skip='_test_pickling') + """ cdef int k, nwords, ncols, sizeof_int cdef PartitionStack other = None cdef int *wd_ents @@ -3047,6 +3078,16 @@ cdef class PartitionStack: cdef class BinaryCodeClassifier: def __cinit__(self): + """ + Initialize. + + TESTS:: + + sage: import sage.coding.binary_code + sage: from sage.coding.binary_code import * + sage: BC = BinaryCodeClassifier() + sage: TestSuite(BC).run(skip='_test_pickling') + """ self.radix = sizeof(codeword) << 3 self.ham_wts = hamming_weights() self.L = 100 # memory limit for Phi and Omega- multiply by 8KB diff --git a/src/sage/coding/channel_constructions.py b/src/sage/coding/channel.py similarity index 97% rename from src/sage/coding/channel_constructions.py rename to src/sage/coding/channel.py index 066eb2ffd93..257ef74ee1c 100644 --- a/src/sage/coding/channel_constructions.py +++ b/src/sage/coding/channel.py @@ -1,8 +1,8 @@ r""" -Base class for Channels and commonly used channels +Channels -Given an input space and an output space, a channel takes element from -the input space (the message) and transforms it into an element of the output space +Given an input space and an output space, a channel takes element from the +input space (the message) and transforms it into an element of the output space (the transmitted message). In Sage, Channels simulate error-prone transmission over communication @@ -87,7 +87,7 @@ def random_error_vector(n, F, error_positions): EXAMPLES:: - sage: from sage.coding.channel_constructions import random_error_vector + sage: from sage.coding.channel import random_error_vector sage: random_error_vector(5, GF(2), [1,3]) (0, 1, 0, 1, 0) """ @@ -116,7 +116,7 @@ def format_interval(t): TESTS:: - sage: from sage.coding.channel_constructions import format_interval + sage: from sage.coding.channel import format_interval sage: t = (5, 5) sage: format_interval(t) '5' @@ -128,6 +128,7 @@ def format_interval(t): """ return str(t[0]) if t[0] == t[1] else 'between %s and %s' % (t[0], t[1]) + class Channel(SageObject): r""" Abstract top-class for Channel objects. @@ -169,7 +170,7 @@ def __init__(self, input_space, output_space): We first create a new Channel subclass:: - sage: from sage.coding.channel_constructions import Channel + sage: from sage.coding.channel import Channel sage: class ChannelExample(Channel): ....: def __init__(self, input_space, output_space): ....: super(ChannelExample, self).__init__(input_space, output_space) @@ -273,22 +274,22 @@ def output_space(self): @abstract_method def transmit_unsafe(self, message): r""" - Returns ``message``, modified accordingly with the algorithm of the channel it was + Return ``message``, modified accordingly with the algorithm of the channel it was transmitted through. This method does not check if ``message`` belongs to the input space of``self``. This is an abstract method which should be reimplemented in all the subclasses of Channel. - """ - - - - - - + EXAMPLES:: + sage: n_err = 2 + sage: Chan = channels.StaticErrorRateChannel(GF(59)^6, n_err) + sage: v = Chan.input_space().random_element() + sage: Chan.transmit_unsafe(v) # random + (1, 33, 46, 18, 20, 49) + """ class StaticErrorRateChannel(Channel): @@ -440,14 +441,6 @@ def number_errors(self): return self._number_errors - - - - - - - - class ErrorErasureChannel(Channel): r""" Channel which adds errors and erases several positions in any message it transmits. @@ -649,14 +642,6 @@ def number_erasures(self): return self._number_erasures - - - - - - - - class QarySymmetricChannel(Channel): r""" The q-ary symmetric, memoryless communication channel. @@ -670,9 +655,9 @@ class QarySymmetricChannel(Channel): Though `\Sigma` is usually taken to be a finite field, this implementation allows any structure for which Sage can represent `\Sigma^n` and for which - `\Sigma` has a `random_element()` method. However, beware that if `\Sigma` + `\Sigma` has a ``random_element()`` method. However, beware that if `\Sigma` is infinite, errors will not be uniformly distributed (since - `random_element()` does not draw uniformly at random). + ``random_element()`` does not draw uniformly at random). The input space and the output space of this channel are the same: `\Sigma^n`. diff --git a/src/sage/coding/channels_catalog.py b/src/sage/coding/channels_catalog.py index 1ee638e949c..e8532f28d6d 100644 --- a/src/sage/coding/channels_catalog.py +++ b/src/sage/coding/channels_catalog.py @@ -1,11 +1,13 @@ r""" -Index of Channels: the information theoretic notion of transmission +Index of channels + +Channels in Sage implement the information theoretic notion of transmission of messages. The ``channels`` object may be used to access the codes that Sage can build. -- :class:`channel_constructions.ErrorErasureChannel ` -- :class:`channel_constructions.QarySymmetricChannel ` -- :class:`channel_constructions.StaticErrorRateChannel ` +- :class:`channel.ErrorErasureChannel ` +- :class:`channel.QarySymmetricChannel ` +- :class:`channel.StaticErrorRateChannel ` .. NOTE:: @@ -25,8 +27,8 @@ from __future__ import absolute_import from sage.misc.lazy_import import lazy_import as _lazy_import -_lazy_import('sage.coding.channel_constructions', ['ErrorErasureChannel', - 'QarySymmetricChannel', - 'StaticErrorRateChannel']) +_lazy_import('sage.coding.channel', ['ErrorErasureChannel', + 'QarySymmetricChannel', + 'StaticErrorRateChannel']) # We don't want this to appear in tab completion del absolute_import diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index f8c11b793cd..5252fdbc20a 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -1,5 +1,5 @@ r""" -Bounds for Parameters of Codes +Bounds for parameters of codes This module provided some upper and lower bounds for the parameters of codes. @@ -399,7 +399,7 @@ def griesmer_upper_bound(n,q,d,algorithm=None): Returns the Griesmer upper bound for the number of elements in a largest linear code of minimum distance `d` in `\GF{q}^n`, cf. [HP2003]_. - If the method is "gap", it wraps GAP's ``UpperBoundGriesmer``. + If the method is "gap", it wraps GAP's ``UpperBoundGriesmer``. The bound states: @@ -456,7 +456,7 @@ def elias_upper_bound(n,q,d,algorithm=None): Returns the Elias upper bound for number of elements in the largest code of minimum distance `d` in `\GF{q}^n`, cf. [HP2003]_. - If the method is "gap", it wraps GAP's ``UpperBoundElias``. + If the method is "gap", it wraps GAP's ``UpperBoundElias``. EXAMPLES:: diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 185ffbce00c..95f3c6fc842 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -5,7 +5,7 @@ of special (or random) linear codes and wraps them in a :class:`sage.coding.linear_code.LinearCode` object. These constructions are therefore not rich objects such as -:class:`sage.coding.grs.GeneralizedReedSolomonCode`. +:class:`sage.coding.grs_code.GeneralizedReedSolomonCode`. All codes available here can be accessed through the ``codes`` object:: @@ -16,21 +16,20 @@ - [HP2003]_ -AUTHOR: +AUTHORS: - David Joyner (2007-05): initial version -- " (2008-02): added cyclic codes, Hamming codes +- David Joyner (2008-02): added cyclic codes, Hamming codes -- " (2008-03): added BCH code, LinearCodeFromCheckmatrix, ReedSolomonCode, WalshCode, +- David Joyner (2008-03): added BCH code, LinearCodeFromCheckmatrix, ReedSolomonCode, WalshCode, DuadicCodeEvenPair, DuadicCodeOddPair, QR codes (even and odd) -- " (2008-09) fix for bug in BCHCode reported by F. Voloch +- David Joyner (2008-09) fix for bug in BCHCode reported by F. Voloch -- " (2008-10) small docstring changes to WalshCode and walsh_matrix +- David Joyner (2008-10) small docstring changes to WalshCode and walsh_matrix """ - #***************************************************************************** # Copyright (C) 2007 David Joyner # @@ -42,23 +41,25 @@ #***************************************************************************** from __future__ import print_function, absolute_import +from sage.misc.all import prod +from sage.arith.all import quadratic_residues, gcd + +from sage.structure.sequence import Sequence, Sequence_generic + from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import matrix from sage.matrix.special import random_matrix + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF -from sage.misc.all import prod -from .linear_code import LinearCode -from sage.structure.sequence import Sequence, Sequence_generic -from sage.arith.all import quadratic_residues, gcd +from sage.rings.finite_rings.integer_mod import Mod from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer -from sage.rings.finite_rings.integer_mod import Mod +from .linear_code import LinearCode ############### utility functions ################ - def _is_a_splitting(S1, S2, n, return_automorphism=False): r""" Check wether ``(S1,S2)`` is a splitting of `\ZZ/n\ZZ`. @@ -180,7 +181,6 @@ def _is_a_splitting(S1, S2, n, return_automorphism=False): else: return False - def _lift2smallest_field(a): """ INPUT: a is an element of a finite field GF(q) @@ -216,7 +216,6 @@ def _lift2smallest_field(a): b = pol.roots(F,multiplicities=False)[0] return b, F - def permutation_action(g,v): r""" Returns permutation of rows g\*v. Works on lists, matrices, @@ -290,7 +289,6 @@ def permutation_action(g,v): return gv return V(gv) - def walsh_matrix(m0): """ This is the generator matrix of a Walsh code. The matrix of @@ -324,10 +322,8 @@ def walsh_matrix(m0): return matrix(GF(2), m, 2**m, [[0]*2**(m-1) + [1]*2**(m-1)] + row2) raise ValueError("%s must be an integer > 0."%m0) - ##################### main constructions ##################### - def DuadicCodeEvenPair(F,S1,S2): r""" Constructs the "even pair" of duadic codes associated to the @@ -432,8 +428,6 @@ def DuadicCodeOddPair(F,S1,S2): C2 = CyclicCode(length = n, generator_pol = gg2) return C1,C2 - - def ExtendedQuadraticResidueCode(n,F): r""" The extended quadratic residue code (or XQR code) is obtained from @@ -484,7 +478,7 @@ def from_parity_check_matrix(H): dimension `n-h` and length `n`. EXAMPLES:: - + sage: C = codes.HammingCode(GF(2), 3); C [7, 4] Hamming Code over GF(2) sage: H = C.parity_check_matrix(); H @@ -499,7 +493,6 @@ def from_parity_check_matrix(H): Cd = LinearCode(H) return Cd.dual_code() - def QuadraticResidueCode(n,F): r""" A quadratic residue code (or QR code) is a cyclic code whose @@ -692,7 +685,6 @@ def random_linear_code(F, length, dimension): if G.rank() == dimension: return LinearCode(G) - def ToricCode(P,F): r""" Let `P` denote a list of lattice points in @@ -766,7 +758,6 @@ def ToricCode(P,F): MS = MatrixSpace(F,k,n) return LinearCode(MS(B)) - def WalshCode(m): r""" Return the binary Walsh code of length `2^m`. diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 7df4a6e146f..ce27705e812 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -3,7 +3,59 @@ The ``codes`` object may be used to access the codes that Sage can build. -{INDEX_OF_FUNCTIONS} +Families of Codes (Rich representation) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: @ + + :meth:`~sage.coding.parity_check_code.ParityCheckCode` @ Parity check codes + :meth:`~sage.coding.cyclic_code.CyclicCode` @ Cyclic codes + :meth:`~sage.coding.bch_code.BCHCode` @ BCH Codes + :meth:`~sage.coding.grs_code.GeneralizedReedSolomonCode` @ Generalized Reed-Solomon codes + :meth:`~sage.coding.grs_code.ReedSolomonCode` @ Reed-Solomon codes + :meth:`~sage.coding.reed_muller_code.BinaryReedMullerCode` @ Binary Reed-Muller codes + :meth:`~sage.coding.reed_muller_code.ReedMullerCode` @ q-ary Reed-Muller codes + :meth:`~sage.coding.hamming_code.HammingCode` @ Hamming codes + :meth:`~sage.coding.golay_code.GolayCode` @ Golay codes + :meth:`~sage.coding.goppa_code.GoppaCode` @ Goppa codes + + +Families of Codes (Generator matrix representation) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: @ + + :meth:`~sage.coding.code_constructions.DuadicCodeEvenPair` @ Duadic codes, even pair + :meth:`~sage.coding.code_constructions.DuadicCodeOddPair` @ Duadic codes, odd pair + :meth:`~sage.coding.code_constructions.QuadraticResidueCode` @ Quadratic residue codes + :meth:`~sage.coding.code_constructions.ExtendedQuadraticResidueCode` @ Extended quadratic residue codes + :meth:`~sage.coding.code_constructions.QuadraticResidueCodeEvenPair` @ Even-like quadratic residue codes + :meth:`~sage.coding.code_constructions.QuadraticResidueCodeOddPair` @ Odd-like quadratic residue codes + :meth:`~sage.coding.guava.QuasiQuadraticResidueCode` @ Quasi quadratic residue codes (Requires GAP/Guava) + :meth:`~sage.coding.code_constructions.ToricCode` @ Toric codes + :meth:`~sage.coding.code_constructions.WalshCode` @ Walsh codes + :meth:`~sage.coding.code_constructions.from_parity_check_matrix` @ Construct a code from a parity check matrix + :meth:`~sage.coding.code_constructions.random_linear_code` @ Construct a random linear code + :meth:`~sage.coding.guava.RandomLinearCodeGuava` @ Construct a random linear code through Guava (Requires GAP/Guava) + + +Derived Codes +^^^^^^^^^^^^^ + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: @ + + :meth:`~sage.coding.subfield_subcode.SubfieldSubcode` @ Subfield subcodes + :meth:`~sage.coding.extended_code.ExtendedCode` @ Extended codes + :meth:`~sage.coding.punctured_code.PuncturedCode` @ Puncturedcodes .. NOTE:: @@ -11,11 +63,6 @@ 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 @@ -26,48 +73,37 @@ # https://www.gnu.org/licenses/ #***************************************************************************** +# This module is imported as "codes" in all.py so that codes. is +# available in the global namespace. -# Implementation note: -# -# This module is imported as "codes" in all.py so that codes. is available -# in the global namespace. - -from __future__ import absolute_import -from sage.misc.lazy_import import lazy_import +from sage.misc.lazy_import import lazy_import as _lazy_import -from sage.coding.linear_code import LinearCode +from .linear_code import LinearCode -lazy_import('sage.coding.code_constructions', +_lazy_import('sage.coding.code_constructions', ['DuadicCodeEvenPair', 'DuadicCodeOddPair', 'ExtendedQuadraticResidueCode', 'from_parity_check_matrix', 'QuadraticResidueCode', 'QuadraticResidueCodeEvenPair', 'QuadraticResidueCodeOddPair', 'random_linear_code', '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', 'ReedSolomonCode']) -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', - 'ReedMullerCode']) -lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') -lazy_import('sage.coding.goppa', 'GoppaCode') +_lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') +_lazy_import('sage.coding.extended_code', 'ExtendedCode') +_lazy_import('sage.coding.punctured_code', 'PuncturedCode') + +_lazy_import('sage.coding.parity_check_code', 'ParityCheckCode') +_lazy_import('sage.coding.cyclic_code', 'CyclicCode') +_lazy_import('sage.coding.bch_code', 'BCHCode') +_lazy_import('sage.coding.grs_code', ['GeneralizedReedSolomonCode', 'ReedSolomonCode']) +_lazy_import('sage.coding.reed_muller_code', ['BinaryReedMullerCode', 'ReedMullerCode']) +_lazy_import('sage.coding.hamming_code', 'HammingCode') +_lazy_import('sage.coding.golay_code', 'GolayCode') +_lazy_import('sage.coding.goppa_code', 'GoppaCode') + +_lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', 'RandomLinearCodeGuava']) from . import decoders_catalog as decoders from . import encoders_catalog as encoders from . import bounds_catalog as bounds -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)) - -# We don't want this to appear in tab completion -del absolute_import, lazy_import, sys, gen_rest_table_index +_lazy_import('sage.coding','databases') diff --git a/src/sage/coding/cyclic_code.py b/src/sage/coding/cyclic_code.py index 7f88adb50cc..a6562ae199a 100644 --- a/src/sage/coding/cyclic_code.py +++ b/src/sage/coding/cyclic_code.py @@ -1,5 +1,5 @@ r""" -Cyclic Code +Cyclic code Let `F` be a field. A `[n, k]` code `C` over `F` is called cyclic if every cyclic shift of a codeword is also a codeword [Rot2006]_: @@ -819,7 +819,7 @@ def surrounding_bch_code(self): sage: all(r in CC for r in C.generator_matrix()) True """ - from .bch import BCHCode + from .bch_code import BCHCode delta, params = self.bch_bound(arithmetic=True) return BCHCode(self.base_field(), self.length(), delta, offset=params[1], jump_size=params[0]) diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index 033b1d0f7e5..2c422493e1d 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- r""" -Databases and accessors of online databases for coding theory +Access functions to online databases for coding theory """ from six.moves import range from sage.interfaces.all import gap from sage.features.gap import GapPackage -#Don't put any global imports here since this module is accessible as sage.codes.databases. - +# Don't put any global imports here since this module is accessible as +# sage.codes.databases. def best_linear_code_in_guava(n, k, F): r""" @@ -25,7 +25,6 @@ def best_linear_code_in_guava(n, k, F): - ``F`` -- the base field of the code to look up - OUTPUT: - A :class:`LinearCode` which is a best linear code of the given parameters known to GUAVA. @@ -49,8 +48,6 @@ def best_linear_code_in_guava(n, k, F): from .linear_code import LinearCode return LinearCode(C.GeneratorMat()._matrix_(F)) - - def bounds_on_minimum_distance_in_guava(n, k, F): r""" Computes a lower and upper bound on the greatest minimum distance of a @@ -116,7 +113,6 @@ def bounds_on_minimum_distance_in_guava(n, k, F): Ldata = gap.eval("Display(data)") return Ldata - def best_linear_code_in_codetables_dot_de(n, k, F, verbose=False): r""" Return the best linear code and its construction as per the web database @@ -182,7 +178,6 @@ def best_linear_code_in_codetables_dot_de(n, k, F, verbose=False): text = s[i+5:j].strip() return text - def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, in_test=None): """ @@ -313,6 +308,7 @@ def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, for N in self_orthogonal_binary_codes(n, k, d, child, BC, in_test=in_test): if out_test(N): yield N -# Import the following function so that it is available as sage.codes.databases.self_dual_binary_codes -# sage.codes.databases functions somewhat like a catalog in this respect. +# Import the following function so that it is available as +# sage.codes.databases.self_dual_binary_codes sage.codes.databases functions +# somewhat like a catalog in this respect. from sage.coding.self_dual_codes import self_dual_binary_codes diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index b9d774471c9..2fb9b4b6ea3 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -1,11 +1,12 @@ r""" -Base class for Decoders +Decoders Representation of an error-correction algorithm for a code. AUTHORS: - David Joyner (2009-02-01): initial version + - David Lucas (2015-06-29): abstract class version """ @@ -219,11 +220,9 @@ def __ne__(self, other): """ return not self == other - - def decode_to_code(self, r): r""" - Corrects the errors in ``r`` and returns a codeword. + Correct the errors in ``r`` and returns a codeword. This is a default implementation which assumes that the method :meth:`decode_to_message` has been implemented, else it returns an exception. @@ -258,7 +257,7 @@ def decode_to_code(self, r): def connected_encoder(self): r""" - Returns the connected encoder of ``self``. + Return the connected encoder of ``self``. EXAMPLES:: @@ -301,7 +300,7 @@ def decode_to_message(self, r): def code(self): r""" - Returns the code for this :class:`Decoder`. + Return the code for this :class:`Decoder`. EXAMPLES:: @@ -315,7 +314,7 @@ def code(self): def message_space(self): r""" - Returns the message space of ``self``'s :meth:`connected_encoder`. + Return the message space of ``self``'s :meth:`connected_encoder`. EXAMPLES:: @@ -329,7 +328,7 @@ def message_space(self): def input_space(self): r""" - Returns the input space of ``self``. + Return the input space of ``self``. EXAMPLES:: @@ -347,7 +346,7 @@ def input_space(self): @abstract_method(optional = True) def decoding_radius(self, **kwargs): r""" - Returns the maximal number of errors that ``self`` is able to correct. + Return the maximal number of errors that ``self`` is able to correct. This is an abstract method and it should be implemented in subclasses. diff --git a/src/sage/coding/decoders_catalog.py b/src/sage/coding/decoders_catalog.py index ba78a8dcc64..8beca59aa72 100644 --- a/src/sage/coding/decoders_catalog.py +++ b/src/sage/coding/decoders_catalog.py @@ -16,11 +16,11 @@ **Generalized Reed-Solomon code decoders** -- :class:`grs.GRSBerlekampWelchDecoder ` -- :class:`grs.GRSErrorErasureDecoder ` -- :class:`grs.GRSGaoDecoder ` +- :class:`grs_code.GRSBerlekampWelchDecoder ` +- :class:`grs_code.GRSErrorErasureDecoder ` +- :class:`grs_code.GRSGaoDecoder ` +- :class:`grs_code.GRSKeyEquationSyndromeDecoder ` - :class:`guruswami_sudan.gs_decoder.GRSGuruswamiSudanDecoder ` -- :class:`grs.GRSKeyEquationSyndromeDecoder ` **Generic decoders** @@ -34,7 +34,7 @@ **BCH code decoder** -- :class:`bch.BCHUnderlyingGRSDecoder ` +- :class:`bch_code.BCHUnderlyingGRSDecoder ` **Punctured codes decoders** @@ -46,7 +46,6 @@ sage: from sage.coding.decoders_catalog import * """ -from __future__ import absolute_import #***************************************************************************** # Copyright (C) 2009 David Joyner # 2015 David Lucas @@ -56,20 +55,25 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import + +from sage.misc.lazy_import import lazy_import -from sage.misc.lazy_import import lazy_import as _lazy_import +lazy_import('sage.coding.bch_code', 'BCHUnderlyingGRSDecoder') +lazy_import('sage.coding.cyclic_code', 'CyclicCodeSurroundingBCHDecoder') +lazy_import('sage.coding.extended_code', 'ExtendedCodeOriginalCodeDecoder') +lazy_import('sage.coding.grs_code', ['GRSBerlekampWelchDecoder', + 'GRSErrorErasureDecoder', + 'GRSGaoDecoder', + 'GRSKeyEquationSyndromeDecoder']) -_lazy_import('sage.coding.bch', 'BCHUnderlyingGRSDecoder') -_lazy_import('sage.coding.cyclic_code', 'CyclicCodeSurroundingBCHDecoder') -_lazy_import('sage.coding.extended_code', 'ExtendedCodeOriginalCodeDecoder') -_lazy_import('sage.coding.grs', ['GRSBerlekampWelchDecoder', - 'GRSErrorErasureDecoder', - 'GRSGaoDecoder', - 'GRSKeyEquationSyndromeDecoder']) from .guruswami_sudan.gs_decoder import GRSGuruswamiSudanDecoder -_lazy_import('sage.coding.linear_code', ['LinearCodeNearestNeighborDecoder', - 'LinearCodeSyndromeDecoder', - 'LinearCodeInformationSetDecoder']) -_lazy_import('sage.coding.punctured_code', 'PuncturedCodeOriginalCodeDecoder') -_lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcodeOriginalCodeDecoder') -_lazy_import('sage.coding.information_set_decoder', 'LinearCodeInformationSetDecoder') +lazy_import('sage.coding.linear_code', ['LinearCodeNearestNeighborDecoder', + 'LinearCodeSyndromeDecoder', + 'LinearCodeInformationSetDecoder']) + +lazy_import('sage.coding.punctured_code', 'PuncturedCodeOriginalCodeDecoder') +lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcodeOriginalCodeDecoder') +lazy_import('sage.coding.information_set_decoder', 'LinearCodeInformationSetDecoder') + +del lazy_import diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index a89e5059d92..e6787830f1c 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -1,16 +1,23 @@ # -*- coding: utf-8 -*- r""" -Delsarte, a.k.a. Linear Programming (LP), upper bounds +Delsarte (or linear programming) bounds -This module provides LP upper bounds for the parameters of codes, -introduced in [De1973]_. +This module provides LP upper bounds for the parameters of codes, introduced in +[De1973]_. The exact LP solver PPL is used by default, ensuring that no rounding/overflow problems occur. AUTHORS: -- Dmitrii V. (Dima) Pasechnik (2012-10): initial implementation. Minor fixes (2015) +- Dmitrii V. (Dima) Pasechnik (2012-10): initial implementation + +- Dmitrii V. (Dima) Pasechnik (2015): minor fixes + +REFERENCES: + +.. [De73] \P. Delsarte, An algebraic approach to the association schemes of coding theory, + Philips Res. Rep., Suppl., vol. 10, 1973. """ #***************************************************************************** # Copyright (C) 2012 Dima Pasechnik @@ -22,8 +29,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, division -from sage.misc.superseded import deprecation, deprecated_function_alias - def krawtchouk(n, q, l, x, check=True): r""" diff --git a/src/sage/coding/encoder.py b/src/sage/coding/encoder.py index 4cd7a526a53..a40c64f7b66 100644 --- a/src/sage/coding/encoder.py +++ b/src/sage/coding/encoder.py @@ -1,9 +1,13 @@ r""" -Base class for Encoders +Encoders Representation of a bijection between a message space and a code. -""" +AUTHORS: + +- David Lucas (2015): initial version + +""" #***************************************************************************** # Copyright (C) 2015 David Lucas # @@ -239,7 +243,7 @@ def unencode(self, c, nocheck=False): sage: C = LinearCode(G) Traceback (most recent call last): ... - ValueError: length must be a non-zero positive integer + ValueError: length must be a positive integer """ if not nocheck and c not in self.code(): raise EncodingError("Given word is not in the code") diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index 1439813fff0..e1bed60ae9c 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -19,8 +19,8 @@ **Generalized Reed-Solomon code encoders** -- :class:`grs.GRSEvaluationVectorEncoder ` -- :class:`grs.GRSEvaluationPolynomialEncoder ` +- :class:`grs_code.GRSEvaluationVectorEncoder ` +- :class:`grs_code.GRSEvaluationPolynomialEncoder ` **Punctured codes encoders** @@ -48,11 +48,11 @@ _lazy_import('sage.coding.cyclic_code', ['CyclicCodePolynomialEncoder', 'CyclicCodeVectorEncoder']) _lazy_import('sage.coding.extended_code', 'ExtendedCodeExtendedMatrixEncoder') -_lazy_import('sage.coding.grs', ['GRSEvaluationVectorEncoder', 'GRSEvaluationPolynomialEncoder']) +_lazy_import('sage.coding.grs_code', ['GRSEvaluationVectorEncoder', 'GRSEvaluationPolynomialEncoder']) _lazy_import('sage.coding.linear_code', ['LinearCodeGeneratorMatrixEncoder', 'LinearCodeSystematicEncoder']) _lazy_import('sage.coding.punctured_code', 'PuncturedCodePuncturedMatrixEncoder') _lazy_import('sage.coding.reed_muller_code', ['ReedMullerVectorEncoder', 'ReedMullerPolynomialEncoder']) _lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcodeParityCheckEncoder') _lazy_import('sage.coding.parity_check_code', ['ParityCheckCodeGeneratorMatrixEncoder','ParityCheckCodeStraightforwardEncoder']) -_lazy_import('sage.coding.goppa', ['GoppaCodeEncoder']) +_lazy_import('sage.coding.goppa_code', ['GoppaCodeEncoder']) diff --git a/src/sage/coding/extended_code.py b/src/sage/coding/extended_code.py index 01d500a1bc8..975439f352d 100644 --- a/src/sage/coding/extended_code.py +++ b/src/sage/coding/extended_code.py @@ -21,9 +21,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from .linear_code import (AbstractLinearCode,\ - LinearCodeGeneratorMatrixEncoder,\ - LinearCodeSyndromeDecoder,\ +from .linear_code import (AbstractLinearCode, + LinearCodeSyndromeDecoder, LinearCodeNearestNeighborDecoder) from .encoder import Encoder from .decoder import Decoder diff --git a/src/sage/coding/golay_code.py b/src/sage/coding/golay_code.py index a9dc91e241c..d88f4cfa601 100644 --- a/src/sage/coding/golay_code.py +++ b/src/sage/coding/golay_code.py @@ -30,8 +30,6 @@ from sage.rings.finite_rings.finite_field_constructor import GF from .linear_code import (AbstractLinearCode, LinearCodeGeneratorMatrixEncoder) -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.integer_ring import ZZ class GolayCode(AbstractLinearCode): r""" diff --git a/src/sage/coding/goppa.py b/src/sage/coding/goppa.py deleted file mode 100755 index 11a0e5ff17d..00000000000 --- a/src/sage/coding/goppa.py +++ /dev/null @@ -1,310 +0,0 @@ -r""" -Goppa code - -""" - -from sage.coding.linear_code import AbstractLinearCode -from sage.coding.encoder import Encoder -from sage.coding.decoder import Decoder -from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF -from sage.modules.free_module_element import vector -from sage.coding.all import codes - -def _columnize(element): - v = vector(element) - return v.column() - -class GoppaCode(AbstractLinearCode): - """ - Implementation of Goppa codes - - Goppa codes are a generalization of narrow-sense BCH codes. - These codes are defined by a generating polynomial `g` over a finite field - `F_{p^m}`, and a defining set `L` of elements from `F_{p^m}`, which are not roots - of `g`. The number of defining elements determines the length of the code. - - In binary cases, the minimum distance is `2t + 1`, where `t` is the degree - of `g`. - - INPUTS: - - - ``generating_pol`` -- a monic polynomial with coefficients in a finite - field `F_{p^m}`, the code is defined over `F_p`, `p` must be a prime number - - - ``defining_set`` -- a set of elements of `F_{p^m}` that are not roots - of `g`, its cardinality is the length of the code - - EXAMPLES:: - - sage: F8=GF(2**6) - sage: RR. = F8[] - sage: g = xx^9+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [55, 16] Goppa code - """ - _registered_encoders = {} - _registered_decoders = {} - - def __init__(self, generating_pol, defining_set): - - self._field = generating_pol.base_ring().prime_subfield() - self._length = len(defining_set) - self._generating_pol = generating_pol - self._defining_set = defining_set - - super(GoppaCode, self).__init__(self._field, self._length, "GoppaEncoder", "Syndrome") - - if not generating_pol.is_monic(): - raise ValueError("generating polynomial must be monic") - F = self._field - if (not F.is_field() or not F.is_finite()): - raise ValueError("generating polynomial must be defined over a finite field") - for a in defining_set: - if generating_pol(a) == 0: - raise ValueError("defining elements cannot be roots of generating polynomial") - - - - def _repr_(self): - """ - Representation of a Goppa code - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C._repr_() - '[8, 2] Goppa code' - """ - return "[%s, %s] Goppa code" %(self.length(), self.dimension()) - - def _latex_(self): - """ - latex representation of ``self``. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C._latex_() - '\\textnormal{Goppa code of length } 8' - """ - return ("\\textnormal{Goppa code of length } %s" % self.length()) - - def __eq__(self, other): - """ - Equality check between GoppaCode objects - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: D = codes.GoppaCode(g, L) - sage: D.__eq__(C) - True - """ - return (isinstance(other, GoppaCode) - and self.length() == other.length() - and self._generating_pol == other._generating_pol - and self._defining_set == other._defining_set) - - def parity_check_matrix(self): - r""" - Parity check matrix for ``self``. - - The element in row `t`, column `i` is `h[i](D[i]^t)`, where: - - - `h[i]` -- is the inverse of `g(D[i])` - - `D[i]` -- is the `i`-th element of the defining set - - In the resulting `d \times n` matrix we interpret each entry as an - `m`-column vector and return a `dm \times n` matrix. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [8, 2] Goppa code - sage: C.parity_check_matrix() - [1 0 0 0 0 0 0 1] - [0 0 1 0 1 1 1 0] - [0 1 1 1 0 0 1 0] - [0 1 1 1 1 1 1 1] - [0 1 0 1 1 0 1 0] - [0 0 1 1 1 1 0 0] - """ - g = self._generating_pol - F = g.base_ring() - m = self.base_field().degree() - n = self._length - d = g.degree() - alpha = F.primitive_element() - - D = self._defining_set - h = [(g(D[i]).inverse_of_unit()) for i in range(n)] - - #assemble top row - M = _columnize(alpha) - for i in range(n): - v = _columnize(h[i]) - M = M.augment(v) - M = M.delete_columns([0]) - old = M - - for t in range(1,d): - #assemble row - M = _columnize(alpha) - for i in range(n): - v = _columnize(h[i]*(D[i]**t)) - M = M.augment(v) - M = M.delete_columns([0]) - new = M - old = old.stack(new) - - return old - - def _parity_check_matrix_Vandermonde(self): - """ - Alternative way to generate parity check matrix for ``self``. - - An alternative way to generate parity check matrix for Goppa codes using - Vandermonde matrix. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [8, 2] Goppa code - sage: C._parity_check_matrix_Vandermonde() == C.parity_check_matrix() - True - """ - L = self._defining_set - g = self._generating_pol - t = g.degree() - - from sage.matrix.constructor import vandermonde, matrix, diagonal_matrix, block_matrix - - V = matrix.vandermonde(L) - V = V.transpose() - GL = [g(i) for i in L] - GLI = [j.inverse_of_unit() for j in GL] - D = diagonal_matrix(GLI) - VF = matrix([V.row(i) for i in range(t)]) - H = VF*D - - matrices = [matrix([vector(i) for i in H.row(j)]) for j in range(t)] - matrices = [m.transpose() for m in matrices] - - m = block_matrix(t, 1, matrices) - return m - - def distance_bound(self): - """ - A lower bound for the minimum distance of the code. - - Computed using the degree of the generating polynomial of ``self``. - The minimum distance is guaranteed to be bigger than or equal to this bound. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [8, 2] Goppa code - sage: C.distance_bound() - 3 - sage: C.minimum_distance() - 5 - """ - return 1 + (self._generating_pol).degree() - - -class GoppaCodeEncoder(Encoder): - """ - Encoder for Goppa codes - - Encodes words represented as vectors of length `k`, where `k` is - the dimension of ``self``, with entries from `F_p`, the prime field of - the base field of the generating polynomial of ``self``, into codewords - of length `n`, with entries from `F_p`. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2 + xx + 1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [8, 2] Goppa code - sage: E = codes.encoders.GoppaCodeEncoder(C) - sage: E - Goppa encoder for the [8, 2] Goppa code - sage: word = vector(GF(2), (0, 1)) - sage: c = E.encode(word) - sage: c - (0, 1, 1, 1, 1, 1, 1, 0) - sage: c in C - True - """ - def __init__(self, code): - super(GoppaCodeEncoder, self).__init__(code) - - def _repr_(self): - return "Goppa encoder for the %s" % self.code() - - def _latex_(self): - return "\textnormal{Goppa encoder for the } %s" % self.code() - - def __eq__(self, other): - return (isinstance(other, GoppaCodeEncoder) - and self.code() == other.code()) - - def generator_matrix(self): - r""" - A generator matrix for ``self`` - - Dimension of resulting matrix is `k \times n`, where `k` is - the dimension of ``self`` and `n` is the length of ``self``. - - EXAMPLES:: - - sage: F8=GF(2**3) - sage: RR. = F8[] - sage: g = xx^2+xx+1 - sage: L = [a for a in F8.list() if g(a)!=0] - sage: C = codes.GoppaCode(g, L) - sage: C - [8, 2] Goppa code - sage: C.generator_matrix() - [1 0 0 1 0 1 1 1] - [0 1 1 1 1 1 1 0] - """ - c = self.code() - pmat = c.parity_check_matrix() - aux = codes.from_parity_check_matrix(pmat) - return aux.generator_matrix() - -GoppaCode._registered_encoders["GoppaEncoder"] = GoppaCodeEncoder diff --git a/src/sage/coding/goppa_code.py b/src/sage/coding/goppa_code.py new file mode 100644 index 00000000000..091312c52fa --- /dev/null +++ b/src/sage/coding/goppa_code.py @@ -0,0 +1,435 @@ +r""" +Goppa code + +This module implements Goppa codes and an encoder for them. + +EXAMPLES:: + + sage: F = GF(2^6) + sage: R. = F[] + sage: g = x^9 + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [55, 16] Goppa code over GF(2) + sage: E = codes.encoders.GoppaCodeEncoder(C) + sage: E + Encoder for [55, 16] Goppa code over GF(2) + +AUTHORS: + +- Filip Ion, Marketa Slukova (2019-06): initial version + +""" +#***************************************************************************** +# Copyright (C) 2019 Filip Ion , +# Marketa Slukova +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** +from sage.coding.linear_code import AbstractLinearCode +from sage.coding.encoder import Encoder +from sage.modules.free_module_element import vector +from sage.coding.all import codes + +def _columnize(element): + """ + Convert a finite field element to a column vector over the prime field. + + TESTS:: + + sage: from sage.coding.goppa_code import _columnize + sage: F. = GF(2^6) + sage: _columnize(a) + [0] + [1] + [0] + [0] + [0] + [0] + sage: _columnize(a+1) + [1] + [1] + [0] + [0] + [0] + [0] + """ + v = vector(element) + return v.column() + +class GoppaCode(AbstractLinearCode): + r""" + Implementation of Goppa codes. + + Goppa codes are a generalization of narrow-sense BCH codes. + These codes are defined by a generating polynomial `g` over a finite field + `\GF{p^m}`, and a defining set `L` of elements from `\GF{p^m}`, which are not roots + of `g`. The number of defining elements determines the length of the code. + + In binary cases, the minimum distance is `2t + 1`, where `t` is the degree + of `g`. + + INPUTS: + + - ``generating_pol`` -- a monic polynomial with coefficients in a finite + field `\GF{p^m}`, the code is defined over `\GF{p}`, `p` must be a prime number + + - ``defining_set`` -- a set of elements of `\GF{p^m}` that are not roots + of `g`, its cardinality is the length of the code + + EXAMPLES:: + + sage: F = GF(2^6) + sage: R. = F[] + sage: g = x^9 + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [55, 16] Goppa code over GF(2) + """ + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, generating_pol, defining_set): + """ + Initialize. + + TESTS:: + + sage: F = GF(2^6) + sage: R. = F[] + sage: g = x^9 + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: TestSuite(C).run() + """ + self._field = generating_pol.base_ring().prime_subfield() + self._length = len(defining_set) + self._generating_pol = generating_pol + self._defining_set = defining_set + + super(GoppaCode, self).__init__(self._field, self._length, "GoppaEncoder", "Syndrome") + + if not generating_pol.is_monic(): + raise ValueError("generating polynomial must be monic") + F = self._field + if (not F.is_field() or not F.is_finite()): + raise ValueError("generating polynomial must be defined over a finite field") + for a in defining_set: + if generating_pol(a) == 0: + raise ValueError("defining elements cannot be roots of generating polynomial") + + def _repr_(self): + """ + Representation of a Goppa code + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x+ 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + """ + return "[{}, {}] Goppa code over GF({})".format( + self.length(), self.dimension(), self.base_field().cardinality()) + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x+ 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: latex(C) + [8, 2]\text{ Goppa code over }\Bold{F}_{2} + """ + return r"[{}, {}]\text{{ Goppa code over }}{}".format(self.length(), self.dimension(), + self.base_field()._latex_()) + + def __eq__(self, other): + """ + Test equality with ``other``. + + Two Goppa codes are considered the same when defining sets and + generating polynomials are the same. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x+ 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: D = codes.GoppaCode(g, L) + sage: C == D + True + + Note that equality check will be false if ``other`` represents the same + linear code as ``self`` but not constructed as a Goppa code:: + + sage: E = LinearCode(C.generator_matrix()) + sage: C == E + False + """ + return (isinstance(other, GoppaCode) + and self.length() == other.length() + and self._generating_pol == other._generating_pol + and self._defining_set == other._defining_set) + + def parity_check_matrix(self): + r""" + Return a parity check matrix for ``self``. + + The element in row `t`, column `i` is `h[i](D[i]^t)`, where: + + - `h[i]` -- is the inverse of `g(D[i])` + - `D[i]` -- is the `i`-th element of the defining set + + In the resulting `d \times n` matrix we interpret each entry as an + `m`-column vector and return a `dm \times n` matrix. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x+ 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + sage: C.parity_check_matrix() + [1 0 0 0 0 0 0 1] + [0 0 1 0 1 1 1 0] + [0 1 1 1 0 0 1 0] + [0 1 1 1 1 1 1 1] + [0 1 0 1 1 0 1 0] + [0 0 1 1 1 1 0 0] + """ + g = self._generating_pol + F = g.base_ring() + n = self._length + d = g.degree() + alpha = F.primitive_element() + + D = self._defining_set + h = [(g(D[i]).inverse_of_unit()) for i in range(n)] + + #assemble top row + M = _columnize(alpha) + for i in range(n): + v = _columnize(h[i]) + M = M.augment(v) + M = M.delete_columns([0]) + old = M + + for t in range(1,d): + #assemble row + M = _columnize(alpha) + for i in range(n): + v = _columnize(h[i]*(D[i]**t)) + M = M.augment(v) + M = M.delete_columns([0]) + new = M + old = old.stack(new) + + return old + + def _parity_check_matrix_vandermonde(self): + """ + Return a parity check matrix for ``self`` using Vandermonde matrix. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x+ 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + sage: C._parity_check_matrix_vandermonde() + [1 0 0 0 0 0 0 1] + [0 0 1 0 1 1 1 0] + [0 1 1 1 0 0 1 0] + [---------------] + [0 1 1 1 1 1 1 1] + [0 1 0 1 1 0 1 0] + [0 0 1 1 1 1 0 0] + """ + L = self._defining_set + g = self._generating_pol + t = g.degree() + + from sage.matrix.constructor import matrix, diagonal_matrix, block_matrix + + V = matrix.vandermonde(L) + V = V.transpose() + GL = [g(i) for i in L] + GLI = [j.inverse_of_unit() for j in GL] + D = diagonal_matrix(GLI) + VF = matrix([V.row(i) for i in range(t)]) + H = VF*D + + matrices = [matrix([vector(i) for i in H.row(j)]) for j in range(t)] + matrices = [m.transpose() for m in matrices] + + m = block_matrix(t, 1, matrices) + return m + + def distance_bound(self): + """ + Return a lower bound for the minimum distance of the code. + + Computed using the degree of the generating polynomial of ``self``. + The minimum distance is guaranteed to be bigger than or equal to this bound. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + sage: C.distance_bound() + 3 + sage: C.minimum_distance() + 5 + """ + return 1 + (self._generating_pol).degree() + + +class GoppaCodeEncoder(Encoder): + r""" + Encoder for Goppa codes + + Encodes words represented as vectors of length `k`, where `k` is + the dimension of ``self``, with entries from `\GF{p}`, the prime field of + the base field of the generating polynomial of ``self``, into codewords + of length `n`, with entries from `\GF{p}`. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + sage: E = codes.encoders.GoppaCodeEncoder(C) + sage: E + Encoder for [8, 2] Goppa code over GF(2) + sage: word = vector(GF(2), (0, 1)) + sage: c = E.encode(word) + sage: c + (0, 1, 1, 1, 1, 1, 1, 0) + sage: c in C + True + """ + def __init__(self, code): + """ + Initialize. + + TESTS:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = x^2 + x + 1 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: E = codes.encoders.GoppaCodeEncoder(C) + sage: TestSuite(E).run() + """ + super(GoppaCodeEncoder, self).__init__(code) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = (x^2 + x + 1)^2 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: E = codes.encoders.GoppaCodeEncoder(C) + sage: E + Encoder for [8, 2] Goppa code over GF(2) + """ + return "Encoder for {}".format(self.code()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = (x^2 + x + 1)^2 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: E = codes.encoders.GoppaCodeEncoder(C) + sage: latex(E) + \text{Encoder for }[8, 2]\text{ Goppa code over }\Bold{F}_{2} + """ + return r"\text{{Encoder for }}{}".format(self.code()._latex_()) + + def __eq__(self, other): + """ + Test equality with ``other`` + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = (x^2 + x + 1)^2 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: E1 = codes.encoders.GoppaCodeEncoder(C) + sage: E2 = codes.encoders.GoppaCodeEncoder(C) + sage: E1 == E2 + True + """ + return (isinstance(other, GoppaCodeEncoder) + and self.code() == other.code()) + + def generator_matrix(self): + r""" + A generator matrix for ``self`` + + Dimension of resulting matrix is `k \times n`, where `k` is + the dimension of ``self`` and `n` is the length of ``self``. + + EXAMPLES:: + + sage: F = GF(2^3) + sage: R. = F[] + sage: g = (x^2 + x + 1)^2 + sage: L = [a for a in F.list() if g(a) != 0] + sage: C = codes.GoppaCode(g, L) + sage: C + [8, 2] Goppa code over GF(2) + sage: C.generator_matrix() + [1 0 0 1 0 1 1 1] + [0 1 1 1 1 1 1 0] + """ + c = self.code() + pmat = c.parity_check_matrix() + aux = codes.from_parity_check_matrix(pmat) + return aux.generator_matrix() + +GoppaCode._registered_encoders["GoppaEncoder"] = GoppaCodeEncoder diff --git a/src/sage/coding/grs.py b/src/sage/coding/grs_code.py similarity index 99% rename from src/sage/coding/grs.py rename to src/sage/coding/grs_code.py index c27534a6274..f53f31b1520 100644 --- a/src/sage/coding/grs.py +++ b/src/sage/coding/grs_code.py @@ -47,26 +47,31 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - from __future__ import absolute_import from six.moves import range -from sage.matrix.constructor import matrix, diagonal_matrix -from sage.rings.finite_rings.finite_field_constructor import GF from sage.categories.cartesian_product import cartesian_product + +from sage.matrix.constructor import matrix + +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ + from sage.modules.free_module_element import vector from sage.modules.free_module import VectorSpace -from sage.rings.integer import Integer + from sage.misc.cachefunc import cached_method +from sage.misc.functional import symbolic_sum +from sage.misc.misc_c import prod + from copy import copy +from sage.functions.other import binomial, floor +from sage.calculus.var import var + from .linear_code import AbstractLinearCode from .encoder import Encoder from .decoder import Decoder, DecodingError -from sage.misc.misc_c import prod -from sage.functions.other import binomial, floor, sqrt -from sage.calculus.var import var -from sage.misc.functional import symbolic_sum -from sage.rings.integer_ring import ZZ class GeneralizedReedSolomonCode(AbstractLinearCode): r""" @@ -256,7 +261,7 @@ def __hash__(self): return hash((self.base_field(), self.length(), self.dimension(), tuple(self.evaluation_points()), tuple(self.column_multipliers()))) - + def _repr_(self): r""" Return a string representation of ``self``. @@ -394,8 +399,8 @@ def multipliers_product(self): @cached_method def parity_column_multipliers(self): r""" - Return the list of column multipliers of the parity check matrix of - ``self``. They are also column multipliers of the generator matrix for + Return the list of column multipliers of the parity check matrix of + ``self``. They are also column multipliers of the generator matrix for the dual GRS code of ``self``. EXAMPLES:: @@ -496,7 +501,7 @@ def weight_distribution(self): TESTS: Test that this method agrees with the generic algorithm:: - + sage: F = GF(7) sage: C = codes.GeneralizedReedSolomonCode(F.list(), 3) sage: C.weight_distribution() == super(codes.GeneralizedReedSolomonCode, C).weight_distribution() # long time @@ -577,8 +582,6 @@ def decode_to_message(self, r): return vector(self.decoder().decode_to_message(r)) - - def ReedSolomonCode(base_field, length, dimension, primitive_root=None): r""" Construct a classical Reed-Solomon code. @@ -655,8 +658,6 @@ def ReedSolomonCode(base_field, length, dimension, primitive_root=None): raise ValueError("Supplied primitive_root is not a primitive n'th root of unity") return GeneralizedReedSolomonCode([ primitive_root**i for i in range(length) ], dimension) - - ####################### encoders ############################### @@ -801,7 +802,6 @@ def generator_matrix(self): return g - class GRSEvaluationPolynomialEncoder(Encoder): r""" Encoder for (Generalized) Reed-Solomon codes which uses evaluation of @@ -1094,11 +1094,9 @@ def message_space(self): polynomial_ring = message_space - - - ####################### decoders ############################### + class GRSBerlekampWelchDecoder(Decoder): r""" Decoder for (Generalized) Reed-Solomon codes which uses Berlekamp-Welch @@ -1540,7 +1538,6 @@ def _polynomial_vanishing_at_alphas(self, PolRing): sage: D._polynomial_vanishing_at_alphas(P) x^10 + 10*x^9 + x^8 + 10*x^7 + x^6 + 10*x^5 + x^4 + 10*x^3 + x^2 + 10*x """ - alphas = self.code().evaluation_points() G = PolRing.one() x = PolRing.gen() for i in range(0, self.code().length()): @@ -1790,11 +1787,9 @@ def decoding_radius(self): return (self.code().minimum_distance() - 1) // 2 - - class GRSErrorErasureDecoder(Decoder): r""" - Decoder for (Generalized) Reed-Solomon codes which is able to correct both + Decoder for (Generalized) Reed-Solomon codes which is able to correct both errors and erasures in codewords. Let `C` be a GRS code of length `n` and dimension `k`. @@ -2035,9 +2030,6 @@ def decoding_radius(self, number_erasures): return diff // 2 - - - class GRSKeyEquationSyndromeDecoder(Decoder): r""" Decoder for (Generalized) Reed-Solomon codes which uses a @@ -2317,7 +2309,6 @@ def decode_to_code(self, r): C = self.code() if r not in C.ambient_space(): raise ValueError("The word to decode has to be in the ambient space of the code") - F = C.base_field() PolRing = C.base_field()['x'] x = PolRing.gen() @@ -2390,7 +2381,6 @@ def decoding_radius(self): return (self.code().minimum_distance()-1) // 2 - ####################### registration ############################### GeneralizedReedSolomonCode._registered_encoders["EvaluationVector"] = GRSEvaluationVectorEncoder diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index 15713ae3a76..1a22f1e59fe 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -24,15 +24,14 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.coding.grs import GeneralizedReedSolomonCode -from sage.modules.free_module_element import vector +from sage.coding.grs_code import GeneralizedReedSolomonCode from sage.rings.integer_ring import ZZ from sage.coding.decoder import Decoder from sage.coding.guruswami_sudan.interpolation import gs_interpolation_linalg, gs_interpolation_lee_osullivan from sage.coding.guruswami_sudan.utils import (johnson_radius, gilt, solve_degree2_to_integer_range) -from sage.functions.other import binomial, floor, sqrt +from sage.functions.other import floor, sqrt def n_k_params(C, n_k): r""" @@ -820,8 +819,11 @@ def decode_to_code(self, r): [] """ C = self.code() - n, k, d, alphas, colmults, s, l = C.length(), C.dimension(), C.minimum_distance(),\ - C.evaluation_points(), C.column_multipliers(), self.multiplicity(), self.list_size() + k = C.dimension() + alphas = C.evaluation_points() + colmults = C.column_multipliers() + s = self.multiplicity() + l = self.list_size() tau = self.decoding_radius() ## SETUP INTERPOLATION PROBLEM wy = k-1 diff --git a/src/sage/coding/hamming_code.py b/src/sage/coding/hamming_code.py index 1247ccc5081..0ba7ac06e5e 100644 --- a/src/sage/coding/hamming_code.py +++ b/src/sage/coding/hamming_code.py @@ -1,17 +1,14 @@ r""" -Hamming Code +Hamming codes -Given an integer `r` and a field `F`, such that `F=GF(q)`, -the `[n, k, d]` code with length `n=\frac{q^{r}-1}{q-1}`, -dimension `k=\frac{q^{r}-1}{q-1} - r` and minimum distance -`d=3` is called the Hamming Code of order `r`. +Given an integer `r` and a field `F`, such that `F=GF(q)`, the `[n, k, d]` code +with length `n=\frac{q^{r}-1}{q-1}`, dimension `k=\frac{q^{r}-1}{q-1} - r` and +minimum distance `d=3` is called the Hamming Code of order `r`. REFERENCES: - [Rot2006]_ """ -from __future__ import absolute_import - # **************************************************************************** # Copyright (C) 2016 David Lucas # @@ -21,7 +18,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from .linear_code import AbstractLinearCode from sage.matrix.matrix_space import MatrixSpace from sage.schemes.projective.projective_space import ProjectiveSpace diff --git a/src/sage/coding/information_set_decoder.py b/src/sage/coding/information_set_decoder.py index 6602f5653e2..b0fe180bce3 100644 --- a/src/sage/coding/information_set_decoder.py +++ b/src/sage/coding/information_set_decoder.py @@ -42,10 +42,9 @@ # python3 from __future__ import division, print_function, absolute_import from six.moves import range -from six import iteritems from sage.all import ZZ, Integer, vector, SageObject, binomial -from .decoder import Decoder, DecodingError +from .decoder import Decoder def _format_decoding_interval(decoding_interval): @@ -577,7 +576,6 @@ def time_search_loop(p): before = process_time() for m in scalars: e = y - sum(m[i]*g[i] for i in range(p)) - errs = e.hamming_weight() return (process_time() - before)/100. T = mean([ time_information_set_steps() for s in range(5) ]) P = [ time_search_loop(p) for p in range(tau+1) ] diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 489317a386a..ee51946a539 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1,29 +1,28 @@ # -*- coding: utf-8 -*- r""" -Generic structures for linear codes +Linear codes Linear Codes ============ Let `F = \GF{q}` be a finite field. A rank `k` linear subspace of the vector -space `F^n` is called an `[n, k]`-linear code, `n` being the length of the -code and `k` its dimension. Elements of a code `C` are called codewords. +space `F^n` is called an `[n, k]`-linear code, `n` being the length of the code +and `k` its dimension. Elements of a code `C` are called codewords. A linear map from `F^k` to an `[n,k]` code `C` is called an "encoding", and it can be represented as a `k \times n` matrix, called a generator matrix. Alternatively, `C` can be represented by its orthogonal complement in `F^n`, -i.e. the `n-k`-dimensional vector space `C^\perp` such that the inner product -of any element from `C` and any element from `C^\perp` is zero. `C^\perp` -is called the dual code of `C`, and any generator matrix for `C^\perp` is called -a parity check matrix for `C`. +i.e. the `(n-k)`-dimensional vector space `C^\perp` such that the inner product +of any element from `C` and any element from `C^\perp` is zero. `C^\perp` is +called the dual code of `C`, and any generator matrix for `C^\perp` is called a +parity check matrix for `C`. We commonly endow `F^n` with the Hamming metric, i.e. the weight of a vector is -the number of non-zero elements in it. The central operation of a linear code is -then "decoding": given a linear code `C \subset F^n` and a "received word" `r -\in F^n` , retrieve the codeword `c \in C` such that the Hamming distance +the number of non-zero elements in it. The central operation of a linear code +is then "decoding": given a linear code `C \subset F^n` and a "received word" +`r \in F^n` , retrieve the codeword `c \in C` such that the Hamming distance between `r` and `c` is minimal. - Families or Generic codes ========================= @@ -59,9 +58,9 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never ``LinearCode`` -------------- -This class is used to represent arbitrary and unstructured linear codes. -It mostly rely directly on generic methods provided by ``AbstractLinearCode``, which -means that basic operations on the code (e.g. computation of the minimum +This class is used to represent arbitrary and unstructured linear codes. It +mostly rely directly on generic methods provided by ``AbstractLinearCode``, +which means that basic operations on the code (e.g. computation of the minimum distance) will use slow algorithms. A ``LinearCode`` is instantiated by providing a generator matrix:: @@ -100,7 +99,8 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never Further references ------------------ -If you want to get started on Sage's linear codes library, see https://doc.sagemath.org/html/en/thematic_tutorials/coding_theory.html +If you want to get started on Sage's linear codes library, see +https://doc.sagemath.org/html/en/thematic_tutorials/coding_theory.html If you want to learn more on the design of this library, see https://doc.sagemath.org/html/en/thematic_tutorials/structures_in_coding_theory.html @@ -173,10 +173,11 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never - David Joyner (2009-05): removed dependence on Guava, allowing it to be an option. Fixed errors in some docstrings. -- Kwankyu Lee (2010-01): added methods generator_matrix_systematic, information_set, and - magma interface for linear codes. +- Kwankyu Lee (2010-01): added methods generator_matrix_systematic, + information_set, and magma interface for linear codes. -- Niles Johnson (2010-08): :trac:`3893`: ``random_element()`` should pass on ``*args`` and ``**kwds``. +- Niles Johnson (2010-08): :trac:`3893`: ``random_element()`` should pass on + ``*args`` and ``**kwds``. - Thomas Feulner (2012-11): :trac:`13723`: deprecation of ``hamming_weight()`` @@ -191,7 +192,6 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never sage: C == loads(dumps(C)) True """ - #****************************************************************************** # Copyright (C) 2005 David Joyner # 2006 William Stein @@ -201,49 +201,46 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never # # http://www.gnu.org/licenses/ #****************************************************************************** -# python3 -from __future__ import division, print_function, absolute_import - -import inspect +from __future__ import division, print_function from six.moves import range from six import iteritems -from sage.cpython.string import bytes_to_str -from sage.modules.module import Module -from sage.categories.modules import Modules +import inspect from copy import copy + +from sage.cpython.string import bytes_to_str from sage.interfaces.all import gap -from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF -from sage.groups.perm_gps.permgroup import PermutationGroup +from sage.categories.modules import Modules +from sage.categories.cartesian_product import cartesian_product +from sage.categories.fields import Fields from sage.matrix.matrix_space import MatrixSpace +from sage.modules.free_module import VectorSpace +from sage.modules.module import Module from sage.modules.free_module_element import vector from sage.arith.all import GCD, binomial from sage.groups.all import SymmetricGroup -from sage.misc.all import prod -from sage.misc.functional import is_even +from sage.groups.perm_gps.permgroup import PermutationGroup from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ -from sage.structure.parent import Parent from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer -from sage.modules.free_module import VectorSpace +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF +from sage.structure.parent import Parent +from sage.misc.all import prod +from sage.misc.functional import is_even from sage.misc.cachefunc import cached_method from sage.misc.sageinspect import sage_getargspec from sage.misc.randstate import current_randstate +from sage.combinat.subset import Subsets from sage.features.gap import GapPackage + from .encoder import Encoder from .decoder import Decoder -from sage.combinat.subset import Subsets -from sage.categories.cartesian_product import cartesian_product -# import compatible with py2 and py3 - -from sage.categories.fields import Fields - - - -####################### coding theory functions ############################### +#****************************************************************************** +# coding theory functions +#****************************************************************************** def _dump_code_in_leon_format(C): r""" @@ -300,7 +297,9 @@ def _explain_constructor(cl): sage: from sage.coding.linear_code import _explain_constructor, LinearCodeSyndromeDecoder sage: cl = LinearCodeSyndromeDecoder sage: _explain_constructor(cl) - "The constructor requires no arguments.\nIt takes the optional arguments ['maximum_error_weight'].\nSee the documentation of sage.coding.linear_code.LinearCodeSyndromeDecoder for more details." + "The constructor requires no arguments.\nIt takes the optional + arguments ['maximum_error_weight'].\nSee the documentation of + sage.coding.linear_code.LinearCodeSyndromeDecoder for more details." sage: from sage.coding.information_set_decoder import LinearCodeInformationSetDecoder sage: cl = LinearCodeInformationSetDecoder @@ -335,12 +334,11 @@ def _explain_constructor(cl): class AbstractLinearCode(Module): """ - Abstract class for linear codes. + Abstract base class for linear codes. - This class contains all methods that can be used on Linear Codes - and on Linear Codes families. - So, every Linear Code-related class should inherit from this abstract - class. + This class contains all methods that can be used on Linear Codes and on + Linear Codes families. So, every Linear Code-related class should inherit + from this abstract class. To implement a linear code, you need to: @@ -480,7 +478,7 @@ def __init__(self, base_field, length, default_encoder_name, default_decoder_nam sage: C = MyCodeFamily(GF(17), 10.0, 5, generator_matrix) Traceback (most recent call last): ... - ValueError: length must be a Python int or a Sage Integer + ValueError: length must be a positive integer If the length of the code is not a non-zero positive integer (See :trac:`21326`), it will raise an exception:: @@ -489,7 +487,7 @@ def __init__(self, base_field, length, default_encoder_name, default_decoder_nam sage: C = MyCodeFamily(GF(17), 0, 1, empty_generator_matrix) Traceback (most recent call last): ... - ValueError: length must be a non-zero positive integer + ValueError: length must be a positive integer If the name of the default decoder is not known by the class, it will raise a exception:: @@ -534,21 +532,19 @@ def __init__(self, base_field, length, default_encoder_name, default_decoder_nam ... ValueError: 'generator' must be defined on a field (not a ring) """ - ### Add here any generic encoder/decoder ### - #This allows any class which inherits from AbstractLinearCode - #to use generic decoders/encoders - self._registered_encoders["Systematic"] = LinearCodeSystematicEncoder - self._registered_decoders["Syndrome"] = LinearCodeSyndromeDecoder - self._registered_decoders["NearestNeighbor"] = LinearCodeNearestNeighborDecoder from sage.coding.information_set_decoder import LinearCodeInformationSetDecoder - self._registered_decoders["InformationSet"] = LinearCodeInformationSetDecoder - if not isinstance(length, (int, Integer)): - raise ValueError("length must be a Python int or a Sage Integer") - if length <= 0: - raise ValueError("length must be a non-zero positive integer") + # Add here any generic encoder or decoder. This allows any class which + # inherits from AbstractLinearCode to use generic decoders/encoders + self._registered_encoders['Systematic'] = LinearCodeSystematicEncoder + self._registered_decoders['Syndrome'] = LinearCodeSyndromeDecoder + self._registered_decoders['NearestNeighbor'] = LinearCodeNearestNeighborDecoder + self._registered_decoders['InformationSet'] = LinearCodeInformationSetDecoder + + if not isinstance(length, (int, Integer)) or length <= 0: + raise ValueError("length must be a positive integer") if not base_field.is_field(): - raise ValueError("'base_field' must be a field (and {} is not one)".format(base_field)) + raise ValueError("{} is not a field".format(base_field)) if not default_encoder_name in self._registered_encoders: raise ValueError("You must set a valid encoder as default encoder for this code, by filling in the dictionary of registered encoders") if not default_decoder_name in self._registered_decoders: @@ -868,7 +864,7 @@ def automorphism_group_gens(self, equivalence="semilinear"): def ambient_space(self): r""" - Returns the ambient vector space of `self`. + Returns the ambient vector space of ``self``. EXAMPLES:: @@ -1002,11 +998,11 @@ def base_field(self): def basis(self): r""" - Returns a basis of `self`. + Returns a basis of ``self``. OUTPUT: - - ``Sequence`` - an immutable sequence whose universe is ambient space of `self`. + - ``Sequence`` - an immutable sequence whose universe is ambient space of ``self``. EXAMPLES:: @@ -1192,7 +1188,7 @@ def canonical_representative(self, equivalence="semilinear"): def __contains__(self, v): r""" - Returns True if `v` can be coerced into `self`. Otherwise, returns False. + Returns True if `v` can be coerced into ``self``. Otherwise, returns False. EXAMPLES:: @@ -1210,7 +1206,7 @@ def __contains__(self, v): def characteristic(self): r""" - Returns the characteristic of the base ring of `self`. + Returns the characteristic of the base ring of ``self``. EXAMPLES:: @@ -1605,7 +1601,6 @@ def is_projective(self): False """ M = self.generator_matrix().transpose() - R = self.base_field() def projectivize(row): if not row.is_zero(): @@ -1840,7 +1835,7 @@ def construction_x(self, other, aux): raise ValueError("%s is not a subcode of %s"%(self,other)) G2 = self.generator_matrix() - left = G1 = other.generator_matrix() + left = other.generator_matrix() # G1 k = self.dimension() for r in G2.rows(): @@ -2454,7 +2449,6 @@ def is_information_set(self, positions): except ValueError: return False - def is_permutation_automorphism(self,g): r""" Returns `1` if `g` is an element of `S_n` (`n` = length of self) and @@ -2754,8 +2748,6 @@ def minimum_distance(self, algorithm=None): "of size at most 256") G = self.generator_matrix() - n = self.length() - k = self.dimension() if (q == 2 or q == 3) and algorithm=="guava": gap.load_package("guava") C = gap(G).GeneratorMatCode(gap(F)) @@ -2801,7 +2793,6 @@ def _minimum_weight_codeword(self, algorithm = None): - David Joyner (11-2005) """ - G = self.generator_matrix() n, k = self.length(), self.dimension() F = self.base_field() Gmat = self.generator_matrix()._gap_init_() @@ -2972,7 +2963,6 @@ def permutation_automorphism_group(self, algorithm="partition"): q = F.order() G = self.generator_matrix() if 2*self.dimension() <= self.length() else self.dual_code().generator_matrix() n = len(G.columns()) - k = len(G.rows()) if "gap" in algorithm: GapPackage("guava", spkg="gap_packages").require() gap.load_package('guava') @@ -3224,7 +3214,6 @@ def redundancy_matrix(self): G = E.generator_matrix() return G.delete_columns(E.systematic_positions()) - def shortened(self, L): r""" Returns the code shortened at the positions ``L``, where @@ -3326,12 +3315,9 @@ def weight_distribution(self, algorithm=None): algorithm = "gap" F = self.base_ring() n = self.length() - G = self.generator_matrix() if algorithm=="gap": Gmat = self.generator_matrix()._gap_init_() - G = gap(Gmat) q = self.base_ring().order() - k = gap(self.base_ring()) z = 'Z(%s)*%s'%(q, [0]*self.length()) # GAP zero vector as a string _ = gap.eval("w:=DistancesDistributionMatFFEVecFFE("+Gmat+", GF("+str(q)+"),"+z+")") v = [eval(gap.eval("w["+str(i)+"]")) for i in range(1,self.length()+2)] # because GAP returns vectors in compressed form @@ -3353,7 +3339,6 @@ def weight_distribution(self, algorithm=None): from six import StringIO # to use the already present output parser wts = [0] * (n + 1) - s = 0 for L in StringIO(bytes_to_str(lines)).readlines(): L = L.strip() if L: @@ -3693,9 +3678,6 @@ def zeta_function(self, name="T"): return P/((1-T)*(1-q*T)) - - - ############################ linear codes python class ######################## class LinearCode(AbstractLinearCode): @@ -3956,14 +3938,8 @@ def generator_matrix(self, encoder_name=None, **kwargs): return g - - - - - - - ####################### encoders ############################### + class LinearCodeGeneratorMatrixEncoder(Encoder): r""" Encoder based on generator_matrix for Linear codes. @@ -4052,21 +4028,6 @@ def generator_matrix(self): return g - - - - - - - - - - - - - - - class LinearCodeSystematicEncoder(Encoder): r""" Encoder based on a generator matrix in systematic form for Linear codes. @@ -4400,16 +4361,8 @@ def systematic_positions(self): return self._systematic_positions if self._systematic_positions else self.generator_matrix().pivots() - - - - - - - - - ####################### decoders ############################### + class LinearCodeSyndromeDecoder(Decoder): r""" Constructs a decoder for Linear Codes based on syndrome lookup table. @@ -4962,10 +4915,6 @@ def decoding_radius(self): return (self.code().minimum_distance()-1) // 2 - - - - ####################### registration ############################### LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 0a74a03038f..bf24a23838e 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -29,8 +29,6 @@ from operator import mul from sage.matrix.constructor import matrix from sage.functions.other import binomial -from sage.calculus.var import var -from sage.misc.functional import symbolic_sum from sage.coding.linear_code import AbstractLinearCode, LinearCodeSyndromeDecoder from sage.coding.encoder import Encoder from sage.combinat.subset import Subsets @@ -40,8 +38,6 @@ from sage.rings.integer import Integer from sage.modules.free_module_element import vector from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.interfaces.gap import gfq_gap_to_sage -from sage.interfaces.all import gap from sage.misc.cachefunc import cached_method from functools import reduce @@ -653,7 +649,6 @@ def generator_matrix(self): order = C.order() num_of_var = C.number_of_variables() q = base_field.cardinality() - dimension = C.dimension() points = base_field**num_of_var matrix_list = [] max_individual_degree = min(order, (q - 1)) diff --git a/src/sage/coding/self_dual_codes.py b/src/sage/coding/self_dual_codes.py index 75040abb429..b6af9ebe7d5 100644 --- a/src/sage/coding/self_dual_codes.py +++ b/src/sage/coding/self_dual_codes.py @@ -1,14 +1,12 @@ r""" Enumerating binary self-dual codes -This module implements functions useful for studying binary -self-dual codes. -The main function is ``self_dual_binary_codes``, -which is a case-by-case list of entries, each represented by a -Python dictionary. +This module implements functions useful for studying binary self-dual codes. +The main function is ``self_dual_binary_codes``, which is a case-by-case list +of entries, each represented by a Python dictionary. -Format of each entry: a Python dictionary with keys "order -autgp", "spectrum", "code", "Comment", "Type", where +Format of each entry: a Python dictionary with keys "order autgp", "spectrum", +"code", "Comment", "Type", where - "code" - a sd code C of length n, dim n/2, over GF(2) @@ -89,14 +87,15 @@ """ from __future__ import print_function -from sage.misc.lazy_import import lazy_import from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.matrix.matrix_space import MatrixSpace -lazy_import("sage.coding.linear_code", "LinearCode") +from sage.matrix.all import matrix from sage.matrix.constructor import block_diagonal_matrix from sage.rings.integer_ring import ZZ from sage.groups.perm_gps.permgroup import PermutationGroup -from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_function + +from sage.coding.linear_code import LinearCode _F = GF(2) @@ -177,12 +176,42 @@ def _MS2(n): return MatrixSpace(_F, n2, n2) def _I2(n): - r"""Internal function""" + r""" + Internal function + + EXAMPLES:: + + sage: from sage.coding.self_dual_codes import _I2 + sage: _I2(3) + [1] + sage: _I2(5) + [1 0] + [0 1] + sage: _I2(7) + [1 0 0] + [0 1 0] + [0 0 1] + """ return _MS2(n).identity_matrix() -@cached_method +@cached_function def _And7(): - return MS7(_F, [[1, 1, 1, 0, 0, 1, 1],\ + """ + Auxiliary matrix And7. + + EXAMPLES:: + + sage: from sage.coding.self_dual_codes import _And7 + sage: _And7() + [1 1 1 0 0 1 1] + [1 1 1 0 1 0 1] + [1 1 1 0 1 1 0] + [0 0 0 0 1 1 1] + [0 1 1 1 0 0 0] + [1 0 1 1 0 0 0] + [1 1 0 1 0 0 0] + """ + return matrix(_F, [[1, 1, 1, 0, 0, 1, 1],\ [1, 1, 1, 0, 1, 0, 1],\ [1, 1, 1, 0, 1, 1, 0],\ [0, 0, 0, 0, 1, 1, 1],\ @@ -190,9 +219,25 @@ def _And7(): [1, 0, 1, 1, 0, 0, 0],\ [1, 1, 0, 1, 0, 0, 0]]) -@cached_method +@cached_function def _H8(): - return MS8(ZZ, [[1, 1, 1, 1, 1, 1, 1, 1],\ + """ + Auxiliary matrix H8. + + EXAMPLES:: + + sage: from sage.coding.self_dual_codes import _H8 + sage: _H8() + [ 1 1 1 1 1 1 1 1] + [ 1 -1 1 -1 1 -1 1 -1] + [ 1 1 -1 -1 1 1 -1 -1] + [ 1 -1 -1 1 1 -1 -1 1] + [ 1 1 1 1 -1 -1 -1 -1] + [ 1 -1 1 -1 -1 1 -1 1] + [ 1 1 -1 -1 -1 -1 1 1] + [ 1 -1 -1 1 -1 1 1 -1] + """ + return matrix(ZZ, [[1, 1, 1, 1, 1, 1, 1, 1],\ [1, -1, 1, -1, 1, -1, 1, -1],\ [1, 1, -1, -1, 1, 1, -1, -1],\ [1, -1, -1, 1, 1, -1, -1, 1],\ diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py index 3e23a601d15..ed0d458f231 100644 --- a/src/sage/coding/source_coding/huffman.py +++ b/src/sage/coding/source_coding/huffman.py @@ -1,5 +1,5 @@ r""" -Huffman Encoding +Huffman encoding This module implements functionalities relating to Huffman encoding and decoding. @@ -8,7 +8,6 @@ - Nathann Cohen (2010-05): initial version. - Classes and functions ===================== """ @@ -548,7 +547,7 @@ def _generate_edges(self, tree, parent="", bit=""): sage: from sage.coding.source_coding.huffman import Huffman sage: H = Huffman("Sage") sage: T = H.tree() - sage: T.edges(labels=None) + sage: T.edges(labels=None) # indirect doctest [('0', 'S: 00'), ('0', 'a: 01'), ('1', 'e: 10'), ('1', 'g: 11'), ('root', '0'), ('root', '1')] """ if parent == "": diff --git a/src/sage/coding/subfield_subcode.py b/src/sage/coding/subfield_subcode.py index a43543bd2b6..0400059fb7b 100644 --- a/src/sage/coding/subfield_subcode.py +++ b/src/sage/coding/subfield_subcode.py @@ -21,8 +21,6 @@ from .linear_code import AbstractLinearCode from sage.misc.cachefunc import cached_method -from sage.rings.integer import Integer -from sage.rings.finite_rings.finite_field_constructor import GF from sage.categories.homset import Hom from .relative_finite_field_extension import RelativeFiniteFieldExtension from sage.matrix.constructor import matrix @@ -82,7 +80,6 @@ def __init__(self, original_code, subfield, embedding=None): raise ValueError("original_code must be a linear code") if not subfield.is_finite(): raise ValueError("subfield has to be a finite field") - p = subfield.characteristic() F = original_code.base_field() s = subfield.degree() sm = F.degree() @@ -146,6 +143,12 @@ def dimension(self): r""" Returns the dimension of ``self``. + EXAMPLES:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(16, 'aa').list()[:13], 5) + sage: Cs = codes.SubfieldSubcode(C, GF(4, 'a')) + sage: Cs.dimension() + 3 """ return self.generator_matrix().nrows() @@ -228,7 +231,6 @@ def parity_check_matrix(self): [ 0 0 0 0 0 0 0 0 0 1 a 0 a + 1] """ C = self.original_code() - Fqm = C.base_field() Fq = self.base_field() H_original = C.parity_check_matrix() n = self.length() diff --git a/src/sage/coding/two_weight_db.py b/src/sage/coding/two_weight_db.py index 4e84a36994c..1fb1f90ad84 100644 --- a/src/sage/coding/two_weight_db.py +++ b/src/sage/coding/two_weight_db.py @@ -30,7 +30,6 @@ """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.matrix.constructor import Matrix -from sage.coding.linear_code import LinearCode # The following is a list of two-weight codes stored as dictionaries. Each entry # sets the base field, the matrix and the source: other parameters are computed diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index ac33d3466e9..3ba63f647b7 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -359,8 +359,6 @@ def __init__(self, parent, l, latex_options={}): CombinatorialElement.__init__(self, parent, l) self._latex_options = dict(latex_options) - _has_2D_print = False - def set_latex_options(self, D): r""" Set the latex options for use in the ``_latex_`` function. @@ -464,24 +462,40 @@ def latex_options(self): def _repr_(self): r""" + Return a string representation of ``self`` depending on + :meth:`DyckWords.options`. + TESTS:: sage: DyckWord([1, 0, 1, 0]) [1, 0, 1, 0] sage: DyckWord([1, 1, 0, 0]) [1, 1, 0, 0] - sage: type(DyckWord([]))._has_2D_print = True + sage: DyckWords.options.display="lattice" + sage: DyckWords.options.diagram_style="line" sage: DyckWord([1, 0, 1, 0]) /\/\ sage: DyckWord([1, 1, 0, 0]) /\ / \ - sage: type(DyckWord([]))._has_2D_print = False + sage: DyckWords.options._reset() """ - if self._has_2D_print: - return self.to_path_string() - else: - return super(DyckWord, self)._repr_() + return self.parent().options._dispatch(self, '_repr_', 'display') + + def _repr_list(self): + r""" + Return a string representation of ``self`` as a list. + + TESTS:: + + sage: DyckWord([]) + [] + sage: DyckWord([1, 0]) + [1, 0] + sage: DyckWord('(())') + [1, 1, 0, 0] + """ + return super(DyckWord, self)._repr_() def _repr_lattice(self, type=None, labelling=None, underpath=True): r""" @@ -595,10 +609,7 @@ def __str__(self): sage: print(DyckWord([1, 1, 0, 0])) (()) """ - if self._has_2D_print: - return self.to_path_string() - else: - return "".join(map(replace_symbols, [x for x in self])) + return "".join(map(replace_symbols, [x for x in self])) def to_path_string(self, unicode=False): r""" @@ -3242,13 +3253,13 @@ class options(GlobalOptions): sage: D [1, 1, 0, 1, 0, 0] sage: DyckWords.options.display="lattice" - sage: D # known bug (Trac #24324) + sage: D ___ _| x | x . | . . sage: DyckWords.options(diagram_style="line") - sage: D # known bug (Trac #24324) + sage: D /\/\ / \ sage: DyckWords.options._reset() diff --git a/src/sage/combinat/output.py b/src/sage/combinat/output.py index 505a3c1be35..93d9f3b7971 100644 --- a/src/sage/combinat/output.py +++ b/src/sage/combinat/output.py @@ -253,7 +253,7 @@ def tex_from_skew_array(array, with_lines=False, align='b'): This function creates latex code for a "skew composition" ``array``. That is, for a two dimensional array in which each row can begin with an arbitrary number ``None``'s and the remaining entries could, in - principe, be anything but probably should be strings or integers of similar + principle, be anything but probably should be strings or integers of similar width. A row consisting completely of ``None``'s is allowed. INPUT: @@ -275,6 +275,13 @@ def tex_from_skew_array(array, with_lines=False, align='b'): &\\ \lr{5}&\lr{6}&\lr{7}&\lr{8}\\ \end{array}$} + + TESTS:: + + sage: sage.combinat.output.tex_from_skew_array([(1,2,3), (2,3,4)]) + '\\raisebox{-.6ex}{$\\begin{array}[b]{*{3}c}\\\\\n\\lr{1}&\\lr{2}&\\lr{3}\\\\\n\\lr{2}&\\lr{3}&\\lr{4}\\\\\n\\end{array}$}' + sage: sage.combinat.output.tex_from_skew_array([((1,2,),)]) + '\\raisebox{-.6ex}{$\\begin{array}[b]{*{1}c}\\\\\n\\lr{(1, 2)}\\\\\n\\end{array}$}' """ # first identify where the None's appear in ``array`` and define a # function end_line which puts in the required \cline's. @@ -299,7 +306,7 @@ def end_line(r): tex=r'\raisebox{-.6ex}{$\begin{array}[%s]{*{%s}c}'%(align,max(map(len,array))) tex+=end_line(0)+'\n' for r in range(len(array)): - tex+='&'.join('' if c is None else r'\lr{%s}'%c for c in array[r]) + tex+='&'.join('' if c is None else r'\lr{%s}'%(c,) for c in array[r]) tex+=end_line(r+1)+'\n' return tex+r'\end{array}$}' diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index 37a68008456..9dd45aaa20e 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -218,7 +218,7 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): Lazy family (...)_{i in Coroot lattice of the Root system of type ['C', 2, 1]} sage: alphacheck = Y.keys().simple_roots() sage: Y1 = Y[alphacheck[1]] - sage: Y1(x) + sage: Y1(x) # py2 ((q1^3+q1^2*q2)/(-q2^3))*B[-e[0] - 2*e[1] - e['delta'] + e['deltacheck']] + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[0] - e['delta'] + e['deltacheck']] + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - 2*e[1] - 2*e['delta'] + e['deltacheck']] @@ -230,6 +230,21 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): + ((q1^3+q1^2*q2)/(-q2^3))*B[3*e[0] - 3*e['delta'] + e['deltacheck']] + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[1] - e['delta'] + e['deltacheck']] + ((-q1^2-2*q1*q2-q2^2)/(-q2^2))*B[-e[1] + e['deltacheck']] + ((q1+q2)/(-q2))*B[e[1] + e['deltacheck']] + sage: Y1(x) # py3 + ((q1^2+2*q1*q2+q2^2)/(-q1*q2))*B[e[0] + e['deltacheck']] + + ((-q1^2-2*q1*q2-q2^2)/(-q2^2))*B[-e[1] + e['deltacheck']] + + ((-q1^2-q1*q2)/(-q2^2))*B[2*e[0] - e[1] - e['delta'] + + e['deltacheck']] + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - e['delta'] + + e['deltacheck']] + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - 2*e[1] - e['delta'] + + e['deltacheck']] + ((q1+q2)/(-q2))*B[e[1] + e['deltacheck']] + + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[1] - e['delta'] + e['deltacheck']] + + ((q1^3+q1^2*q2)/(-q2^3))*B[2*e[0] - e[1] - 2*e['delta'] + e['deltacheck']] + + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[-e[0] - e['delta'] + e['deltacheck']] + + ((q1^3+2*q1^2*q2+q1*q2^2)/(-q2^3))*B[e[0] - 2*e['delta'] + e['deltacheck']] + + ((q1^3+q1^2*q2)/(-q2^3))*B[3*e[0] - 3*e['delta'] + e['deltacheck']] + + ((q1^3+q1^2*q2)/(-q2^3))*B[-e[0] - 2*e[1] - e['delta'] + e['deltacheck']] + + ((q1^3+q1^2*q2)/(-q2^3))*B[e[0] - 2*e[1] - 2*e['delta'] + e['deltacheck']] + + (q1^3/(-q2^3))*B[3*e[0] - 2*e[1] - 3*e['delta'] + e['deltacheck']] The Cherednik operators span a Laurent polynomial ring inside the affine Hecke algebra; namely `\lambda\mapsto Y_\lambda` is a group diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index b7fbcb77258..08ffe502703 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -7705,8 +7705,12 @@ def cardinality(self): sage: sts = [StandardTableaux(n) for n in ns] sage: all(st.cardinality() == len(st.list()) for st in sts) True - sage: StandardTableaux(50).cardinality() # long time - 27886995605342342839104615869259776 + + The cardinality can be computed without constructing all elements in + this set, so this computation is fast (see also :trac:`28273`):: + + sage: StandardTableaux(500).cardinality() + 423107565308608549951551753690...221285999236657443927937253376 TESTS:: diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index a94c55f0b73..acc46406754 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -267,15 +267,14 @@ .. [Knuth1] Knuth, Donald (2000). "Dancing links". :arxiv:`cs/0011047`. """ -#***************************************************************************** -# Copyright (C) 2011-2015 Sebastien Labbe +# **************************************************************************** +# Copyright (C) 2011-2015 Sébastien Labbé # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -# python3 +# https://www.gnu.org/licenses/ +# **************************************************************************** from __future__ import division from builtins import zip @@ -286,7 +285,6 @@ from sage.structure.sage_object import SageObject from sage.modules.free_module_element import vector from sage.misc.cachefunc import cached_method, cached_function -from sage.misc.superseded import deprecated_function_alias ####################################### @@ -306,7 +304,7 @@ def ncube_isometry_group(n, orientation_preserving=True): OUTPUT: - list of matrices + list of matrices EXAMPLES:: @@ -351,14 +349,6 @@ def ncube_isometry_group(n, orientation_preserving=True): Traceback (most recent call last): ... ValueError: ['B', 0] is not a valid Cartan type - - Is deprecated:: - - sage: from sage.combinat.tiling import orthogonal_transformation - sage: L = orthogonal_transformation(2) - doctest:...: DeprecationWarning: orthogonal_transformation is - deprecated. Please use sage.combinat.tiling.ncube_isometry_group - instead. See http://trac.sagemath.org/19107 for details. """ from sage.combinat.root_system.weyl_group import WeylGroup L = [w.matrix() for w in WeylGroup(['B', n])] @@ -367,7 +357,7 @@ def ncube_isometry_group(n, orientation_preserving=True): else: return L -orthogonal_transformation = deprecated_function_alias(19107, ncube_isometry_group) + @cached_function def ncube_isometry_group_cosets(n, orientation_preserving=True): r""" @@ -382,7 +372,7 @@ def ncube_isometry_group_cosets(n, orientation_preserving=True): OUTPUT: - list of cosets, each coset being a sorted list of matrices + list of cosets, each coset being a sorted list of matrices EXAMPLES:: @@ -650,7 +640,7 @@ def __eq__(self, other): OUTPUT: - boolean + boolean EXAMPLES:: @@ -675,7 +665,7 @@ def __ne__(self, other): OUTPUT: - boolean + boolean EXAMPLES:: @@ -700,7 +690,7 @@ def __le__(self, other): OUTPUT: - boolean + boolean EXAMPLES:: @@ -724,7 +714,7 @@ def __ge__(self, other): OUTPUT: - boolean + boolean EXAMPLES:: @@ -749,7 +739,7 @@ def __sub__(self, v): OUTPUT: - polyomino + polyomino EXAMPLES:: @@ -771,7 +761,7 @@ def __add__(self, v): OUTPUT: - polyomino + polyomino EXAMPLES:: @@ -794,7 +784,7 @@ def __rmul__(self, m): OUTPUT: - Polyomino + Polyomino EXAMPLES:: @@ -822,7 +812,7 @@ def __rmul__(self, m): def canonical(self): r""" - Returns the translated copy of self having minimal and nonnegative + Return the translated copy of self having minimal and nonnegative coordinates EXAMPLES:: @@ -865,7 +855,7 @@ def canonical_isometric_copies(self, orientation_preserving=True, OUTPUT: - set of Polyomino + set of Polyomino EXAMPLES:: @@ -907,7 +897,7 @@ def canonical_isometric_copies(self, orientation_preserving=True, def translated_copies(self, box): r""" - Returns an iterator over the translated images of self inside a + Return an iterator over the translated images of self inside a polyomino. INPUT: @@ -916,7 +906,7 @@ def translated_copies(self, box): OUTPUT: - iterator of 3d polyominoes + iterator of 3d polyominoes EXAMPLES:: @@ -1192,7 +1182,7 @@ def boundary(self): def show3d(self, size=1): r""" - Returns a 3d Graphic object representing the polyomino. + Return a 3d Graphic object representing the polyomino. INPUT: @@ -1222,7 +1212,7 @@ def show3d(self, size=1): def show2d(self, size=0.7, color='black', thickness=1): r""" - Returns a 2d Graphic object representing the polyomino. + Return a 2d Graphic object representing the polyomino. INPUT: @@ -1259,9 +1249,6 @@ def show2d(self, size=0.7, color='black', thickness=1): G += line(edge, color=color, thickness=thickness) return G - canonical_orthogonals = deprecated_function_alias(19107, canonical_isometric_copies) - translated = deprecated_function_alias(19107, translated_copies) - translated_orthogonals = deprecated_function_alias(19107, isometric_copies) ####################### # General tiling solver @@ -1420,7 +1407,7 @@ def pieces(self): OUTPUT: - list of 3d polyominoes + list of 3d polyominoes EXAMPLES:: @@ -1438,7 +1425,7 @@ def pieces(self): def space(self): r""" - Returns an iterator over all the non negative integer coordinates + Return an iterator over all the non negative integer coordinates contained in the space to tile. EXAMPLES:: @@ -1456,11 +1443,11 @@ def space(self): @cached_method def coord_to_int_dict(self): r""" - Returns a dictionary mapping coordinates to integers. + Return a dictionary mapping coordinates to integers. OUTPUT: - dict + dict EXAMPLES:: @@ -1492,7 +1479,7 @@ def coord_to_int_dict(self): @cached_method def int_to_coord_dict(self): r""" - Returns a dictionary mapping integers to coordinates. + Return a dictionary mapping integers to coordinates. EXAMPLES:: @@ -1722,7 +1709,7 @@ def nrows_per_piece(self): OUTPUT: - list + list EXAMPLES:: @@ -1765,7 +1752,7 @@ def row_to_polyomino(self, row_number): OUTPUT: - polyomino + polyomino EXAMPLES:: @@ -1806,7 +1793,7 @@ def dlx_solver(self): OUTPUT: - DLX Solver + DLX Solver EXAMPLES:: @@ -1827,7 +1814,7 @@ def _dlx_solutions_iterator(self): OUTPUT: - iterator + iterator EXAMPLES:: @@ -1856,7 +1843,7 @@ def _dlx_common_prefix_solutions_iterator(self): OUTPUT: - iterator + iterator EXAMPLES:: @@ -1925,7 +1912,7 @@ def _dlx_incremental_solutions_iterator(self): OUTPUT: - iterator + iterator EXAMPLES:: @@ -1978,7 +1965,7 @@ def _dlx_incremental_solutions_iterator(self): def solve(self, partial=None): r""" - Returns an iterator of list of polyominoes that are an exact cover + Return an iterator of list of polyominoes that are an exact cover of the box. INPUT: @@ -1993,7 +1980,7 @@ def solve(self, partial=None): OUTPUT: - iterator of list of polyominoes + iterator of list of polyominoes EXAMPLES:: @@ -2078,7 +2065,7 @@ def number_of_solutions(self): OUTPUT: - integer + integer EXAMPLES:: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 94e5cf483ca..8cc89181090 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -112,6 +112,7 @@ def _process_letter(self, letter): Suffix Trie of the word: ababbab """ r = self._active_state + old_s = None # While r is not the auxiliary vertex, or # there is not transition from r along letter, ... while r != -1 and (r, letter) not in self._transition_function: @@ -1333,6 +1334,87 @@ def LZ_decomposition(self): iB.append(i) return iB + def _count_and_skip(self, node, i, j): + r""" + Use count and skip trick to follow the path starting at ``node`` and + reading ``self.word()[i:j]``. We assume that reading + ``self.word()[i:j]`` is possible from ``node`` + + INPUT: + + - ``node`` -- explicit node of ``self`` + - ``i`` -- beginning of factor ``T.word()[i:j]`` + - ``j`` -- end of factor ``T.word()[i:j]`` + + OUTPUT: + + The node obtained by starting at ``node`` and following the edges + labeled by the letters of ``T.word()[i:j]``. + Return ``("explicit", end_node)`` if w ends at the node ``end_node``, + and ``("implicit", edge, d)`` if it ends after reading ``d`` letters along + the edge ``edge``. + + EXAMPLES:: + + sage: T = Word('00110111011').suffix_tree() + sage: T._count_and_skip(5, 2, 5) + ('implicit', (9, 10), 2) + sage: T._count_and_skip(0, 1, 4) + ('explicit', 7) + sage: T._count_and_skip(0, 8, 10) + ('implicit', (2, 7), 1) + sage: T = Word('cacao').suffix_tree() + sage: T._count_and_skip(3, 2, 5) + ('explicit', 1) + """ + trans = self._find_transition(node, self._letters[i]) + while (trans[0][1] is not None and trans[0][1] - trans[0][0] + 1 <= j - i): + node = trans[1] + i += trans[0][1] - trans[0][0] + 1 + if i == j: + return ('explicit', node) + else: + trans = self._find_transition(node, self._letters[i]) + if trans[0][1] is None and len(self.word()) - trans[0][0] + 1 <= j - i: + return ('explicit', trans[1]) + else: + return ('implicit', (node, trans[1]), j - i) + + def suffix_walk(self, edge, l): + r""" + Return the state of "w" if the input state is "aw". + + If the input state ``(edge, l)`` is path labeled "aw" with "a" a letter, the output is + the state which is path labeled "w". + + INPUT: + + - ``edge`` -- the edge containing the state + - ``l`` -- the string-depth of the state on edge (``l``>0) + + OUTPUT: + + Return ``("explicit", end_node)`` if the state of w is an explicit + state and ``("implicit", edge, d)`` is obtained by reading ``d`` + letters on ``edge``. + + EXAMPLES:: + + sage: T = Word('00110111011').suffix_tree() + sage: T.suffix_walk((0, 5), 1) + ('explicit', 0) + sage: T.suffix_walk((7, 3), 1) + ('implicit', (9, 4), 1) + """ + # Select the transition that corresponds to edge + for (i, j) in self._transition_function[edge[0]]: + if self._transition_function[edge[0]][(i, j)] == edge[1]: + break + # self.word()[i-1:j] is the word on the edges + i -= 1 + parent = self.suffix_link(edge[0]) + return self._count_and_skip(parent, i, i+l) + ##### # Miscellaneous methods ##### diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 21104e98132..20680aaa635 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -197,7 +197,7 @@ def init_sage(): debug.refine_category_hash_check = True # We import readline before forking, otherwise Pdb doesn't work - # os OS X: http://trac.sagemath.org/14289 + # on OS X: http://trac.sagemath.org/14289 import readline try: diff --git a/src/sage/dynamics/arithmetic_dynamics/all.py b/src/sage/dynamics/arithmetic_dynamics/all.py index c8db0a80276..b434f8001bc 100644 --- a/src/sage/dynamics/arithmetic_dynamics/all.py +++ b/src/sage/dynamics/arithmetic_dynamics/all.py @@ -4,5 +4,6 @@ from .generic_ds import DynamicalSystem from .affine_ds import DynamicalSystem_affine from .projective_ds import DynamicalSystem_projective +from .product_projective_ds import DynamicalSystem_product_projective lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'WehlerK3Surface') lazy_import('sage.dynamics.arithmetic_dynamics.wehlerK3', 'random_WehlerK3Surface') diff --git a/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py index 2c8ac392607..783a3e92cae 100644 --- a/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/product_projective_ds.py @@ -285,3 +285,53 @@ def nth_iterate_map(self, n): F = [poly(*F) for poly in F] #'square' D >>= 1 return DynamicalSystem_projective(PHI, domain=self.domain()) + +class DynamicalSystem_product_projective_field(DynamicalSystem_product_projective): + + pass + +class DynamicalSystem_product_projective_finite_field(DynamicalSystem_product_projective_field): + + def cyclegraph(self): + r""" + Return the digraph of all orbits of this morphism mod `p`. + + OUTPUT: a digraph + + EXAMPLES:: + + sage: P. = ProductProjectiveSpaces(GF(3), [1,1]) + sage: f = DynamicalSystem_projective([a^2,b^2,c^2,d^2], domain=P) + sage: f.cyclegraph() + Looped digraph on 16 vertices + + :: + + sage: P. = ProductProjectiveSpaces(GF(5), [1,1]) + sage: f = DynamicalSystem_projective([a^2,b^2,c,d], domain=P) + sage: f.cyclegraph() + Looped digraph on 36 vertices + + :: + + sage: P. = ProductProjectiveSpaces(GF(2), [1,2]) + sage: f = DynamicalSystem_projective([a^2,b^2,c,d,e], domain=P) + sage: f.cyclegraph() + Looped digraph on 21 vertices + + .. TODO:: Dynamical systems for subschemes of product projective spaces needs work. + Thus this is not implemented for subschemes. + """ + V = [] + E = [] + from sage.schemes.product_projective.space import is_ProductProjectiveSpaces + if is_ProductProjectiveSpaces(self.domain()) == True: + for P in self.domain(): + V.append(str(P)) + Q = self(P) + E.append([str(Q)]) + else: + raise NotImplementedError("Cyclegraph for product projective spaces not implemented for subschemes") + from sage.graphs.digraph import DiGraph + g = DiGraph(dict(zip(V, E)), loops=True) + return g diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 470e733accb..605435292d2 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -53,7 +53,7 @@ class initialization directly. # **************************************************************************** from __future__ import print_function, absolute_import -from sage.arith.misc import is_prime +from sage.arith.misc import (is_prime, is_square) from sage.categories.fields import Fields from sage.categories.function_fields import FunctionFields from sage.categories.number_fields import NumberFields @@ -102,7 +102,7 @@ class initialization directly. from copy import copy from sage.parallel.ncpus import ncpus from sage.parallel.use_fork import p_iter_fork -from sage.dynamics.arithmetic_dynamics.projective_ds_helper import _fast_possible_periods +from sage.dynamics.arithmetic_dynamics.projective_ds_helper import (_fast_possible_periods,_all_periodic_points) from sage.sets.set import Set from sage.combinat.permutation import Arrangements from sage.combinat.subset import Subsets @@ -424,6 +424,9 @@ def __classcall_private__(cls, morphism_or_polys, domain=None, names=None): if not all(split_d == domain._degree(f) for f in split_poly): msg = 'polys (={}) must be multi-homogeneous of the same degrees (by component)' raise TypeError(msg.format(polys)) + if is_FiniteField(R): + from sage.dynamics.arithmetic_dynamics.product_projective_ds import DynamicalSystem_product_projective_finite_field + return DynamicalSystem_product_projective_finite_field(polys, domain) return DynamicalSystem_product_projective(polys, domain) # Now polys define an endomorphism of a scheme in P^n @@ -3322,7 +3325,7 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety', find only the periodic points of minimal period ``n`` and ``False`` specifies to find all periodic points of period ``n`` - - ``R`` a commutative ring + - ``R`` - a commutative ring - ``algorithm`` -- (default: ``'variety'``) must be one of the following: @@ -3466,6 +3469,13 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety', Traceback (most recent call last): ... NotImplementedError: return_subscheme only implemented for minimal=False + + :: + + sage: P.=ProjectiveSpace(GF(3), 1) + sage: f = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) + sage: f.periodic_points(2, R=GF(3^2,'t')) + [(t + 2 : 1), (2*t : 1)] """ if n <= 0: raise ValueError("a positive integer period must be specified") @@ -3474,6 +3484,7 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety', R = self.base_ring() else: f = self.change_ring(R) + R = f.base_ring() CR = f.coordinate_ring() dom = f.domain() PS = f.codomain().ambient_space() @@ -3531,7 +3542,7 @@ def periodic_points(self, n, minimal=True, R=None, algorithm='variety', break return points else: - raise NotImplementedError("ring must a number field or finite field") + raise NotImplementedError("ring must be a number field or finite field") else: #a higher dimensional scheme raise TypeError("use return_scheme=True") else: @@ -4562,7 +4573,7 @@ def lift_to_rational_periodic(self, points_modp, B=None): return(good_points) - def rational_periodic_points(self, **kwds): + def all_periodic_points(self, **kwds): r""" Determine the set of rational periodic points for this dynamical system. @@ -4595,13 +4606,23 @@ def rational_periodic_points(self, **kwds): INPUT: kwds: - + + - ``R`` -- (default: domain of dynamical system) the base ring + over which the periodic points of the dynamical system are found + - ``prime_bound`` -- (default: ``[1,20]``) a pair (list or tuple) of positive integers that represent the limits of primes to use in the reduction step or an integer that represents the upper bound - ``lifting_prime`` -- (default: 23) a prime integer; argument that specifies modulo which prime to try and perform the lifting + + - ``period_degree_bounds`` -- (default: ``[4,4]``) a pair of positive integers + (max period, max degree) for which the dynatomic polynomial should be solved for + + - ``algorithm`` -- (optional) specifies which algorithm to use; + current options are `dynatomic` and `lifting`; defaults to solving the + dynatomic for low periods and degrees and lifts for everything else - ``periods`` -- (optional) a list of positive integers that is the list of possible periods @@ -4617,7 +4638,7 @@ def rational_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-3/4*y^2, y^2]) - sage: sorted(f.rational_periodic_points(prime_bound=20, lifting_prime=7)) # long time + sage: sorted(f.all_periodic_points(prime_bound=20, lifting_prime=7)) # long time [(-1/2 : 1), (1 : 0), (3/2 : 1)] :: @@ -4625,7 +4646,7 @@ def rational_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ,2) sage: f = DynamicalSystem_projective([2*x^3 - 50*x*z^2 + 24*z^3, ....: 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) - sage: sorted(f.rational_periodic_points(prime_bound=[1,20])) # long time + sage: sorted(f.all_periodic_points(prime_bound=[1,20])) # long time [(-3 : -1 : 1), (-3 : 0 : 1), (-3 : 1 : 1), (-3 : 3 : 1), (-1 : -1 : 1), (-1 : 0 : 1), (-1 : 1 : 1), (-1 : 3 : 1), (0 : 1 : 0), (1 : -1 : 1), (1 : 0 : 0), (1 : 0 : 1), (1 : 1 : 1), (1 : 3 : 1), (3 : -1 : 1), @@ -4636,7 +4657,7 @@ def rational_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([-5*x^2 + 4*y^2, 4*x*y]) - sage: sorted(f.rational_periodic_points()) # long time + sage: sorted(f.all_periodic_points()) # long time [(-2 : 1), (-2/3 : 1), (2/3 : 1), (1 : 0), (2 : 1)] :: @@ -4645,8 +4666,8 @@ def rational_periodic_points(self, **kwds): sage: K. = NumberField(x^2-x+1) sage: P. = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([u^2 + v^2,v^2]) - sage: f.rational_periodic_points() - [(w : 1), (1 : 0), (-w + 1 : 1)] + sage: sorted(f.all_periodic_points()) + [(-w + 1 : 1), (w : 1), (1 : 0)] :: @@ -4654,12 +4675,80 @@ def rational_periodic_points(self, **kwds): sage: K. = NumberField(x^2-x+1) sage: P. = ProjectiveSpace(K,1) sage: f = DynamicalSystem_projective([u^2+v^2,u*v]) - sage: f.rational_periodic_points() + sage: f.all_periodic_points() Traceback (most recent call last): ... NotImplementedError: rational periodic points for number fields only implemented for polynomials + + :: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: K. = QuadraticField(5) + sage: phi = QQ.embeddings(K)[0] + sage: f = DynamicalSystem_projective([x^2 - y^2, y^2]) + sage: sorted(f.all_periodic_points(R=phi)) + [(-1 : 1), (-1/2*v + 1/2 : 1), (0 : 1), (1 : 0), (1/2*v + 1/2 : 1)] + + :: + + sage: P. = ProjectiveSpace(QQ, 3) + sage: f = DynamicalSystem_projective([x^2 - (3/4)*w^2, y^2 - 3/4*w^2, z^2 - 3/4*w^2, w^2]) + sage: sorted(f.all_periodic_points(algorithm="dynatomic")) + [(-1/2 : -1/2 : -1/2 : 1), + (-1/2 : -1/2 : 3/2 : 1), + (-1/2 : 3/2 : -1/2 : 1), + (-1/2 : 3/2 : 3/2 : 1), + (0 : 0 : 1 : 0), + (0 : 1 : 0 : 0), + (0 : 1 : 1 : 0), + (1 : 0 : 0 : 0), + (1 : 0 : 1 : 0), + (1 : 1 : 0 : 0), + (1 : 1 : 1 : 0), + (3/2 : -1/2 : -1/2 : 1), + (3/2 : -1/2 : 3/2 : 1), + (3/2 : 3/2 : -1/2 : 1), + (3/2 : 3/2 : 3/2 : 1)] + + :: + + sage: P. = ProjectiveSpace(QQ, 3) + sage: f = DynamicalSystem_projective([x^2 - (3/4)*w^2, y^2 - 3/4*w^2, z^2 - 3/4*w^2, w^2]) + sage: sorted(f.all_periodic_points(period_degree_bounds=[10,10])) + [(-1/2 : -1/2 : -1/2 : 1), + (-1/2 : -1/2 : 3/2 : 1), + (-1/2 : 3/2 : -1/2 : 1), + (-1/2 : 3/2 : 3/2 : 1), + (0 : 0 : 1 : 0), + (0 : 1 : 0 : 0), + (0 : 1 : 1 : 0), + (1 : 0 : 0 : 0), + (1 : 0 : 1 : 0), + (1 : 1 : 0 : 0), + (1 : 1 : 1 : 0), + (3/2 : -1/2 : -1/2 : 1), + (3/2 : -1/2 : 3/2 : 1), + (3/2 : 3/2 : -1/2 : 1), + (3/2 : 3/2 : 3/2 : 1)] + + TESTS:: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem([x^2+ y^2, x*y]) + sage: f.all_periodic_points(algorithm="dnyatomic") + Traceback (most recent call last): + ... + ValueError: algorithm must be 'dynatomic' or 'lifting' """ - PS = self.domain() + ring = kwds.pop("R", None) + if not ring is None: + #changes to the new ring + DS = self.change_ring(ring) + #ensures that the correct method is run, in case user switches to a finite field + return DS.all_periodic_points(**kwds) + else: + DS = self + PS = DS.domain() K = PS.base_ring() if K in NumberFields(): if not K.is_absolute(): @@ -4674,11 +4763,11 @@ def rational_periodic_points(self, **kwds): #separately. We also check here that we are working with a polynomial. If the map #is not a polynomial, the Weil restriction will not be a morphism and we cannot #apply this algorithm. - g = self.dehomogenize(1) + g = DS.dehomogenize(1) inf = PS([1,0]) k = 1 if isinstance(g[0], FractionFieldElement): - g = self.dehomogenize(0) + g = DS.dehomogenize(0) inf = PS([0,1]) k = 0 if isinstance(g[0], FractionFieldElement): @@ -4690,7 +4779,7 @@ def rational_periodic_points(self, **kwds): G = g.weil_restriction() F = G.homogenize(d) #find the QQ rational periodic points for the weil restriction - Fper = F.rational_periodic_points(**kwds) + Fper = F.all_periodic_points(**kwds) for P in Fper: #take the 'good' points in the weil restriction and find the #associated number field points. @@ -4702,19 +4791,23 @@ def rational_periodic_points(self, **kwds): if not Q in periodic_points: #check periodic not preperiodic and add all points in cycle orb = set([Q]) - Q2 = self(Q) + Q2 = DS(Q) while Q2 not in orb: orb.add(Q2) - Q2 = self(Q2) + Q2 = DS(Q2) if Q2 == Q: periodic_points = periodic_points.union(orb) return list(periodic_points) else: primebound = kwds.pop("prime_bound", [1, 20]) p = kwds.pop("lifting_prime", 23) + pd_bounds = kwds.pop("period_degree_bounds", [4,4]) + alg = kwds.pop("algorithm", None) periods = kwds.pop("periods", None) badprimes = kwds.pop("bad_primes", None) num_cpus = kwds.pop("ncpus", ncpus()) + if not alg is None and alg not in ['dynatomic','lifting']: + raise ValueError("algorithm must be 'dynatomic' or 'lifting'") if not isinstance(primebound, (list, tuple)): try: @@ -4729,31 +4822,105 @@ def rational_periodic_points(self, **kwds): raise TypeError("prime bounds must be integers") if badprimes is None: - badprimes = self.primes_of_bad_reduction() + badprimes = DS.primes_of_bad_reduction() if periods is None: - periods = self.possible_periods(prime_bound=primebound, bad_primes=badprimes, ncpus=num_cpus) - PS = self.domain() + periods = DS.possible_periods(prime_bound=primebound, bad_primes=badprimes, ncpus=num_cpus) + PS = DS.domain() periodic = set() + + if alg != 'lifting': + for i in periods[:]: + if (alg == 'dynatomic') or (i <= pd_bounds[0] and DS.degree() <= pd_bounds[1]): + periodic.update(DS.periodic_points(i)) + periods.remove(i) + if not periods: + return list(periodic) while p in badprimes: p = next_prime(p + 1) - B = e ** self.height_difference_bound() - - f = self.change_ring(GF(p)) + B = e ** DS.height_difference_bound() + f = DS.change_ring(GF(p)) all_points = f.possible_periods(True) #return the list of points and their periods. pos_points = [] + #check period, remove duplicates for i in range(len(all_points)): - if all_points[i][1] in periods and not (all_points[i] in pos_points): #check period, remove duplicates + if all_points[i][1] in periods and not (all_points[i] in pos_points): pos_points.append(all_points[i]) - periodic_points = self.lift_to_rational_periodic(pos_points,B) + periodic_points = DS.lift_to_rational_periodic(pos_points,B) for p,n in periodic_points: for k in range(n): p.normalize_coordinates() periodic.add(p) - p = self(p) + p = DS(p) return list(periodic) else: raise TypeError("base field must be an absolute number field") + def rational_periodic_points(self, **kwds): + r""" + Determine the set of rational periodic points + for this dynamical system. + + The map must be defined over `\QQ` and be an endomorphism of + projective space. If the map is a polynomial endomorphism of + `\mathbb{P}^1`, i.e. has a totally ramified fixed point, then + the base ring can be an absolute number field. + This is done by passing to the Weil restriction. + + The default parameter values are typically good choices for + `\mathbb{P}^1`. If you are having trouble getting a particular + map to finish, try first computing the possible periods, then + try various different ``lifting_prime`` values. + + ALGORITHM: + + Modulo each prime of good reduction `p` determine the set of + periodic points modulo `p`. For each cycle modulo `p` compute + the set of possible periods (`mrp^e`). Take the intersection + of the list of possible periods modulo several primes of good + reduction to get a possible list of minimal periods of rational + periodic points. Take each point modulo `p` associated to each + of these possible periods and try to lift it to a rational point + with a combination of `p`-adic approximation and the LLL basis + reduction algorithm. + + See [Hutz2015]_. + + INPUT: + + kwds: + + - ``prime_bound`` -- (default: ``[1,20]``) a pair (list or tuple) + of positive integers that represent the limits of primes to use + in the reduction step or an integer that represents the upper bound + + - ``lifting_prime`` -- (default: 23) a prime integer; argument that + specifies modulo which prime to try and perform the lifting + + - ``periods`` -- (optional) a list of positive integers that is + the list of possible periods + + - ``bad_primes`` -- (optional) a list or tuple of integer primes; + the primes of bad reduction + + - ``ncpus`` -- (default: all cpus) number of cpus to use in parallel + + OUTPUT: a list of rational points in projective space + + EXAMPLES:: + + sage: R. = QQ[] + sage: K. = NumberField(x^2-x+1) + sage: P. = ProjectiveSpace(K,1) + sage: f = DynamicalSystem_projective([u^2 + v^2,v^2]) + sage: f.rational_periodic_points() + doctest:warning + ... + [(w : 1), (1 : 0), (-w + 1 : 1)] + """ + from sage.misc.superseded import deprecation + deprecation(28109, "use sage.dynamics.arithmetic_dynamics.projective_ds.all_periodic_points instead") + return self.all_periodic_points(**kwds) + def all_rational_preimages(self, points): r""" Given a set of rational points in the domain of this @@ -4989,7 +5156,7 @@ def rational_preperiodic_points(self, **kwds): else: p = kwds.pop("lifting_prime", 23) #find the rational preperiodic points - T = self.rational_periodic_points(prime_bound=primebound, lifting_prime=p, + T = self.all_periodic_points(prime_bound=primebound, lifting_prime=p, periods=periods, bad_primes=badprimes, ncpus=num_cpus) preper = self.all_rational_preimages(T) #find the preperiodic points @@ -5167,14 +5334,16 @@ def connected_rational_component(self, P, n=0): return points[0] - def conjugating_set(self, other): + def conjugating_set(self, other, R=None): r""" - Return the set of elements in PGL that conjugates one - dynamical system to the other. + Return the set of elements in PGL over the base ring + that conjugates one dynamical system to the other. Given two nonconstant rational functions of equal degree - determine to see if there is an element of PGL that - conjugates one rational function to another. It does this + determine to see if there is a rational element of PGL that + conjugates one rational function to another. + The option argument `R` specifies the field of definiton + of the PGL elements. The set is determined by by taking the fixed points of one map and mapping them to all unique permutations of the fixed points of the other map. If there are not enough fixed points the @@ -5183,6 +5352,11 @@ def conjugating_set(self, other): fixed points until there are enough points; such that there are `n+2` points with all `n+1` subsets linearly independent. + .. WARNING:: + + For degree 1 maps that are conjugate, there is a positive dimensional + set of conjugations. This function returns only one such element. + ALGORITHM: Implementing invariant set algorithm from the paper [FMV2014]_. @@ -5192,8 +5366,10 @@ def conjugating_set(self, other): INPUT: - - ``other`` -- a nonconstant rational function of same degree - as ``self`` + - ``other`` -- a rational function of same degree + as this map + + - ``R`` -- a field or embedding OUTPUT: @@ -5271,6 +5447,71 @@ def conjugating_set(self, other): [0 1 0] [0 0 1] ] + + :: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: R = P.coordinate_ring() + sage: f = DynamicalSystem_projective([R(3), R(4)]) + sage: g = DynamicalSystem_projective([R(5), R(2)]) + sage: m = f.conjugating_set(g)[0] + sage: f.conjugate(m) == g + True + + :: + + sage: P. = ProjectiveSpace(QQbar, 1) + sage: f = DynamicalSystem_projective([7*x + 12*y, 8*x]) + sage: g = DynamicalSystem_projective([1645*x - 318*y, 8473*x - 1638*y]) + sage: m = f.conjugating_set(g)[0] + sage: f.conjugate(m) == g + True + + note that only one possible conjugation is returned:: + + sage: P. = ProjectiveSpace(GF(11),2) + sage: f = DynamicalSystem_projective([2*x + 12*y, 11*y+2*z, x+z]) + sage: m1 = matrix(GF(11), 3, 3, [1,4,1,0,2,1,1,1,1]) + sage: g = f.conjugate(m1) + sage: f.conjugating_set(g) + [ + [ 1 0 0] + [ 9 1 4] + [ 4 10 8] + ] + + :: + + sage: L. = CyclotomicField(8) + sage: P. = ProjectiveSpace(L, 2) + sage: f = DynamicalSystem_projective([2*x + 12*y, 11*y+2*z, x+z]) + sage: m1 = matrix(L, 3, 3, [1,4,v^2,0,2,1,1,1,1]) + sage: g = f.conjugate(m1) + sage: m = f.conjugating_set(g)[0] + sage: f.conjugate(m) == g + True + + TESTS: + + Make sure the caching problem is fixed, see #28070 :: + + sage: K. = QuadraticField(-1) + sage: P. = ProjectiveSpace(QQ,1) + sage: f = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) + sage: m = matrix(QQ, 2, 2, [-1, 3, 2, 1]) + sage: g = f.conjugate(m) + sage: f.conjugating_set(g) + [ + [-1 3] + [ 2 1] + ] + sage: f = f.change_ring(K) + sage: g = g.change_ring(K) + sage: f.conjugating_set(g) + [ + [-1 3] + [ 2 1] + ] """ f = copy(self) g = copy(other) @@ -5281,7 +5522,64 @@ def conjugating_set(self, other): pass if f.degree() != g.degree():# checks that maps are of equal degree return [] + gens = f[0].parent().gens() + M = len(gens) + base = f.base_ring() + if f.degree() == 0: # all constant maps are conjugate + zer = [0 for i in range(M-1)] + m = [] + for i in range(M): + m1 = copy(zer) + m1.insert(i, f[i]/g[i]) + m += m1 + return [matrix(base, M, M, m)] + if f.degree() == 1: # for degree 1 maps, check if matrix representations are similar + # make matrix forms of f1 and f2 + m1 = matrix(base,M,M,[F.coefficient(var) for F in f for var in gens]) + m2 = matrix(base,M,M,[F.coefficient(var) for F in g for var in gens]) + # Note: det_ratio will be nonzero for invertible f1, f2 + if m1.det() != m2.det(): + det_ratio = m1.det()/m2.det() + try: + det_root = det_ratio.nth_root(M) + except ValueError: #no root in field + return [] + #matrices must have same determinant to be similar, but were in PGL + #so we can scale so the determinants are equal + m1 = (1/det_root)*m1 + bol,m = m2.is_similar(m1, transformation=True) + if bol: + if m.base_ring() == base: + return [m] + #else is_similar went to algebraic closure + if base in NumberFields(): + from sage.rings.qqbar import number_field_elements_from_algebraics + K,mK,phi = number_field_elements_from_algebraics([u for t in list(m) for u in t],\ + minimal=True) + if K == base: + return [matrix(K, M, M, mK)] + else: #may be a subfield + embeds = K.embeddings(base) + if len(embeds) == 0: + #not a subfield + return [] + else: + for emb in embeds: + m_emb = matrix(base, M,M, [emb(u) for u in mK]) + #check that it is the right embedding + if f.conjugate(m_emb) == g: + return [m_emb] + else: #finite field case + #always comes from prime field so an coerce + m = matrix(base, M, M, [base(u.as_finite_field_element()[1]) for t in list(m) for u in t]) + return [m] + #not similar + return [] + # sigma invariants are invariant under conjugacy but are only implemented in dim 1 n = f.domain().dimension_relative() + if (n == 1) and (R in NumberFields() or R in FiniteFields())\ + and (f.sigma_invariants(1) != g.sigma_invariants(1)): + return [] L = Set(f.periodic_points(1)) K = Set(g.periodic_points(1)) if len(L) != len(K): # checks maps have the same number of fixed points @@ -5290,11 +5588,12 @@ def conjugating_set(self, other): r = f.domain().base_ring() more = True if d >= n+2: # need at least n+2 points - for i in Subsets(L, n+2): + for i in Subsets(range(len(L)), n+2): # make sure all n+1 subsets are linearly independent - Ml = matrix(r, [list(s) for s in i]) + TL = [L[il] for il in i] + Ml = matrix(r, [list(s) for s in TL]) if not any(j == 0 for j in Ml.minors(n + 1)): - Tf = list(i) + Tf = list(TL) more = False break while more: @@ -5309,26 +5608,31 @@ def conjugating_set(self, other): raise ValueError("not enough rational preimages") d = len(L) if d >= n + 2: # makes sure all n+1 subsets are linearly independent - for i in Subsets(L, n+2): - Ml = matrix(r, [list(s) for s in i]) + for i in Subsets(range(len(L)), n+2): + TL = [L[il] for il in i] + Ml = matrix(r, [list(s) for s in TL]) if not any(j == 0 for j in Ml.minors(n + 1)): more = False - Tf = list(i) + Tf = list(TL) break Conj = [] - for i in Arrangements(K,(n+2)): + for i in Arrangements(range(len(K)),(n+2)): + TK = [K[ik] for ik in i] # try all possible conjugations between invariant sets try: # need all n+1 subsets linearly independent - s = f.domain().point_transformation_matrix(i,Tf)# finds elements of PGL that maps one map to another + s = f.domain().point_transformation_matrix(TK,Tf) + # finds elements of PGL that maps one map to another if self.conjugate(s) == other: Conj.append(s) except (ValueError): pass return Conj - def is_conjugate(self, other): + def is_conjugate(self, other, R=None): r""" - Return whether or not two dynamical systems are conjugate. + Return whether two dynamical systems are conjugate over their + base ring (by default) or over the ring `R` entered as an + optional parameter. ALGORITHM: @@ -5338,8 +5642,10 @@ def is_conjugate(self, other): INPUT: - - ``other`` -- a nonconstant rational function of same degree - as ``self`` + - ``other`` -- a nonconstant rational function of the same + degree as this map + + - ``R`` -- a field or embedding OUTPUT: boolean @@ -5392,9 +5698,75 @@ def is_conjugate(self, other): sage: g = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) sage: f.is_conjugate(g) False + + :: + + sage: P. = ProjectiveSpace(QQbar, 1) + sage: f = DynamicalSystem_projective([7*x + 12*y, 8*x]) + sage: g = DynamicalSystem_projective([1645*x - 318*y, 8473*x - 1638*y]) + sage: f.is_conjugate(g) + True + + conjugation is only checked over the base field by default:: + + sage: P. = ProjectiveSpace(QQ, 1) + sage: f = DynamicalSystem_projective([-3*y^2, 3*x^2]) + sage: g = DynamicalSystem_projective([-x^2 - 2*x*y, 2*x*y + y^2]) + sage: f.is_conjugate(g), f.is_conjugate(g, R=QQbar) # long time + (False, True) + + :: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: f = DynamicalSystem_projective([7*x + 12*y, 8*y+2*z, x+z]) + sage: m1 = matrix(QQ, 3, 3, [1,4,1,0,2,1,1,1,1]) + sage: g = f.conjugate(m1) + sage: f.is_conjugate(g) + True + + :: + + sage: P. = ProjectiveSpace(GF(7), 2) + sage: f = DynamicalSystem_projective([2*x + 12*y, 11*y+2*z, x+z]) + sage: m1 = matrix(GF(7), 3, 3, [1,4,1,0,2,1,1,1,1]) + sage: g = f.conjugate(m1) + sage: f.is_conjugate(g) + True + + :: + + sage: P. = ProjectiveSpace(QQ,2) + sage: f = DynamicalSystem_projective([2*x^2 + 12*y*x, 11*y*x+2*y^2, x^2+z^2]) + sage: m1 = matrix(QQ, 3, 3, [1,4,1,0,2,1,1,1,1]) + sage: g = f.conjugate(m1) + sage: f.is_conjugate(g) # long time + True + + TESTS: + + Make sure the caching problem is fixed, see #28070 :: + + sage: K. = QuadraticField(5) + sage: P. = ProjectiveSpace(QQ,1) + sage: f = DynamicalSystem_projective([x^2 - 2*y^2, y^2]) + sage: m = matrix(QQ, 2, 2, [-1, 3, 2, 1]) + sage: g = f.conjugate(m) + sage: f.is_conjugate(g) + True + sage: f = f.change_ring(K) + sage: g = g.change_ring(K) + sage: f.is_conjugate(g) + True """ f = copy(self) g = copy(other) + if R == None: + R = f.base_ring() + else: + f = self.change_ring(R) + g = other.change_ring(R) + if not (R in NumberFields() or R is QQbar or R in FiniteFields()): + raise NotImplementedError("ring must be a number field or finite field") try: f.normalize_coordinates() g.normalize_coordinates() @@ -5402,7 +5774,30 @@ def is_conjugate(self, other): pass if f.degree() != g.degree(): # checks that maps are of equal degree return False + if f.degree() == 0: # all constant maps are conjugate + return True + if f.degree() == 1: # for degree 1 maps, check if matrix representations are similar + # make matrix forms of f1 and f2 + gens = f[0].parent().gens() + M = len(gens) + m1 = matrix(f.base_ring(),M,M,[F.coefficient(var) for F in f for var in gens]) + m2 = matrix(f.base_ring(),M,M,[F.coefficient(var) for F in g for var in gens]) + # Note: det_ratio will be nonzero for invertible f1, f2 + if m1.det() != m2.det(): + det_ratio = m1.det()/m2.det() + try: + det_root = det_ratio.nth_root(M) + except ValueError: #no root in field + return False + # matrices must have same determinant to be similar, but were in PGL + # so we can scale to have the determinants equal + m1 = (1/det_root)*m1 + return m1.is_similar(m2) + # sigma invariants are invariant under conjugacy but are only implemented in dim 1 n = f.domain().dimension_relative() + if (n==1) and (R in NumberFields() or R in FiniteFields())\ + and (f.sigma_invariants(1) != g.sigma_invariants(1)): + return False L = Set(f.periodic_points(1)) K = Set(g.periodic_points(1)) if len(L) != len(K): # checks maps have the same number of fixed points @@ -5411,10 +5806,12 @@ def is_conjugate(self, other): r = f.domain().base_ring() more = True if d >= n+2: # need at least n+2 points - for i in Subsets(L, n+2): # makes sure all n+1 subsets are linearly independent - Ml = matrix(r, [list(s) for s in i]) + for i in Subsets(range(len(L)), n+2): + # make sure all n+1 subsets are linearly independent + TL = [L[il] for il in i] + Ml = matrix(r, [list(s) for s in TL]) if not any(j == 0 for j in Ml.minors(n + 1)): - Tf = list(i) + Tf = list(TL) more = False break while more: @@ -5428,18 +5825,20 @@ def is_conjugate(self, other): if d == len(L):# if no new preimages then not enough points raise ValueError("not enough rational preimages") d = len(L) - if d >= n + 2: - # make sure all n+1 subsets are linearly independent - for i in Subsets(L, n+2): # checks at least n+1 are linearly independent - Ml = matrix(r, [list(s) for s in i]) + if d >= n + 2: # makes sure all n+1 subsets are linearly independent + for i in Subsets(range(len(L)), n+2): + TL = [L[il] for il in i] + Ml = matrix(r, [list(s) for s in TL]) if not any(j == 0 for j in Ml.minors(n + 1)): more = False - Tf = list(i) + Tf = list(TL) break - for i in Arrangements(K, n+2): + for i in Arrangements(range(len(K)),(n+2)): + TK = [K[ik] for ik in i] # try all possible conjugations between invariant sets try: # need all n+1 subsets linearly independent - s = f.domain().point_transformation_matrix(i,Tf) # finds elements of PGL that maps one map to another + s = f.domain().point_transformation_matrix(TK,Tf) + # finds elements of PGL that maps one map to another if self.conjugate(s) == other: return True except (ValueError): @@ -6112,3 +6511,50 @@ def automorphism_group(self, absolute=False, iso_type=False, return_functions=Fa from .endPN_automorphism_group import automorphism_group_FF return(automorphism_group_FF(F, absolute, iso_type, return_functions)) + def all_periodic_points(self, **kwds): + r""" + Returns a list of all periodic points over a finite field. + + INPUT: + + keywords: + + - ``R`` -- (default: base ring of dynamical system) the base ring + over which the periodic points of the dynamical system are found + + OUTPUT: a list of elements which are periodic + + EXAMPLES:: + + sage: P. = ProjectiveSpace(GF(5^2),1) + sage: f = DynamicalSystem_projective([x^2+y^2, x*y]) + sage: f.all_periodic_points() + [(1 : 0), (z2 + 2 : 1), (4*z2 + 3 : 1)] + + :: + + sage: P. = ProjectiveSpace(GF(5),2) + sage: f = DynamicalSystem_projective([x^2+y^2+z^2, x*y+x*z, z^2]) + sage: f.all_periodic_points() + [(1 : 0 : 0), + (0 : 0 : 1), + (1 : 0 : 1), + (2 : 1 : 1), + (1 : 4 : 1), + (3 : 0 : 1), + (0 : 3 : 1)] + + :: + + sage: P.=ProjectiveSpace(GF(3), 1) + sage: f = DynamicalSystem_projective([x^2 - y^2, y^2]) + sage: f.all_periodic_points(R=GF(3^2, 't')) + [(1 : 0), (0 : 1), (2 : 1), (t : 1), (2*t + 1 : 1)] + """ + R = kwds.pop("R", None) + if R is None: + DS = self + else: + DS = self.change_ring(R) + return DS.all_periodic_points(**kwds) #ensures that the correct method is run, in case user switches to infinite fields + return _all_periodic_points(DS) diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx index f4dcd7874f1..547044ecb17 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds_helper.pyx @@ -268,4 +268,34 @@ cpdef _normalize_coordinates(list point, int prime, int len_points): for coefficient in xrange(len_points): point[coefficient] = (point[coefficient] * mod_inverse) % prime + +cpdef _all_periodic_points(self): + """ + Find all periodic points over a finite field. + + EXAMPELS:: + + sage: from sage.dynamics.arithmetic_dynamics.projective_ds_helper import _all_periodic_points + sage: P. = ProjectiveSpace(GF(7), 1) + sage: f = DynamicalSystem_projective([x^2 - y^2, y^2], domain=P) + sage: _all_periodic_points(f) + [(1 : 0), (0 : 1), (6 : 1)] + """ + cdef list periodic_points, path + cdef set elements + + periodic_points = [] + path = [] + elements = set(self.domain()) + while elements: + number = elements.pop() + path = [number] + next_element = self(number) + while next_element in elements: + path.append(next_element) + elements.remove(next_element) + next_element = self(next_element) + if next_element in path: + periodic_points += path[path.index(next_element):] + return periodic_points diff --git a/src/sage/finance/stock.py b/src/sage/finance/stock.py index 9596533aae7..9fcc391fb5d 100644 --- a/src/sage/finance/stock.py +++ b/src/sage/finance/stock.py @@ -39,7 +39,7 @@ ------------------- """ from __future__ import absolute_import -from sage.misc.superseded import deprecated_function_alias + from sage.structure.all import Sequence from datetime import date @@ -182,7 +182,7 @@ def __repr__(self): sage: finance.Stock('ibm').__repr__() # optional -- internet # known bug 'IBM (...)' """ - return "%s (%s)"%(self.symbol, self.market_value()) + return "%s (%s)" % (self.symbol, self.market_value()) def market_value(self): """ @@ -290,8 +290,6 @@ def current_price_data(self): data['short_ratio'] = values[19] return data - yahoo = deprecated_function_alias(18355,current_price_data) - def history(self, startdate='Jan+1,+1900', enddate=None, histperiod='daily'): """ Return an immutable sequence of historical price data @@ -405,8 +403,6 @@ def history(self, startdate='Jan+1,+1900', enddate=None, histperiod='daily'): self.__historical = self._load_from_csv(R) return self.__historical - google = deprecated_function_alias(18355,history) - def open(self, *args, **kwds): r""" Return a time series containing historical opening prices for this @@ -636,9 +632,9 @@ def _get_data(self, exchange, startdate, enddate, histperiod='daily'): symbol = self.symbol cid = self.cid if cid == '': - url = 'http://finance.google.com/finance/historical?q=%s%s&startdate=%s&enddate=%s&histperiod=%s&output=csv'%(exchange, symbol.upper(), startdate, enddate, histperiod) + url = 'http://finance.google.com/finance/historical?q=%s%s&startdate=%s&enddate=%s&histperiod=%s&output=csv' % (exchange, symbol.upper(), startdate, enddate, histperiod) else: - url = 'http://finance.google.com/finance/historical?cid=%s&startdate=%s&enddate=%s&histperiod=%s&output=csv'%(cid, startdate, enddate, histperiod) + url = 'http://finance.google.com/finance/historical?cid=%s&startdate=%s&enddate=%s&histperiod=%s&output=csv' % (cid, startdate, enddate, histperiod) data = urlopen(url).read() if "Bad Request" in data or "The requested URL was not found on this server." in data: raise RuntimeError("Google reported a wrong request (did you specify a cid?)") diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 078767c26b8..27c174f9f07 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -594,26 +594,6 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = Polyhedron(rays=[[1,0,0],[0,1,0],[0,0,1],[0,0,-1]]) sage: CombinatorialPolyhedron(P).dimension() 3 - - TESTS:: - - sage: from itertools import combinations - sage: N = combinations(range(1200), 1199) - sage: C = CombinatorialPolyhedron(N) - sage: try: - ....: alarm(0.1) - ....: C.dimension() - ....: except: - ....: print("alarm!") - ....: - alarm! - sage: try: - ....: alarm(0.1) - ....: C.f_vector() - ....: except: - ....: print("alarm!") - ....: - alarm! """ if self._dimension == -2: # Dimension not computed yet. @@ -909,21 +889,6 @@ cdef class CombinatorialPolyhedron(SageObject): ('b', 'c'), ('a', 'c'), ('a', 'b')) - - TESTS:: - - sage: from itertools import combinations - sage: N = combinations(range(200),199) - sage: C = CombinatorialPolyhedron(N) - sage: try: - ....: alarm(0.1) - ....: C.edges() - ....: except: - ....: print("alarm!") - ....: - alarm! - sage: len(C.edges()) - 19900 """ cdef size_t len_edge_list = self._length_edges_list if self._edges is NULL: @@ -1061,21 +1026,6 @@ cdef class CombinatorialPolyhedron(SageObject): sage: it = C.face_iter(0) sage: for face in it: face.Hrepr() (An inequality (1, 0) x + 0 >= 0, An equation (0, 1) x + 0 == 0) - - TESTS:: - - sage: from itertools import combinations - sage: N = combinations(range(200),199) - sage: C = CombinatorialPolyhedron(N) - sage: try: - ....: alarm(0.1) - ....: C.ridges() - ....: except: - ....: print("alarm!") - ....: - alarm! - sage: len(C.ridges()) - 19900 """ cdef size_t len_ridge_list = self._length_edges_list if self._ridges is NULL: @@ -1167,46 +1117,6 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.f_vector() (1, 10, 45, 120, 185, 150, 50, 1) - - TESTS:: - - sage: from itertools import combinations - sage: N = combinations(range(25),24) - sage: C = CombinatorialPolyhedron(N) - sage: try: - ....: alarm(0.5) - ....: C.f_vector() - ....: except: - ....: print("alarm!") - ....: - alarm! - sage: C.f_vector() # long time - (1, - 25, - 300, - 2300, - 12650, - 53130, - 177100, - 480700, - 1081575, - 2042975, - 3268760, - 4457400, - 5200300, - 5200300, - 4457400, - 3268760, - 2042975, - 1081575, - 480700, - 177100, - 53130, - 12650, - 2300, - 300, - 25, - 1) """ if not self._f_vector: self._compute_f_vector() diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index afe4152d67b..bbb756b3015 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -13,6 +13,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ ########################################################################### +from __future__ import print_function, division from six.moves import range import sys # import from Sage library @@ -1534,119 +1535,125 @@ def RandomToleranceGraph(n): # uniform random triangulation using Schaeffer-Poulalhon algorithm - -def _auxiliary_random_word(n): +def _auxiliary_random_forest_word(n, k): r""" Return a random word used to generate random triangulations. INPUT: - n -- an integer + - ``n`` -- an integer + + - ``k`` -- an integer OUTPUT: - A binary sequence `w` of length `4n-2` with `n-1` ones, such that any proper - prefix `u` of `w` satisfies `3|u|_1 - |u|_0 > -2` (where `|u|_1` and `|u|_0` - are respectively the number of 1s and 0s in `u`). Those words are the - expected input of :func:`_contour_and_graph_from_word`. + A binary sequence `w` of length `4n+2k-4` with `n` ones, such that any + proper prefix `u` of `w` satisfies `3|u|_1 - |u|_0 \geq -2k+4` (where + `|u|_1` and `|u|_0` are respectively the number of 1s and 0s in `u`). Those + words are the expected input of :func:`_contour_and_graph_from_words`. ALGORITHM: - A random word with these numbers of `0` and `1` is chosen. This - word is then rotated in order to give an admissible code for a - tree (as explained in Proposition 4.2, [PS2006]_). There are - exactly two such rotations, one of which is chosen at random. + A random word with these numbers of `0` and `1` plus one additional `0` is + chosen. This word is then rotated such the prefix property is fulfilled for + each proper prefix and only violated by the final `0` (which is deleted + afterwards). There is exactly one such rotation (compare Section 4.3 in + [PS2006]_). Let us consider a word `w` satisfying the expected conditions. By - drawing a step (1,3) for each 1 and a step (1,-1) for each 0 in - `w`, one gets a path starting at height 0, ending at height -2 and - staying above (or on) the horizontal line of height -1 except at the - end point. By cutting the word at the first position of height -1, - let us write `w=uv`. One can then see that `v` can only touch the line - of height -1 at its initial point and just before its end point - (these two points may be the same). - - Now consider a word `w'` obtained from `w` by any - rotation. Because `vu` is another word satisfying the expected - conditions, one can assume that `w'` is obtained from `w` by - starting at some point in `u`. The algorithm must then recognize - the end of `u` and the end of `v` inside `w'`. The end of `v` is - the unique point of minimal height `h`. The end of `u` is the first - point reaching the height `h+1`. + drawing a step `(1,3)` for each `1` and a step `(1,-1)` for each `0` in + `w`, one gets a path starting at height `0`, ending at height `-2k+3` + (before removing the final `0`) and staying above (or on) the horizontal + line of height `-2k+4` except at the end point. + + Now consider an arbitrary word `w` with `n` ones and `3n+2k-3` zeros. By + cutting the word at the first position of minimum height, let us write + `w=uv`. One can then see that the word `vu` touches the line of height + `-2k+3` only after the last step. Further one can see that this is the only + rotation of the word `w` with this property. EXAMPLES:: - sage: from sage.graphs.generators.random import _auxiliary_random_word - sage: _auxiliary_random_word(4) # random - [1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0] + sage: from sage.graphs.generators.random import _auxiliary_random_forest_word + sage: with(seed(94364165)): + ....: _auxiliary_random_forest_word(4, 3) + ....: _auxiliary_random_forest_word(3, 5) + [1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0] + [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - sage: def check(w): - ....: steps = {1: 3, 0: -1} - ....: return all(sum(steps[u] for u in w[:i]) >= -1 for i in range(len(w))) + TESTS:: - sage: for n in range(1, 10): - ....: w = _auxiliary_random_word(n) - ....: assert len(w) == 4 * n - 2 - ....: assert w.count(0) == 3 * n - 1 - ....: assert check(w) + sage: def partial_sums(w): + ....: steps = {1: 3, 0: -1} + ....: curr_sum = 0 + ....: for x in w: + ....: curr_sum += steps[x] + ....: yield curr_sum + + sage: for k in range(3,6): + ....: for n in range(k, 10): + ....: w = _auxiliary_random_forest_word(n, k) + ....: assert len(w) == 4*n + 2*k - 4 + ....: assert w.count(1) == n + ....: for partial_sum in partial_sums(w): + ....: assert partial_sum >= -2*k + 4 """ from sage.misc.prandom import shuffle - w = [0] * (3 * n - 1) + [1] * (n - 1) + w = [0] * (3*n + 2*k - 3) + [1] * n shuffle(w) - # Finding the two admissible shifts. - # The 'if height' is true at least once. - # If it is true just once, then the word is admissible - # and cuts = [0, first position of -1] (ok) - # Otherwise, cuts will always contain - # [first position of hmin, first position of hmin - 1] (ok) - cuts = [0, 0] - height = 0 - height_min = 0 - for i in range(4 * n - 3): - if w[i] == 1: - height += 3 + # Finding the admissible shift + partial_sum = 0 + min_value = 0 + min_pos = 0 + for i, x in enumerate(w): + if x: + partial_sum += 3 else: - height -= 1 - if height < height_min: - height_min = height - cuts = cuts[1], i + 1 + partial_sum -= 1 + if partial_sum < min_value: + min_value = partial_sum + min_pos = i + return w[min_pos+1:] + w[:min_pos] - # random choice of one of the two possible cuts - idx = cuts[randint(0, 1)] - return w[idx:] + w[:idx] - -def _contour_and_graph_from_word(w): +def _contour_and_graph_from_words(pendant_word, forest_word): r""" - Return the contour word and the graph of inner vertices of the tree - associated with the word `w`. + Return the contour word and the graph of inner vertices of the `k`-gonal + forest associated with the words ``pendant_word`` and ``forest_word``. INPUT: - - `w` -- a word in `0` and `1` as given by :func:`_auxiliary_random_word` + - ``pendant_word`` -- a word with `k-1` zeros and `k-3` ones + + - ``forest_word`` -- a word in `0` and `1` as given by + :func:`_auxiliary_random_word` with the parameter ``k`` set to the number + of zeros in ``pendant_word`` plus `1` - This word must satisfy the conditions described in Proposition 4.2 of - [PS2006]_ (see :func:`_auxiliary_random_word`). + ``forest_word`` must satisfy the conditions hinted in Proposition 5.4 of + [PS2006]_ (see :func:`_auxiliary_random_forest_word`). OUTPUT: a pair ``(seq, G)`` where: - ``seq`` is a sequence of pairs (label, integer) representing the - contour walk along the tree associated with `w` + contour walk along the `k`-gonal forest associated with the words + ``pendant_word`` and ``forest_word``. - - ``G`` is the tree obtained by restriction to the set of inner vertices + - ``G`` is the `k`-gonal forest associated with the words ``pendant_word`` + and ``forest_word``. - The underlying bijection from words to trees is given by lemma 4.1 - in [PS2006]_. It maps the admissible words to planar trees where - every inner vertex has two leaves. + The underlying bijection from words to `k`-gonal forests is described in + Section 5.1 of [PS2006]_. The ``pendant_word`` corresponds to the factor + `\binom{2k-4}{k-3}` in the counting formula of Proposition 5.4 and the + ``forest_word`` corresponds to the factor `\frac{2k-3}{3m+2k-3} + \binom{4m+2k-4}{m}`. - In the word `w`, the letter `1` means going away from the root ("up") from - an inner vertex to another inner vertex. The letter `0` denotes all other - steps of the discovery, i.e. either discovering a leaf vertex or going - toward the root ("down"). Thus, the length of `w` is twice the number of - edges between inner vertices, plus the number of leaves. + In the ``forest_word``, the letter `1` means going away from the root ("up") + from an inner vertex to another inner vertex. The letter `0` denotes all + other steps of the discovery, i.e. either discovering a leaf vertex or going + toward the root ("down"). Inner vertices are tagged with 'in' and leaves are tagged with 'lf'. Inner vertices are moreover labelled by integers, and leaves @@ -1654,102 +1661,150 @@ def _contour_and_graph_from_word(w): EXAMPLES:: - sage: from sage.graphs.generators.random import _contour_and_graph_from_word - sage: seq, G = _contour_and_graph_from_word([1,0,0,0,0,0]) + sage: from sage.graphs.generators.random import _contour_and_graph_from_words + sage: seq, G = _contour_and_graph_from_words([0, 0], [1, 0, 0, 0, 0, 0]) sage: seq [('in', 0), - ('in', 1), - ('lf', 1), - ('in', 1), - ('lf', 1), - ('in', 1), - ('in', 0), - ('lf', 0), + ('in', 3), + ('lf', 3), + ('in', 3), + ('lf', 3), + ('in', 3), ('in', 0), - ('lf', 0)] + ('in', 1), + ('in', 2)] sage: G - Graph on 2 vertices + Graph on 4 vertices - sage: from sage.graphs.generators.random import _auxiliary_random_word - sage: seq, G = _contour_and_graph_from_word(_auxiliary_random_word(20)) - sage: G.is_tree() - True + sage: from sage.graphs.generators.random import _auxiliary_random_forest_word + sage: _, G = _contour_and_graph_from_words([0, 1, 0, 0, 1, 0], _auxiliary_random_forest_word(20, 5)) # random + sage: len(G.faces()) + 2 sage: longw = [1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0] - sage: seq, G = _contour_and_graph_from_word(longw) + sage: _, G = _contour_and_graph_from_words([0, 0], longw) sage: G.get_embedding() - {0: [1], 1: [0, 2], 2: [1, 3, 4], 3: [2], 4: [2, 5, 6], 5: [4], 6: [4]} + {0: [1, 2, 3], + 1: [2, 0], + 2: [0, 1], + 3: [0, 4], + 4: [3, 5, 6], + 5: [4], + 6: [4, 7, 8], + 7: [6], + 8: [6]} """ - index = 0 # numbering of inner vertices - word = [('in', 0)] # initial vertex is inner - leaf_stack = [0, 0] # stack of leaves still to be created - inner_stack = [0] # stack of active inner nodes - edges = [] - embedding = {0: []} # records the planar embedding of the tree - for x in w: - if x == 1: # going up to a new inner vertex + k = (len(pendant_word)+4) // 2 + + index = 0 # numbering of inner vertices + word = [('in',0)] # the word representing the contour walk + + # start with the outer face, a cycle of length k + edges = [[i, (i+1) % k] for i in range(k)] + embedding = {i: [(i+1) % k, (i-1+k) % k] for i in range(k)} + + # add the pendant edges + for x in pendant_word: + if x: + word.extend([('lf', index), ('in', index)]) + else: index += 1 - embedding[index] = inner_stack[-1:] - embedding[inner_stack[-1]].append(index) - leaf_stack.extend([index, index]) - inner_stack.append(index) - edges.append(inner_stack[-2:]) word.append(('in', index)) + + # add trees + curr_word_pos = 0 + curr_forest_word_pos = 0 + while curr_forest_word_pos < len(forest_word): + x = forest_word[curr_forest_word_pos] + # insert a tree at current position + if x: + index += 1 + embedding[index] = [word[curr_word_pos][1]] + embedding[word[curr_word_pos][1]].append(index) + edges.append([word[curr_word_pos][1], index]) + # stack of leaves still to be created + leaf_stack = [index, index] + # stack of active inner nodes + inner_stack = [word[curr_word_pos][1], index] + word.insert(curr_word_pos+1, ('in', index)) + curr_word_pos += 1 + while len(inner_stack) > 1: + curr_forest_word_pos += 1 + x = forest_word[curr_forest_word_pos] + if x: + index += 1 + embedding[index] = inner_stack[-1:] + embedding[inner_stack[-1]].append(index) + leaf_stack.extend([index, index]) + inner_stack.append(index) + edges.append(inner_stack[-2:]) + word.insert(curr_word_pos+1, ('in', index)) + curr_word_pos += 1 + else: + # up and down to a new leaf + if leaf_stack and inner_stack[-1] == leaf_stack[-1]: + leaf_stack.pop() + word.insert(curr_word_pos+1, ('lf', inner_stack[-1])) + word.insert(curr_word_pos+2, ('in', inner_stack[-1])) + curr_word_pos += 2 + # going down to a known inner vertex + else: + inner_stack.pop() + word.insert(curr_word_pos+1, ('in', inner_stack[-1])) + curr_word_pos += 1 + # go to next insertion position else: - if leaf_stack and inner_stack[-1] == leaf_stack[-1]: # up and down to a new leaf - leaf_stack.pop() - word.extend([('lf', inner_stack[-1]), ('in', inner_stack[-1])]) - else: # going down to a known inner vertex - inner_stack.pop() - word.append(('in', inner_stack[-1])) + curr_word_pos += 1 + if word[curr_word_pos][0] == 'lf': + curr_word_pos += 1 + curr_forest_word_pos += 1 + G = Graph(edges, format='list_of_edges') G.set_embedding(embedding) - return word[:-1], G - + return word, G -def RandomTriangulation(n, set_position=False): +def RandomTriangulation(n, set_position=False, k=3): r""" - Return a random triangulation on `n` vertices. + Return a random inner triangulation of an outer face of degree ``k`` with + ``n`` vertices in total. - A triangulation is a planar graph all of whose faces are - triangles (3-cycles). + An inner triangulation is a plane graph all of whose faces (except the + outer/unbounded face) are triangles (3-cycles). INPUT: - - `n` -- an integer + - ``n`` -- the number of vertices of the graph - - ``set_position`` -- boolean (default ``False``) if set to ``True``, this + - ``k`` -- the size of the outer face + + - ``set_position`` -- boolean (default ``False``); if set to ``True``, this will compute coordinates for a planar drawing of the graph. OUTPUT: - A random triangulation chosen uniformly among the *rooted* triangulations on - `n` vertices. This is a planar graph and comes with a combinatorial - embedding. + A random graph chosen uniformly among the inner triangulations of a *rooted* + `k`-gon with `n` vertices (including the `k` vertices from the outer face). + This is a planar graph and comes with a combinatorial embedding. The + vertices of the root edge are labelled ``-1`` and ``-2`` and the outer face + is the face returned by :meth:`Graph.faces` in which ``-1`` and ``-2`` are + consecutive vertices in this order. Because some triangulations have nontrivial automorphism - groups, this may not be equal to the uniform distribution among unrooted - triangulations. + groups, this may not be equal to the uniform distribution among inner + triangulations of unrooted `k`-gons. ALGORITHM: - The algorithm is taken from [PS2006]_, section 2.1. + The algorithm is taken from [PS2006]_, Section 5. - Starting from a planar tree (represented by its contour as a - sequence of vertices), one first performs local closures, until no + Starting from a planar `k`-gonal forest (represented by its contour as a + sequence of vertices), one performs local closures, until no one is possible. A local closure amounts to replace in the cyclic - contour word a sequence ``in1,in2,in3,lf,in3`` by - ``in1,in3``. After all local closures are done, one has reached - the partial closure, as in [PS2006]_, figure 5 (a). - - Then one has to perform complete closure by adding two more - vertices, in order to reach the situation of [PS2006]_, figure 5 - (b). For this, it is necessary to find inside the final contour - one of the two subsequences ``lf,in,lf``. + contour word a sequence ``in1, in2, in3, lf, in3`` by + ``in1, in3``. At every step of the algorithm, newly created edges are recorded in a graph, which will be returned at the end. - The combinatorial embedding is also computed and recorded in the output graph. @@ -1769,15 +1824,29 @@ def RandomTriangulation(n, set_position=False): sage: G.plot(vertex_size=0, vertex_labels=False) Graphics object consisting of 13 graphics primitives + sage: H = graphs.RandomTriangulation(7, k=5) + sage: sorted(len(f) for f in H.faces()) + [3, 3, 3, 3, 3, 3, 3, 5] + TESTS:: sage: G.get_embedding() is not None True + + sage: graphs.RandomTriangulation(3, k=4) + Traceback (most recent call last): + ... + ValueError: The number 'n' of vertices must be at least the size 'k' of the outer face. + sage: graphs.RandomTriangulation(3, k=2) + Traceback (most recent call last): + ... + ValueError: The size 'k' of the outer face must be at least 3. + sage: for i in range(10): - ....: g = graphs.RandomTriangulation(30) + ....: g = graphs.RandomTriangulation(30) # random ....: assert g.is_planar() - sage: for i in range(10): - ....: g = graphs.RandomTriangulation(10) + sage: for k in range(3, 10): + ....: g = graphs.RandomTriangulation(10, k=k) # random ....: assert g.is_planar(on_embedding=g.get_embedding()) REFERENCES: @@ -1788,16 +1857,21 @@ def RandomTriangulation(n, set_position=False): http://www.lix.polytechnique.fr/~poulalho/Articles/PoSc_Algorithmica06.pdf """ - if n < 3: - raise ValueError('only defined for n >= 3') - w = _auxiliary_random_word(n - 2) - word, graph = _contour_and_graph_from_word(w) - edges = [] + if k < 3: + raise ValueError("The size 'k' of the outer face must be at least 3.") + if n < k: + raise ValueError("The number 'n' of vertices must be at least the size " + "'k' of the outer face.") + from sage.misc.prandom import shuffle + pendant_word = [0] * (k-1) + [1] * (k-3) + shuffle(pendant_word) + forest_word = _auxiliary_random_forest_word(n-k, k) + word, graph = _contour_and_graph_from_words(pendant_word, forest_word) + edges = [] embedding = graph.get_embedding() - # 'partial closures' described in 2.1 of [PS2006]_. - pattern = ['in', 'in', 'in', 'lf', 'in'] + pattern = ['in', 'in', 'in', 'lf', 'in'] # 'partial closures' def rotate_word_to_next_occurrence(word): """ @@ -1826,56 +1900,12 @@ def rotate_word_to_next_occurrence(word): break graph.add_edges(edges) - # This is the end of partial closure. - - # There remains to add two new vertices a and b. - a = -1 - b = -2 - graph.add_edge((a, b)) - - # Every remaining 'lf' vertex is linked either to a or to b. - # Switching a/b happens when one meets the sequence 'lf','in','lf'. - a_or_b = a - embedding[a] = [] - embedding[b] = [] - last_lf_occurrence = -42 - change = {} - for x in word: - last_lf_occurrence -= 1 - if x[0] == 'lf': - if last_lf_occurrence == -2: - change[a_or_b] = x[1] - a_or_b = b if a_or_b == a else a - graph.add_edge((a_or_b, x[1])) - embedding[a_or_b].insert(0, x[1]) - last_lf_occurrence = 0 - - # conjugates the embeddings of a and b - # in a way that helps to complete the embedding - for a_or_b in [a, b]: - emba = embedding[a_or_b] - idx = emba.index(change[a_or_b]) - embedding[a_or_b] = emba[idx:] + emba[:idx] - embedding[a].append(b) - embedding[b].append(a) - - # completes the embedding by inserting missing half-edges - for a_or_b in [a, b]: - emb = embedding[a_or_b] - for i, v in enumerate(emb[:-1]): - if i == 0: - embedding[v].insert(embedding[v].index(emb[1]) + 1, a_or_b) - else: - embedding[v].insert(embedding[v].index(emb[i - 1]), a_or_b) - - assert graph.num_edges() == 3 * (n - 2) - assert graph.num_verts() == n - graph.set_embedding(embedding) - + graph.relabel({0: -2, 1: -1}) + assert graph.num_edges() == 3*n - 3 - k + assert graph.num_verts() == n if set_position: graph.layout(layout="planar", save_pos=True) - return graph diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 10afc1a7815..436f0aea224 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -5153,29 +5153,40 @@ def set_planar_positions(self, test = False, **layout_options): else: return - def layout_planar(self, set_embedding=False, on_embedding=None, external_face=None, test=False, circular=False, **options): + def layout_planar(self, set_embedding=False, on_embedding=None, + external_face=None, test=False, circular=False, + **options): """ Compute a planar layout of the graph using Schnyder's algorithm. + If ``set_embedding`` is set, a new combinatorial embedding is computed + for the layout. Otherwise: if ``on_embedding`` is provided, then that + combinatorial embedding is used for the layout. Otherwise: if a + combinatorial embedding is set to the instance field variable of the + graph (e.g. using + :meth:`~sage/graphs/generic_graph.GenericGraph.set_embedding`), then + that one is used, and if no combinatorial embedding is set, then one is + computed. + If the graph is not planar, an error is raised. INPUT: - ``set_embedding`` -- boolean (default: ``False``); whether to set the - combinatorial embedding used (see - :meth:`~sage/graphs/generic_graph.GenericGraph.get_embedding`) + instance field variable that contains a combinatorial embedding to the + combinatorial embedding used for the planar layout (see + :meth:`~sage/graphs/generic_graph.GenericGraph.get_embedding`) - ``on_embedding`` -- dictionary (default: ``None``); provide a - combinatorial embedding + combinatorial embedding - ``external_face`` -- ignored - ``test`` -- boolean (default: ``False``); whether to perform sanity - tests along the way + tests along the way - ``circular`` -- ignored - EXAMPLES:: sage: g = graphs.PathGraph(10) @@ -5192,6 +5203,21 @@ def layout_planar(self, set_embedding=False, on_embedding=None, external_face=No Traceback (most recent call last): ... ValueError: Complete graph is not a planar graph + + TESTS: + + Check the dependence of the computed position on the given combinatorial + embedding (:trac:`28152`):: + + sage: G = Graph([[0, 1, 2, 3], [[0, 1], [0, 2], [0, 3]]]) + sage: G.set_embedding({0: [1, 2, 3], 1: [0], 2: [0], 3: [0]}) + sage: pos1 = G.layout('planar', save_pos=True) + sage: G._check_embedding_validity() + True + sage: G.set_embedding({0: [3, 2, 1], 1: [0], 2: [0], 3: [0]}) + sage: pos2 = G.layout('planar', save_pos=True) + sage: pos1 == pos2 + False """ from sage.graphs.graph import Graph from sage.graphs.schnyder import _triangulate, _normal_label, _realizer, _compute_coordinates @@ -5205,7 +5231,7 @@ def layout_planar(self, set_embedding=False, on_embedding=None, external_face=No if set_embedding: if not G.is_planar(set_embedding=True): raise ValueError('%s is not a planar graph'%self) - embedding_copy = G._embedding + embedding_copy = {v: neighbors[:] for v, neighbors in G._embedding.items()} else: if on_embedding is not None: G._check_embedding_validity(on_embedding,boolean=False) @@ -5216,7 +5242,7 @@ def layout_planar(self, set_embedding=False, on_embedding=None, external_face=No if G._check_embedding_validity(): if not G.is_planar(on_embedding=G._embedding): raise ValueError('%s has nonplanar _embedding attribute. Try putting set_embedding=True'%self) - embedding_copy = G._embedding + embedding_copy = {v: neighbors[:] for v, neighbors in G._embedding.items()} else: raise ValueError('provided embedding is not a valid embedding for %s. Try putting set_embedding=True'%self) else: @@ -5251,13 +5277,14 @@ def layout_planar(self, set_embedding=False, on_embedding=None, external_face=No # Optional error-checking if test: - G.is_planar(set_embedding=True) # to get new embedding + if G._check_embedding_validity(): + if not G.is_planar(on_embedding=G._embedding): + raise ValueError('%s has nonplanar _embedding attribute. Try putting set_embedding=True' % self) test_faces = G.faces(G._embedding) for face in test_faces: if len(face) != 3: raise RuntimeError('BUG: Triangulation returned face: %s'%face) - G.is_planar(set_embedding=True) faces = G.faces(G._embedding) # Assign a normal label to the graph label = _normal_label(G, G._embedding, faces[0]) diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index 9e765ae91ea..ae7ba3a0ee3 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -34,7 +34,8 @@ def _triangulate(g, comb_emb): Given a connected graph g with at least 3 vertices and a planar combinatorial embedding comb_emb of g, modify g in place to form a graph whose faces are - all triangles, and return the set of newly created edges. + all triangles, and return the set of newly created edges. Also ``comb_emb`` + is updated in place. The simple way to triangulate a face is to just pick a vertex and draw an edge from that vertex to every other vertex in the face. Think that this @@ -66,8 +67,9 @@ def _triangulate(g, comb_emb): sage: g = graphs.PathGraph(3) sage: g.is_planar(set_embedding=True) True - sage: _triangulate(g, g._embedding) - [(0, 2)] + sage: new_edges = _triangulate(g, g._embedding) + sage: [sorted(e) for e in new_edges] + [[0, 2]] """ # first make sure that the graph has at least 3 vertices, and that it is connected if g.order() < 3: @@ -75,20 +77,8 @@ def _triangulate(g, comb_emb): if not g.is_connected(): raise NotImplementedError("_triangulate() only knows how to handle connected graphs.") - if g.order() == 3 and len(g.edges()) == 2: # if g is o--o--o - vertex_list = g.vertices() - if len(g.neighbors(vertex_list[0])) == 2: # figure out which of the vertices already has two neighbors - new_edge = (vertex_list[1], vertex_list[2]) # and connect the other two together. - elif len(g.neighbors(vertex_list[1])) == 2: - new_edge = (vertex_list[0], vertex_list[2]) - else: - new_edge = (vertex_list[0], vertex_list[1]) - - g.add_edge(new_edge) - return [new_edge] - - # At this point we know that the graph is connected, has at least 3 vertices, and - # that it is not the graph o--o--o. This is where the real work starts. + # At this point we know that the graph is connected, has at least 3 + # vertices. This is where the real work starts. faces = g.faces(comb_emb) # We start by finding all of the faces of this embedding. @@ -103,9 +93,12 @@ def _triangulate(g, comb_emb): if len(face) == 3: continue # This face is already triangulated elif len(face) == 4: # In this special case just add diagonal edge to square - new_face = (face[1][1], face[0][0]) - if g.has_edge(new_face): - new_face = (face[2][1], face[1][0]) + u, v, w, x = (e[0] for e in face) + if w == u or g.has_edge(w,u): + u, v, w, x = v, w, x, u + new_face = (w, u) + comb_emb[w].insert(comb_emb[w].index(x), u) + comb_emb[u].insert(comb_emb[u].index(v), w) g.add_edge(new_face) edges_added.append(new_face) else: @@ -122,6 +115,8 @@ def _triangulate(g, comb_emb): g.add_edge(new_edge) edges_added.append(new_edge) + comb_emb[new_edge[0]].insert(comb_emb[new_edge[0]].index((face + new_face)[i + 2][1]), new_edge[1]) + comb_emb[new_edge[1]].insert(comb_emb[new_edge[1]].index(face[i][1]), new_edge[0]) new_face.append((new_edge[1], new_edge[0])) i += 2 if i != N: @@ -172,13 +167,15 @@ def _normal_label(g, comb_emb, external_face): sage: tn = _normal_label(g, g._embedding, faces[0]) sage: _realizer(g, tn) ({0: []}, - (0, 1, 2)) + (1, 0, 2)) """ contracted = [] contractible = [] labels = {} + # For now we will not take the order of the outer face into account. + # We will correct this in the end of this function. external_vertices = sorted([external_face[0][0], external_face[1][0], external_face[2][0]]) @@ -326,6 +323,21 @@ def _normal_label(g, comb_emb, external_face): for w in new_neighbors: g.add_edge((v, w)) + # Up to this point we did not take the order of the external face into + # account. Since the combinatorial embedding of a triangulation is unique up + # to the choice of the outer face and reflection, this might lead to a + # reflection of the Schnyder drawing resulting from this labeling which is + # not conformal with comb_emb any longer. Therefore, we might have to swap + # the labels 1 and 2. + if (v1, v2) in external_face: + for u in labels: + for v, w in labels[u]: + if labels[u][v, w] == 1: + labels[u][v, w] = 2 + elif labels[u][v, w] == 2: + labels[u][v, w] = 1 + v1, v2 = v2, v1 + return labels, (v1, v2, v3) @@ -379,7 +391,7 @@ def _realizer(g, x, example=False): sage: tn = _normal_label(g, g._embedding, faces[0]) sage: _realizer(g, tn) ({0: []}, - (0, 1, 2)) + (1, 0, 2)) """ normal_labeling, (v1, v2, v3) = x @@ -467,7 +479,7 @@ def _compute_coordinates(g, x): sage: r = _realizer(g, tn) sage: _compute_coordinates(g,r) sage: g.get_pos() - {0: [5, 1], 1: [0, 5], 2: [1, 0], 3: [1, 3], 4: [2, 1], 5: [2, 2], 6: [3, 2]} + {0: [0, 5], 1: [5, 1], 2: [1, 0], 3: [4, 1], 4: [1, 1], 5: [2, 2], 6: [1, 4]} """ tree_nodes, (v1, v2, v3) = x @@ -703,8 +715,11 @@ def minimal_schnyder_wood(graph, root_edge=None, minimal=True, check=True): OUTPUT: - a planar graph, with edges oriented and colored. The three outer - edges of the initial graph are removed. + A planar graph, with edges oriented and colored. The three outer + edges of the initial graph are removed. For the three outer vertices the + list of the neighbors stored in the combinatorial embedding is in the order + of the incident edges between the two incident (and removed) outer edges, + and not a cyclic shift of it. The algorithm is taken from [Brehm2000]_ (section 4.2). @@ -839,9 +854,13 @@ def minimal_schnyder_wood(graph, root_edge=None, minimal=True, check=True): def relabel(w): return -3 if w == c else w - emb = {relabel(v): [relabel(u) for u in emb[v] if not(u in [a, b, c] and - v in [a, b, c])] - for v in graph} + emb = {relabel(v): [relabel(u) for u in emb[v]] for v in graph} + for u, v, w in (a, b, -3), (b, -3, a), (-3, a, b): + idx = emb[u].index(v) + if idx == 0: + emb[u] = emb[u][1:-1] + else: + emb[u] = emb[u][idx+1:] + emb[u][:idx-1] new_g.set_embedding(emb) return new_g diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 68c95279e63..d2cc2b6cebc 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -1037,7 +1037,7 @@ def _sage_(self, locals={}): - numbers, i.e. integers, floats, complex numbers; - functions and named constants also present in Sage, where: - Sage knows how to translate the function or constant's name - from Giac's naming scheme through the symbols_table, or + from Giac's naming scheme through the ``symbol_table``, or - you provide a translation dictionary ``locals``. New conversions can be added using Pynac's ``register_symbol``. @@ -1063,7 +1063,7 @@ def _sage_(self, locals={}): sage: ex._sage_({'myFun': sin}) sin(x) - Same but by adding a new entry to the ``symbols_table``:: + Same but by adding a new entry to the ``symbol_table``:: sage: ex = giac('myFun(x)') sage: sage.libs.pynac.pynac.register_symbol(sin, {'giac':'myFun'}) diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py index f8b16f4b3e8..8b8901d62f8 100644 --- a/src/sage/interfaces/kash.py +++ b/src/sage/interfaces/kash.py @@ -13,8 +13,9 @@ It is not enough to just have KASH installed on your computer. Note that the KASH Sage package is currently only available for Linux -and OSX. If you need Windows, support contact me -(wstein@gmail.com). +and OSX. If you need Windows support, contact the +`sage-support `_ +mailing list. The KASH interface offers three pieces of functionality: diff --git a/src/sage/libs/ntl/ntlwrap_impl.h b/src/sage/libs/ntl/ntlwrap_impl.h index f34c61a5385..083bab824cc 100644 --- a/src/sage/libs/ntl/ntlwrap_impl.h +++ b/src/sage/libs/ntl/ntlwrap_impl.h @@ -8,6 +8,7 @@ #include #include +#include //////// ZZ ////////// @@ -24,16 +25,9 @@ static CYTHON_INLINE int ZZ_to_int(const ZZ* x) Joel B. Mohler moved the ZZX_getitem_as_mpz code out to this function (2007-03-13) */ static void ZZ_to_mpz(mpz_t output, const struct ZZ* x) { - unsigned char stack_bytes[4096]; - unsigned long size = NumBytes(*x); - int use_heap = (size > sizeof(stack_bytes)); - unsigned char* bytes = use_heap ? (unsigned char*) malloc(size) : stack_bytes; - BytesFromZZ(bytes, *x, size); - mpz_import(output, size, -1, 1, 0, 0, bytes); + mpz_import(output, x->size(), -1, sizeof(mp_limb_t), 0, 0, ZZ_limbs_get(*x)); if (sign(*x) < 0) mpz_neg(output, output); - if (use_heap) - free(bytes); } /* Copies the mpz_t into the ZZ diff --git a/src/sage/libs/singular/option.pyx b/src/sage/libs/singular/option.pyx index 33b76b890f8..6243a179646 100644 --- a/src/sage/libs/singular/option.pyx +++ b/src/sage/libs/singular/option.pyx @@ -346,7 +346,7 @@ cdef class LibSingularOptions(LibSingularOptions_abstract): ``res``, ``slimgb``, ``sres``, ``std``, ``stdfglm``, ``stdhilb``, ``syz``. - - `red_sb`` or ``redSB`` - computes a reduced standard basis in + - ``red_sb`` or ``redSB`` - computes a reduced standard basis in any standard basis computation. - ``red_tail`` or ``redTail`` - reduction of the tails of diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 2f4a1c3b916..8ee0c4977df 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -2848,15 +2848,15 @@ def contract(self, *args): Check 1: components with respect to the manifold's default frame (``eU``):: - sage: all([bool(s[i,j] == sum(a[i,k]*b[k,j] for k in M.irange())) - ....: for j in M.irange()] for i in M.irange()) + sage: all(bool(s[i,j] == sum(a[i,k]*b[k,j] for k in M.irange())) + ....: for i in M.irange() for j in M.irange()) True Check 2: components with respect to the frame ``eV``:: - sage: all([bool(s[eV,i,j] == sum(a[eV,i,k]*b[eV,k,j] - ....: for k in M.irange())) - ....: for j in M.irange()] for i in M.irange()) + sage: all(bool(s[eV,i,j] == sum(a[eV,i,k]*b[eV,k,j] + ....: for k in M.irange())) + ....: for i in M.irange() for j in M.irange()) True Instead of the explicit call to the method :meth:`contract`, one diff --git a/src/sage/misc/viewer.py b/src/sage/misc/viewer.py index 7bb1c42918d..4a6c70434fc 100644 --- a/src/sage/misc/viewer.py +++ b/src/sage/misc/viewer.py @@ -76,15 +76,13 @@ def default_viewer(viewer=None): elif os.uname()[0][:6] == 'CYGWIN': # Windows is also easy, since it has a system for - # determining what opens things. - # Bobby Moreti provided the following. - if not 'BROWSER' in os.environ: - systemroot = os.environ['SYSTEMROOT'].replace(':','/').replace('\\','') - systemroot = '/cygdrive/' + systemroot - BROWSER = '%s/system32/rundll32.exe url.dll,FileProtocolHandler'%\ - systemroot - else: - BROWSER = os.environ['BROWSER'] + # determining what opens things. However, on Cygwin we + # should access this through the 'cygstart' program rather + # than trying to run rundll32 directly, which on newer Windows versions + # has security implications + # Indeed, on Sage for Windows, BROWSER is set by default to cygstart, + # so we just canonize that here + BROWSER = os.environ.get('BROWSER', 'cygstart') DVI_VIEWER = BROWSER PDF_VIEWER = BROWSER PNG_VIEWER = BROWSER diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 779d83ffd3a..21d21afd3d3 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -207,13 +207,13 @@ def display(self, args): for allowed values. If the mode is ``ascii_art``, it can optionally be followed by a width. - How to use: if you want activate the ASCII art mod:: + How to use: if you want to activate the ASCII art mode:: sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.run_cell('%display ascii_art') - That means you don't have to use :func:`ascii_art` to get an ASCII art + That means you do not have to use :func:`ascii_art` to get an ASCII art output:: sage: shell.run_cell("i = var('i')") @@ -221,7 +221,7 @@ def display(self, args): 10 9 8 7 6 5 4 3 2 100*x + 81*x + 64*x + 49*x + 36*x + 25*x + 16*x + 9*x + 4*x + x - Then when you want return in 'textual mode':: + Then when you want to return to 'textual mode':: sage: shell.run_cell('%display text plain') sage: shell.run_cell('%display plain') # shortcut for "text plain" diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 141477a297b..ff55f6814bb 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -127,7 +127,7 @@ Check that :trac:`19839` is fixed:: :trac:`24621`:: sage: CBF(NumberField(polygen(QQ, 'y')^3 + 20, 'a', embedding=CC(1.35,2.35)).gen()) - [1.357208808297453 +/- ...e-16] + [2.350754612451197 +/- ...e-16]*I + [1.35720880829745...] + [2.35075461245119...]*I Classes and Methods =================== @@ -540,12 +540,16 @@ class ComplexBallField(UniqueRepresentation, Field): sage: CBF.convert_map_from(QuadraticField(-2)) Conversion via _acb_ method map: ... + sage: CBF.coerce_map_from(NumberField(x^7 + 2, 'a', + ....: embedding=QQbar(-2)^(1/7))) + Conversion via _acb_ method map: + ... """ if isinstance(other, RealBallField): return other._prec >= self._prec elif isinstance(other, ComplexBallField): return other._prec >= self._prec - elif isinstance(other, number_field.NumberField_quadratic): + elif isinstance(other, number_field.NumberField_generic): emb = other.coerce_embedding() return emb is not None and self.has_coerce_map_from(emb.codomain()) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 1a7e3d3b29a..80e123cdb6b 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -1131,7 +1131,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): [406444, 406444, 406444, 406444, 406444, 406444, 406444, 406444] sage: v = R(169).sqrt(all=True); min(v), -max(v), len(v) (13, 13, 104) - sage: all([x^2==169 for x in v]) + sage: all(x^2 == 169 for x in v) True :: @@ -2895,7 +2895,7 @@ cdef class IntegerMod_int(IntegerMod_abstract): [406444, 406444, 406444, 406444, 406444, 406444, 406444, 406444] sage: v = R(169).sqrt(all=True); min(v), -max(v), len(v) (13, 13, 104) - sage: all([x^2==169 for x in v]) + sage: all(x^2 == 169 for x in v) True Modulo a power of 2:: @@ -3899,9 +3899,9 @@ cpdef square_root_mod_prime(IntegerMod_abstract a, p=None): :: sage: from sage.rings.finite_rings.integer_mod import square_root_mod_prime # sqrt() uses brute force for small p - sage: all([square_root_mod_prime(a*a)^2 == a*a - ....: for p in prime_range(100) - ....: for a in Integers(p)]) + sage: all(square_root_mod_prime(a*a)^2 == a*a + ....: for p in prime_range(100) + ....: for a in Integers(p)) True """ if not a or a.is_one(): @@ -3985,9 +3985,9 @@ def lucas_q1(mm, IntegerMod_abstract P): TESTS:: sage: from sage.rings.finite_rings.integer_mod import lucas_q1 - sage: all([lucas_q1(k, a) == BinaryRecurrenceSequence(a, -1, 2, a)(k) - ....: for a in Integers(23) - ....: for k in range(13)]) + sage: all(lucas_q1(k, a) == BinaryRecurrenceSequence(a, -1, 2, a)(k) + ....: for a in Integers(23) + ....: for k in range(13)) True """ if mm == 0: @@ -4053,8 +4053,8 @@ def lucas(k, P, Q=1, n=None): sage: p = randint(0,100000) sage: q = randint(0,100000) sage: n = randint(0,100) - sage: all([lucas(k,p,q,n)[0] == Mod(lucas_number2(k,p,q),n) - ....: for k in Integers(20)]) + sage: all(lucas(k,p,q,n)[0] == Mod(lucas_number2(k,p,q),n) + ....: for k in Integers(20)) True sage: from sage.rings.finite_rings.integer_mod import lucas sage: p = randint(0,100000) @@ -4201,7 +4201,7 @@ cdef class IntegerMod_to_IntegerMod(IntegerMod_hom): sage: [type(R(0)) for R in Rs] [, , , , , ] sage: fs = [IntegerMod_to_IntegerMod(S, R) for R in Rs for S in Rs if S is not R and S.order() > R.order()] - sage: all([f(-1) == f.codomain()(-1) for f in fs]) + sage: all(f(-1) == f.codomain()(-1) for f in fs) True sage: [f(-1) for f in fs] [2, 2, 2, 2, 2, 728, 728, 728, 728, 177146, 177146, 177146, 43046720, 43046720, 10460353202] diff --git a/src/sage/rings/function_field/constructor.py b/src/sage/rings/function_field/constructor.py index aa35dbd75c1..3d713b45653 100644 --- a/src/sage/rings/function_field/constructor.py +++ b/src/sage/rings/function_field/constructor.py @@ -191,7 +191,7 @@ def create_object(self,version,key,**extra_args): k = base_field.constant_field() if k.is_finite(): # then we are in positive characteristic # irreducible and separable - if f.is_irreducible() and not all([e % k.characteristic() == 0 for e in f.exponents()]): + if f.is_irreducible() and not all(e % k.characteristic() == 0 for e in f.exponents()): # monic and integral if f.is_monic() and all(e in base_field.maximal_order() for e in f.coefficients()): return function_field.FunctionField_global_integral(f, names) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index c8f36b4463d..01d15358a5e 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1326,7 +1326,7 @@ def monic_integral_model(self, names=None): to_ret = self.hom( [L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())] ) return ret, from_ret, to_ret else: - if self.polynomial().is_monic() and all([c.denominator().is_one() for c in self.polynomial()]): + if self.polynomial().is_monic() and all(c.denominator().is_one() for c in self.polynomial()): # self is already monic and integral if names is None or names == (): names = (self.variable_name(),) diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 2f0f32b66c3..9b3836a16d1 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -882,25 +882,25 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: ilist = [-4r,-1r,0r,1r,2r,5r] sage: llist = [-12345678901234567890123456788r, 12345678901234567890123456788r, 12345678901234567890123456900r] sage: flist = [-21.8r, -1.2r, -.000005r, 0.0r, .999999r, 1000000000000.0r] - sage: all([(a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a == b) == (RR(a) == RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a == b) == (RR(a) == RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a <= b) == (RR(a) <= RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a <= b) == (RR(a) <= RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a >= b) == (RR(a) >= RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a >= b) == (RR(a) >= RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a != b) == (RR(a) != RR(b)) for (a, b) in zip(Ilist, ilist)]) + sage: all((a != b) == (RR(a) != RR(b)) for (a, b) in zip(Ilist, ilist)) True - sage: all([(a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, llist)]) + sage: all((a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, llist)) True - sage: all([(a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, llist)]) + sage: all((a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, llist)) True - sage: all([(a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, flist)]) + sage: all((a < b) == (RR(a) < RR(b)) for (a, b) in zip(Ilist, flist)) True - sage: all([(a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, flist)]) + sage: all((a > b) == (RR(a) > RR(b)) for (a, b) in zip(Ilist, flist)) True Verify that :trac:`12149` was fixed (and the fix is consistent diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index bedc46cca4d..4ddca9ae7be 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -1815,6 +1815,50 @@ cdef class NumberFieldElement(FieldElement): else: return R(R.complex_field()(self)) + def _acb_(self, R): + r""" + Convert this number field element to a complex ball. + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: NF. = NumberField(x^7 + 2, embedding=CC(0.99, 0.47)) + sage: CBF(a) + [0.9947502791976272 +/- 1.09e-17] + [0.4790464865132800 +/- 1.46e-17]*I + sage: NF. = NumberField(x^7 + 2, embedding=QQbar(-2)^(1/7)) + sage: CBF(a) + [0.9947502791976272 +/- 1.09e-17] + [0.4790464865132800 +/- 1.46e-17]*I + sage: NF. = NumberField(x^7 + 2) + sage: CBF(NF(3)) + 3.000000000000000 + sage: CBF(a) + Traceback (most recent call last): + ... + TypeError: Unable to coerce a to a rational + """ + from sage.rings.complex_arb import ComplexBallField + if self.parent().coerce_embedding() is None: + return R(self.base_ring()(self)) + coef = self._coefficients() + if not coef: + return R.zero() + cdef int ini_prec = R.precision() + cdef int max_prec = ini_prec + max(mpz_sizeinbase(mpq_numref(( c).value), 2) + for c in coef) + cdef int prec = ini_prec + 2*len(coef) + gen = self._parent.gen_embedding() + while True: + C = ComplexBallField(prec) + g = C(gen) + val = C(coef[0]) + p = C.one() + for c in coef[1:]: + p *= g + val += C(c)*p + if prec > max_prec or val.accuracy() >= ini_prec - 4: + return R(val) + prec *= 2 + def __float__(self): """ EXAMPLES:: @@ -3030,7 +3074,7 @@ cdef class NumberFieldElement(FieldElement): h ^= mpz_pythonhash(z) + ( 2701463124188384701) + (h << 16) + (h >> 2) ZZ_to_mpz(z, &self.__denominator) - # magic number below is floor((1+sqrt(5)) * 2^63) + # magic number below is floor((1+sqrt(5)) * 2^61) h += (mpz_pythonhash(z) - 1) * ( 7461864723258187525) mpz_clear(z) @@ -3053,14 +3097,20 @@ cdef class NumberFieldElement(FieldElement): sage: (b^2 + 1)._coefficients() [1, 0, 1] """ - cdef list coeffs = [] - cdef Integer den = (ZZ)._coerce_ZZ(&self.__denominator) - cdef Integer numCoeff + cdef Rational coeff cdef int i - for i from 0 <= i <= ZZX_deg(self.__numerator): - numCoeff = Integer.__new__(Integer) - ZZX_getitem_as_mpz(numCoeff.value, &self.__numerator, i) - coeffs.append( numCoeff / den ) + cdef mpz_t den + mpz_init(den) + ZZ_to_mpz(den, &self.__denominator) + cdef int size = ZZX_deg(self.__numerator) + 1 + cdef list coeffs = [None]*size + for i in range(size): + coeff = Rational.__new__(Rational) + ZZX_getitem_as_mpz(mpq_numref(coeff.value), &self.__numerator, i) + mpz_set(mpq_denref(coeff.value), den) + mpq_canonicalize(coeff.value) + coeffs[i] = coeff + mpz_clear(den) return coeffs cdef void _ntl_coeff_as_mpz(self, mpz_t z, long i): diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 9cc36352d3a..fe80f111b63 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -2024,7 +2024,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): [a + (2*a^3 + 2*a^2 + 3*a + 4)*5 + (4*a^3 + 3*a^2 + 3*a + 2)*5^2 + (4*a^2 + 2*a + 2)*5^3 + O(5^4), (3*a^3 + 3*a^2 + 2*a + 1) + (a^3 + 4*a^2 + 1)*5 + (a^2 + 4*a + 4)*5^2 + O(5^3), (4*a^3 + 2*a^2 + a + 1) + (2*a^3 + 2*a^2 + 2*a + 4)*5 + O(5^2), (a^3 + a^2 + a + 4) + O(5)] sage: sum([c * 5^i for i, c in enumerate(E)]) a + O(5^4) - sage: all([c^625 == c for c in E]) + sage: all(c^625 == c for c in E) True sage: S. = ZZ[] @@ -2034,7 +2034,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): [1 + O(w^9), 5 + 5*w^3 + w^6 + 4*w^7 + O(w^8), 3 + 3*w^3 + O(w^7), 3 + 3*w^3 + O(w^6), O(w^5), 4 + 5*w^3 + O(w^4), 3 + O(w^3), 6 + O(w^2), 6 + O(w)] sage: sum([w^i*L[i] for i in range(9)]) == b True - sage: all([L[i]^(7^3) == L[i] for i in range(9)]) + sage: all(L[i]^(7^3) == L[i] for i in range(9)) True sage: L = W(3).teichmuller_expansion(); L diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 81c2c1e4184..a09cbe246d6 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -2901,7 +2901,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): [a + (2*a^3 + 2*a^2 + 3*a + 4)*5 + (4*a^3 + 3*a^2 + 3*a + 2)*5^2 + (4*a^2 + 2*a + 2)*5^3 + O(5^4), (3*a^3 + 3*a^2 + 2*a + 1) + (a^3 + 4*a^2 + 1)*5 + (a^2 + 4*a + 4)*5^2 + O(5^3), (4*a^3 + 2*a^2 + a + 1) + (2*a^3 + 2*a^2 + 2*a + 4)*5 + O(5^2), (a^3 + a^2 + a + 4) + O(5)] sage: sum([c * 5^i for i, c in enumerate(E)]) a + O(5^4) - sage: all([c^625 == c for c in E]) + sage: all(c^625 == c for c in E) True sage: S. = ZZ[] @@ -2911,7 +2911,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): [1 + O(w^9), 5 + 5*w^3 + w^6 + 4*w^7 + O(w^8), 3 + 3*w^3 + O(w^7), 3 + 3*w^3 + O(w^6), O(w^5), 4 + 5*w^3 + O(w^4), 3 + O(w^3), 6 + O(w^2), 6 + O(w)] sage: sum([w^i*L[i] for i in range(9)]) == b True - sage: all([L[i]^(7^3) == L[i] for i in range(9)]) + sage: all(L[i]^(7^3) == L[i] for i in range(9)) True sage: L = W(3).teichmuller_expansion(); L diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 152a880231d..fd758bb1bff 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1357,7 +1357,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): [a + (2*a^3 + 2*a^2 + 3*a + 4)*5 + (4*a^3 + 3*a^2 + 3*a + 2)*5^2 + (4*a^2 + 2*a + 2)*5^3, (3*a^3 + 3*a^2 + 2*a + 1) + (a^3 + 4*a^2 + 1)*5 + (a^2 + 4*a + 4)*5^2 + (4*a^2 + a + 3)*5^3, (4*a^3 + 2*a^2 + a + 1) + (2*a^3 + 2*a^2 + 2*a + 4)*5 + (3*a^3 + 2*a^2 + a + 1)*5^2 + (a^3 + a^2 + 2)*5^3, (a^3 + a^2 + a + 4) + (3*a^3 + 1)*5 + (3*a^3 + a + 2)*5^2 + (3*a^3 + 3*a^2 + 3*a + 1)*5^3] sage: sum([c * 5^i for i, c in enumerate(E)]) a - sage: all([c^625 == c for c in E]) + sage: all(c^625 == c for c in E) True sage: S. = ZZ[] @@ -1375,7 +1375,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): 6 + w^3 + 5*w^7] sage: sum([w^i*L[i] for i in range(len(L))]) == b True - sage: all([L[i]^(7^3) == L[i] for i in range(9)]) + sage: all(L[i]^(7^3) == L[i] for i in range(9)) True sage: L = W(3).teichmuller_expansion(); L diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 181b0d17562..b853eab734f 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -2111,7 +2111,7 @@ cdef class MPolynomial(CommutativeRingElement): def reduced_form(self, **kwds): r""" - Returns a reduced form of this polynomial. + Return a reduced form of this polynomial. The algorithm is from Stoll and Cremona's "On the Reduction Theory of Binary Forms" [CS2003]_. This takes a two variable homogenous polynomial and @@ -2123,7 +2123,7 @@ cdef class MPolynomial(CommutativeRingElement): This reduction should also minimize the sum of the squares of the coefficients, but this is not always the case. By default the coefficient minimizing algorithm in [HS2018]_ is applied. The coefficients can be minimized - either with respect to the sum of their squares of the maximum of their + either with respect to the sum of their squares or the maximum of their global heights. A portion of the algorithm uses Newton's method to find a solution to @@ -2131,7 +2131,7 @@ cdef class MPolynomial(CommutativeRingElement): in the upper half plane, the function will use the less precise `z_0` covariant from the `Q_0` form as defined on page 7 of [CS2003]_. Additionally, if this polynomial has - a root with multiplicity at lease half the total degree of the polynomial, + a root with multiplicity at least half the total degree of the polynomial, then we must also use the `z_0` covariant. See [CS2003]_ for details. Note that, if the covariant is within ``error_limit`` of the boundary diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 535d0f50c6e..8849a2aa949 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2644,12 +2644,14 @@ def hilbert_polynomial(self, algorithm='sage'): TESTS: - Check that :trac:`27483` is fixed:: + Check that :trac:`27483` and :trac:`28110` are fixed:: sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3, x*y^2, y^4, x^2*y*z, y^3*z, x^2*z^2, x*y*z^2, x*z^3]) sage: I.hilbert_polynomial(algorithm='singular') 3 + sage: I.hilbert_polynomial() + 3 Check that this method works over QQbar (:trac:`25351`):: @@ -2671,10 +2673,12 @@ def hilbert_polynomial(self, algorithm='sage'): s = denom[0][1] # this is the pole order of the Hilbert-Poincaré series at t=1 else: return t.parent().zero() + # we assume the denominator of the Hilbert series is of the form (1-t)^s, scale if needed + if hilbert_poincare.denominator().leading_coefficient() == 1: + second_hilbert = second_hilbert*(-1)**s denom = ZZ(s-1).factorial() out = sum(c / denom * prod(s - 1 - n - nu + t for nu in range(s-1)) for n,c in enumerate(second_hilbert)) + t.parent().zero() - assert out.leading_coefficient() >= 0 return out elif algorithm == 'singular': import sage.libs.singular.function_factory @@ -3817,7 +3821,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal The Singular and libSingular versions of the respective algorithms are identical, but the former calls an external - Singular process while the later calls a C function, + Singular process while the latter calls a C function, i.e. the calling overhead is smaller. However, the libSingular interface does not support pretty printing of computation protocols. diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 2ede9c00eaf..a6f14d87b42 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -123,18 +123,17 @@ from sage.misc.cachefunc import cached_function from sage.categories.map cimport Map from sage.categories.morphism cimport Morphism -from sage.misc.superseded import deprecation, deprecated_function_alias +from sage.misc.superseded import deprecation from sage.misc.cachefunc import cached_method + cpdef is_Polynomial(f): """ Return True if f is of type univariate polynomial. INPUT: - - - ``f`` - an object - + - ``f`` -- an object EXAMPLES:: @@ -344,7 +343,6 @@ cdef class Polynomial(CommutativeAlgebraElement): INPUT: - - ``xmin`` - float - ``xmax`` - float @@ -2722,7 +2720,6 @@ cdef class Polynomial(CommutativeAlgebraElement): INPUT: - - ``n`` - an integer - ``value`` - value to set the n-th coefficient to @@ -9043,8 +9040,6 @@ cdef class Polynomial(CommutativeAlgebraElement): """ return self.base_ring().ideal(self.coefficients()) - content = deprecated_function_alias(16613, content_ideal) - def norm(self, p): r""" Return the `p`-norm of this polynomial. @@ -11395,7 +11390,7 @@ cdef class ConstantPolynomialSection(Map): """ This class is used for conversion from a polynomial ring to its base ring. - Since :trac:`9944`, it calls the constant_coefficient method, + Since :trac:`9944`, it calls the ``constant_coefficient`` method, which can be optimized for a particular polynomial type. EXAMPLES:: diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 62f447a0f66..0dc5e1f4ccf 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -1467,9 +1467,9 @@ def random_element(self, poly_degree=2, *args, **kwds): sage: QQbar(0) in z False - If you just want real algebraic numbers you can filter them out. - Using an odd degree for the polynomials will insure some degree of - success. :: + If you just want real algebraic numbers you can filter them out. + Using an odd degree for the polynomials will ensure some degree of + success. :: sage: r = [] sage: while len(r) < 3: diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index ccbe66889e4..49a36383adc 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -63,6 +63,7 @@ # Schemes import sage.schemes.projective.projective_space as projective_space from sage.schemes.projective.projective_homset import SchemeHomset_points_abelian_variety_field +import sage.schemes.curves.projective_curve as plane_curve from . import ell_point from . import ell_torsion @@ -70,14 +71,12 @@ from . import formal_group from . import weierstrass_morphism as wm - sqrt = math.sqrt exp = math.exp oo = rings.infinity # infinity O = rings.O # big oh -import sage.schemes.curves.projective_curve as plane_curve def is_EllipticCurve(x): r""" @@ -94,6 +93,7 @@ def is_EllipticCurve(x): """ return isinstance(x, EllipticCurve_generic) + class EllipticCurve_generic(WithEqualityById, plane_curve.ProjectivePlaneCurve): r""" Elliptic curve over a generic base ring. @@ -166,7 +166,7 @@ def __init__(self, K, ainvs): def _defining_params_(self): r""" - Internal function. Returns a tuple of the base ring of this + Internal function. Return a tuple of the base ring of this elliptic curve and its `a`-invariants, from which it can be reconstructed. @@ -197,10 +197,7 @@ def _repr_(self): Elliptic Curve defined by y^2 = x^3 + (a^2-3)*x + (-2/3*a+3) over Number Field in a with defining polynomial x^3 - 17 """ - #return "Elliptic Curve with a-invariants %s over %s"%(self.ainvs(), self.base_ring()) b = self.ainvs() - #return "y^2 + %s*x*y + %s*y = x^3 + %s*x^2 + %s*x + %s"%\ - # (a[0], a[2], a[1], a[3], a[4]) a = [z._coeff_repr() for z in b] s = "Elliptic Curve defined by " s += "y^2 " @@ -209,39 +206,39 @@ def _repr_(self): elif a[0] == '1': s += "+ x*y " elif b[0]: - s += "+ %s*x*y "%a[0] + s += "+ %s*x*y " % a[0] if a[2] == "-1": s += "- y " elif a[2] == '1': s += "+ y " elif b[2]: - s += "+ %s*y "%a[2] + s += "+ %s*y " % a[2] s += "= x^3 " if a[1] == "-1": s += "- x^2 " elif a[1] == '1': s += "+ x^2 " elif b[1]: - s += "+ %s*x^2 "%a[1] + s += "+ %s*x^2 " % a[1] if a[3] == "-1": s += "- x " elif a[3] == '1': s += "+ x " elif b[3]: - s += "+ %s*x "%a[3] + s += "+ %s*x " % a[3] if a[4] == '-1': s += "- 1 " elif a[4] == '1': s += "+ 1 " elif b[4]: - s += "+ %s "%a[4] + s += "+ %s " % a[4] s = s.replace("+ -","- ") - s += "over %s"%self.base_ring() + s += "over %s" % self.base_ring() return s def _latex_(self): """ - Internal function. Returns a latex string for this elliptic curve. + Internal function. Return a latex string for this elliptic curve. Users will normally use latex() instead. @@ -276,7 +273,7 @@ def _latex_(self): def _pari_init_(self): """ - Internal function. Returns a string to initialize this elliptic + Internal function. Return a string to initialize this elliptic curve in the PARI system. EXAMPLES:: @@ -285,11 +282,12 @@ def _pari_init_(self): sage: E._pari_init_() 'ellinit([0/1,0/1,0/1,1/1,1/1])' """ - return 'ellinit([%s])'%(','.join([x._pari_init_() for x in self.ainvs()])) + return 'ellinit([%s])' % (','.join(x._pari_init_() + for x in self.ainvs())) def _magma_init_(self, magma): """ - Internal function. Returns a string to initialize this elliptic + Internal function. Return a string to initialize this elliptic curve in the Magma subsystem. EXAMPLES:: @@ -310,8 +308,8 @@ def _magma_init_(self, magma): Elliptic Curve defined by y^2 = x^3 + x*x + (x + 1) over Univariate rational function field over Rational Field """ kmn = magma(self.base_ring())._ref() - return 'EllipticCurve([%s|%s])'%(kmn,','.join([x._magma_init_(magma) for x in self.ainvs()])) - + return 'EllipticCurve([%s|%s])' % (kmn,','.join(x._magma_init_(magma) + for x in self.ainvs())) def _symbolic_(self, SR): r""" @@ -398,8 +396,9 @@ def _symbolic_(self, SR): def __contains__(self, P): """ - Returns True if and only if P is a point on the elliptic curve. P - just has to be something that can be coerced to a point. + Return True if and only if P is a point on the elliptic curve. + + P just has to be something that can be coerced to a point. EXAMPLES:: @@ -535,9 +534,9 @@ def __call__(self, *args, **kwds): if isinstance(P, groups.AdditiveAbelianGroupElement) and isinstance(P.parent(),ell_torsion.EllipticCurveTorsionSubgroup): return self(P.element()) if isinstance(args[0], - (ell_point.EllipticCurvePoint_field, \ - ell_point.EllipticCurvePoint_number_field, \ - ell_point.EllipticCurvePoint)): + (ell_point.EllipticCurvePoint_field, + ell_point.EllipticCurvePoint_number_field, + ell_point.EllipticCurvePoint)): # check if denominator of the point contains a factor of the # characteristic of the base ring. if so, coerce the point to # infinity. @@ -606,11 +605,11 @@ def _reduce_point(self, R, p): def is_x_coord(self, x): r""" - Returns True if ``x`` is the `x`-coordinate of a point on this curve. + Return True if ``x`` is the `x`-coordinate of a point on this curve. .. note:: - See also ``lift_x()`` to find the point(s) with a given + See also :meth:`lift_x` to find the point(s) with a given `x`-coordinate. This function may be useful in cases where testing an element of the base field for being a square is faster than finding its square root. @@ -643,7 +642,7 @@ def is_x_coord(self, x): AUTHORS: - - John Cremona (2008-08-07): adapted from lift_x() + - John Cremona (2008-08-07): adapted from :meth:`lift_x` TESTS:: @@ -671,13 +670,13 @@ def is_x_coord(self, x): if K.characteristic() == 2: R = PolynomialRing(K, 'y') F = R([-fx,b,1]) - return len(F.roots())>0 + return bool(F.roots()) D = b*b + 4*fx return D.is_square() def lift_x(self, x, all=False, extend=False): r""" - Returns one or all points with given `x`-coordinate. + Return one or all points with given `x`-coordinate. INPUT: @@ -704,9 +703,9 @@ def lift_x(self, x, all=False, extend=False): A point or list of up to 2 points on this curve, or a base-change of this curve to a larger ring. - .. note:: + .. SEEALSO:: - See also ``is_x_coord()``. + :meth:`is_x_coord` EXAMPLES:: @@ -834,10 +833,10 @@ def lift_x(self, x, all=False, extend=False): E = self # Check that the x-coordinate is in K and extend otherwise if possible: - phi = K.coerce_map_from(L) + phi = K.coerce_map_from(L) if phi: x = phi(x) - L = K # new parent of x + L = K # new parent of x else: if L.coerce_map_from(K): E = E.change_ring(L) @@ -854,15 +853,15 @@ def lift_x(self, x, all=False, extend=False): # If possible find the associated y coorindates in L: - if K.characteristic()==2: + if K.characteristic() == 2: R = PolynomialRing(L, 'y') F = R([-f,b,1]) ys = F.roots(L, multiplicities=False) else: D = b*b+4*f ys = [] - if D.is_square(): # avoid automatic creation of sqrts - ys = [(-b+d)/2 for d in D.sqrt(all = True)] + if D.is_square(): # avoid automatic creation of sqrts + ys = [(-b+d)/2 for d in D.sqrt(all=True)] # Return the point(s) if any: @@ -883,17 +882,17 @@ def lift_x(self, x, all=False, extend=False): # Now make the extension needed to contain the y-coordinates: - if K.characteristic()!=2: # else we already defined F + if K.characteristic() != 2: # else we already defined F R = PolynomialRing(L, 'y') F = R([-f,b,1]) M = L.fraction_field().extension(F, 'y') EM = E.change_ring(M) y1 = M.gen() y2 = -b-y1 - if y2==y1: + if y2 == y1: ys = [y1] else: - ys = [y1,y2] + ys = [y1, y2] one = M.one() if all: return [EM.point([x, y, one], check=False) for y in ys] @@ -902,7 +901,7 @@ def lift_x(self, x, all=False, extend=False): def _point_homset(self, *args, **kwds): r""" - Internal function. Returns the (abstract) group of points on this + Internal function. Return the (abstract) group of points on this elliptic curve over a ring. EXAMPLES:: @@ -946,7 +945,7 @@ def __getitem__(self, n): def __is_over_RationalField(self): r""" - Internal function. Returns true iff the base ring of this elliptic + Internal function. Return true iff the base ring of this elliptic curve is the field of rational numbers. EXAMPLES:: @@ -962,7 +961,7 @@ def __is_over_RationalField(self): def is_on_curve(self, x, y): r""" - Returns True if `(x,y)` is an affine point on this curve. + Return True if `(x,y)` is an affine point on this curve. INPUT: @@ -977,7 +976,7 @@ def is_on_curve(self, x, y): False """ a = self.ainvs() - return y**2 +a[0]*x*y + a[2]*y == x**3 + a[1]*x**2 + a[3]*x + a[4] + return y**2 + a[0]*x*y + a[2]*y == x**3 + a[1]*x**2 + a[3]*x + a[4] def a_invariants(self): r""" @@ -992,23 +991,23 @@ def a_invariants(self): sage: E = EllipticCurve([1,2,3,4,5]) sage: E.a_invariants() (1, 2, 3, 4, 5) - sage: E = EllipticCurve([0,1]) - sage: E + + sage: E = EllipticCurve([0,1]); E Elliptic Curve defined by y^2 = x^3 + 1 over Rational Field sage: E.a_invariants() (0, 0, 0, 0, 1) + sage: E = EllipticCurve([GF(7)(3),5]) sage: E.a_invariants() (0, 0, 0, 3, 5) - :: + TESTS:: sage: E = EllipticCurve([1,0,0,0,1]) sage: E.a_invariants()[0] = 100000000 Traceback (most recent call last): ... TypeError: 'tuple' object does not support item assignment - """ return self.__ainvs @@ -1016,7 +1015,7 @@ def a_invariants(self): def a1(self): r""" - Returns the `a_1` invariant of this elliptic curve. + Return the `a_1` invariant of this elliptic curve. EXAMPLES:: @@ -1028,7 +1027,7 @@ def a1(self): def a2(self): r""" - Returns the `a_2` invariant of this elliptic curve. + Return the `a_2` invariant of this elliptic curve. EXAMPLES:: @@ -1040,7 +1039,7 @@ def a2(self): def a3(self): r""" - Returns the `a_3` invariant of this elliptic curve. + Return the `a_3` invariant of this elliptic curve. EXAMPLES:: @@ -1052,7 +1051,7 @@ def a3(self): def a4(self): r""" - Returns the `a_4` invariant of this elliptic curve. + Return the `a_4` invariant of this elliptic curve. EXAMPLES:: @@ -1064,7 +1063,7 @@ def a4(self): def a6(self): r""" - Returns the `a_6` invariant of this elliptic curve. + Return the `a_6` invariant of this elliptic curve. EXAMPLES:: @@ -1074,25 +1073,27 @@ def a6(self): """ return self.__ainvs[4] + @cached_method def b_invariants(self): r""" - Returns the `b`-invariants of this elliptic curve, as a tuple. + Return the `b`-invariants of this elliptic curve, as a tuple. OUTPUT: (tuple) - a 4-tuple of the `b`-invariants of this elliptic curve. + This method is cached. + EXAMPLES:: sage: E = EllipticCurve([0, -1, 1, -10, -20]) sage: E.b_invariants() (-4, -20, -79, -21) + sage: E = EllipticCurve([-4,0]) sage: E.b_invariants() (0, -8, 0, -16) - :: - sage: E = EllipticCurve([1,2,3,4,5]) sage: E.b_invariants() (9, 11, 29, 35) @@ -1113,19 +1114,15 @@ def b_invariants(self): - William Stein (2005-04-25) """ - try: - return self.__b_invariants - except AttributeError: - a1,a2,a3,a4,a6 = self.ainvs() - self.__b_invariants = a1*a1 + 4*a2, \ - a1*a3 + 2*a4, \ - a3**2 + 4*a6, \ - a1**2 * a6 + 4*a2*a6 - a1*a3*a4 + a2*a3**2 - a4**2 - return self.__b_invariants + a1, a2, a3, a4, a6 = self.ainvs() + return (a1*a1 + 4*a2, + a1*a3 + 2*a4, + a3**2 + 4*a6, + a1**2 * a6 + 4*a2*a6 - a1*a3*a4 + a2*a3**2 - a4**2) def b2(self): r""" - Returns the `b_2` invariant of this elliptic curve. + Return the `b_2` invariant of this elliptic curve. EXAMPLES:: @@ -1133,14 +1130,11 @@ def b2(self): sage: E.b2() 9 """ - try: - return self.__b_invariants[0] - except AttributeError: - return self.b_invariants()[0] + return self.b_invariants()[0] def b4(self): r""" - Returns the `b_4` invariant of this elliptic curve. + Return the `b_4` invariant of this elliptic curve. EXAMPLES:: @@ -1148,14 +1142,11 @@ def b4(self): sage: E.b4() 11 """ - try: - return self.__b_invariants[1] - except AttributeError: - return self.b_invariants()[1] + return self.b_invariants()[1] def b6(self): r""" - Returns the `b_6` invariant of this elliptic curve. + Return the `b_6` invariant of this elliptic curve. EXAMPLES:: @@ -1163,14 +1154,11 @@ def b6(self): sage: E.b6() 29 """ - try: - return self.__b_invariants[2] - except AttributeError: - return self.b_invariants()[2] + return self.b_invariants()[2] def b8(self): r""" - Returns the `b_8` invariant of this elliptic curve. + Return the `b_8` invariant of this elliptic curve. EXAMPLES:: @@ -1178,14 +1166,14 @@ def b8(self): sage: E.b8() 35 """ - try: - return self.__b_invariants[3] - except AttributeError: - return self.b_invariants()[3] + return self.b_invariants()[3] + @cached_method def c_invariants(self): r""" - Returns the `c`-invariants of this elliptic curve, as a tuple. + Return the `c`-invariants of this elliptic curve, as a tuple. + + This method is cached. OUTPUT: @@ -1196,6 +1184,7 @@ def c_invariants(self): sage: E = EllipticCurve([0, -1, 1, -10, -20]) sage: E.c_invariants() (496, 20008) + sage: E = EllipticCurve([-4,0]) sage: E.c_invariants() (192, 0) @@ -1208,17 +1197,14 @@ def c_invariants(self): - William Stein (2005-04-25) """ - try: - return self.__c_invariants - except AttributeError: - b2,b4,b6,b8 = self.b_invariants() - self.__c_invariants = b2**2 - 24*b4,\ - -b2**3 + 36*b2*b4 - 216*b6 # note: c6 is wrong in Silverman, but right in Cremona - return self.__c_invariants + b2, b4, b6, b8 = self.b_invariants() + # note: c6 is wrong in Silverman, but right in Cremona + return (b2**2 - 24*b4, + -b2**3 + 36*b2*b4 - 216*b6) def c4(self): r""" - Returns the `c_4` invariant of this elliptic curve. + Return the `c_4` invariant of this elliptic curve. EXAMPLES:: @@ -1226,15 +1212,11 @@ def c4(self): sage: E.c4() 496 """ - try: - return self.__c_invariants[0] - except AttributeError: - pass return self.c_invariants()[0] def c6(self): r""" - Returns the `c_6` invariant of this elliptic curve. + Return the `c_6` invariant of this elliptic curve. EXAMPLES:: @@ -1242,67 +1224,59 @@ def c6(self): sage: E.c6() 20008 """ - try: - return self.__c_invariants[1] - except AttributeError: - pass return self.c_invariants()[1] + @cached_method def discriminant(self): r""" - Returns the discriminant of this elliptic curve. + Return the discriminant of this elliptic curve. + + This method is cached. EXAMPLES:: sage: E = EllipticCurve([0,0,1,-1,0]) sage: E.discriminant() 37 + sage: E = EllipticCurve([0, -1, 1, -10, -20]) sage: E.discriminant() -161051 - :: - sage: E = EllipticCurve([GF(7)(2),1]) sage: E.discriminant() 1 """ - try: - return self.__discriminant - except AttributeError: - b2, b4, b6, b8 = self.b_invariants() - self.__discriminant = -b2**2*b8 - 8*b4**3 - 27*b6**2 + 9*b2*b4*b6 - return self.__discriminant + b2, b4, b6, b8 = self.b_invariants() + return -b2**2*b8 - 8*b4**3 - 27*b6**2 + 9*b2*b4*b6 + @cached_method def j_invariant(self): r""" - Returns the `j`-invariant of this elliptic curve. + Return the `j`-invariant of this elliptic curve. + + This method is cached. EXAMPLES:: sage: E = EllipticCurve([0,0,1,-1,0]) sage: E.j_invariant() 110592/37 + sage: E = EllipticCurve([0, -1, 1, -10, -20]) sage: E.j_invariant() -122023936/161051 + sage: E = EllipticCurve([-4,0]) sage: E.j_invariant() 1728 - :: - sage: E = EllipticCurve([GF(7)(2),1]) sage: E.j_invariant() 1 """ - try: - return self.__j_invariant - except AttributeError: - c4, _ = self.c_invariants() - self.__j_invariant = c4**3 / self.discriminant() - return self.__j_invariant - + c4, _ = self.c_invariants() + return c4**3 / self.discriminant() def base_extend(self, R): r""" @@ -1348,7 +1322,7 @@ def change_ring(self, R): def base_ring(self): r""" - Returns the base ring of the elliptic curve. + Return the base ring of the elliptic curve. EXAMPLES:: @@ -1414,7 +1388,7 @@ def gen(self, i): def rst_transform(self, r, s, t): r""" - Returns the transform of the curve by `(r,s,t)` (with `u=1`). + Return the transform of the curve by `(r,s,t)` (with `u=1`). INPUT: @@ -1427,7 +1401,8 @@ def rst_transform(self, r, s, t): .. note:: - This is just a special case of ``change_weierstrass_model()``, with `u=1`. + This is just a special case of + :meth:`change_weierstrass_model`, with `u=1`. EXAMPLES:: @@ -1436,11 +1411,11 @@ def rst_transform(self, r, s, t): sage: E.rst_transform(r,s,t) Elliptic Curve defined by y^2 + (2*s+1)*x*y + (r+2*t+3)*y = x^3 + (-s^2+3*r-s+2)*x^2 + (3*r^2-r*s-2*s*t+4*r-3*s-t+4)*x + (r^3+2*r^2-r*t-t^2+4*r-3*t+5) over Multivariate Polynomial Ring in r, s, t over Rational Field """ - return self.change_weierstrass_model(1,r,s,t) + return self.change_weierstrass_model(1, r, s, t) def scale_curve(self, u): r""" - Returns the transform of the curve by scale factor `u`. + Return the transform of the curve by scale factor `u`. INPUT: @@ -1453,7 +1428,8 @@ def scale_curve(self, u): .. note:: - This is just a special case of ``change_weierstrass_model()``, with `r=s=t=0`. + This is just a special case of + :meth:`change_weierstrass_model`, with `r=s=t=0`. EXAMPLES:: @@ -1462,13 +1438,13 @@ def scale_curve(self, u): sage: E = EllipticCurve([1,2,3,4,5]) sage: E.scale_curve(u) Elliptic Curve defined by y^2 + u*x*y + 3*u^3*y = x^3 + 2*u^2*x^2 + 4*u^4*x + 5*u^6 over Fraction Field of Univariate Polynomial Ring in u over Rational Field + """ if isinstance(u, integer_types): u = self.base_ring()(u) # because otherwise 1/u would round! - return self.change_weierstrass_model(1/u,0,0,0) - + return self.change_weierstrass_model(1/u, 0, 0, 0) -############################################################# +# ########################################################### # # Explanation of the division (also known as torsion) polynomial # functions in Sage. @@ -1505,11 +1481,11 @@ def scale_curve(self, u): # \code{E.division_polynomial(m,two_torsion_multiplicity=0)} and h # is the quotient, so that h=1 when m is odd. -############################################################# +# ########################################################### def division_polynomial_0(self, n, x=None): r""" - Returns the `n^{th}` torsion (division) polynomial, without + Return the `n^{th}` torsion (division) polynomial, without the 2-torsion factor if `n` is even, as a polynomial in `x`. These are the polynomials `g_n` defined in [MT1991]_, but with @@ -1642,6 +1618,7 @@ def division_polynomial_0(self, n, x=None): x = polygen(self.base_ring()) b2, b4, b6, b8 = self.b_invariants() + @cached_function def poly(n): if n == -2: @@ -1662,18 +1639,18 @@ def poly(n): else: m = (n-1) // 2 if m % 2 == 0: - return poly(-2) * poly(m+2) * poly(m)**3 - poly(m-1) * poly(m+1)**3 + return poly(-2) * poly(m+2) * poly(m)**3 - poly(m-1) * poly(m+1)**3 else: - return poly(m+2) * poly(m)**3 - poly(-2) * poly(m-1) * poly(m+1)**3 + return poly(m+2) * poly(m)**3 - poly(-2) * poly(m-1) * poly(m+1)**3 if not isinstance(n, (list, tuple)): return poly(int(n)) else: return [poly(int(k)) for k in n] - def two_division_polynomial(self, x = None): + def two_division_polynomial(self, x=None): r""" - Returns the 2-division polynomial of this elliptic curve evaluated + Return the 2-division polynomial of this elliptic curve evaluated at ``x``. INPUT: @@ -1686,7 +1663,6 @@ def two_division_polynomial(self, x = None): element is ok. This permits fast calculation of the torsion polynomial *evaluated* on any element of a ring. - EXAMPLES:: sage: E = EllipticCurve('5077a1') @@ -1702,12 +1678,11 @@ def two_division_polynomial(self, x = None): def division_polynomial(self, m, x=None, two_torsion_multiplicity=2): r""" - Returns the `m^{th}` division polynomial of this elliptic + Return the `m^{th}` division polynomial of this elliptic curve evaluated at ``x``. INPUT: - - ``m`` - positive integer. - ``x`` - optional ring element to use as the "x" @@ -1787,8 +1762,7 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2): sage: E.division_polynomial(4,P,two_torsion_multiplicity=1) -1771561 """ - - if not two_torsion_multiplicity in [0,1,2]: + if two_torsion_multiplicity not in [0, 1, 2]: raise ValueError("two_torsion_multiplicity must be 0,1 or 2") # Coerce the input m to be an integer @@ -1822,7 +1796,7 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2): self.__divpoly1[(m,(x,y))] = f return f else: - if isinstance(xy,tuple) and len(xy)==2 or isinstance(xy, ell_point.EllipticCurvePoint_field): + if isinstance(xy,tuple) and len(xy) == 2 or isinstance(xy, ell_point.EllipticCurvePoint_field): fxy = f(xy[0],xy[1]) self.__divpoly1[(m,xy)] = fxy return fxy @@ -1837,7 +1811,7 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2): except KeyError: pass f = self.division_polynomial_0(m,x) - if m%2 == 0: + if m % 2 == 0: f *= self.division_polynomial_0(-1,x) self.__divpoly2[(m,x)] = f return f @@ -1846,12 +1820,12 @@ def division_polynomial(self, m, x=None, two_torsion_multiplicity=2): def _multiple_x_numerator(self, n, x=None): r""" - Returns the numerator of the `x`-coordinate of the `n\th` multiple of a + Return the numerator of the `x`-coordinate of the `n\th` multiple of a point, using torsion polynomials (division polynomials). INPUT: - - ``n``, ``x``, -- as described in :meth:`division_polynomial_0()`. + - ``n``, ``x``, -- as described in :meth:`division_polynomial_0`. The result is cached. This is so that on calling ``P.division_points(n)`` for the same `n` and different @@ -1925,6 +1899,7 @@ def _multiple_x_numerator(self, n, x=None): def __multiple_x_numerator(self, n, x): r""" Helper method for :meth:`_multiple_x_numerator` which adds caching. + Input and output are the same as for that method. TESTS: @@ -1940,13 +1915,13 @@ def __multiple_x_numerator(self, n, x): polys = self.division_polynomial_0([-2,-1,n-1,n,n+1], x) if n % 2 == 0: - return x * polys[1] * polys[3]**2 - polys[2] * polys[4] + return x * polys[1] * polys[3]**2 - polys[2] * polys[4] else: - return x * polys[3]**2 - polys[1] * polys[2] * polys[4] + return x * polys[3]**2 - polys[1] * polys[2] * polys[4] def _multiple_x_denominator(self, n, x=None): r""" - Returns the denominator of the `x`-coordinate of the `n\th` multiple + Return the denominator of the `x`-coordinate of the `n\th` multiple of a point, using torsion polynomials (division polynomials). INPUT: @@ -2144,7 +2119,7 @@ def multiplication_by_m(self, m, x_only=False): # here is done by functions defined earlier: mx = (x.parent()(self._multiple_x_numerator(m.abs(), x)) - / x.parent()(self._multiple_x_denominator(m.abs(), x))) + / x.parent()(self._multiple_x_denominator(m.abs(), x))) if x_only: # Return it if the optional parameter x_only is set. @@ -2161,7 +2136,9 @@ def multiplication_by_m(self, m, x_only=False): def multiplication_by_m_isogeny(self, m): r""" Return the ``EllipticCurveIsogeny`` object associated to the - multiplication-by-`m` map on self. The resulting isogeny will + multiplication-by-`m` map on self. + + The resulting isogeny will have the associated rational maps (i.e. those returned by `self.multiplication_by_m()`) already computed. @@ -2210,7 +2187,8 @@ def isomorphism_to(self, other): .. note:: - If the curves in question are not isomorphic, a ValueError is raised. + If the curves in question are not isomorphic, a ``ValueError`` + is raised. EXAMPLES:: @@ -2248,7 +2226,7 @@ def automorphisms(self, field=None): INPUT: - - ``field`` (default None) -- a field into which the + - ``field`` (default ``None``) -- a field into which the coefficients of the curve may be coerced (by default, uses the base field of the curve). @@ -2296,7 +2274,7 @@ def isomorphisms(self, other, field=None): - ``other`` -- another elliptic curve. - - ``field`` (default None) -- a field into which the + - ``field`` (default ``None``) -- a field into which the coefficients of the curves may be coerced (by default, uses the base field of the curves). @@ -2341,7 +2319,7 @@ def isomorphisms(self, other, field=None): def is_isomorphic(self, other, field=None): """ - Returns whether or not self is isomorphic to other. + Return whether or not self is isomorphic to other. INPUT: @@ -2408,7 +2386,7 @@ def change_weierstrass_model(self, *urst): def short_weierstrass_model(self, complete_cube=True): """ - Returns a short Weierstrass model for self. + Return a short Weierstrass model for self. INPUT: @@ -2471,7 +2449,7 @@ def short_weierstrass_model(self, complete_cube=True): # any curve of the form y^2 = x^3 +.. is singular in characteristic 2 if K.characteristic() == 2: - raise ValueError("short_weierstrass_model(): no short model for %s (characteristic is %s)"%(self,K.characteristic())) + raise ValueError("short_weierstrass_model(): no short model for %s (characteristic is %s)" % (self, K.characteristic())) # in characteristic 3 we can complete the square but we can only complete the cube if b2 is 0 if K.characteristic() == 3: @@ -2499,11 +2477,8 @@ def short_weierstrass_model(self, complete_cube=True): b2, b4, b6, _ = self.b_invariants() return constructor.EllipticCurve([0,b2,0,8*b4,16*b6]) - - # Plotting - def plot(self, xmin=None, xmax=None, components='both', **args): """ Draw a graph of this elliptic curve. @@ -2590,10 +2565,15 @@ def plot(self, xmin=None, xmax=None, components='both', **args): a1, a2, a3, a4, a6 = self.ainvs() d = self.division_polynomial(2) - # Internal function for plotting first branch of the curve - f1 = lambda z: (-(a1*z + a3) + sqrt(abs(d(z))))/2 - # Internal function for plotting second branch of the curve - f2 = lambda z: (-(a1*z + a3) - sqrt(abs(d(z))))/2 + + def f1(z): + # Internal function for plotting first branch of the curve + return (-(a1*z + a3) + sqrt(abs(d(z))))/2 + + def f2(z): + # Internal function for plotting second branch of the curve + return (-(a1*z + a3) - sqrt(abs(d(z))))/2 + r = sorted(d.roots(RR, multiplicities=False)) if components == 'bounded' and len(r) == 1: raise ValueError("no bounded component for this curve") @@ -2699,9 +2679,12 @@ def plot(self, xmin=None, xmax=None, components='both', **args): g += plot.line(w, **args) return g + @cached_method def formal_group(self): r""" - The formal group associated to this elliptic curve. + Return the formal group associated to this elliptic curve. + + This method is cached. EXAMPLES:: @@ -2709,11 +2692,7 @@ def formal_group(self): sage: E.formal_group() Formal Group associated to the Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field """ - try: - return self.__formal_group - except AttributeError: - self.__formal_group = formal_group.EllipticCurveFormalGroup(self) - return self.__formal_group + return formal_group.EllipticCurveFormalGroup(self) formal = formal_group @@ -2800,7 +2779,7 @@ def _p_primary_torsion_basis(self, p, m=None): k = 1 if m == 1: return [[P, k]] - pts = P.division_points(p) # length 0 or p + pts = P.division_points(p) # length 0 or p while pts: k += 1 P = pts[0] @@ -2810,7 +2789,7 @@ def _p_primary_torsion_basis(self, p, m=None): # now P generates the p-power-torsion and has order p^k return [[P, k]] - Epi = iter(Ep) # used to iterate through Ep + Epi = iter(Ep) # used to iterate through Ep # Find P1,P2 which generate the p-torsion: P1 = next(Epi) while P1.is_zero(): @@ -2826,13 +2805,13 @@ def _p_primary_torsion_basis(self, p, m=None): pts1 = P1.division_points(p) pts2 = P2.division_points(p) - while len(pts1)>0 and len(pts2)>0: + while pts1 and pts2: k += 1 P1 = pts1[0] P2 = pts2[0] log_order += 2 - if m<=log_order: - return [[P1,k],[P2,k]] + if m <= log_order: + return [[P1, k], [P2, k]] pts1 = P1.division_points(p) pts2 = P2.division_points(p) diff --git a/src/sage/sets/disjoint_union_enumerated_sets.py b/src/sage/sets/disjoint_union_enumerated_sets.py index 68bba9e26f8..c6d35ea8bde 100644 --- a/src/sage/sets/disjoint_union_enumerated_sets.py +++ b/src/sage/sets/disjoint_union_enumerated_sets.py @@ -35,7 +35,7 @@ class DisjointUnionEnumeratedSets(UniqueRepresentation, Parent): - ``facade`` -- a boolean This models the enumerated set obtained by concatenating together - the specified ordered sets. The later are supposed to be pairwise + the specified ordered sets. The latter are supposed to be pairwise disjoint; otherwise, a multiset is created. The argument ``family`` can be a list, a tuple, a dictionary, or a @@ -284,8 +284,11 @@ def __init__(self, family, facade=True, keepkey=False, category=None): self._family = family self._facade = facade if facade: + # Note that family is not copied when it is a finite enumerated + # set, thus, any subclass must ensure that it does not mutate this + # input. if family in FiniteEnumeratedSets(): - self._facade_for = tuple(family) + self._facade_for = family else: # This allows the test suite to pass its tests by essentially # stating that this is a facade for any parent. Technically @@ -576,7 +579,7 @@ def _element_constructor_facade(self, el): raise ValueError("cannot coerce `%s` in the parent `%s`"%(el[1], P)) # Check first to see if the parent of el is in the family - if (isinstance(el, Element) and isinstance(self._facade_for, tuple) + if (isinstance(el, Element) and self._facade_for is not True and el.parent() in self._facade_for): return el diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index 0896b82978d..26d36882623 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -887,9 +887,7 @@ def __init__(self, set, function, name=None): sage: from sage.sets.family import LazyFamily sage: f = LazyFamily([3,4,7], lambda i: 2*i); f Lazy family ((i))_{i in [3, 4, 7]} - sage: TestSuite(f).run() # __contains__ is not implemented - Failure ... - The following tests failed: _test_an_element, _test_enumerated_set_contains, _test_some_elements + sage: TestSuite(f).run() Check for :trac:`5538`:: @@ -1061,6 +1059,27 @@ def __iter__(self): for i in self.set: yield self[i] + def __contains__(self, x): + """ + EXAMPLES:: + + sage: from sage.sets.family import LazyFamily + sage: f = LazyFamily([3,4,7], lambda i: 2*i) + sage: 3 in f, 14 in f + (False, True) + + By default this expands the lazy family, which is only done for + families known to be finite:: + + sage: 5 in LazyFamily(NonNegativeIntegers(), lambda i: 2*i) + Traceback (most recent call last): + ... + ValueError: family must be finite to check containment + """ + if self not in FiniteEnumeratedSets(): + raise ValueError('family must be finite to check containment') + return x in iter(self) + def __getitem__(self, i): """ EXAMPLES:: diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index eb70fd79a3f..d420d686604 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -638,7 +638,7 @@ cdef class SageObject: """ Return coercion of self to an object of the interface I. - The result of coercion is cached, unless self is not a C + The result of coercion is cached, unless self is a C extension class or ``self._interface_is_cached_()`` returns False. """ diff --git a/src/sage/version.py b/src/sage/version.py index 6ded86f0871..edec3915abe 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.9.beta5' -date = '2019-08-03' -banner = 'SageMath version 8.9.beta5, Release Date: 2019-08-03' +version = '8.9.beta6' +date = '2019-08-10' +banner = 'SageMath version 8.9.beta6, Release Date: 2019-08-10'