diff --git a/VERSION.txt b/VERSION.txt index d01f871103c..f001902c9fd 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.0.beta9, Release Date: 2017-05-31 +SageMath version 8.0.beta10, Release Date: 2017-06-11 diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index c7e29d04fd5..f8d1a646dac 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -786,13 +786,16 @@ fi # Mark that the new package has been installed (and tested, if # applicable). PKG_NAME_INSTALLED="$SAGE_SPKG_INST/$PKG_NAME" -echo "PACKAGE NAME: $PKG_NAME" > "$PKG_NAME_INSTALLED" -echo "INSTALL DATE: `date`" >> "$PKG_NAME_INSTALLED" -echo "UNAME: `uname -a`" >> "$PKG_NAME_INSTALLED" -if [ -n "$TEST_SUITE_RESULT" ]; then - echo "TEST SUITE: $TEST_SUITE_RESULT" >> "$PKG_NAME_INSTALLED" -fi -cat "$SAGE_ROOT/VERSION.txt" >> "$PKG_NAME_INSTALLED" +cat > "$PKG_NAME_INSTALLED" << __EOF__ +{ + "package_name": "$PKG_BASE", + "package_version": "$PKG_VER", + "install_date": "$(date)", + "system_uname": "$(uname -a)", + "sage_version": "$(cat "${SAGE_ROOT}/VERSION.txt")", + "test_result": "$TEST_SUITE_RESULT" +} +__EOF__ ################################################################## diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 289e7d03321..13cf41a1d16 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=1468ff678f41a3552bce2ba952af0795127d64d8 -md5=931c98793b007e28874089c17c40e53d -cksum=1227610551 +sha1=4fa024947bd555ae00d155c022d52f5fddad045f +md5=35c48d9ed0328cc92e2100a7721a996e +cksum=2690821246 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 5f277ae787b..20c90807cc9 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -223 +224 diff --git a/build/pkgs/gf2x/package-version.txt b/build/pkgs/gf2x/package-version.txt index 07c205584e8..fb97a3ebb49 100644 --- a/build/pkgs/gf2x/package-version.txt +++ b/build/pkgs/gf2x/package-version.txt @@ -1 +1 @@ -1.1.p1 +1.1.p2 diff --git a/build/pkgs/gf2x/patches/0007-fix_tuning.patch b/build/pkgs/gf2x/patches/0007-fix_tuning.patch new file mode 100644 index 00000000000..764e8e53047 --- /dev/null +++ b/build/pkgs/gf2x/patches/0007-fix_tuning.patch @@ -0,0 +1,13 @@ +diff --git a/src/mul9clk2.c b/src/mul9clk2.c +index bf7ad4a..5c6b980 100644 +--- a/src/mul9clk2.c ++++ b/src/mul9clk2.c +@@ -149,7 +149,7 @@ void gf2x_mul9 (unsigned long *c, const unsigned long *a, const unsigned long *b + e = p2e[1]; h=l; l=p2o[1]; + _mm_storeu_si128((__v2di*)(c+14), e ^ _mm_slli_si128(l, 8) ^ _mm_srli_si128(h, 8)); + +- e = p2e[2]; ++ e = p2e[2]; h=l; + _mm_storeu_si128((__v2di*)(c+16), e ^ _mm_srli_si128(h, 8)); + + /* diff --git a/build/pkgs/mpir/spkg-install b/build/pkgs/mpir/spkg-install index 09fb7e68c72..a3a403653e0 100755 --- a/build/pkgs/mpir/spkg-install +++ b/build/pkgs/mpir/spkg-install @@ -166,12 +166,18 @@ fi # Workaround old GNU as version by disabling assembly use. if [ "$UNAME" = Linux ]; then as_version=`$AS --version | head -1 | awk 'NF>1{print $NF}'` - case "$as_version" in - 1.*|2.1*|2.[0-2]*) - echo "Disable use of assembly because of GNU as < 2.23." + as_version_major=${as_version%%.*} + as_version_rest=${as_version#*.} + as_version_minor=${as_version_rest%%.*} + if [ $as_version_major -lt 2 ] || \ + [ $as_version_major -eq 2 -a $as_version_minor -lt 24 ]; then + echo "Disable use of assembly because of GNU as <= 2.23." export MPN_PATH=generic - ;; - esac + if [ "$SAGE_FAT_BINARY" = "yes" ]; then + echo "Cannot build with SAGE_FAT_BINARY=yes." + exit 1 + fi + fi fi # Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs. diff --git a/build/pkgs/sagenb/checksums.ini b/build/pkgs/sagenb/checksums.ini index 076dffc1a8f..234b621d739 100644 --- a/build/pkgs/sagenb/checksums.ini +++ b/build/pkgs/sagenb/checksums.ini @@ -1,4 +1,4 @@ tarball=sagenb-VERSION.tar.bz2 -sha1=9fc8addff1c543521225acaf60a2336204e67d62 -md5=04ddd5a1ccf0764cd72dc0abcf322c1e -cksum=1784359449 +sha1=cd46f50bf3f71a452735e7ad5573928ebc190f23 +md5=fea1225a9d630ac4a4f35fc6d936de0a +cksum=1620865525 diff --git a/build/pkgs/sagenb/package-version.txt b/build/pkgs/sagenb/package-version.txt index f3040840fd7..7dea76edb3d 100644 --- a/build/pkgs/sagenb/package-version.txt +++ b/build/pkgs/sagenb/package-version.txt @@ -1 +1 @@ -0.13 +1.0.1 diff --git a/build/pkgs/singular/spkg-install b/build/pkgs/singular/spkg-install index 8daa218849d..8191c0bbc70 100755 --- a/build/pkgs/singular/spkg-install +++ b/build/pkgs/singular/spkg-install @@ -61,17 +61,32 @@ remove_old_version() rm -f "$SAGE_LOCAL/include/omlimits.h" # 3.x only rm -rf "$SAGE_LOCAL/include/resources" #4.x only rm -rf "$SAGE_LOCAL/include/gfanlib" #4.x only - rm -f "$SAGE_LOCAL"/lib/libsingular* # 3.x with lower case - rm -f "$SAGE_LOCAL"/lib/libsingcf*.a # 3.x only additional archives - rm -f "$SAGE_LOCAL"/lib/libsingfac*.a # 3.x only additional archives - rm -f "$SAGE_LOCAL"/lib/libSingular* # 4.x with upper case + + # Clean up all Singular-related libraries + libs=( + singular # 3.x with lower case + singcf # 3.x only additional archives + singfac # 3.x only additional archives + Singular # 4.x with upper case + polys + factory + omalloc + resources # 3.x + singular_resources # 4.x and up + gfan + ) + if [ "$UNAME" = "CYGWIN" ]; then + for name in ${libs[*]}; do + rm -f "$SAGE_LOCAL"/bin/cyg${name}*.dll + rm -f "$SAGE_LOCAL"/lib/lib${name}*.a + done + else + for name in ${libs[*]}; do + rm -f "$SAGE_LOCAL"/lib/lib${name}* + done + fi + rm -f "$SAGE_LOCAL"/lib/p_Procs_Field* # 3.x only - # a bunch of additional libraries for 4.x - rm -f "$SAGE_LOCAL"/lib/libpolys* - rm -f "$SAGE_LOCAL"/lib/libfactory* - rm -f "$SAGE_LOCAL"/lib/libomalloc* - rm -f "$SAGE_LOCAL"/lib/libresources* - rm -r "$SAGE_LOCAL"/lib/libgfan* rm -rf "$SAGE_LOCAL/share/singular" rm -f "$SAGE_LOCAL"/share/info/singular* } diff --git a/src/bin/sage-banner b/src/bin/sage-banner index e29022a5da0..97412eb77ae 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.0.beta9, Release Date: 2017-05-31 │ +│ SageMath version 8.0.beta10, Release Date: 2017-06-11 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 2317480273c..e582d343200 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='8.0.beta9' -SAGE_RELEASE_DATE='2017-05-31' +SAGE_VERSION='8.0.beta10' +SAGE_RELEASE_DATE='2017-06-11' diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 71ebb39b6f3..a6cf3cab270 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -80,6 +80,7 @@ Non-associative algebras lie_algebras sage/algebras/jordan_algebra + sage/combinat/free_dendriform_algebra sage/combinat/free_prelie_algebra sage/algebras/shuffle_algebra sage/algebras/free_zinbiel_algebra diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 06a3214d6ef..2d8863be5ca 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -118,6 +118,7 @@ Comprehensive Module list sage/combinat/finite_state_machine sage/combinat/finite_state_machine_generators sage/combinat/free_module + sage/combinat/free_dendriform_algebra sage/combinat/free_prelie_algebra sage/combinat/fully_packed_loop sage/combinat/gelfand_tsetlin_patterns diff --git a/src/doc/en/reference/plot3d/index.rst b/src/doc/en/reference/plot3d/index.rst index f314e640259..2cbabe1b75c 100644 --- a/src/doc/en/reference/plot3d/index.rst +++ b/src/doc/en/reference/plot3d/index.rst @@ -4,7 +4,7 @@ .. toctree:: :maxdepth: 2 - sage/plot/plot3d/examples + sage/plot/plot3d/introduction Function and Data Plots ----------------------- diff --git a/src/doc/en/reference/structure/index.rst b/src/doc/en/reference/structure/index.rst index 8bbbdf03f71..a24b48183a6 100644 --- a/src/doc/en/reference/structure/index.rst +++ b/src/doc/en/reference/structure/index.rst @@ -74,6 +74,7 @@ Utilities .. toctree:: :maxdepth: 1 + sage/structure/richcmp sage/structure/unique_representation sage/structure/factory sage/structure/dynamic_class diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 8cbbe2ea643..6b4c5468816 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -367,7 +367,7 @@ This gives rise to the following code:: ....: def _repr_(self): ....: return "(%s):(%s)"%(self.n,self.d) ....: def _richcmp_(self, other, op): - ....: from sage.structure.sage_object import richcmp + ....: from sage.structure.richcmp import richcmp ....: return richcmp(self.n*other.denominator(), other.numerator()*self.d, op) ....: def _add_(self, other): ....: C = self.__class__ @@ -1858,7 +1858,7 @@ Appendix: The complete code # are allowed to use the denominator() and numerator() methods # on the second argument. def _richcmp_(self, other, op): - from sage.structure.sage_object import richcmp + from sage.structure.richcmp import richcmp return richcmp(self.n*other.denominator(), other.numerator()*self.d, op) # Arithmetic methods, single underscore. We can assume that both diff --git a/src/doc/en/thematic_tutorials/numerical_sage/index.rst b/src/doc/en/thematic_tutorials/numerical_sage/index.rst index 985670a1908..babb3dd44e9 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/index.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/index.rst @@ -5,19 +5,24 @@ .. _numerical_computing: Numerical Computing with Sage -========================================== +============================= + +.. WARNING:: + + Beware that this document may be obsolete. + This document is designed to introduce the reader to the tools in Sage that are useful for doing numerical computation. By numerical computation we essentially mean machine precision floating point computations. In particular, things such as optimization, numerical -linear algebra, solving ODE's/PDE's numerically, etc. or the first -part of this document the reader is only assumed to be familiar with -Python/Sage. In the second section on using compiled code, the -computational prerequisites increase and I assume the reader is -comfortable with writing programs in C or Fortran. The third section -is on mpi and parallel programming and only requires knowledge of -Python, though familiarity with mpi would be helpful. -Finally the last section is about 3d visualization in Sage. +linear algebra, solving ODE's or PDE's numerically, etc. + +In the first part of this document the reader is only assumed to be +familiar with Python/Sage. In the second section on using compiled +code, the computational prerequisites increase and I assume the reader +is comfortable with writing programs in C or Fortran. The third +section is on mpi and parallel programming and only requires knowledge +of Python, though familiarity with mpi would be helpful. In the current version of this document the reader is assumed to be familiar with the techniques of numerical analysis. The goal of this @@ -33,4 +38,3 @@ where they can find more information. numerical_tools using_compiled_code_iteractively parallel_computation - visualization diff --git a/src/doc/en/thematic_tutorials/numerical_sage/installation.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation.rst deleted file mode 100644 index 99507b9820e..00000000000 --- a/src/doc/en/thematic_tutorials/numerical_sage/installation.rst +++ /dev/null @@ -1,8 +0,0 @@ -Installation of Visualization Tools -=================================== - -.. toctree:: - :maxdepth: 2 - - installation_linux - installation_osx diff --git a/src/doc/en/thematic_tutorials/numerical_sage/installation_linux.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation_linux.rst deleted file mode 100644 index 16df05f84ed..00000000000 --- a/src/doc/en/thematic_tutorials/numerical_sage/installation_linux.rst +++ /dev/null @@ -1,80 +0,0 @@ -Installing Visualization Tools on Linux -======================================= - -This section assumes you are running linux. You may need -administrator rights to complete this section. First try - -.. skip - -:: - - sage: import Tkinter - -If this works great if not it means your sage was not compiled with -tcl/tk bindings. There are two possible reasons. If you used a -binary then this will be the case. If you built from source but -don't have the tcl/tk libraries on your system you will have the -same problem. To fix this install the tcl and tk libraries and -development (source and headers) packages for your linux -distribution. Now you need to rebuild Sage's python - -.. skip - -:: - - sage: install_package('python-2.5.spkg') - -where you should replace :math:`<\text{version}>` by the version -of python. Type - -.. skip - -:: - - sage: !ls spkg/standard | grep python-2.5 - -This will give you the name of the python package to use above. In -the case that it gives multiple names, choose the one with the -highest version number. Now again try - -.. skip - -:: - - sage: import Tkinter - -If this works we can install vtk, but first you need cmake. Test if -your system has it by typing cmake at the shell, (or !cmake in -sage). If cmake is on your system then you are ready. If not either -install cmake on your system using your distributions tools or do - -.. skip - -:: - - sage: install_package('cmake-2.4.7') - -Now we want to compile VTK which does all the hard work. To do this -make sure you have the opengl libraries for you system installed. -This will be something like libgl1-mesa-glx, libgl1-mesa-dev. - -.. skip - -:: - - sage: install_package('vtk-5.0.3.p1') - -This will take quite a while to compile, probably 20 min to an hour -or so. Once this is done we install the python wrappers, the next -part takes about 10 seconds, - -.. skip - -:: - - sage: install_package('MayaVi-1.5') - sage: install_package('scitools++') - sage: install_package('PyVTK-0.4.74') - -Now you're done. - diff --git a/src/doc/en/thematic_tutorials/numerical_sage/installation_osx.rst b/src/doc/en/thematic_tutorials/numerical_sage/installation_osx.rst deleted file mode 100644 index 05c27e86ee6..00000000000 --- a/src/doc/en/thematic_tutorials/numerical_sage/installation_osx.rst +++ /dev/null @@ -1,38 +0,0 @@ -Installing Visualization Software on OS X -========================================= - -The first thing we need to do is rebuild Python to use OSX's -frameworks, so that it can create graphical windows. To do this -first from the terminal do - -:: - - cd $SAGE_ROOT/local/lib - rm libpng*.dylib - -where ``$SAGE_ROOT`` is the directory of your -Sage install. Next from within Sage, - -.. skip - -:: - - sage: install_package('python-2.5.1-framework') - -Next we will build vtk, this will take a while - -.. skip - -:: - - sage: install_package('vtk-5.0.3.p1') - -Finally - -.. skip - -:: - - sage: install_package('MayaVi-1.5') - sage: install_package('scitools++') - sage: install_package('PyVTK-0.4.74') diff --git a/src/doc/en/thematic_tutorials/numerical_sage/plotting.rst b/src/doc/en/thematic_tutorials/numerical_sage/plotting.rst deleted file mode 100644 index 6e52bb17175..00000000000 --- a/src/doc/en/thematic_tutorials/numerical_sage/plotting.rst +++ /dev/null @@ -1,97 +0,0 @@ -Plotting -======== - -We will plot a surface two ways. First we will use easyviz. -Consider the following code:: - - import numpy - from scitools import easyviz - x = numpy.arange(-8,8,.2) - xx,yy = numpy.meshgrid(x,x) - r = numpy.sqrt(xx**2+yy**2) + 0.01 - zz = numpy.sin(r)/r - easyviz.surfc(x,x,zz) - -The function surfc takes a list of x coordinates, and y coordinates -and a numpy array z. Its plots a surface that has height z[i,j] at -the point (x[i],y[i]). Note the use of meshgrid, and vectorized -numpy functions that let us evaluate -:math:`\frac{\sin(\sqrt{x^2+y^2})+1}{\sqrt{x^2+y^2}+1}` over the -grid very easily. We discussed meshgrid at the beginning when we -were talking about numpy. Note that you can drag the plot around -with your mouse and look at it from different angles. - -We can make this plot look a bit nicer by adding some shading and -nicer coloring and some labels as follows. - -:: - - import numpy - RealNumber=float - Integer =int - from scitools import easyviz - x = numpy.arange(-8,8,.2) - xx,yy = numpy.meshgrid(x,x) - r = numpy.sqrt(xx**2+yy**2) + 0.01 - zz = numpy.sin(r)/r - l = easyviz.Light(lightpos=(-10,-10,5), lightcolor=(1,1,1)) - easyviz.surfc(x,x,zz,shading='interp',colormap=easyviz.jet(), - zmin=-0.5,zmax=1,clevels=10, - title='r=sqrt(x**2+y**2)+eps\nsin(r)/r', - light=l, - legend='sin', - ) - -Let us now try to plot some vector fields. Consider the following -code - -:: - - import numpy - from scitools import easyviz - RealNumber=float - Integer=int - j=numpy.complex(0,1) - w=numpy.zeros((5,5,5)) - u=w+1.0 - xx,yy,zz=numpy.mgrid[-1.0:1.0:5*j,-1:1:5*j,-1:1:5*j] - easyviz.quiver3(xx,yy,zz,w,w,u) - -This should plot a vector field that points up everywhere. The -arguments to quiver3 are 6, :math:`n\times n\times n` arrays. The -first three arrays are the location of the vectors, that is there -will be a vector at :math:`(xx[i,j,k],yy[i,j,k],zz[i,j,k])` for -:math:`0\le i,j,k < n`. The second three arrays are the -directions, i.e., the vector at -:math:`(xx[i,j,k],yy[i,j,k],zz[i,j,k])` points in the direction -:math:`(w[i,j,k],w[i,j,k],u[i,j,k])`. - -Now let us give some examples with MayaVi. First lets see how to -plot a function like we did with easyviz. - -:: - - import numpy - from mayavi.tools import imv - x=numpy.arange(-8,8,.2) - def f(x,y): - r=numpy.sqrt(x**2+y**2)+.01 - return numpy.sin(r)/r - imv.surf(x,x,f) - -This will open mayavi, and display the plot of the function. The -first two arguments to surf are arrays :math:`x` and :math:`y`, -s.t. the function will be evaluated at :math:`(x[i],y[j])`. The -last argument is the function to graph. It probably looks a bit -different than the easyviz example. Lets try to make it look -similar to the easyviz example. First note that on the left there -is a list of filters and modules. Double-click the warpscalars -button in the filters menu, and change the scale factor from -:math:`1` to say :math:`5`. This should redraw the graph -similar to how easyviz drew it. There are quite a few other options -you can play around with. For example, next click on the module -surfacemap, and you will see you can make the graph transparent by -changing the opacity. You can also change it to a wireframe or make -it plot contours. - -TODO: More examples diff --git a/src/doc/en/thematic_tutorials/numerical_sage/visualization.rst b/src/doc/en/thematic_tutorials/numerical_sage/visualization.rst deleted file mode 100644 index 0913f08e7fc..00000000000 --- a/src/doc/en/thematic_tutorials/numerical_sage/visualization.rst +++ /dev/null @@ -1,22 +0,0 @@ -Visualization -============= - -One of the most common uses of computer algebra systems is of course -plotting. At the moment Sage does not have much in the way of -intrinsic support for visualization. However, there are some very -powerful open source programs for visualization that you can use from -within Sage. The goal of this chapter is to explain how to set them -up. The main tool we will be using is VTK, http://www.vtk.org. VTK is -an amazing visualization tool, and the results that can be achieved -are incredible. It is probably one of the best visualization tools in -existence, and can probably do anything you might want a visualization -tool to do. However, because it can do so much, it is a bit tricky to -use potentially. Luckily there are a few tools for using VTK from -within python that make it easier to get started. The ones we will -focus on are MavaVi, http://mayavi.sourceforge.net/, and easyviz. - -.. toctree:: - :maxdepth: 2 - - installation - plotting diff --git a/src/module_list.py b/src/module_list.py index 7b5f2fe7064..3d4d7c0e482 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1056,6 +1056,9 @@ def uname_specific(name, value, alternative): ["sage/numerical/linear_tensor_element.pyx"], libraries=["stdc++"]), + Extension("sage.numerical.gauss_legendre", + ["sage/numerical/gauss_legendre.pyx"]), + Extension("sage.numerical.sdp", ["sage/numerical/sdp.pyx"]), @@ -1393,6 +1396,9 @@ def uname_specific(name, value, alternative): Extension('sage.rings.padics.padic_capped_relative_element', sources = ['sage/rings/padics/padic_capped_relative_element.pyx']), + Extension('sage.rings.padics.padic_floating_point_element', + sources = ['sage/rings/padics/padic_floating_point_element.pyx']), + Extension('sage.rings.padics.padic_ext_element', sources = ['sage/rings/padics/padic_ext_element.pyx'], libraries=['ntl', 'gmp', 'm'], @@ -1453,6 +1459,10 @@ def uname_specific(name, value, alternative): Extension('sage.rings.padics.qadic_flint_FM', sources = ['sage/rings/padics/qadic_flint_FM.pyx']), + Extension('sage.rings.padics.qadic_flint_FP', + sources = ['sage/rings/padics/qadic_flint_FP.pyx'], + libraries = ["flint"]), + ################################ ## ## sage.rings.polynomial diff --git a/src/sage/__init__.py b/src/sage/__init__.py index 7127bcda4cc..8cd4918ff97 100644 --- a/src/sage/__init__.py +++ b/src/sage/__init__.py @@ -1,5 +1,10 @@ __all__ = ['all'] +# Make sure that the correct zlib library is loaded. This is needed +# to prevent the system zlib to be loaded instead of the Sage one. +# See https://trac.sagemath.org/ticket/23122 +import zlib + # IPython calls this when starting up def load_ipython_extension(*args): import sage.repl.ipython_extension diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 449268d5c54..b1795f21afa 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -20,7 +20,8 @@ ` - :class:`algebras.Free ` - :class:`algebras.FreeZinbiel ` -- :class:`algebras.PreLieAlgebra ` +- :class:`algebras.FreePreLie ` +- :class:`algebras.FreeDendriform ` - :func:`algebras.GradedCommutative ` - :class:`algebras.Group ` @@ -82,6 +83,6 @@ lazy_import('sage.combinat.diagram_algebras', 'TemperleyLiebAlgebra', 'TemperleyLieb') lazy_import('sage.combinat.posets.moebius_algebra', 'MoebiusAlgebra', 'Moebius') lazy_import('sage.combinat.free_prelie_algebra', 'FreePreLieAlgebra', 'FreePreLie') +lazy_import('sage.combinat.free_dendriform_algebra', 'FreeDendriformAlgebra', 'FreeDendriform') lazy_import('sage.algebras.tensor_algebra', 'TensorAlgebra', 'Tensor') del lazy_import # We remove the object from here so it doesn't appear under tab completion - diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index a7983b7c5f4..eae3a35bfd5 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -15,8 +15,9 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from six import iteritems -from sage.misc import six +from sage.misc.six import with_metaclass from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from copy import copy @@ -119,7 +120,7 @@ def _mul_(self, other): # the dictionary describing the element # ``e[i]`` * (the element described by the dictionary ``cur``) # (where ``e[i]`` is the ``i``-th standard basis vector). - for mr,cr in six.iteritems(cur): + for mr,cr in iteritems(cur): # Commute the factor as necessary until we are in order pos = 0 for j in mr: @@ -155,7 +156,7 @@ def _mul_(self, other): cur = next # Add the distributed terms to the total - for index,coeff in six.iteritems(cur): + for index,coeff in iteritems(cur): d[index] = d.get(index, zero) + cl * coeff if d[index] == zero: del d[index] @@ -729,8 +730,8 @@ def _element_constructor_(self, x): if x in self.free_module(): R = self.base_ring() if x.parent().base_ring() is R: - return self.element_class(self, {(i,): c for i,c in six.iteritems(x)}) - return self.element_class(self, {(i,): R(c) for i,c in six.iteritems(x) if R(c) != R.zero()}) + return self.element_class(self, {(i,): c for i,c in iteritems(x)}) + return self.element_class(self, {(i,): R(c) for i,c in iteritems(x) if R(c) != R.zero()}) if isinstance(x, CliffordAlgebraElement): if x.parent() is self: @@ -1206,7 +1207,7 @@ def center_basis(self): for m,c in (Bi*Bj - Bj*Bi): d[(a, K.index(m)+k*b)] = c m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True) - from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in six.iteritems(x)), + from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), distinct=True) return tuple(map( from_vector, m.kernel().basis() )) @@ -1220,7 +1221,7 @@ def center_basis(self): # v = B[i]*B[j] - B[j]*B[i] # eqns[a].extend([v[k] for k in K]) # m = Matrix(R, eqns) - # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in six.iteritems(x)), + # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), # distinct=True) # return tuple(map( from_vector, m.kernel().basis() )) @@ -1303,7 +1304,7 @@ def supercenter_basis(self): for m,c in supercommutator: d[(a, K.index(m)+k*b)] = c m = Matrix(R, d, nrows=k, ncols=k*k, sparse=True) - from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in six.iteritems(x)), + from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), distinct=True) return tuple(map( from_vector, m.kernel().basis() )) @@ -1317,7 +1318,7 @@ def supercenter_basis(self): # v = B[i].supercommutator(B[j]) # or better an if-loop as above # eqns[a].extend([v[k] for k in K]) # m = Matrix(R, eqns) - # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in six.iteritems(x)), + # from_vector = lambda x: self.sum_of_terms(((K[i], c) for i,c in iteritems(x)), # distinct=True) # return tuple(map( from_vector, m.kernel().basis() )) @@ -2160,7 +2161,7 @@ def scalar(self, other): ##################################################################### ## Differentials -class ExteriorAlgebraDifferential(six.with_metaclass( +class ExteriorAlgebraDifferential(with_metaclass( InheritComparisonClasscallMetaclass, ModuleMorphismByLinearity, UniqueRepresentation )): @@ -2202,13 +2203,13 @@ def __classcall__(cls, E, s_coeff): """ d = {} - for k,v in six.iteritems(dict(s_coeff)): + for k,v in iteritems(dict(s_coeff)): if not v: # Strip terms with 0 continue if isinstance(v, dict): R = E.base_ring() - v = E._from_dict({(i,): R(c) for i,c in six.iteritems(v)}) + v = E._from_dict({(i,): R(c) for i,c in iteritems(v)}) else: # Make sure v is in ``E`` v = E(v) @@ -2627,7 +2628,7 @@ def __init__(self, E, s_coeff): self._cos_coeff = {} zero = E.zero() B = E.basis() - for k,v in six.iteritems(dict(s_coeff)): + for k, v in iteritems(dict(s_coeff)): k = B[k] for m,c in v: self._cos_coeff[m] = self._cos_coeff.get(m, zero) + c * k diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 02f61a8554b..6e39df3e880 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -344,11 +344,13 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - from __future__ import absolute_import +from six.moves import range +from future_builtins import map + from copy import copy from functools import wraps -from future_builtins import map + from sage.categories.homset import Hom from sage.categories.morphism import SetMorphism from sage.categories.rings import Rings @@ -374,7 +376,7 @@ from sage.structure.parent import Parent from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation -from six.moves import range as range + ############################################################################## # Elements of a cluster algebra diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index a44f14a0f90..e571a51c30a 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -71,9 +71,10 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function +from __future__ import print_function, absolute_import +from six import string_types -from sage.misc import six +from sage.misc.six import with_metaclass from sage.structure.unique_representation import UniqueRepresentation from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method @@ -98,7 +99,7 @@ from sage.rings.quotient_ring_element import QuotientRingElement -class Differential(six.with_metaclass( +class Differential(with_metaclass( InheritComparisonClasscallMetaclass, UniqueRepresentation, Morphism )): @@ -849,7 +850,7 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None): else: n = len(degrees) names = tuple('x{}'.format(i) for i in range(n)) - elif isinstance(names, six.string_types): + elif isinstance(names, string_types): names = tuple(names.split(',')) n = len(names) else: diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py index 52969d597ff..f2541a0538d 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_ideal.py @@ -24,8 +24,8 @@ from sage.misc.cachefunc import cached_method from functools import reduce -from sage.structure.sage_object import (op_LT, op_LE, op_EQ, op_NE, - op_GT, op_GE) +from sage.structure.richcmp import (op_LT, op_LE, op_EQ, op_NE, + op_GT, op_GE) class FiniteDimensionalAlgebraIdeal(Ideal_generic): diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index b719b53b733..9aaa39fa91a 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -40,7 +40,7 @@ from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.element import AlgebraElement -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp import six diff --git a/src/sage/algebras/free_algebra_quotient_element.py b/src/sage/algebras/free_algebra_quotient_element.py index 441670da879..a44d42e8f85 100644 --- a/src/sage/algebras/free_algebra_quotient_element.py +++ b/src/sage/algebras/free_algebra_quotient_element.py @@ -26,7 +26,7 @@ from sage.misc.misc import repr_lincomb from sage.structure.element import RingElement, AlgebraElement from sage.structure.parent_gens import localvars -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.rings.integer import Integer from sage.modules.free_module_element import FreeModuleElement from sage.monoids.free_monoid_element import FreeMonoidElement diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index 1162172fd3b..7f7a0ecc102 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -24,7 +24,7 @@ from sage.misc.misc import repr_lincomb from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.element cimport have_same_parent, coercion_model, parent from sage.structure.element_wrapper cimport ElementWrapper -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from sage.data_structures.blas_dict cimport axpy, negate, scal # TODO: Inherit from IndexedFreeModuleElement and make cdef once #22632 is merged diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index daafa6d8eff..da118c0e357 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -18,7 +18,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.latex import latex -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.structure.element import AlgebraElement from sage.structure.unique_representation import UniqueRepresentation from copy import copy diff --git a/src/sage/arith/numerical_approx.pyx b/src/sage/arith/numerical_approx.pyx index d445abf71f7..b53d7679099 100644 --- a/src/sage/arith/numerical_approx.pyx +++ b/src/sage/arith/numerical_approx.pyx @@ -36,13 +36,14 @@ def numerical_approx_generic(x, prec): P = parent(x) cdef Parent RR = RealField(prec) - map = RR.coerce_map_from(P) - if map is not None: - return map(x) + cmap = RR.coerce_map_from(P) + if cmap is not None: + return cmap(x) + cdef Parent CC = ComplexField(prec) - map = CC.coerce_map_from(P) - if map is not None: - return map(x) + cmap = CC.coerce_map_from(P) + if cmap is not None: + return cmap(x) # Coercion didn't work: there are 3 possibilities: # (1) There is a coercion possible to a lower precision diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index 64daed71629..8aed1258316 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -14,33 +14,41 @@ Commands: -- ``desolve`` - Compute the "general solution" to a 1st or 2nd order +- :func:`desolve` - Compute the "general solution" to a 1st or 2nd order ODE via Maxima. -- ``desolve_laplace`` - Solve an ODE using Laplace transforms via +- :func:`desolve_laplace` - Solve an ODE using Laplace transforms via Maxima. Initial conditions are optional. -- ``desolve_rk4`` - Solve numerically IVP for one first order +- :func:`desolve_rk4` - Solve numerically an IVP for one first order equation, return list of points or plot. -- ``desolve_system_rk4`` - Solve numerically IVP for system of first +- :func:`desolve_system_rk4` - Solve numerically an IVP for a system of first order equations, return list of points. -- ``desolve_odeint`` - Solve numerically a system of first-order ordinary - differential equations using ``odeint`` from scipy.integrate module. +- :func:`desolve_odeint` - Solve numerically a system of first-order ordinary + differential equations using ``odeint`` from `scipy.integrate module. + `_ -- ``desolve_system`` - Solve any size system of 1st order odes using +- :func:`desolve_system` - Solve a system of 1st order ODEs of any size using Maxima. Initial conditions are optional. -- ``eulers_method`` - Approximate solution to a 1st order DE, +- :func:`eulers_method` - Approximate solution to a 1st order DE, presented as a table. -- ``eulers_method_2x2`` - Approximate solution to a 1st order system +- :func:`eulers_method_2x2` - Approximate solution to a 1st order system of DEs, presented as a table. -- ``eulers_method_2x2_plot`` - Plot the sequence of points obtained +- :func:`eulers_method_2x2_plot` - Plot the sequence of points obtained from Euler's method. +The following functions require the optional package ``tides``: + +- :func:`desolve_mintides` - Numerical solution of a system of 1st order ODEs via + the Taylor series integrator method implemented in TIDES. + +- :func:`desolve_tides_mpfr` - Arbitrary precision Taylor series integrator implemented in TIDES. + AUTHORS: - David Joyner (3-2006) - Initial version of functions @@ -81,31 +89,29 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False): r""" - Solves a 1st or 2nd order linear ODE via maxima. Including IVP and BVP. - - *Use* ``desolve? `` *if the output in truncated in notebook.* + Solves a 1st or 2nd order linear ODE via Maxima, including IVP and BVP. INPUT: - ``de`` - an expression or equation representing the ODE - - ``dvar`` - the dependent variable (hereafter called ``y``) + - ``dvar`` - the dependent variable (hereafter called `y`) - ``ics`` - (optional) the initial or boundary conditions - - for a first-order equation, specify the initial ``x`` and ``y`` + - for a first-order equation, specify the initial `x` and `y` - - for a second-order equation, specify the initial ``x``, ``y``, - and ``dy/dx``, i.e. write `[x_0, y(x_0), y'(x_0)]` + - for a second-order equation, specify the initial `x`, `y`, + and `dy/dx`, i.e. write `[x_0, y(x_0), y'(x_0)]` - for a second-order boundary solution, specify initial and - final ``x`` and ``y`` boundary conditions, i.e. write `[x_0, y(x_0), x_1, y(x_1)]`. + final `x` and `y` boundary conditions, i.e. write `[x_0, y(x_0), x_1, y(x_1)]`. - gives an error if the solution is not SymbolicEquation (as happens for example for a Clairaut equation) - ``ivar`` - (optional) the independent variable (hereafter called - x), which must be specified if there is more than one + `x`), which must be specified if there is more than one independent variable in the equation. - ``show_method`` - (optional) if true, then Sage returns pair @@ -114,23 +120,26 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) following order for first order equations: linear, separable, exact (including exact with integrating factor), homogeneous, bernoulli, generalized homogeneous) - use carefully in class, - see below for the example of the equation which is separable but + see below the example of an equation which is separable but this property is not recognized by Maxima and the equation is solved as exact. - - ``contrib_ode`` - (optional) if true, desolve allows to solve + - ``contrib_ode`` - (optional) if true, ``desolve`` allows to solve Clairaut, Lagrange, Riccati and some other equations. This may take a long time and is thus turned off by default. Initial conditions can be used only if the result is one SymbolicEquation (does not - contain a singular solution, for example) + contain a singular solution, for example). OUTPUT: In most cases return a SymbolicEquation which defines the solution - implicitly. If the result is in the form y(x)=... (happens for - linear eqs.), return the right-hand side only. The possible - constant solutions of separable ODE's are omitted. + implicitly. If the result is in the form `y(x)=\ldots` (happens for + linear eqs.), return the right-hand side only. The possible + constant solutions of separable ODEs are omitted. + NOTES: + + Use ``desolve? `` if the output in the Sage notebook is truncated. EXAMPLES:: @@ -149,7 +158,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: plot(f) Graphics object consisting of 1 graphics primitive - We can also solve second-order differential equations.:: + We can also solve second-order differential equations:: sage: x = var('x') sage: y = function('y')(x) @@ -260,8 +269,8 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) You can solve Bessel equations, also using initial conditions, but you cannot put (sometimes desired) the initial - condition at x=0, since this point is a singular point of the - equation. Anyway, if the solution should be bounded at x=0, then + condition at `x=0`, since this point is a singular point of the + equation. Anyway, if the solution should be bounded at `x=0`, then _K2=0.:: sage: desolve(x^2*diff(y,x,x)+x*diff(y,x)+(x^2-4)*y==0,y) @@ -278,7 +287,7 @@ def desolve(de, dvar, ics=None, ivar=None, show_method=False, contrib_ode=False) sage: desolve(sqrt(y)*diff(y,x)+e^(y)+cos(x)-sin(x+y)==0,y,contrib_ode=True) # not tested - Some more types of ODE's:: + Some more types of ODEs:: sage: desolve(x*diff(y,x)^2-(1+x*y)*diff(y,x)+y==0,y,contrib_ode=True,show_method=True) [[y(x) == _C + log(x), y(x) == _C*e^x], 'factor'] @@ -580,17 +589,17 @@ def desolve_laplace(de, dvar, ics=None, ivar=None): INPUT: - - ``de`` - a lambda expression representing the ODE (eg, de = - diff(y,x,2) == diff(y,x)+sin(x)) + - ``de`` - a lambda expression representing the ODE (e.g. ``de = + diff(y,x,2) == diff(y,x)+sin(x)``) - - ``dvar`` - the dependent variable (eg y) + - ``dvar`` - the dependent variable (e.g. ``y``) - ``ivar`` - (optional) the independent variable (hereafter called - x), which must be specified if there is more than one + `x`), which must be specified if there is more than one independent variable in the equation. - - ``ics`` - a list of numbers representing initial conditions, (eg, - f(0)=1, f'(0)=2 is ics = [0,1,2]) + - ``ics`` - a list of numbers representing initial conditions, (e.g. + ``f(0)=1``, ``f'(0)=2`` corresponds to ``ics = [0,1,2]``) OUTPUT: @@ -703,9 +712,9 @@ def sanitize_var(exprs): # 'y(x) -> y(x) def desolve_system(des, vars, ics=None, ivar=None): """ - Solve any size system of 1st order ODE's. Initial conditions are optional. + Solve a system of any size of 1st order ODEs. Initial conditions are optional. - Onedimensional systems are passed to :meth:`desolve_laplace`. + One dimensional systems are passed to :meth:`desolve_laplace`. INPUT: @@ -713,8 +722,8 @@ def desolve_system(des, vars, ics=None, ivar=None): - ``vars`` - list of dependent variables - - ``ics`` - (optional) list of initial values for ivar and vars. - If ics is defined, it should provide initial conditions for each variable, + - ``ics`` - (optional) list of initial values for ``ivar`` and ``vars``. + If ``ics`` is defined, it should provide initial conditions for each variable, otherwise an exception would be raised. - ``ivar`` - (optional) the independent variable, which must be @@ -777,7 +786,7 @@ def desolve_system(des, vars, ics=None, ivar=None): sage: P1 = plot([solx,soly], (0,1)) sage: P2 = parametric_plot((solx,soly), (0,1)) - Now type show(P1), show(P2) to view these plots. + Now type ``show(P1)``, ``show(P2)`` to view these plots. Check that :trac:`9824` is fixed:: @@ -837,13 +846,15 @@ def desolve_system(des, vars, ics=None, ivar=None): def eulers_method(f,x0,y0,h,x1,algorithm="table"): r""" This implements Euler's method for finding numerically the - solution of the 1st order ODE ``y' = f(x,y)``, ``y(a)=c``. The "x" - column of the table increments from ``x0`` to ``x1`` by ``h`` (so - ``(x1-x0)/h`` must be an integer). In the "y" column, the new - y-value equals the old y-value plus the corresponding entry in the + solution of the 1st order ODE `y' = f(x,y)`, `y(a)=c`. The ``x`` + column of the table increments from `x_0` to `x_1` by `h` (so + `(x_1-x_0)/h` must be an integer). In the ``y`` column, the new + `y`-value equals the old `y`-value plus the corresponding entry in the last column. - *For pedagogical purposes only.* + .. NOTE:: + + This function is for pedagogical purposes only. EXAMPLES:: @@ -924,18 +935,23 @@ def eulers_method_2x2(f,g, t0, x0, y0, h, t1,algorithm="table"): This implements Euler's method for finding numerically the solution of the 1st order system of two ODEs - ``x' = f(t, x, y), x(t0)=x0.`` + .. MATH:: - ``y' = g(t, x, y), y(t0)=y0.`` + \begin{aligned} + x' &= f(t, x, y), x(t_0)=x_0 \\ + y' &= g(t, x, y), y(t_0)=y_0. + \end{aligned} - The "t" column of the table increments from `t_0` to `t_1` by `h` - (so `\\frac{t_1-t_0}{h}` must be an integer). In the "x" column, - the new x-value equals the old x-value plus the corresponding - entry in the next (third) column. In the "y" column, the new - y-value equals the old y-value plus the corresponding entry in the + The ``t`` column of the table increments from `t_0` to `t_1` by `h` + (so `\frac{t_1-t_0}{h}` must be an integer). In the ``x`` column, + the new `x`-value equals the old `x`-value plus the corresponding + entry in the next (third) column. In the ``y`` column, the new + `y`-value equals the old `y`-value plus the corresponding entry in the next (last) column. - *For pedagogical purposes only.* + .. NOTE:: + + This function is for pedagogical purposes only. EXAMPLES:: @@ -969,7 +985,7 @@ def eulers_method_2x2(f,g, t0, x0, y0, h, t1,algorithm="table"): To numerically approximate `y(1)`, where `(1+t^2)y''+y'-y=0`, `y(0)=1`, `y'(0)=-1`, using 4 steps of Euler's method, first convert to a system: `y_1' = y_2`, `y_1(0)=1`; `y_2' = - \\frac{y_1-y_2}{1+t^2}`, `y_2(0)=-1`.:: + \frac{y_1-y_2}{1+t^2}`, `y_2(0)=-1`.:: sage: RR = RealField(sci_not=0, prec=4, rnd='RNDU') sage: t, x, y=PolynomialRing(RR,3,"txy").gens() @@ -982,7 +998,7 @@ def eulers_method_2x2(f,g, t0, x0, y0, h, t1,algorithm="table"): 3/4 0.63 -0.0078 -0.031 0.11 1 0.63 0.020 0.079 0.071 - To numerically approximate y(1), where `y''+ty'+y=0`, `y(0)=1`, `y'(0)=0`:: + To numerically approximate `y(1)`, where `y''+ty'+y=0`, `y(0)=1`, `y'(0)=0`:: sage: t,x,y=PolynomialRing(RR,3,"txy").gens() sage: f = y; g = -x-y*t @@ -1018,16 +1034,16 @@ def eulers_method_2x2_plot(f,g, t0, x0, y0, h, t1): r""" Plot solution of ODE. - This plots the soln in the rectangle ``(xrange[0],xrange[1]) - x (yrange[0],yrange[1])`` and plots using Euler's method the + This plots the solution in the rectangle with sides ``(xrange[0],xrange[1])`` and + ``(yrange[0],yrange[1])``, and plots using Euler's method the numerical solution of the 1st order ODEs `x' = f(t,x,y)`, `x(a)=x_0`, `y' = g(t,x,y)`, `y(a) = y_0`. - *For pedagogical purposes only.* + .. NOTE:: - EXAMPLES:: + This function is for pedagogical purposes only. - sage: from sage.calculus.desolvers import eulers_method_2x2_plot + EXAMPLES: The following example plots the solution to `\theta''+\sin(\theta)=0`, `\theta(0)=\frac 34`, `\theta'(0) = @@ -1035,6 +1051,7 @@ def eulers_method_2x2_plot(f,g, t0, x0, y0, h, t1): ``(P[0]+P[1]).show()`` to plot `(t,\theta(t))` and `(t,\theta'(t))`:: + sage: from sage.calculus.desolvers import eulers_method_2x2_plot sage: f = lambda z : z[2]; g = lambda z : -sin(z[1]) sage: P = eulers_method_2x2_plot(f,g, 0.0, 0.75, 0.0, 0.1, 1.0) """ @@ -1055,14 +1072,14 @@ def desolve_rk4_determine_bounds(ics,end_points=None): """ Used to determine bounds for numerical integration. - - If end_points is None, the interval for integration is from ics[0] - to ics[0]+10 + - If ``end_points`` is None, the interval for integration is from ``ics[0]`` + to ``ics[0]+10`` - - If end_points is a or [a], the interval for integration is from min(ics[0],a) - to max(ics[0],a) + - If ``end_points`` is ``a`` or ``[a]``, the interval for integration is from ``min(ics[0],a)`` + to ``max(ics[0],a)`` - - If end_points is [a,b], the interval for integration is from min(ics[0],a) - to max(ics[0],b) + - If ``end_points`` is ``[a,b]``, the interval for integration is from ``min(ics[0],a)`` + to ``max(ics[0],b)`` EXAMPLES:: @@ -1099,12 +1116,12 @@ def desolve_rk4_determine_bounds(ics,end_points=None): def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output='list', **kwds): """ Solve numerically one first-order ordinary differential - equation. See also ``ode_solver``. + equation. INPUT: - input is similar to ``desolve`` command. The differential equation can be - written in a form close to the plot_slope_field or desolve command + Input is similar to ``desolve`` command. The differential equation can be + written in a form close to the ``plot_slope_field`` or ``desolve`` command. - Variant 1 (function in two variables) @@ -1122,25 +1139,28 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output - ``ivar`` - should be specified, if there are more variables or if the equation is autonomous - - ``ics`` - initial conditions in the form [x0,y0] + - ``ics`` - initial conditions in the form ``[x0,y0]`` - ``end_points`` - the end points of the interval - - if end_points is a or [a], we integrate on between min(ics[0],a) and max(ics[0],a) - - if end_points is None, we use end_points=ics[0]+10 + - if ``end_points`` is a or [a], we integrate between ``min(ics[0],a)`` and ``max(ics[0],a)`` + - if ``end_points`` is None, we use ``end_points=ics[0]+10`` - - if end_points is [a,b] we integrate on between min(ics[0],a) and max(ics[0],b) + - if end_points is [a,b] we integrate between ``min(ics[0], a)`` and ``max(ics[0], b)`` - ``step`` - (optional, default:0.1) the length of the step (positive number) - - ``output`` - (optional, default: 'list') one of 'list', - 'plot', 'slope_field' (graph of the solution with slope field) + - ``output`` - (optional, default: ``'list'``) one of ``'list'``, + ``'plot'``, ``'slope_field'`` (graph of the solution with slope field) OUTPUT: - Return a list of points, or plot produced by list_plot, + Return a list of points, or plot produced by ``list_plot``, optionally with slope field. + .. SEEALSO:: + + :func:`ode_solver`. EXAMPLES:: @@ -1247,7 +1267,7 @@ def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1 r""" Solve numerically a system of first-order ordinary differential equations using the 4th order Runge-Kutta method. Wrapper for - Maxima command ``rk``. See also ``ode_solver``. + Maxima command ``rk``. INPUT: @@ -1261,14 +1281,14 @@ def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1 if the equation is autonomous and the independent variable is missing - - ``ics`` - initial conditions in the form [x0,y01,y02,y03,....] + - ``ics`` - initial conditions in the form ``[x0,y01,y02,y03,....]`` - ``end_points`` - the end points of the interval - - if end_points is a or [a], we integrate on between min(ics[0],a) and max(ics[0],a) - - if end_points is None, we use end_points=ics[0]+10 + - if ``end_points`` is a or [a], we integrate on between ``min(ics[0], a)`` and ``max(ics[0], a)`` + - if ``end_points`` is None, we use ``end_points=ics[0]+10`` - - if end_points is [a,b] we integrate on between min(ics[0],a) and max(ics[0],b) + - if ``end_points`` is [a,b] we integrate on between ``min(ics[0], a)`` and ``max(ics[0], b)`` - ``step`` -- (optional, default: 0.1) the length of the step @@ -1276,6 +1296,10 @@ def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1 Return a list of points. + .. SEEALSO:: + + :func:`ode_solver`. + EXAMPLES:: sage: from sage.calculus.desolvers import desolve_system_rk4 @@ -1360,20 +1384,20 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() - ``times`` -- a sequence of time points in which the solution must be found - ``dvars`` -- dependent variables. ATTENTION: the order must be the same as - in des, that means: d(dvars[i])/dt=des[i] + in ``des``, that means: ``d(dvars[i])/dt=des[i]`` - ``ivar`` -- independent variable, optional. - ``compute_jac`` -- boolean. If True, the Jacobian of des is computed and used during the integration of Stiff Systems. Default value is False. - Other Parameters (taken from the documentation of odeint function from - scipy.integrate module) + Other Parameters (taken from the documentation of odeint function from `scipy.integrate module. + `_) - ``rtol``, ``atol`` : float - The input parameters rtol and atol determine the error + The input parameters ``rtol`` and ``atol`` determine the error control performed by the solver. The solver will control the - vector, e, of estimated local errors in y, according to an + vector, `e`, of estimated local errors in `y`, according to an inequality of the form: max-norm of (e / ewt) <= 1 @@ -1382,7 +1406,7 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() ewt = rtol * abs(y) + atol - rtol and atol can be either vectors the same length as y or scalars. + ``rtol`` and ``atol`` can be either vectors the same length as `y` or scalars. - ``tcrit`` : array Vector of critical points (e.g. singularities) where integration @@ -1415,7 +1439,7 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() OUTPUT: - Return a list with the solution of the system at each time in times. + Return a list with the solution of the system at each time in ``times``. EXAMPLES: @@ -1603,9 +1627,9 @@ def desolve_mintides(f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-16) - A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM Transactions on Mathematical Software* , *39* (1), 1-28. - - (http://www.unizar.es/acz/05Publicaciones/Monografias/MonografiasPublicadas/Monografia36/IndMonogr36.htm) - A. Abad, R. Barrio, F. Blesa, M. Rodriguez. - TIDES tutorial: Integrating ODEs by using the Taylor Series Method. + - A. Abad, R. Barrio, F. Blesa, M. Rodriguez. + `TIDES tutorial: Integrating ODEs by using the Taylor Series Method. + `_ """ import subprocess if subprocess.call('command -v gcc', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE): @@ -1706,9 +1730,9 @@ def desolve_tides_mpfr(f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-1 .. A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM Transactions on Mathematical Software* , *39* (1), 1-28. - .. (http://www.unizar.es/acz/05Publicaciones/Monografias/MonografiasPublicadas/Monografia36/IndMonogr36.htm) - A. Abad, R. Barrio, F. Blesa, M. Rodriguez. - TIDES tutorial: Integrating ODEs by using the Taylor Series Method. + .. A. Abad, R. Barrio, F. Blesa, M. Rodriguez. + `TIDES tutorial: Integrating ODEs by using the Taylor Series Method. + `_ """ import subprocess diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index b231350c8da..08849b7d824 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -275,9 +275,9 @@ class inheritance from ``C.parent_class``. sage: As().parent_class sage: As().parent_class.__bases__ - (,) + (<... 'object'>,) sage: As().parent_class.mro() - [, ] + [, <... 'object'>] :: @@ -286,7 +286,7 @@ class inheritance from ``C.parent_class``. sage: Bs().parent_class.__bases__ (,) sage: Bs().parent_class.mro() - [, , ] + [, , <... 'object'>] :: @@ -295,7 +295,7 @@ class inheritance from ``C.parent_class``. sage: Cs().parent_class.__bases__ (,) sage: Cs().parent_class.__mro__ - (, , ) + (, , <... 'object'>) :: @@ -304,7 +304,7 @@ class inheritance from ``C.parent_class``. sage: Ds().parent_class.__bases__ (, ) sage: Ds().parent_class.mro() - [, , , , ] + [, , , , <... 'object'>] Note that that two categories in the same class need not have the same ``super_categories``. For example, ``Algebras(QQ)`` has @@ -351,7 +351,7 @@ class inheritance from ``C.parent_class``. , , , - ] + <... 'object'>] sage: D.fA() 'A' sage: D.fB() @@ -376,7 +376,7 @@ class inheritance from ``C.parent_class``. , , , - ] + <... 'object'>] TESTS:: diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index 5da62ca3b07..51b0e197147 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -224,7 +224,7 @@ class Category_singleton(Category): , , , - ] + <... 'object'>] sage: R() is R() True sage: R() is R().__class__() diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index fbecf7d6a6e..77313bacea9 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -29,7 +29,7 @@ import operator import homset from sage.structure.element cimport Element -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool def is_Morphism(x): return isinstance(x, Morphism) diff --git a/src/sage/categories/polyhedra.py b/src/sage/categories/polyhedra.py index 7fae8ec433d..a20538948f2 100644 --- a/src/sage/categories/polyhedra.py +++ b/src/sage/categories/polyhedra.py @@ -40,7 +40,7 @@ class PolyhedralSets(Category_over_base_ring): , , , - ] + <... 'object'>] sage: isinstance(P, P.parent().category().element_class) True """ diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 8e1ff35786b..eb1c74577ae 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -870,8 +870,24 @@ def __getitem__(self, arg): sage: QQ[I] Number Field in I with defining polynomial x^2 + 1 + sage: QQ[I].coerce_embedding() + Generic morphism: + From: Number Field in I with defining polynomial x^2 + 1 + To: Complex Lazy Field + Defn: I -> 1*I + + :: + sage: QQ[sqrt(2)] Number Field in sqrt2 with defining polynomial x^2 - 2 + sage: QQ[sqrt(2)].coerce_embedding() + Generic morphism: + From: Number Field in sqrt2 with defining polynomial x^2 - 2 + To: Real Lazy Field + Defn: sqrt2 -> 1.414213562373095? + + :: + sage: QQ[sqrt(2),sqrt(3)] Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field @@ -884,6 +900,18 @@ def __getitem__(self, arg): sage: ZZ[sqrt(2)+sqrt(3)] Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 + Embeddings are found for simple extensions (when that makes sense):: + + sage: QQi. = QuadraticField(-1, 'i') + sage: QQ[i].coerce_embedding() + Generic morphism: + From: Number Field in i with defining polynomial x^2 + 1 + To: Complex Lazy Field + Defn: i -> 1*I + sage: QQi. = QuadraticField(-1, embedding=None) + sage: QQ[i].coerce_embedding() is None + True + TESTS: A few corner cases:: @@ -929,6 +957,21 @@ def __getitem__(self, arg): sage: K.base_field().base_field() Number Field in b with defining polynomial x^3 - 3 + Embeddings:: + + sage: QQ[I](I.pyobject()) + I + sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1) + sage: QQ[expr].coerce_embedding() is None + False + sage: QQ[sqrt(5)].gen() > 0 + True + sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2) + sage: QQ[expr].coerce_embedding() + Generic morphism: + From: Number Field in a with defining polynomial x^2 - 2 + To: Real Lazy Field + Defn: a -> 1.414213562373095? """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): @@ -973,8 +1016,36 @@ def normalize_arg(arg): if minpolys: # how to pass in names? - # TODO: set up embeddings names = tuple(_gen_names(elts)) + if len(elts) == 1: + from sage.rings.all import CIF, CLF, RIF, RLF + elt = elts[0] + try: + iv = CIF(elt) + except (TypeError, ValueError): + emb = None + else: + # First try creating an ANRoot manually, because + # extension(..., embedding=CLF(expr)) (or + # ...QQbar(expr)) would normalize the expression in + # QQbar, which currently is VERY slow in many cases. + # This may fail when minpoly has close roots or elt is + # a complicated symbolic expression. + # TODO: Rewrite using #19362 and/or #17886 and/or + # #15600 once those issues are solved. + from sage.rings.qqbar import AlgebraicNumber, ANRoot + try: + elt = AlgebraicNumber(ANRoot(minpolys[0], iv)) + except ValueError: + pass + # Force a real embedding when possible, to get the + # right ordered ring structure. + if (iv.imag().is_zero() or iv.imag().contains_zero() + and elt.imag().is_zero()): + emb = RLF(elt) + else: + emb = CLF(elt) + return self.extension(minpolys[0], names[0], embedding=emb) try: # Doing the extension all at once is best, if possible... return self.extension(minpolys, names) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index c498cc55447..11246a597c5 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -125,7 +125,7 @@ class Sets(Category_singleton): - + <... 'object'> We run some generic checks on P:: @@ -183,7 +183,7 @@ class Sets(Category_singleton): - + <... 'object'> FIXME: Objects.element_class is not very meaningful ... diff --git a/src/sage/combinat/algebraic_combinatorics.py b/src/sage/combinat/algebraic_combinatorics.py index d241a8b7bd5..5883e42f82e 100644 --- a/src/sage/combinat/algebraic_combinatorics.py +++ b/src/sage/combinat/algebraic_combinatorics.py @@ -60,5 +60,7 @@ Operads and their algebras -------------------------- +- :ref:`sage.combinat.free_dendriform_algebra` - :ref:`sage.combinat.free_prelie_algebra` +- :ref:`sage.algebras.free_zinbiel_algebra` """ diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index e273bdc8e87..723be98f59d 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -40,7 +40,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element import Element -from sage.structure.sage_object import op_NE, richcmp +from sage.structure.richcmp import op_NE, richcmp from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import matrix diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 21864b8b462..84324f048da 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -643,6 +643,35 @@ def node_to_str(bt): t_repr._baseline = t_repr._h - 1 return t_repr + def _sort_key(self): + """ + Return a tuple of nonnegative integers encoding the binary + tree ``self``. + + The first entry of the tuple is the number of children of the + root. Then the rest of the tuple is the concatenation of the + tuples associated to these children (we view the children of + a tree as trees themselves) from left to right. + + This tuple characterizes the tree uniquely, and can be used to + sort the binary trees. + + EXAMPLES:: + + sage: x = BinaryTree([]) + sage: y = (x.under(x)).over(x) + sage: y._sort_key() + (2, 2, 0, 0, 2, 0, 0) + sage: z = (x.over(x)).under(x) + sage: z._sort_key() + (2, 2, 0, 2, 0, 0, 0) + """ + l = len(self) + if l == 0: + return (0,) + resu = [l] + [u for t in self for u in t._sort_key()] + return tuple(resu) + def is_empty(self): """ Return whether ``self`` is empty. @@ -2453,6 +2482,10 @@ def comb(self, side='left'): A list of binary trees. + .. SEEALSO:: + + :meth:`over_decomposition`, :meth:`under_decomposition` + EXAMPLES:: sage: BT = BinaryTree( '.' ) @@ -3193,10 +3226,9 @@ def under(self, bt): [ \ ] [ 4 ] """ - B = self.parent()._element_constructor_ if bt.is_empty(): return self - lab = None + B = self.parent()._element_constructor_ if hasattr(bt, "label"): lab = bt.label() return B([self.under(bt[0]), bt[1]], lab) @@ -3205,6 +3237,177 @@ def under(self, bt): _backslash_ = under + def under_decomposition(self): + r""" + Return the unique maximal decomposition as an under product. + + This means that the tree is cut along all edges of its leftmost path. + + Beware that the factors are ordered starting from the root. + + .. SEEALSO:: + + :meth:`comb`, :meth:`over_decomposition` + + EXAMPLES:: + + sage: g = BinaryTree([]) + sage: r = g.over(g); r + [., [., .]] + sage: l = g.under(g); l + [[., .], .] + sage: l.under_decomposition() + [[., .], [., .]] + sage: r.under_decomposition() == [r] + True + + sage: x = r.under(g).under(r).under(g) + sage: ascii_art(x) + o + / + o + / \ + o o + / + o + \ + o + sage: x.under_decomposition() == [g,r,g,r] + True + """ + if self.is_empty(): + return [] + B = self.parent()._element_constructor_ + resu = [] + bt = self + while not bt.is_empty(): + if hasattr(bt, "label"): + lab = bt.label() + resu.append(B([None, bt[1]], lab)) + else: + resu.append(B([None, bt[1]])) + bt = bt[0] + return resu + + def over_decomposition(self): + """ + Return the unique maximal decomposition as an over product. + + This means that the tree is cut along all edges of its rightmost path. + + Beware that the factors are ordered starting from the root. + + .. SEEALSO:: + + :meth:`comb`, :meth:`under_decomposition` + + EXAMPLES:: + + sage: g = BinaryTree([]) + sage: r = g.over(g); r + [., [., .]] + sage: l = g.under(g); l + [[., .], .] + sage: r.over_decomposition() + [[., .], [., .]] + sage: l.over_decomposition() == [l] + True + + sage: x = g.over(l).over(l).over(g).over(g) + sage: ascii_art(x) + o + \ + _o_ + / \ + o o + / \ + o o + \ + o + sage: x.over_decomposition() == [g,l,l,g,g] + True + """ + if self.is_empty(): + return [] + B = self.parent()._element_constructor_ + resu = [] + bt = self + while not bt.is_empty(): + if hasattr(bt, "label"): + lab = bt.label() + resu.append(B([bt[0], None], lab)) + else: + resu.append(B([bt[0], None])) + bt = bt[1] + return resu + + def dendriform_shuffle(self, other): + """ + Return the list of terms in the dendriform product. + + This is the list of all binary trees that can be obtained by + identifying the rightmost path in ``self`` and the leftmost + path in ``other``. Every term corresponds to a shuffle of the + vertices on the rightmost path in ``self`` and the vertices on + the leftmost path in ``other``. + + EXAMPLES:: + + sage: u = BinaryTree() + sage: g = BinaryTree([]) + sage: l = BinaryTree([g, u]) + sage: r = BinaryTree([u, g]) + + sage: list(g.dendriform_shuffle(g)) + [[[., .], .], [., [., .]]] + + sage: list(l.dendriform_shuffle(l)) + [[[[[., .], .], .], .], [[[., .], [., .]], .], + [[., .], [[., .], .]]] + + sage: list(l.dendriform_shuffle(r)) + [[[[., .], .], [., .]], [[., .], [., [., .]]]] + + TESTS:: + + sage: list(u.dendriform_shuffle(u)) + [.] + sage: list(u.dendriform_shuffle(g)) + [[., .]] + sage: list(u.dendriform_shuffle(l)) + [[[., .], .]] + sage: list(u.dendriform_shuffle(r)) + [[., [., .]]] + sage: list(r.dendriform_shuffle(u)) + [[., [., .]]] + sage: list(l.dendriform_shuffle(u)) + [[[., .], .]] + """ + from sage.combinat.words.shuffle_product import ShuffleProduct_w1w2 + from sage.combinat.words.word import Word + if self.is_empty(): + yield other + elif other.is_empty(): + yield self + else: + B = self.parent()._element_constructor_ + left_list = self.over_decomposition() + right_list = other.under_decomposition() + w_left = Word('L' * len(left_list)) + w_right = Word('R' * len(right_list)) + for w in ShuffleProduct_w1w2(w_left, w_right): + t = B(None) + c_left_list = list(left_list) + c_right_list = list(right_list) + for letter in w: + if letter == 'L': + lt = c_left_list.pop() + t = lt.over(t) + else: + rt = c_right_list.pop() + t = t.under(rt) + yield t + def sylvester_class(self, left_to_right=False): r""" Iterate over the sylvester class corresponding to the binary tree @@ -4062,6 +4265,34 @@ def _repr_(self): else: return "%s%s" % (self._label, self[:]) + def _sort_key(self): + """ + Return a tuple encoding the labelled binary tree ``self``. + + The first entry of the tuple is a pair consisting of the + number of children of the root and the label of the root. Then + the rest of the tuple is the concatenation of the tuples + associated to these children (we view the children of + a tree as trees themselves) from left to right. + + This tuple characterizes the labelled tree uniquely, and can + be used to sort the labelled binary trees provided that the + labels belong to a type which is totally ordered. + + EXAMPLES:: + + sage: L2 = LabelledBinaryTree([], label='a') + sage: L3 = LabelledBinaryTree([], label='b') + sage: T23 = LabelledBinaryTree([L2, L3], label='c') + sage: T23._sort_key() + ((2, 'c'), (2, 'a'), (0,), (0,), (2, 'b'), (0,), (0,)) + """ + l = len(self) + if l == 0: + return ((0,),) + resu = [(l, self.label())] + [u for t in self for u in t._sort_key()] + return tuple(resu) + def binary_search_insert(self, letter): """ Return the result of inserting a letter ``letter`` into the @@ -4520,7 +4751,7 @@ def labelled_trees(self): # sage: BTsp_to_bintrees(BT.isotypes(range(5))[0]) # [., [., [., [., [., .]]]]] # sage: def spls(size): -# ....: return map(BTsp_to_bintrees, BT.isotypes(range(size)).list()) +# ....: return [BTsp_to_bintrees(u) for u in BT.isotypes(range(size)).list()] # sage: spls(3) # [[., [., [., .]]], [., [[., .], .]], [[., .], [., .]], [[., [., .]], .], [[[., .], .], .]] # sage: all(spls(i) == BinaryTrees(i).list() for i in range(5)) diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py index 3edee8ee181..d59ff36a105 100644 --- a/src/sage/combinat/constellation.py +++ b/src/sage/combinat/constellation.py @@ -41,14 +41,18 @@ .. [LaZv04] S. Lando and A. Zvonkine, "Graphs on surfaces and their applications", Springer-Verlag, 2004. """ -# ************************************************************************* -# Copyright (C) 2015-2016 Vincent Delecroix <20100.delecroix@gmail.com> -# Frederic Chapoton -# -# Distributed under the terms of the GNU General Public License (GPL) + +#***************************************************************************** +# Copyright (C) 2015-2016 Vincent Delecroix <20100.delecroix@gmail.com> +# Frederic Chapoton # -# The full text of the GPL is available at http://www.gnu.org/licenses/ -# ************************************************************************* +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from six.moves import range from six import integer_types @@ -56,8 +60,8 @@ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import (op_NE, op_EQ, richcmp_not_equal, - rich_to_bool) +from sage.structure.richcmp import (op_NE, op_EQ, richcmp_not_equal, + rich_to_bool) from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.rings.integer import Integer @@ -1464,7 +1468,7 @@ def perm_sym_domain(g): domain = set().union(*[a for cyc in g[1:-1].split(')(') for a in cyc.split(',')]) if all(s.isdigit() for s in domain): - return map(int, domain) + return [int(x) for x in domain] else: return domain elif parent(g) in Groups: diff --git a/src/sage/combinat/crystals/affine.py b/src/sage/combinat/crystals/affine.py index 286784f5174..ff18f2801d5 100644 --- a/src/sage/combinat/crystals/affine.py +++ b/src/sage/combinat/crystals/affine.py @@ -19,7 +19,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element_wrapper import ElementWrapper from sage.combinat.root_system.cartan_type import CartanType -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp class AffineCrystalFromClassical(UniqueRepresentation, Parent): diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index e1dbce27e3b..e89f37ff8c1 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -21,7 +21,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import Element -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.categories.regular_crystals import RegularCrystals from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.rings.infinity import Infinity diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 3a766a82c72..6baeae9166d 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -27,7 +27,7 @@ from sage.structure.element import Element from sage.structure.element_wrapper import ElementWrapper from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.categories.finite_crystals import FiniteCrystals from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.loop_crystals import LoopCrystals diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py index 6e1976cb8ff..c007c4dbd39 100644 --- a/src/sage/combinat/crystals/fast_crystals.py +++ b/src/sage/combinat/crystals/fast_crystals.py @@ -25,7 +25,7 @@ from sage.categories.classical_crystals import ClassicalCrystals from sage.structure.element import Element, parent from sage.combinat.root_system.cartan_type import CartanType -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp class FastCrystal(UniqueRepresentation, Parent): diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py index 4edb3576587..42467d2e85c 100644 --- a/src/sage/combinat/crystals/pbw_crystal.py +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -26,7 +26,7 @@ from sage.structure.element import Element from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.categories.highest_weight_crystals import HighestWeightCrystals from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.root_system.cartan_type import CartanType diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index 476fa40907c..c1bfd52854f 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -33,7 +33,7 @@ from sage.combinat.root_system.cartan_type import CartanType from sage.rings.integer import Integer from sage.rings.infinity import infinity -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp class Subcrystal(UniqueRepresentation, Parent): diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index ed1d9d9586f..38556471f11 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4202,10 +4202,11 @@ def BIBD_45_9_8(from_code=False): '6q533lm6w', '6rsie7cbk', '6tjgpxic0', '70k7ao9m0', '7103zqlvk', '71i1x52bm', '7447g0dfw', '7sogja9z4', '7up5z9m9u', '7w7esu6fm', '7zmqtlrpd', '81tsbnzsw', '8kofgi1he', '8mhi35nc1', '9cv1pjiaw', '9d6ef1dah', '9dftsor9c', '9du8c1vcw', '9jr5vsnj4', 'a8b405mps', 'ajqhmxkj4', 'ax2xsvfic'] - B = [Integer(x,base=36) for x in B] + B = [Integer(x, base=36) for x in B] return [[i for i in range(45) if x&(1<, +# +# 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.categories.algebras import Algebras +from sage.categories.magmatic_algebras import MagmaticAlgebras +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.words.alphabet import Alphabet +from sage.combinat.binary_tree import (BinaryTrees, BinaryTree, + LabelledBinaryTrees, + LabelledBinaryTree) +from sage.combinat.ordered_tree import OrderedTree, LabelledOrderedTree +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.cachefunc import cached_method +from sage.categories.rings import Rings +from sage.sets.family import Family + + +class FreeDendriformAlgebra(CombinatorialFreeModule): + r""" + The free dendriform algebra. + + Dendriform algebras are associative algebras, where the associative + product `*` is decomposed as a sum of two binary operations + + .. MATH:: + + x * y = x \succ y + x \prec y + + that satisfy the axioms: + + .. MATH:: + + (x \succ y) \prec z = x \succ (y \prec z), + + .. MATH:: + + (x \prec y) \prec z = x \prec (y * z). + + .. MATH:: + + (x * y) \succ z = x \succ (y \succ z). + + The free Dendriform algebra on a given set `E` has an explicit + description using (planar) binary trees, just as the free + associative algebra can be described using words. The underlying + vector space has a basis indexed by finite binary trees endowed + with a map from their vertices to `E`. In this basis, the + associative product of two (decorated) binary trees `S * T` is the + sum over all possible ways of identifying (glueing) the rightmost path in + `S` and the leftmost path in `T`. + + The decomposition of the associative product as the sum of two + binary operations `\succ` and + `\prec` is made by separating the terms according to the origin of + the root vertex. For `x \succ y`, one keeps the terms where the root + vertex comes from `y`, whereas for `x \prec y` one keeps the terms + where the root vertex comes from `x`. + + The free dendriform algebra can also be considered as the free + algebra over the Dendriform operad. + + .. NOTE:: + + The usual binary operator `*` is used for the + associative product. + + EXAMPLES:: + + sage: F = algebras.FreeDendriform(ZZ, 'xyz') + sage: x,y,z = F.gens() + sage: (x * y) * z + B[x[., y[., z[., .]]]] + B[x[., z[y[., .], .]]] + B[y[x[., .], z[., .]]] + B[z[x[., y[., .]], .]] + B[z[y[x[., .], .], .]] + + The free dendriform algebra is associative:: + + sage: x * (y * z) == (x * y) * z + True + + The associative product decomposes in two parts:: + + sage: x * y == F.prec(x, y) + F.succ(x, y) + True + + The axioms hold:: + + sage: F.prec(F.succ(x, y), z) == F.succ(x, F.prec(y, z)) + True + sage: F.prec(F.prec(x, y), z) == F.prec(x, y * z) + True + sage: F.succ(x * y, z) == F.succ(x, F.succ(y, z)) + True + + When there is only one generator, unlabelled trees are used instead:: + + sage: F1 = algebras.FreeDendriform(QQ, 'w') + sage: w = F1.gen(0); w + B[[., .]] + sage: w * w * w + B[[., [., [., .]]]] + B[[., [[., .], .]]] + B[[[., .], [., .]]] + B[[[., [., .]], .]] + B[[[[., .], .], .]] + + REFERENCES: + + - [LodayRonco]_ + """ + @staticmethod + def __classcall_private__(cls, R, names): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: F1 = algebras.FreeDendriform(QQ, 'xyz') + sage: F2 = algebras.FreeDendriform(QQ, ['x','y','z']) + sage: F3 = algebras.FreeDendriform(QQ, Alphabet('xyz')) + sage: F1 is F2 and F1 is F3 + True + """ + if R not in Rings(): + raise TypeError("argument R must be a ring") + return super(FreeDendriformAlgebra, cls).__classcall__(cls, R, + Alphabet(names)) + + def __init__(self, R, names=None): + """ + Initialize ``self``. + + TESTS:: + + sage: A = algebras.FreeDendriform(QQ, '@'); A + Free Dendriform algebra on one generator ['@'] over Rational Field + sage: TestSuite(A).run() + + sage: F = algebras.FreeDendriform(QQ, 'xy') + sage: TestSuite(F).run() # long time + """ + if names.cardinality() == 1: + Trees = BinaryTrees() + key = BinaryTree._sort_key + else: + Trees = LabelledBinaryTrees() + key = LabelledBinaryTree._sort_key + # Here one would need LabelledBinaryTrees(names) + # so that one can restrict the labels to some fixed set + self._alphabet = names + cat = Algebras(R).WithBasis().Graded() + CombinatorialFreeModule.__init__(self, R, Trees, + latex_prefix="", + sorting_key=key, + category=cat) + + def variable_names(self): + r""" + Return the names of the variables. + + EXAMPLES:: + + sage: R = algebras.FreeDendriform(QQ, 'xy') + sage: R.variable_names() + {'x', 'y'} + """ + return self._alphabet + + def _repr_(self): + """ + Return the string representation of ``self``. + + EXAMPLES:: + + sage: algebras.FreeDendriform(QQ, '@') # indirect doctest + Free Dendriform algebra on one generator ['@'] over Rational Field + """ + n = self.algebra_generators().cardinality() + if n == 1: + gen = "one generator" + else: + gen = "{} generators".format(n) + s = "Free Dendriform algebra on {} {} over {}" + try: + return s.format(gen, self._alphabet.list(), self.base_ring()) + except NotImplementedError: + return s.format(gen, self._alphabet, self.base_ring()) + + def gen(self, i): + r""" + Return the ``i``-th generator of the algebra. + + INPUT: + + - ``i`` -- an integer + + EXAMPLES:: + + sage: F = algebras.FreeDendriform(ZZ, 'xyz') + sage: F.gen(0) + B[x[., .]] + + sage: F.gen(4) + Traceback (most recent call last): + ... + IndexError: argument i (= 4) must be between 0 and 2 + """ + G = self.algebra_generators() + n = G.cardinality() + if i < 0 or not i < n: + m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) + raise IndexError(m) + return G[G.keys().unrank(i)] + + @cached_method + def algebra_generators(self): + r""" + Return the generators of this algebra. + + These are the binary trees with just one vertex. + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(ZZ, 'fgh'); A + Free Dendriform algebra on 3 generators ['f', 'g', 'h'] + over Integer Ring + sage: list(A.algebra_generators()) + [B[f[., .]], B[g[., .]], B[h[., .]]] + + sage: A = algebras.FreeDendriform(QQ, ['x1','x2']) + sage: list(A.algebra_generators()) + [B[x1[., .]], B[x2[., .]]] + """ + Trees = self.basis().keys() + return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) + + def gens(self): + """ + Return the generators of ``self`` (as an algebra). + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(ZZ, 'fgh') + sage: A.gens() + (B[f[., .]], B[g[., .]], B[h[., .]]) + """ + return tuple(self.algebra_generators()) + + def degree_on_basis(self, t): + """ + Return the degree of a binary tree in the free Dendriform algebra. + + This is the number of vertices. + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ,'@') + sage: RT = A.basis().keys() + sage: u = RT([], '@') + sage: A.degree_on_basis(u.over(u)) + 2 + """ + return t.node_number() + + @cached_method + def an_element(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, 'xy') + sage: A.an_element() + B[x[., .]] + 2*B[x[., x[., .]]] + 2*B[x[x[., .], .]] + """ + o = self.gen(0) + return o + 2 * o * o + + def some_elements(self): + """ + Return some elements of the free dendriform algebra. + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ,'@') + sage: A.some_elements() + [B[.], + B[[., .]], + B[[., [., .]]] + B[[[., .], .]], + B[.] + B[[., [., .]]] + B[[[., .], .]]] + + With several generators:: + + sage: A = algebras.FreeDendriform(QQ, 'xy') + sage: A.some_elements() + [B[.], + B[x[., .]], + B[x[., x[., .]]] + B[x[x[., .], .]], + B[.] + B[x[., x[., .]]] + B[x[x[., .], .]]] + """ + u = self.one() + o = self.gen(0) + x = o * o + y = u + x + return [u, o, x, y] + + @cached_method + def one(self): + r""" + Return the element `1` of ``self``. + + This is the unit for the associative dendriform product `*`. + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: A.one() + B[.] + sage: A = algebras.FreeDendriform(QQ, 'xy') + sage: A.one() + B[.] + """ + Trees = self.basis().keys() + return self._monomial(Trees(None)) + + def product_on_basis(self, x, y): + r""" + Return the `*` associative dendriform product of two trees. + + This is the sum over all possible ways of identifying the + rightmost path in `x` and the leftmost path in `y`. Every term + corresponds to a shuffle of the vertices on the rightmost path + in `x` and the vertices on the leftmost path in `y`. + + .. SEEALSO:: + + - :meth:`succ_product_on_basis`, :meth:`prec_product_on_basis` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = RT([], '@') + sage: A.product_on_basis(x, x) + B[[., [., .]]] + B[[[., .], .]] + """ + return self.sum(self.basis()[u] for u in x.dendriform_shuffle(y)) + + def succ_product_on_basis(self, x, y): + r""" + Return the `\succ` dendriform product of two trees. + + This is the sum over all possible ways to identify the rightmost path + in `x` and the leftmost path in `y`, with the additional condition + that the root vertex of the result comes from `y`. + + The usual symbol for this operation is `\succ`. + + .. SEEALSO:: + + - :meth:`product_on_basis`, :meth:`prec_product_on_basis` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = RT([], '@') + sage: A.succ_product_on_basis(x, x) + B[[[., .], .]] + + TESTS:: + + sage: u = A.one().support()[0] + sage: A.succ_product_on_basis(u, u) + Traceback (most recent call last): + ... + ValueError: dendriform products | < | and | > | are not defined + """ + if y.is_empty(): + if x.is_empty(): + raise ValueError("dendriform products | < | and | > | are " + "not defined") + else: + return [] + if x.is_empty(): + return [y] + K = self.basis().keys() + if hasattr(y, 'label'): + return self.sum(self.basis()[K([u, y[1]], y.label())] + for u in x.dendriform_shuffle(y[0])) + + return self.sum(self.basis()[K([u, y[1]])] + for u in x.dendriform_shuffle(y[0])) + + @lazy_attribute + def succ(self): + r""" + Return the `\succ` dendriform product. + + This is the sum over all possible ways of identifying the + rightmost path in `x` and the leftmost path in `y`, with the + additional condition that the root vertex of the result comes + from `y`. + + The usual symbol for this operation is `\succ`. + + .. SEEALSO:: + + :meth:`product`, :meth:`prec`, :meth:`over`, :meth:`under` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = A.gen(0) + sage: A.succ(x, x) + B[[[., .], .]] + """ + suc = self.succ_product_on_basis + return self._module_morphism(self._module_morphism(suc, position=0, + codomain=self), + position=1) + + def prec_product_on_basis(self, x, y): + r""" + Return the `\prec` dendriform product of two trees. + + This is the sum over all possible ways of identifying the + rightmost path in `x` and the leftmost path in `y`, with the + additional condition that the root vertex of the result comes + from `x`. + + The usual symbol for this operation is `\prec`. + + .. SEEALSO:: + + - :meth:`product_on_basis`, :meth:`succ_product_on_basis` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = RT([], '@') + sage: A.prec_product_on_basis(x, x) + B[[., [., .]]] + + TESTS:: + + sage: u = A.one().support()[0] + sage: A.prec_product_on_basis(u, u) + Traceback (most recent call last): + ... + ValueError: dendriform products | < | and | > | are not defined + """ + if x.is_empty() and y.is_empty(): + raise ValueError("dendriform products | < | and | > | are " + "not defined") + if x.is_empty(): + return [] + if y.is_empty(): + return [x] + K = self.basis().keys() + if hasattr(y, 'label'): + return self.sum(self.basis()[K([x[0], u], x.label())] + for u in x[1].dendriform_shuffle(y)) + + return self.sum(self.basis()[K([x[0], u])] + for u in x[1].dendriform_shuffle(y)) + + @lazy_attribute + def prec(self): + r""" + Return the `\prec` dendriform product. + + This is the sum over all possible ways to identify the rightmost path + in `x` and the leftmost path in `y`, with the additional condition + that the root vertex of the result comes from `x`. + + The usual symbol for this operation is `\prec`. + + .. SEEALSO:: + + :meth:`product`, :meth:`succ`, :meth:`over`, :meth:`under` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = A.gen(0) + sage: A.prec(x, x) + B[[., [., .]]] + """ + pre = self.prec_product_on_basis + return self._module_morphism(self._module_morphism(pre, position=0, + codomain=self), + position=1) + + @lazy_attribute + def over(self): + r""" + Return the over product. + + The over product `x/y` is the binary tree obtained by + grafting the root of `y` at the rightmost leaf of `x`. + + The usual symbol for this operation is `/`. + + .. SEEALSO:: + + :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`under` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = A.gen(0) + sage: A.over(x, x) + B[[., [., .]]] + """ + ov = lambda x, y: self._monomial(x.over(y)) + return self._module_morphism(self._module_morphism(ov, position=0, + codomain=self), + position=1) + + @lazy_attribute + def under(self): + r""" + Return the under product. + + The over product `x \backslash y` is the binary tree obtained by + grafting the root of `x` at the leftmost leaf of `y`. + + The usual symbol for this operation is `\backslash`. + + .. SEEALSO:: + + :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`over` + + EXAMPLES:: + + sage: A = algebras.FreeDendriform(QQ, '@') + sage: RT = A.basis().keys() + sage: x = A.gen(0) + sage: A.under(x, x) + B[[[., .], .]] + """ + und = lambda x, y: self._monomial(x.under(y)) + return self._module_morphism(self._module_morphism(und, position=0, + codomain=self), + position=1) + + # after this line : coercion + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + EXAMPLES:: + + sage: R = algebras.FreeDendriform(QQ, 'xy') + sage: x, y = R.gens() + sage: R(x) + B[x[., .]] + sage: R(x+4*y) + B[x[., .]] + 4*B[y[., .]] + + sage: Trees = R.basis().keys() + sage: R(Trees([],'x')) + B[x[., .]] + sage: D = algebras.FreeDendriform(ZZ, 'xy') + sage: X, Y = D.gens() + sage: R(X-Y).parent() + Free Dendriform algebra on 2 generators ['x', 'y'] over Rational Field + """ + if x in self.basis().keys(): + return self.monomial(x) + try: + P = x.parent() + if isinstance(P, FreeDendriformAlgebra): + if P is self: + return x + return self.element_class(self, x.monomial_coefficients()) + except AttributeError: + raise TypeError('not able to coerce this in this algebra') + # Ok, not a dendriform algebra element (or should not be viewed as one). + + def _coerce_map_from_(self, R): + r""" + Return ``True`` if there is a coercion from ``R`` into ``self`` + and ``False`` otherwise. + + The things that coerce into ``self`` are + + - free dendriform algebras in the same variables over a base with + a coercion map into ``self.base_ring()`` + + EXAMPLES:: + + sage: F = algebras.FreeDendriform(GF(7), 'xyz'); F + Free Dendriform algebra on 3 generators ['x', 'y', 'z'] + over Finite Field of size 7 + + Elements of the free dendriform algebra canonically coerce in:: + + sage: x, y, z = F.gens() + sage: F.coerce(x+y) == x+y + True + + The free dendriform algebra over `\ZZ` on `x, y, z` coerces in, since + `\ZZ` coerces to `\GF{7}`:: + + sage: G = algebras.FreeDendriform(ZZ, 'xyz') + sage: Gx,Gy,Gz = G.gens() + sage: z = F.coerce(Gx+Gy); z + B[x[., .]] + B[y[., .]] + sage: z.parent() is F + True + + However, `\GF{7}` does not coerce to `\ZZ`, so the free dendriform + algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + + sage: G.coerce(y) + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Free Dendriform algebra + on 3 generators ['x', 'y', 'z'] over Finite Field of size + 7 to Free Dendriform algebra on 3 generators ['x', 'y', 'z'] + over Integer Ring + + TESTS:: + + sage: F = algebras.FreeDendriform(ZZ, 'xyz') + sage: G = algebras.FreeDendriform(QQ, 'xyz') + sage: H = algebras.FreeDendriform(ZZ, 'y') + sage: F._coerce_map_from_(G) + False + sage: G._coerce_map_from_(F) + True + sage: F._coerce_map_from_(H) + False + sage: F._coerce_map_from_(QQ) + False + sage: G._coerce_map_from_(QQ) + False + sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) + False + """ + # free prelie algebras in the same variables + # over any base that coerces in: + if isinstance(R, FreeDendriformAlgebra): + if R.variable_names() == self.variable_names(): + if self.base_ring().has_coerce_map_from(R.base_ring()): + return True + return False diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 956059f72fb..b39ab4fde01 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -9,7 +9,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element import Element -from sage.structure.sage_object import op_EQ, op_NE +from sage.structure.richcmp import op_EQ, op_NE from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.six_vertex_model import (SquareIceModel, diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index f036bcb7540..f38d9959893 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -84,7 +84,7 @@ from sage.structure.element import Element from sage.structure.global_options import GlobalOptions from sage.structure.parent import Parent -from sage.structure.sage_object import op_NE, op_EQ, op_LT, op_LE, op_GT, op_GE +from sage.structure.richcmp import op_NE, op_EQ, op_LT, op_LE, op_GT, op_GE from sage.structure.unique_representation import UniqueRepresentation from sage.graphs.digraph import DiGraph diff --git a/src/sage/combinat/lyndon_word.py b/src/sage/combinat/lyndon_word.py index a115885d95c..e278a1f3d97 100644 --- a/src/sage/combinat/lyndon_word.py +++ b/src/sage/combinat/lyndon_word.py @@ -12,6 +12,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import absolute_import +from six.moves import builtins from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -20,9 +21,9 @@ from sage.rings.all import Integer from sage.arith.all import factorial, divisors, gcd, moebius from sage.misc.all import prod -from six.moves import builtins + from . import necklace -from .integer_vector import IntegerVectors +from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.words import FiniteWords @@ -338,6 +339,7 @@ def __iter__(self): for z in necklace._sfc(self._e[k:], equality=True): yield self._words([i+k+1 for i in z], check=False) + class LyndonWords_nk(UniqueRepresentation, Parent): r""" Lyndon words of fixed length `n` over the alphabet `{1, 2, ..., k}`. @@ -481,7 +483,8 @@ def StandardBracketedLyndonWords(n, k): sage: SBLW33.random_element() [1, [1, 2]] """ - return StandardBracketedLyndonWords_nk(n,k) + return StandardBracketedLyndonWords_nk(n, k) + class StandardBracketedLyndonWords_nk(UniqueRepresentation, Parent): def __init__(self, n, k): @@ -506,7 +509,7 @@ def __repr__(self): sage: repr(StandardBracketedLyndonWords(3, 3)) 'Standard bracketed Lyndon words from an alphabet of size 3 of length 3' """ - return "Standard bracketed Lyndon words from an alphabet of size %s of length %s"%(self._n, self._k) + return "Standard bracketed Lyndon words from an alphabet of size %s of length %s" % (self._n, self._k) def cardinality(self): """ @@ -543,8 +546,8 @@ def __iter__(self): [2, [2, 3]], [[2, 3], 3]] """ - from builtins import map - return map(standard_bracketing, self._lyndon) + for x in self._lyndon: + yield standard_bracketing(x) def standard_bracketing(lw): diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 8906c53bdd3..321f4d57a1e 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -984,29 +984,32 @@ def GS_skew_hadamard_smallcases(n, existence=False, check=True): """ from sage.combinat.matrices.hadamard_matrix import\ williamson_goethals_seidel_skew_hadamard_matrix as WGS + def pmtoZ(s): - return map(lambda x: 1 if x=='+' else -1, s) + return [1 if x == '+' else -1 for x in s] if existence: return n in [36, 52, 92] - if n==36: - a=[ 1, 1, 1, -1, 1, -1, 1, -1, -1] - b=[ 1, -1, 1, 1, -1, -1, 1, 1, -1] - c=[-1, -1]+[1]*6+[-1] - d=[ 1, 1, 1, -1, 1, 1, -1, 1, 1] - return WGS(a,b,c,d, check=check) - if n==52: - a=pmtoZ('++++-++--+---') - b=pmtoZ('-+-++----++-+') - c=pmtoZ('--+-+++++-+++') - return WGS(a,b,c,c, check=check) - if n==92: + if n == 36: + a = [ 1, 1, 1, -1, 1, -1, 1, -1, -1] + b = [ 1, -1, 1, 1, -1, -1, 1, 1, -1] + c = [-1, -1]+[1]*6+[-1] + d = [ 1, 1, 1, -1, 1, 1, -1, 1, 1] + return WGS(a, b, c, d, check=check) + + if n == 52: + a = pmtoZ('++++-++--+---') + b = pmtoZ('-+-++----++-+') + c = pmtoZ('--+-+++++-+++') + return WGS(a, b, c, c, check=check) + + if n == 92: a = [1,-1,-1,-1,-1,-1,-1,-1, 1, 1,-1, 1,-1, 1,-1,-1, 1, 1, 1, 1, 1, 1, 1] b = [1, 1,-1,-1, 1,-1,-1, 1, 1, 1, 1,-1,-1, 1, 1, 1, 1,-1,-1, 1,-1,-1, 1] c = [1, 1,-1,-1,-1, 1,-1, 1,-1, 1,-1, 1, 1,-1, 1,-1, 1,-1, 1,-1,-1,-1, 1] d = [1,-1,-1,-1,-1, 1,-1,-1, 1,-1,-1, 1, 1,-1,-1, 1,-1,-1, 1,-1,-1,-1,-1] - return WGS(a,b,c,d, check=check) + return WGS(a, b, c, d, check=check) return None _skew_had_cache={} diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index eedd0ad9d54..abb7dfe554e 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -78,7 +78,7 @@ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import richcmp_not_equal, richcmp +from sage.structure.richcmp import richcmp_not_equal, richcmp from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.modules.free_module import FreeModule diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index 2c336c9fadf..3a25075deda 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -56,7 +56,7 @@ class CartanType(SageObject, CartanType_abstract): super classes (see :meth:`~sage.combinat.root_system.cartan_type.CartanType_abstract._add_abstract_superclass`):: sage: t.__class__.mro() - [, , , , , , ] + [, , , , , , <... 'object'>] The index set of the reducible Cartan type is obtained by relabelling successively the nodes of the Dynkin diagrams of diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index 2e031933e09..3b4718714e6 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -49,7 +49,7 @@ from sage.matrix.constructor import matrix, diagonal_matrix from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import richcmp, richcmp_not_equal +from sage.structure.richcmp import richcmp, richcmp_not_equal from sage.categories.all import WeylGroups, FiniteWeylGroups, AffineWeylGroups from sage.categories.permutation_groups import PermutationGroups from sage.sets.family import Family diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index 73b54f844aa..1c2c2424ad8 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -22,7 +22,7 @@ from sage.rings.integer cimport Integer, smallInteger from sage.rings.rational cimport Rational from libc.string cimport memcpy, memcmp from sage.combinat.words.word_datatypes cimport WordDatatype -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool from cpython.number cimport PyIndex_Check, PyNumber_Check from cpython.sequence cimport PySequence_Check diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index d9df6818912..c9f8ac41410 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -326,7 +326,7 @@ from .mpolynomialsystemgenerator import MPolynomialSystemGenerator from sage.rings.polynomial.term_order import TermOrder -from sage.structure.sage_object import richcmp_not_equal, rich_to_bool, op_LT +from sage.structure.richcmp import richcmp_not_equal, rich_to_bool, op_LT def SR(n=1, r=1, c=1, e=4, star=False, **kwargs): diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 1eec5744f4d..716301e111b 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -115,7 +115,7 @@ from cpython.int cimport PyInt_FromSize_t from cpython.slice cimport PySlice_GetIndicesEx from sage.libs.gmp.mpn cimport mpn_rshift, mpn_lshift, mpn_copyi, mpn_ior_n, mpn_zero, mpn_copyd, mpn_cmp from sage.libs.flint.flint cimport FLINT_BIT_COUNT as BIT_COUNT -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool cimport cython diff --git a/src/sage/databases/db_modular_polynomials.py b/src/sage/databases/db_modular_polynomials.py index ccbfe3f87b6..b75b38b5bd5 100644 --- a/src/sage/databases/db_modular_polynomials.py +++ b/src/sage/databases/db_modular_polynomials.py @@ -65,7 +65,9 @@ def _dbz_to_integer_list(name): """ from sage.rings.integer import Integer data = _dbz_to_string(name) - return [map(Integer, row.strip().split(" ")) for row in data.split("\n")[:-1]] + return [[Integer(v) for v in row.strip().split(" ")] + for row in data.split("\n")[:-1]] + def _dbz_to_integers(name): r""" @@ -76,7 +78,8 @@ def _dbz_to_integers(name): [0, 1] """ from sage.rings.integer import Integer - return map(Integer, _dbz_to_string(name).split()) + return [Integer(i) for i in _dbz_to_string(name).split()] + class ModularPolynomialDatabase: def _dbpath(self, level): @@ -89,7 +92,7 @@ def _dbpath(self, level): sage: C._dbpath(8) 'PolMod/Cls/pol.008.dbz' """ - return "PolMod/%s/pol.%03d.dbz"%(self.model, level) + return "PolMod/%s/pol.%03d.dbz" % (self.model, level) def __repr__(self): r""" diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 48f6a7d19e9..77a1c8f8488 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -1,4 +1,4 @@ -## -*- encoding: utf-8 -*- +# -*- encoding: utf-8 -*- """ Parsing docstrings @@ -22,15 +22,18 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import +from sage.misc.six import u -import re, sys +import re +import sys import doctest import collections from sage.repl.preparse import preparse, strip_string_literals +from Cython.Build.Dependencies import strip_string_literals as cython_strip_string_literals from functools import reduce + from .external import available_software float_regex = re.compile('\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') @@ -64,6 +67,56 @@ ansi_escape_sequence = re.compile(r'(\x1b[@-Z\\-~]|\x1b\[.*?[@-~])') +def remove_unicode_u(string): + """ + Given a string, try to remove all unicode u prefixes inside. + + This will help to keep the same doctest results in Python2 and Python3. + The input string is typically the documentation of a method or function. + This string may contain some letters u that are unicode python2 prefixes. + The aim is to remove all of these u and only them. + + INPUT: + + - ``string`` -- either ``unicode`` or ``bytes`` (if ``bytes``, it + will be converted to ``unicode`` assuming UTF-8) + + OUTPUT: ``unicode`` string + + EXAMPLES:: + + sage: from sage.doctest.parsing import remove_unicode_u as remu + sage: remu("u'you'") + "'you'" + sage: remu('u') + 'u' + sage: remu("[u'am', 'stram', u'gram']") + "['am', 'stram', 'gram']" + sage: remu('[u"am", "stram", u"gram"]') + '["am", "stram", "gram"]' + + This deals correctly with nested quotes:: + + sage: str = '''[u"Singular's stuff", u'good']''' + sage: print(remu(str)) + ["Singular's stuff", 'good'] + + TESTS: + + This supports python2 str type as input:: + + sage: euro = "'€'" + sage: print(remu(euro)) + '€' + """ + stripped, replacements = cython_strip_string_literals(u(string), + "__remove_unicode_u") + string = stripped.replace('u"', '"').replace("u'", "'") + for magic, literal in replacements.items(): + string = string.replace(magic, literal) + return string + + def parse_optional_tags(string): """ Returns a set consisting of the optional tags from the following @@ -811,6 +864,15 @@ def check_output(self, want, got, optionflags): sage: print("[ - 1, 2]") # abs tol 1e-10 [-1,2] + + Tolerance for string results with unicode prefix:: + + sage: a = u'Cyrano'; a + 'Cyrano' + sage: b = [u'Fermat', u'Euler']; b + ['Fermat', 'Euler'] + sage: c = u'you'; c + 'you' """ got = self.human_readable_escape_sequences(got) if isinstance(want, MarkedOutput): @@ -834,8 +896,15 @@ def check_output(self, want, got, optionflags): # The doctest is successful if the "want" and "got" # intervals have a non-empty intersection return all(a.overlaps(b) for a, b in zip(want_intervals, got_values)) + ok = doctest.OutputChecker.check_output(self, want, got, optionflags) - return ok + if ok or 'u' not in got: + return ok + + # accept the same answer where strings have unicode prefix u + # for smoother transition to python3 + got = remove_unicode_u(got) + return doctest.OutputChecker.check_output(self, want, got, optionflags) def output_difference(self, example, got, optionflags): r""" diff --git a/src/sage/dynamics/interval_exchanges/reduced.py b/src/sage/dynamics/interval_exchanges/reduced.py index 2a19e53f88a..776a802240f 100644 --- a/src/sage/dynamics/interval_exchanges/reduced.py +++ b/src/sage/dynamics/interval_exchanges/reduced.py @@ -423,16 +423,17 @@ def ReducedPermutationsIET_iterator( nintervals = Integer(nintervals) - if not(nintervals > 0): + if nintervals <= 0: raise ValueError('number of intervals must be positive') - a0 = range(1,nintervals+1) - f = lambda x: ReducedPermutationIET([a0,list(x)], - alphabet=alphabet) + a0 = range(1, nintervals + 1) + f = lambda x: ReducedPermutationIET([a0, list(x)], + alphabet=alphabet) return map(f, Permutations(nintervals)) else: return filter(lambda x: x.is_irreducible(), - ReducedPermutationsIET_iterator(nintervals,False,alphabet)) + ReducedPermutationsIET_iterator(nintervals, False, alphabet)) + class ReducedPermutationIET(ReducedPermutation, PermutationIET): """ diff --git a/src/sage/dynamics/interval_exchanges/template.py b/src/sage/dynamics/interval_exchanges/template.py index 852d0b993aa..857ceff7fc9 100644 --- a/src/sage/dynamics/interval_exchanges/template.py +++ b/src/sage/dynamics/interval_exchanges/template.py @@ -29,10 +29,9 @@ from six.moves import range from six import iteritems, add_metaclass -from sage.structure.sage_object import SageObject - from copy import copy +from sage.structure.sage_object import SageObject from sage.rings.integer import Integer from sage.combinat.words.alphabet import Alphabet from sage.graphs.graph import DiGraph @@ -302,7 +301,7 @@ def _repr_(self): return '' elif self._repr_type == 'reduced': - return ''.join(map(str,self[1])) + return ''.join(map(str, self[1])) else: f = getattr(self, self._repr_type) @@ -354,8 +353,8 @@ def str(self, sep= "\n"): """ l = self.list() - s0 = ' '.join(map(str,l[0])) - s1 = ' '.join(map(str,l[1])) + s0 = ' '.join(map(str, l[0])) + s1 = ' '.join(map(str, l[1])) return s0 + sep + s1 _repr_type = 'str' @@ -2797,10 +2796,8 @@ def vertex_iterator(self): a b c d d c b a """ - from builtins import map - return map( - lambda x: self._vertex_to_permutation(x), - self._succ.keys()) + for x in self._succ.keys(): + yield self._vertex_to_permutation(x) def edges(self,labels=True): r""" diff --git a/src/sage/env.py b/src/sage/env.py index 68758245a1d..aa43b16ca50 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -17,6 +17,7 @@ ######################################################################## from __future__ import absolute_import +import glob import os import socket import site @@ -142,15 +143,23 @@ def _add_variable_or_fallback(key, fallback, force=False): # locate singular shared object if UNAME[:6] == "CYGWIN": - extension = "dll" -elif UNAME == "Darwin": - extension = "dylib" + SINGULAR_SO = ([None] + glob.glob(os.path.join( + SAGE_LOCAL, "bin", "cygSingular-*.dll")))[-1] else: - extension = "so" -# library name changed from libsingular to libSingular btw 3.x and 4.x -SINGULAR_SO = SAGE_LOCAL+"/lib/libSingular."+extension + if UNAME == "Darwin": + extension = "dylib" + else: + extension = "so" + # library name changed from libsingular to libSingular btw 3.x and 4.x + SINGULAR_SO = SAGE_LOCAL+"/lib/libSingular."+extension + _add_variable_or_fallback('SINGULAR_SO', SINGULAR_SO) +if not SINGULAR_SO or not os.path.exists(SINGULAR_SO): + raise RuntimeError( + "libSingular not found--a working Singular install in $SAGE_LOCAL " + "is required for Sage to work") + # post process if ' ' in DOT_SAGE: if UNAME[:6] == 'CYGWIN': diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx index 476194bd32d..da0aa4c1c99 100644 --- a/src/sage/ext/fast_eval.pyx +++ b/src/sage/ext/fast_eval.pyx @@ -90,7 +90,7 @@ AUTHORS: from cysignals.memory cimport sig_malloc, sig_free from sage.ext.fast_callable import fast_callable, Wrapper -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool cimport cython from cpython.ref cimport Py_INCREF diff --git a/src/sage/functions/airy.py b/src/sage/functions/airy.py index 0c1721f256a..da3b614abe5 100644 --- a/src/sage/functions/airy.py +++ b/src/sage/functions/airy.py @@ -161,12 +161,13 @@ def __init__(self): sage: airy_ai_simple(x)._sympy_() airyai(x) """ - BuiltinFunction.__init__(self, "airy_ai", - latex_name=r'\operatorname{Ai}', + BuiltinFunction.__init__(self, 'airy_ai', + latex_name=r"\operatorname{Ai}", conversions=dict(mathematica='AiryAi', maxima='airy_ai', sympy='airyai', - fricas='airyAi')) + fricas='airyAi', + giac='Airy_Ai')) def _derivative_(self, x, diff_param=None): """ @@ -267,7 +268,7 @@ def __init__(self): sage: airy_ai_prime(x)._sympy_() airyaiprime(x) """ - BuiltinFunction.__init__(self, "airy_ai_prime", + BuiltinFunction.__init__(self, 'airy_ai_prime', latex_name=r"\operatorname{Ai}'", conversions=dict(mathematica='AiryAiPrime', maxima='airy_dai', @@ -590,12 +591,13 @@ def __init__(self): sage: f._sympy_() airybi(x) """ - BuiltinFunction.__init__(self, "airy_bi", - latex_name=r'\operatorname{Bi}', + BuiltinFunction.__init__(self, 'airy_bi', + latex_name=r"\operatorname{Bi}", conversions=dict(mathematica='AiryBi', maxima='airy_bi', sympy='airybi', - fricas='airyBi')) + fricas='airyBi', + giac='Airy_Bi')) def _derivative_(self, x, diff_param=None): """ @@ -698,7 +700,7 @@ def __init__(self): sage: airy_bi_prime(x)._sympy_() airybiprime(x) """ - BuiltinFunction.__init__(self, "airy_bi_prime", + BuiltinFunction.__init__(self, 'airy_bi_prime', latex_name=r"\operatorname{Bi}'", conversions=dict(mathematica='AiryBiPrime', maxima='airy_dbi', diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 40dacbc337e..d2bab7c4ee1 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -341,11 +341,12 @@ def __init__(self): sage: bessel_J(x, x)._sympy_() besselj(x, x) """ - BuiltinFunction.__init__(self, "bessel_J", nargs=2, + BuiltinFunction.__init__(self, 'bessel_J', nargs=2, conversions=dict(mathematica='BesselJ', maxima='bessel_j', sympy='besselj', - fricas='besselJ')) + fricas='besselJ', + giac='BesselJ')) def _eval_(self, n, x): """ @@ -557,11 +558,12 @@ def __init__(self): sage: bessel_Y(x, x)._sympy_() bessely(x, x) """ - BuiltinFunction.__init__(self, "bessel_Y", nargs=2, + BuiltinFunction.__init__(self, 'bessel_Y', nargs=2, conversions=dict(mathematica='BesselY', maxima='bessel_y', sympy='bessely', - fricas='besselY')) + fricas='besselY', + giac='BesselY')) def _eval_(self, n, x): """ @@ -762,7 +764,7 @@ def __init__(self): sage: bessel_I(x, x)._sympy_() besseli(x, x) """ - BuiltinFunction.__init__(self, "bessel_I", nargs=2, + BuiltinFunction.__init__(self, 'bessel_I', nargs=2, conversions=dict(mathematica='BesselI', maxima='bessel_i', sympy='besseli', @@ -962,7 +964,7 @@ def __init__(self): sage: bessel_K(x, x)._sympy_() besselk(x, x) """ - BuiltinFunction.__init__(self, "bessel_K", nargs=2, + BuiltinFunction.__init__(self, 'bessel_K', nargs=2, conversions=dict(mathematica='BesselK', maxima='bessel_k', sympy='besselk', diff --git a/src/sage/functions/error.py b/src/sage/functions/error.py index db523fd85fb..b2d6bc3307f 100644 --- a/src/sage/functions/error.py +++ b/src/sage/functions/error.py @@ -182,7 +182,8 @@ def __init__(self): BuiltinFunction.__init__(self, "erf", latex_name=r"\operatorname{erf}", conversions=dict(maxima='erf', sympy='erf', - fricas='erf')) + fricas='erf', + giac='erf')) def _eval_(self, x): """ @@ -400,7 +401,8 @@ def __init__(self): latex_name=r"\operatorname{erfc}", conversions=dict(maxima='erfc', sympy='erfc', - fricas='erfc')) + fricas='erfc', + giac='erfc')) def _eval_(self, x): """ diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index 4de0da92b24..2e9ca64a7a0 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -435,7 +435,8 @@ def __init__(self): asinh(x) """ GinacFunction.__init__(self, "arcsinh", latex_name=r"{\rm arcsinh}", - conversions=dict(maxima='asinh', sympy='asinh', fricas='asinh')) + conversions=dict(maxima='asinh', sympy='asinh', fricas='asinh', + giac='asinh')) arcsinh = asinh = Function_arcsinh() @@ -519,7 +520,8 @@ def __init__(self): acosh(x) """ GinacFunction.__init__(self, "arccosh", latex_name=r"{\rm arccosh}", - conversions=dict(maxima='acosh', sympy='acosh', fricas='acosh')) + conversions=dict(maxima='acosh', sympy='acosh', fricas='acosh', + giac='acosh')) arccosh = acosh = Function_arccosh() @@ -577,7 +579,8 @@ def __init__(self): atanh(x) """ GinacFunction.__init__(self, "arctanh", latex_name=r"{\rm arctanh}", - conversions=dict(maxima='atanh', sympy='atanh', fricas='atanh')) + conversions=dict(maxima='atanh', sympy='atanh', fricas='atanh', + giac='atanh')) arctanh = atanh = Function_arctanh() diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 931b247ba5f..b9634ddd561 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -574,9 +574,10 @@ def __init__(self): chebyshev_t(_SAGE_VAR_n,chebyshev_t(_SAGE_VAR_n,_SAGE_VAR_x)) """ ChebyshevFunction.__init__(self, 'chebyshev_T', nargs=2, - conversions=dict(maxima='chebyshev_t', - mathematica='ChebyshevT', - sympy='chebyshevt')) + conversions=dict(maxima='chebyshev_t', + mathematica='ChebyshevT', + sympy='chebyshevt', + giac='tchebyshev1')) def _latex_(self): r""" @@ -883,9 +884,10 @@ def __init__(self): chebyshev_u(_SAGE_VAR_n,_SAGE_VAR_x) """ ChebyshevFunction.__init__(self, 'chebyshev_U', nargs=2, - conversions=dict(maxima='chebyshev_u', - mathematica='ChebyshevU', - sympy='chebyshevu')) + conversions=dict(maxima='chebyshev_u', + mathematica='ChebyshevU', + sympy='chebyshevu', + giac='tchebyshev2')) def _latex_(self): r""" @@ -1142,9 +1144,11 @@ def __init__(self): sage: loads(dumps(legendre_P)) legendre_P """ - BuiltinFunction.__init__(self, "legendre_P", nargs=2, latex_name=r"P", - conversions={'maxima':'legendre_p', 'mathematica':'LegendreP', - 'maple':'LegendreP'}) + BuiltinFunction.__init__(self, 'legendre_P', nargs=2, latex_name=r"P", + conversions={'maxima':'legendre_p', + 'mathematica':'LegendreP', + 'maple':'LegendreP', + 'giac':'legendre'}) def _eval_(self, n, x, *args, **kwds): r""" diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 7ea0a63c52b..37dafcf925d 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -107,7 +107,8 @@ def __init__(self): """ GinacFunction.__init__(self, "abs", latex_name=r"\mathrm{abs}", conversions=dict(sympy='Abs', - mathematica='Abs')) + mathematica='Abs', + giac='abs')) abs = abs_symbolic = Function_abs() @@ -196,7 +197,8 @@ def __init__(self): """ BuiltinFunction.__init__(self, "ceil", conversions=dict(maxima='ceiling', - sympy='ceiling')) + sympy='ceiling', + giac='ceil')) def _print_latex_(self, x): r""" @@ -352,7 +354,7 @@ def __init__(self): floor """ BuiltinFunction.__init__(self, "floor", - conversions=dict(sympy='floor')) + conversions=dict(sympy='floor', giac='floor')) def _print_latex_(self, x): r""" @@ -685,12 +687,13 @@ def __init__(self): :meth:`sage.functions.other.gamma` """ - GinacFunction.__init__(self, "gamma", latex_name=r'\Gamma', - ginac_name='tgamma', - conversions={'mathematica':'Gamma', - 'maple':'GAMMA', - 'sympy':'gamma', - 'fricas':'Gamma'}) + GinacFunction.__init__(self, 'gamma', latex_name=r"\Gamma", + ginac_name='tgamma', + conversions={'mathematica':'Gamma', + 'maple':'GAMMA', + 'sympy':'gamma', + 'fricas':'Gamma', + 'giac':'Gamma'}) gamma1 = Function_gamma() @@ -853,7 +856,7 @@ def __init__(self): """ BuiltinFunction.__init__(self, "gamma", nargs=2, latex_name=r"\Gamma", conversions={'maxima':'gamma_incomplete', 'mathematica':'Gamma', - 'maple':'GAMMA', 'sympy':'uppergamma'}) + 'maple':'GAMMA', 'sympy':'uppergamma', 'giac':'ugamma'}) def _eval_(self, x, y): """ @@ -1003,7 +1006,7 @@ def __init__(self): """ BuiltinFunction.__init__(self, "gamma_inc_lower", nargs=2, latex_name=r"\gamma", conversions={'maxima':'gamma_greek', 'mathematica':'Gamma', - 'maple':'GAMMA', 'sympy':'lowergamma'}) + 'maple':'GAMMA', 'sympy':'lowergamma', 'giac':'igamma'}) def _eval_(self, x, y): """ @@ -1302,7 +1305,8 @@ def __init__(self): """ GinacFunction.__init__(self, "psi", nargs=2, latex_name='\psi', conversions=dict(mathematica='PolyGamma', - sympy='polygamma')) + sympy='polygamma', + giac='Psi')) def _maxima_init_evaled_(self, *args): """ @@ -1381,6 +1385,9 @@ def psi(x, *args, **kwds): # two functions with different number of arguments and the same name symbol_table['functions']['psi'] = psi +def _swap_psi(a, b): return psi(b, a) +register_symbol(_swap_psi, {'giac':'Psi'}) + class Function_factorial(GinacFunction): def __init__(self): r""" @@ -1504,7 +1511,8 @@ def __init__(self): conversions=dict(maxima='factorial', mathematica='Factorial', sympy='factorial', - fricas='factorial')) + fricas='factorial', + giac='factorial')) def _eval_(self, x): """ @@ -1636,7 +1644,8 @@ def __init__(self): conversions=dict(maxima='binomial', mathematica='Binomial', sympy='binomial', - fricas='binomial')) + fricas='binomial', + giac='comb')) def _binomial_sym(self, n, k): """ @@ -1834,11 +1843,13 @@ def __init__(self): sage: beta(-1.3,-0.4) -4.92909641669610 """ - GinacFunction.__init__(self, "beta", nargs=2, - conversions=dict(maxima='beta', - mathematica='Beta', - sympy='beta', - fricas='Beta')) + GinacFunction.__init__(self, 'beta', nargs=2, + latex_name=r"\operatorname{B}", + conversions=dict(maxima='beta', + mathematica='Beta', + sympy='beta', + fricas='Beta', + giac='Beta')) beta = Function_beta() @@ -2056,7 +2067,8 @@ def __init__(self): BuiltinFunction.__init__(self, "arg", conversions=dict(maxima='carg', mathematica='Arg', - sympy='arg')) + sympy='arg', + giac='arg')) def _eval_(self, x): """ @@ -2207,7 +2219,8 @@ def __init__(self): """ GinacFunction.__init__(self, "real_part", conversions=dict(maxima='realpart', - sympy='re'), + sympy='re', + giac='re'), alt_name="real") def __call__(self, x, **kwargs): @@ -2266,7 +2279,8 @@ def __init__(self): """ GinacFunction.__init__(self, "imag_part", conversions=dict(maxima='imagpart', - sympy='im'), + sympy='im', + giac='im'), alt_name="imag") def __call__(self, x, **kwargs): @@ -2358,7 +2372,8 @@ def __init__(self): conjugate """ GinacFunction.__init__(self, "conjugate", - conversions=dict(sympy='conjugate')) + conversions=dict(sympy='conjugate', + giac='conj')) conjugate = Function_conjugate() diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index f3db1e3be07..769651f8af9 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -131,7 +131,7 @@ def __init__(self): sage: zeta(SR(1.0)) Infinity """ - GinacFunction.__init__(self, "zeta") + GinacFunction.__init__(self, 'zeta', conversions={'giac':'Zeta'}) zeta = Function_zeta() diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 088a3218948..e7e7a311cd9 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -118,7 +118,7 @@ def __init__(self): sin(1/42*pi) """ GinacFunction.__init__(self, 'sin', latex_name=r"\sin", - conversions=dict(maxima='sin',mathematica='Sin')) + conversions=dict(maxima='sin',mathematica='Sin',giac='sin')) sin = Function_sin() @@ -180,7 +180,7 @@ def __init__(self): -cos(1/42*pi) """ GinacFunction.__init__(self, 'cos', latex_name=r"\cos", - conversions=dict(maxima='cos',mathematica='Cos')) + conversions=dict(maxima='cos',mathematica='Cos',giac='cos')) cos = Function_cos() @@ -538,7 +538,7 @@ def __init__(self): False """ GinacFunction.__init__(self, 'arcsin', latex_name=r"\arcsin", - conversions=dict(maxima='asin', sympy='asin', fricas="asin")) + conversions=dict(maxima='asin', sympy='asin', fricas="asin", giac="asin")) arcsin = asin = Function_arcsin() @@ -604,7 +604,7 @@ def __init__(self): False """ GinacFunction.__init__(self, 'arccos', latex_name=r"\arccos", - conversions=dict(maxima='acos', sympy='acos', fricas='acos')) + conversions=dict(maxima='acos', sympy='acos', fricas='acos', giac='acos')) arccos = acos = Function_arccos() @@ -672,7 +672,7 @@ def __init__(self): 1/2*pi """ GinacFunction.__init__(self, 'arctan', latex_name=r"\arctan", - conversions=dict(maxima='atan', sympy='atan', fricas='atan')) + conversions=dict(maxima='atan', sympy='atan', fricas='atan', giac='atan')) arctan = atan = Function_arctan() @@ -722,7 +722,7 @@ def __init__(self): """ GinacFunction.__init__(self, 'arccot', latex_name=r"\operatorname{arccot}", - conversions=dict(maxima='acot', sympy='acot', fricas='acot')) + conversions=dict(maxima='acot', sympy='acot', fricas='acot',giac='acot')) def _eval_numpy_(self, x): """ @@ -778,7 +778,7 @@ def __init__(self): (0.45227844715119064-0.5306375309525178j) """ GinacFunction.__init__(self, 'arccsc', latex_name=r"\operatorname{arccsc}", - conversions=dict(maxima='acsc', sympy='acsc', fricas='acsc')) + conversions=dict(maxima='acsc', sympy='acsc', fricas='acsc', giac='acsc')) def _eval_numpy_(self, x): """ @@ -836,7 +836,7 @@ def __init__(self): (1.118517879643706+0.5306375309525178j) """ GinacFunction.__init__(self, 'arcsec', latex_name=r"\operatorname{arcsec}", - conversions=dict(maxima='asec', sympy='asec', fricas='asec')) + conversions=dict(maxima='asec', sympy='asec', fricas='asec', giac='asec')) def _eval_numpy_(self, x): """ diff --git a/src/sage/games/sudoku.py b/src/sage/games/sudoku.py index 9564674371c..dd1cbc8fb23 100644 --- a/src/sage/games/sudoku.py +++ b/src/sage/games/sudoku.py @@ -20,11 +20,10 @@ # The full text of the GPL is available at: # http://www.gnu.org/licenses/ ###################################################################### -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import from six.moves import range +from six import string_types -import six from sage.structure.sage_object import SageObject @@ -34,7 +33,7 @@ def sudoku(m): INPUT: - - ``m`` - a square Sage matrix over `\ZZ`, where zeros are blank entries + - ``m`` - a square Sage matrix over `\ZZ`, where zeros are blank entries OUTPUT: @@ -103,7 +102,6 @@ class Sudoku(SageObject): the puzzle. For two-digit entries, a = 10, b = 11, etc. - verify_input - default = ``True``, use ``False`` if you know the input is valid - EXAMPLES:: sage: a = Sudoku('5...8..49...5...3..673....115..........2.8..........187....415..3...2...49..5...3') @@ -136,7 +134,6 @@ class Sudoku(SageObject): |4 9 1|8 5 6|7 2 3| +-----+-----+-----+ """ - def __init__(self, puzzle, verify_input = True): r""" Initialize a Sudoku puzzle, determine its size, sanity-check the inputs. @@ -186,7 +183,7 @@ def __init__(self, puzzle, verify_input = True): if verify_input and not(puzzle.is_square()): raise ValueError('Sudoku puzzle must be a square matrix') self.puzzle = tuple([int(x) for x in puzzle.list()]) - elif isinstance(puzzle, six.string_types): + elif isinstance(puzzle, string_types): puzzle_size = int(round(sqrt(len(puzzle)))) puzzle_numeric = [] for char in puzzle: @@ -207,47 +204,54 @@ def __init__(self, puzzle, verify_input = True): if (x < 0) or (x > self.n*self.n): raise ValueError('Sudoku puzzle has an invalid entry') - - def __cmp__(self, other): + def __eq__(self, other): r""" Compares two Sudoku puzzles, based on the underlying representation of the puzzles as tuples. - A puzzle with fewer entries is considered less than a - puzzle with more entries. For two puzzles of the same - size, their entries are compared lexicographically - based on a row-major order. Since blank entries are - carried as zeros, progressively "more completed" puzzles - are considered larger (but this is not an equivalence). - EXAMPLES:: sage: a = Sudoku('.4..32....14..3.') sage: b = Sudoku('8..6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') sage: c = Sudoku('1..6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') sage: d = Sudoku('81.6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') - sage: a.__cmp__(b) - -1 - sage: b.__cmp__(b) - 0 - sage: b.__cmp__(c) - 1 - sage: b.__cmp__(d) - -1 + + sage: a == b + False + sage: b == b + True + sage: b == c + False + sage: b == d + False """ - left = self.puzzle - right = tuple(other.to_list()) - if left < right: - return -1 - elif left > right: - return 1 - else: - return 0 + return self.puzzle == tuple(other.to_list()) + + def __ne__(self, other): + """ + Check that ``self`` is not equal to ``other``. + + EXAMPLES:: + + sage: a = Sudoku('.4..32....14..3.') + sage: b = Sudoku('8..6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') + sage: c = Sudoku('1..6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') + sage: d = Sudoku('81.6..9.5.............2.31...7318.6.24.....73...........279.1..5...8..36..3......') + sage: a != b + True + sage: b != b + False + sage: b != c + True + sage: b != d + True + """ + return not (self == other) def _repr_(self): r""" - Returns a concise description of a Sudoku puzzle using a string representation. + Return a concise description of a Sudoku puzzle using a string representation. See the docstring for :func:`to_ascii` for more information on the format. @@ -262,7 +266,7 @@ def _repr_(self): def _latex_(self): r"""nodetex - Returns a `\LaTeX` representation of a Sudoku puzzle as an array environment. + Return a `\LaTeX` representation of a Sudoku puzzle as an array environment. EXAMPLES:: @@ -274,7 +278,7 @@ def _latex_(self): def _matrix_(self, R=None): r""" - Returns the puzzle as a matrix to support Sage's + Return the puzzle as a matrix to support Sage's :func:`~sage.matrix.constructor.matrix` constructor. The base ring will be `\ZZ` if ``None`` is provided, @@ -305,7 +309,7 @@ def _matrix_(self, R=None): def to_string(self): r""" - Constructs a string representing a Sudoku puzzle. + Construct a string representing a Sudoku puzzle. Blank entries are represented as periods, single digits are not converted and two digit entries are @@ -352,10 +356,9 @@ def to_string(self): raise ValueError('Sudoku string representation is only valid for puzzles of size 36 or smaller') return ''.join(encoded) - def to_list(self): r""" - Constructs a list representing a Sudoku puzzle, in row-major order. + Construct a list representing a Sudoku puzzle, in row-major order. EXAMPLES:: @@ -373,10 +376,9 @@ def to_list(self): """ return list(self.puzzle) - def to_matrix(self): r""" - Constructs a Sage matrix over `\ZZ` representing a Sudoku puzzle. + Construct a Sage matrix over `\ZZ` representing a Sudoku puzzle. EXAMPLES:: @@ -402,7 +404,7 @@ def to_matrix(self): def to_ascii(self): r""" - Constructs an ASCII-art version of a Sudoku puzzle. + Construct an ASCII-art version of a Sudoku puzzle. This is a modified version of the ASCII version of a subdivided matrix. EXAMPLES:: @@ -432,7 +434,7 @@ def to_ascii(self): def to_latex(self): r""" - Creates a string of `\LaTeX` code representing a Sudoku puzzle or solution. + Create a string of `\LaTeX` code representing a Sudoku puzzle or solution. EXAMPLES:: @@ -468,7 +470,7 @@ def to_latex(self): def solve(self, algorithm = 'dlx'): r""" - Returns a generator object for the solutions of a Sudoku puzzle. + Return a generator object for the solutions of a Sudoku puzzle. INPUT: @@ -606,10 +608,9 @@ def solve(self, algorithm = 'dlx'): for soln in gen: yield Sudoku(soln, verify_input = 'False') - def backtrack(self): r""" - Returns a generator which iterates through all solutions of a Sudoku puzzle. + Return a generator which iterates through all solutions of a Sudoku puzzle. This function is intended to be called from the :func:`~sage.games.sudoku.Sudoku.solve` method @@ -715,10 +716,9 @@ def backtrack(self): for soln in solutions: yield soln - def dlx(self, count_only=False): r""" - Returns a generator that iterates through all solutions of a Sudoku puzzle. + Return a generator that iterates through all solutions of a Sudoku puzzle. INPUT: @@ -881,9 +881,3 @@ def make_row(row, col, entry): yield solution else: yield None - - - - - - diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py index c18ecc4a327..f2b862fa448 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_point.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_point.py @@ -61,7 +61,7 @@ #*********************************************************************** from sage.structure.element import Element -from sage.structure.sage_object import richcmp, op_NE +from sage.structure.richcmp import richcmp, op_NE from sage.symbolic.all import I from sage.misc.latex import latex from sage.matrix.matrix import is_Matrix diff --git a/src/sage/geometry/linear_expression.py b/src/sage/geometry/linear_expression.py index 50b2c74e270..5a93e06f6eb 100644 --- a/src/sage/geometry/linear_expression.py +++ b/src/sage/geometry/linear_expression.py @@ -39,7 +39,7 @@ from six.moves import zip from sage.structure.parent import Parent -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.structure.element import ModuleElement from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index d8755fadca5..795692b10bf 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -17,7 +17,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element import Element -from sage.structure.sage_object import op_EQ, op_NE, op_LE, op_GE, op_LT +from sage.structure.richcmp import op_EQ, op_NE, op_LE, op_GE, op_LT from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity diff --git a/src/sage/geometry/point_collection.pyx b/src/sage/geometry/point_collection.pyx index c901159e462..b0df2f0a541 100644 --- a/src/sage/geometry/point_collection.pyx +++ b/src/sage/geometry/point_collection.pyx @@ -78,7 +78,7 @@ need to spend time and memory four times. from __future__ import print_function from sage.structure.sage_object cimport SageObject -from sage.structure.sage_object cimport richcmp_not_equal, richcmp +from sage.structure.richcmp cimport richcmp_not_equal, richcmp from sage.matrix.all import matrix from sage.misc.all import latex diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 13877571a54..8ed573ade30 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -574,7 +574,7 @@ def plot(self, - ``projection_direction`` -- coordinate list/tuple/iterable or ``None`` (default). The direction to use for the - :meth:`schlegel_projection`` of the polytope. If not + :meth:`schlegel_projection` of the polytope. If not specified, no projection is used in dimensions `< 4` and parallel projection is used in dimension `4`. @@ -1333,7 +1333,7 @@ def Vrepresentation(self, index=None): OUTPUT: The optional argument is an index running from ``0`` to - `self.n_Vrepresentation()-1``. If present, the + ``self.n_Vrepresentation()-1``. If present, the V-representation object at the given index will be returned. Without an argument, returns the list of all V-representation objects. @@ -2225,7 +2225,7 @@ def is_inscribed(self, certificate=False): ALGORITHM: The function first computes the circumsphere of a full-dimensional - simplex with vertices of `self`. It is found by lifting the points on a + simplex with vertices of ``self``. It is found by lifting the points on a paraboloid to find the hyperplane on which the circumsphere is lifted. Then, it checks if all other vertices are equidistant to the circumcenter of that simplex. @@ -3216,8 +3216,8 @@ def truncation(self, cut_frac=None): INPUT: - - ``cut_frac`` -- integer. how deeply to cut into the edge. - Default is `\frac{1}{3}`. + - ``cut_frac`` -- integer, how deeply to cut into the edge. + Default is `\frac{1}{3}`. OUTPUT: diff --git a/src/sage/geometry/toric_lattice_element.pyx b/src/sage/geometry/toric_lattice_element.pyx index 910edcb8f17..1ca5187e4e3 100644 --- a/src/sage/geometry/toric_lattice_element.pyx +++ b/src/sage/geometry/toric_lattice_element.pyx @@ -101,7 +101,7 @@ from sage.modules.vector_integer_dense cimport Vector_integer_dense from sage.structure.coerce_exceptions import CoercionException from sage.structure.element cimport Element, Vector from sage.rings.integer cimport Integer -from sage.structure.sage_object cimport richcmp_not_equal, richcmp +from sage.structure.richcmp cimport richcmp_not_equal, richcmp def is_ToricLatticeElement(x): diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index c15986baf80..73d2f9899eb 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -60,17 +60,22 @@ References Functions --------- """ + #***************************************************************************** -# Copyright (C) 2015 David Coudert +# Copyright (C) 2015 David Coudert # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" +from libc.stdint cimport uint32_t +from cysignals.signals cimport sig_on, sig_off + include "sage/data_structures/bitset.pxi" -from libc.stdint cimport uint32_t from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph from sage.ext.memory_allocator cimport MemoryAllocator diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 832c29ba4ec..796c966b10b 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1,11 +1,3 @@ -#***************************************************************************** -# Copyright (C) 2015 Michele Borassi michele.borassi@imtlucca.it -# -# 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/ -#***************************************************************************** r""" Interface to run Boost algorithms @@ -44,7 +36,18 @@ Functions --------- """ -include "cysignals/signals.pxi" +#***************************************************************************** +# Copyright (C) 2015 Michele Borassi michele.borassi@imtlucca.it +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from cysignals.signals cimport sig_check, sig_on, sig_off + cdef boost_graph_from_sage_graph(BoostGenGraph *g, g_sage): r""" @@ -57,10 +60,10 @@ cdef boost_graph_from_sage_graph(BoostGenGraph *g, g_sage): from sage.graphs.generic_graph import GenericGraph if not isinstance(g_sage, GenericGraph): - raise ValueError("The input parameter must be a Sage graph.") + raise TypeError("the input must be a Sage graph") if g.num_verts() > 0: - raise ValueError("The Boost graph in input must be empty") + raise AssertionError("the given Boost graph must be empty") N = g_sage.num_verts() cdef dict vertex_to_int = {v:i for i,v in enumerate(g_sage.vertices())} @@ -71,9 +74,10 @@ cdef boost_graph_from_sage_graph(BoostGenGraph *g, g_sage): for u,v in g_sage.edge_iterator(labels=None): g.add_edge(vertex_to_int[u], vertex_to_int[v]) + cdef boost_weighted_graph_from_sage_graph(BoostWeightedGraph *g, g_sage, - weight_function = None): + weight_function=None): r""" Initializes the Boost weighted graph ``g`` to be equal to ``g_sage``. @@ -96,10 +100,10 @@ cdef boost_weighted_graph_from_sage_graph(BoostWeightedGraph *g, from sage.graphs.generic_graph import GenericGraph if not isinstance(g_sage, GenericGraph): - raise ValueError("The input parameter must be a Sage graph.") + raise TypeError("the input must be a Sage graph") if g.num_verts() > 0: - raise ValueError("The Boost graph in input must be empty") + raise AssertionError("the given Boost graph must be empty") N = g_sage.num_verts() cdef dict vertex_to_int = {v:i for i,v in enumerate(g_sage.vertices())} @@ -109,28 +113,17 @@ cdef boost_weighted_graph_from_sage_graph(BoostWeightedGraph *g, if weight_function is not None: for e in g_sage.edge_iterator(): - try: - g.add_edge(vertex_to_int[e[0]], - vertex_to_int[e[1]], - float(weight_function(e))) - - except (ValueError, TypeError): - raise ValueError("The weight function cannot find the" + - " weight of " + str(e) + ".") - + g.add_edge(vertex_to_int[e[0]], + vertex_to_int[e[1]], + float(weight_function(e))) elif g_sage.weighted(): for u,v,w in g_sage.edge_iterator(): - try: - g.add_edge(vertex_to_int[u], vertex_to_int[v], float(w)) - except (ValueError, TypeError): - raise ValueError("The weight function cannot find the" + - " weight of " + str((u,v,w)) + ".") + g.add_edge(vertex_to_int[u], vertex_to_int[v], float(w)) else: for u,v in g_sage.edge_iterator(labels=False): g.add_edge(vertex_to_int[u], vertex_to_int[v], 1) - cdef boost_edge_connectivity(BoostVecGenGraph *g): r""" Computes the edge connectivity of the input Boost graph. @@ -138,20 +131,25 @@ cdef boost_edge_connectivity(BoostVecGenGraph *g): The output is a pair ``[ec,edges]``, where ``ec`` is the edge connectivity, ``edges`` is the list of edges in a minimum cut. """ + cdef result_ec result + + sig_on() result = g[0].edge_connectivity() + sig_off() - cdef int i + cdef size_t i edges = [(result.edges[i], result.edges[i+1]) for i in range(0, result.edges.size(), 2)] - return [result.ec, edges] + return (result.ec, edges) + cpdef edge_connectivity(g): r""" Computes the edge connectivity of the input graph, using Boost. - The output is a pair ``[ec,edges]``, where ``ec`` is the edge connectivity, - ``edges`` is the list of edges in a minimum cut. + OUTPUT: a pair ``(ec, edges)``, where ``ec`` is the edge + connectivity, ``edges`` is the list of edges in a minimum cut. .. SEEALSO:: @@ -164,15 +162,14 @@ cpdef edge_connectivity(g): sage: from sage.graphs.base.boost_graph import edge_connectivity sage: g = graphs.CompleteGraph(5) sage: edge_connectivity(g) - [4, [(0, 1), (0, 2), (0, 3), (0, 4)]] + (4, [(0, 1), (0, 2), (0, 3), (0, 4)]) Vertex-labeled graphs:: sage: from sage.graphs.base.boost_graph import edge_connectivity sage: g = graphs.GridGraph([2,2]) sage: edge_connectivity(g) - [2, [((0, 0), (0, 1)), ((0, 0), (1, 0))]] - + (2, [((0, 0), (0, 1)), ((0, 0), (1, 0))]) """ from sage.graphs.graph import Graph from sage.graphs.digraph import DiGraph @@ -184,7 +181,6 @@ cpdef edge_connectivity(g): if isinstance(g, Graph): boost_graph_from_sage_graph(&g_boost_und, g) - sig_check() ec, edges = boost_edge_connectivity(&g_boost_und) elif isinstance(g, DiGraph): @@ -193,13 +189,13 @@ cpdef edge_connectivity(g): "in Boost. The result may be mathematically unreliable.",18753) boost_graph_from_sage_graph(&g_boost_dir, g) - sig_check() ec, edges = boost_edge_connectivity(&g_boost_dir) else: - raise ValueError("The input must be a Sage graph.") + raise TypeError("the input must be a Sage graph") + + return (ec, [(int_to_vertex[u], int_to_vertex[v]) for (u,v) in edges]) - return [ec, [(int_to_vertex[u], int_to_vertex[v]) for (u,v) in edges]] cdef boost_clustering_coeff(BoostGenGraph *g, vertices): r""" @@ -211,28 +207,32 @@ cdef boost_clustering_coeff(BoostGenGraph *g, vertices): each vertex (stored as an integer) its clustering coefficient. """ cdef result_cc result + cdef double result_d + cdef v_index vi cdef dict clust_of_v if len(vertices) == g.num_verts(): + sig_on() result = g[0].clustering_coeff_all() + sig_off() clust_of_v = {v:result.clust_of_v[v] for v in range(g.num_verts())} - return [result.average_clustering_coefficient, clust_of_v] + return (result.average_clustering_coefficient, clust_of_v) else: - clust_of_v = {v:g[0].clustering_coeff(v) for v in vertices} - return [(sum(clust_of_v.itervalues()) / len(clust_of_v)), clust_of_v] + clust_of_v = {} + for v in vertices: + vi = v + sig_on() + result_d = g[0].clustering_coeff(vi) + sig_off() + clust_of_v[v] = result_d + return ((sum(clust_of_v.itervalues()) / len(clust_of_v)), clust_of_v) -cpdef clustering_coeff(g, vertices = None): +cpdef clustering_coeff(g, vertices=None): r""" Computes the clustering coefficient of the input graph, using Boost. - The output is a pair ``[average_clustering_coefficient, clust_of_v]``, where - ``average_clustering_coefficient`` is the average clustering of the vertices - in variable ``vertices``, ``clust_of_v`` is a dictionary that associates to - each vertex its clustering coefficient. If ``vertices`` is ``None``, all - vertices are considered. - .. SEEALSO:: :meth:`sage.graphs.generic_graph.GenericGraph.clustering_coeff` @@ -244,6 +244,12 @@ cpdef clustering_coeff(g, vertices = None): - ``vertices`` (list) - the list of vertices we need to analyze (if ``None``, we will compute the clustering coefficient of all vertices). + OUTPUT: a pair ``(average_clustering_coefficient, clust_of_v)``, where + ``average_clustering_coefficient`` is the average clustering of the vertices + in variable ``vertices``, ``clust_of_v`` is a dictionary that associates to + each vertex its clustering coefficient. If ``vertices`` is ``None``, all + vertices are considered. + EXAMPLES: Computing the clustering coefficient of a clique:: @@ -251,33 +257,31 @@ cpdef clustering_coeff(g, vertices = None): sage: from sage.graphs.base.boost_graph import clustering_coeff sage: g = graphs.CompleteGraph(5) sage: clustering_coeff(g) - [1.0, {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}] + (1.0, {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}) sage: clustering_coeff(g, vertices = [0,1,2]) - [1.0, {0: 1.0, 1: 1.0, 2: 1.0}] + (1.0, {0: 1.0, 1: 1.0, 2: 1.0}) Of a non-clique graph with triangles:: sage: g = graphs.IcosahedralGraph() sage: clustering_coeff(g, vertices=[1,2,3]) - [0.5, {1: 0.5, 2: 0.5, 3: 0.5}] + (0.5, {1: 0.5, 2: 0.5, 3: 0.5}) With labels:: sage: g.relabel(list("abcdefghiklm")) sage: clustering_coeff(g, vertices="abde") - [0.5, {'a': 0.5, 'b': 0.5, 'd': 0.5, 'e': 0.5}] + (0.5, {'a': 0.5, 'b': 0.5, 'd': 0.5, 'e': 0.5}) """ from sage.graphs.graph import Graph - sig_on() # These variables are automatically deleted when the function terminates. cdef BoostVecGraph g_boost cdef list g_vertices = g.vertices() cdef dict vertex_to_int = {v:i for i,v in enumerate(g_vertices)} if not isinstance(g, Graph): - sig_off() - raise ValueError("The input must be a Sage graph.") + raise TypeError("the input must be a Sage Graph") boost_graph_from_sage_graph(&g_boost, g) @@ -285,13 +289,12 @@ cpdef clustering_coeff(g, vertices = None): vertices = g_vertices vertices_boost = [vertex_to_int[v] for v in vertices] - [average_clustering, clust_v_int] = boost_clustering_coeff(&g_boost, vertices_boost) + average_clustering, clust_v_int = boost_clustering_coeff(&g_boost, vertices_boost) clust_v_sage = {g_vertices[v]: clust_v_int[v] for v in vertices_boost} - sig_off() - return [average_clustering, clust_v_sage] + return (average_clustering, clust_v_sage) -cpdef dominator_tree(g, root, return_dict = False): +cpdef dominator_tree(g, root, return_dict=False): r""" Uses Boost to compute the dominator tree of ``g``, rooted at ``root``. @@ -376,50 +379,53 @@ cpdef dominator_tree(g, root, return_dict = False): sage: dominator_tree('I am not a graph', 0) Traceback (most recent call last): ... - ValueError: The input g must be a Sage graph. + TypeError: the input must be a Sage Graph or DiGraph If ``root`` is not a vertex, an error is raised:: sage: digraphs.TransitiveTournament(10).dominator_tree('Not a vertex!') Traceback (most recent call last): ... - ValueError: The input root must be a vertex of g. + ValueError: the input root must be a vertex of the given graph sage: graphs.GridGraph([2,2]).dominator_tree(0) Traceback (most recent call last): ... - ValueError: The input root must be a vertex of g. - + ValueError: the input root must be a vertex of the given graph """ from sage.graphs.graph import Graph from sage.graphs.digraph import DiGraph - if not isinstance(g, Graph) and not isinstance(g, DiGraph): - raise ValueError("The input g must be a Sage graph.") + if not isinstance(g, (Graph, DiGraph)): + raise TypeError("the input must be a Sage Graph or DiGraph") if not root in g.vertices(): - raise ValueError("The input root must be a vertex of g.") + raise ValueError("the input root must be a vertex of the given graph") - sig_on() # These variables are automatically deleted when the function terminates. cdef BoostVecGraph g_boost_und cdef BoostVecDiGraph g_boost_dir cdef vector[v_index] result + cdef v_index vi cdef dict vertex_to_int = {v:i for i,v in enumerate(g.vertices())} cdef list int_to_vertex = g.vertices() if isinstance(g, Graph): boost_graph_from_sage_graph(&g_boost_und, g) - result = g_boost_und.dominator_tree(vertex_to_int[root]) + vi = vertex_to_int[root] + sig_on() + result = g_boost_und.dominator_tree(vi) + sig_off() elif isinstance(g, DiGraph): boost_graph_from_sage_graph(&g_boost_dir, g) - result = g_boost_dir.dominator_tree(vertex_to_int[root]) - - sig_off() + vi = vertex_to_int[root] + sig_on() + result = g_boost_dir.dominator_tree(vi) + sig_off() cdef v_index no_parent = -1 if return_dict: - return {v:(None if result[vertex_to_int[v]] == no_parent else int_to_vertex[ result[vertex_to_int[v]]]) for v in g.vertices()}; + return {v:(None if result[vertex_to_int[v]] == no_parent else int_to_vertex[ result[vertex_to_int[v]]]) for v in g.vertices()} edges = [[int_to_vertex[ result[vertex_to_int[v]]], v] for v in g.vertices() if result[vertex_to_int[v]] != no_parent] @@ -439,7 +445,7 @@ cpdef dominator_tree(g, root, return_dict = False): return Graph(edges) -cpdef bandwidth_heuristics(g, algorithm = 'cuthill_mckee'): +cpdef bandwidth_heuristics(g, algorithm='cuthill_mckee'): r""" Uses Boost heuristics to approximate the bandwidth of the input graph. @@ -492,11 +498,11 @@ cpdef bandwidth_heuristics(g, algorithm = 'cuthill_mckee'): sage: bandwidth_heuristics(digraphs.Path(10)) Traceback (most recent call last): ... - ValueError: The input g must be a Graph. + TypeError: the input must be a Sage Graph sage: bandwidth_heuristics("I am not a graph!") Traceback (most recent call last): ... - ValueError: The input g must be a Graph. + TypeError: the input must be a Sage Graph Given a wrong algorithm:: @@ -504,7 +510,7 @@ cpdef bandwidth_heuristics(g, algorithm = 'cuthill_mckee'): sage: bandwidth_heuristics(graphs.PathGraph(3), algorithm='tip top') Traceback (most recent call last): ... - ValueError: Algorithm 'tip top' not yet implemented. Please contribute. + ValueError: unknown algorithm 'tip top' Given a graph with no edges:: @@ -519,13 +525,12 @@ cpdef bandwidth_heuristics(g, algorithm = 'cuthill_mckee'): # Tests for errors and trivial cases if not isinstance(g, Graph): - raise ValueError("The input g must be a Graph.") + raise TypeError("the input must be a Sage Graph") if not algorithm in ['cuthill_mckee', 'king']: - raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) + raise ValueError(f"unknown algorithm {algorithm!r}") if g.num_edges()==0: - return (0, g.vertices()); + return (0, g.vertices()) - sig_on() # These variables are automatically deleted when the function terminates. cdef BoostVecGraph g_boost cdef vector[v_index] result @@ -533,15 +538,18 @@ cpdef bandwidth_heuristics(g, algorithm = 'cuthill_mckee'): cdef list int_to_vertex = g.vertices() boost_graph_from_sage_graph(&g_boost, g) - result = g_boost.bandwidth_ordering(algorithm=='cuthill_mckee') + cdef bint use_cuthill_mckee = (algorithm == 'cuthill_mckee') + sig_on() + result = g_boost.bandwidth_ordering(use_cuthill_mckee) + sig_off() cdef int n = g.num_verts() cdef dict pos = {int_to_vertex[ result[i]]:i for i in range(n)} cdef int bandwidth = max([abs(pos[u]-pos[v]) for u,v in g.edges(labels=False)]) - sig_off() return (bandwidth, [int_to_vertex[ result[i]] for i in range(n)]) + cpdef min_spanning_tree(g, weight_function=None, algorithm='Kruskal'): @@ -599,7 +607,7 @@ cpdef min_spanning_tree(g, sage: min_spanning_tree("I am not a graph!") Traceback (most recent call last): ... - ValueError: The input g must be a Sage Graph. + TypeError: the input must be a Sage Graph Given a wrong algorithm:: @@ -614,45 +622,45 @@ cpdef min_spanning_tree(g, sage: min_spanning_tree(g) Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, 'a'). + ValueError: could not convert string to float: a sage: g = Graph([(0,1,1), (1,2,[1,2,3])], weighted=True) sage: min_spanning_tree(g) Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, [1, 2, 3]). - + TypeError: float() argument must be a string or a number """ from sage.graphs.graph import Graph if not isinstance(g, Graph): - raise ValueError("The input g must be a Sage Graph.") + raise TypeError("the input must be a Sage Graph") if not algorithm in ['Kruskal', 'Prim']: raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) if g.allows_loops() or g.allows_multiple_edges(): g = g.to_simple() # Now g has no self loops and no multiple edges. - sig_on() # These variables are automatically deleted when the function terminates. cdef BoostVecWeightedGraph g_boost cdef vector[v_index] result cdef dict vertex_to_int = {v:i for i,v in enumerate(g.vertices())} cdef list int_to_vertex = g.vertices() - try: - boost_weighted_graph_from_sage_graph(&g_boost, g, weight_function) - except Exception as e: - sig_off() - raise e + boost_weighted_graph_from_sage_graph(&g_boost, g, weight_function) - if algorithm=='Kruskal': - result = g_boost.kruskal_min_spanning_tree() - elif algorithm=='Prim': - result = g_boost.prim_min_spanning_tree() + if algorithm == 'Kruskal': + sig_on() + result = g_boost.kruskal_min_spanning_tree() + sig_off() + elif algorithm == 'Prim': + sig_on() + result = g_boost.prim_min_spanning_tree() + sig_off() + else: + raise ValueError(f"unknown algorithm {algorithm!r}") - cdef int n = g.num_verts() - sig_off() + cdef size_t i + cdef size_t n = g.num_verts() if result.size() != 2 * (n - 1): return [] @@ -702,7 +710,7 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): OUTPUT: - A pair of dictionaries ``[distances, predecessors]`` such that, for each + A pair of dictionaries ``(distances, predecessors)`` such that, for each vertex ``v``, ``distances[v]`` is the distance from ``start`` to ``v``, ``predecessors[v]`` is the last vertex in a shortest path from ``start`` to ``v``. @@ -714,17 +722,17 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): sage: from sage.graphs.base.boost_graph import shortest_paths sage: g = Graph([(0,1,1),(1,2,2),(1,3,4),(2,3,1)], weighted=True) sage: shortest_paths(g, 1) - [{0: 1, 1: 0, 2: 2, 3: 3}, {0: 1, 1: None, 2: 1, 3: 2}] + ({0: 1, 1: 0, 2: 2, 3: 3}, {0: 1, 1: None, 2: 1, 3: 2}) sage: g = graphs.GridGraph([2,2]) sage: shortest_paths(g,(0,0),weight_function=lambda e:2) - [{(0, 0): 0, (0, 1): 2, (1, 0): 2, (1, 1): 4}, - {(0, 0): None, (0, 1): (0, 0), (1, 0): (0, 0), (1, 1): (0, 1)}] + ({(0, 0): 0, (0, 1): 2, (1, 0): 2, (1, 1): 4}, + {(0, 0): None, (0, 1): (0, 0), (1, 0): (0, 0), (1, 1): (0, 1)}) Directed graphs:: sage: g = DiGraph([(0,1,1),(1,2,2),(1,3,4),(2,3,1)], weighted=True) sage: shortest_paths(g, 1) - [{1: 0, 2: 2, 3: 3}, {1: None, 2: 1, 3: 2}] + ({1: 0, 2: 2, 3: 3}, {1: None, 2: 1, 3: 2}) TESTS: @@ -733,7 +741,7 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): sage: shortest_paths("I am not a graph!", 1) Traceback (most recent call last): ... - ValueError: The input g must be a Sage Graph or DiGraph. + TypeError: the input must be a Sage graph If there is a negative cycle:: @@ -742,7 +750,7 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): sage: shortest_paths(g, 1) Traceback (most recent call last): ... - ValueError: The graph contains a negative cycle. + ValueError: the graph contains a negative cycle If Dijkstra is used with negative weights:: @@ -751,7 +759,7 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): sage: shortest_paths(g, 1, algorithm='Dijkstra') Traceback (most recent call last): ... - ValueError: Dijkstra algorithm does not work with negative weights. Please, use Bellman-Ford. + RuntimeError: Dijkstra algorithm does not work with negative weights. Use Bellman-Ford instead Wrong starting vartex:: @@ -763,11 +771,10 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): from sage.graphs.generic_graph import GenericGraph if not isinstance(g, GenericGraph): - raise ValueError("The input g must be a Sage Graph or DiGraph.") - elif g.num_edges() == 0: - from sage.rings.infinity import Infinity - return [{start:0}, {start:None}] + raise TypeError("the input must be a Sage graph") + if g.num_edges() == 0: + return ({start:0}, {start:None}) # These variables are automatically deleted when the function terminates. cdef dict v_to_int = {v:i for i,v in enumerate(g.vertices())} @@ -784,58 +791,53 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): # Check if there are edges with negative weights if weight_function is not None: for e in g.edge_iterator(): - try: - if float(weight_function(e)) < 0: - algorithm = 'Bellman-Ford' - break - except (ValueError, TypeError): - raise ValueError("I cannot find the weight of edge " + - str(e) + ".") - + if float(weight_function(e)) < 0: + algorithm = 'Bellman-Ford' + break else: for _,_,w in g.edge_iterator(): - try: - if float(w) < 0: - algorithm = 'Bellman-Ford' - break - except (ValueError, TypeError): - raise ValueError("The label '", str(w), "' is not convertible " + - "to a float.") + if float(w) < 0: + algorithm = 'Bellman-Ford' + break if algorithm is None: algorithm = 'Dijkstra' + cdef v_index vi if algorithm in ['Bellman-Ford', 'Bellman-Ford_Boost']: if g.is_directed(): boost_weighted_graph_from_sage_graph(&g_boost_dir, g, weight_function) + vi = v_to_int[start] sig_on() - result = g_boost_dir.bellman_ford_shortest_paths(v_to_int[start]) + result = g_boost_dir.bellman_ford_shortest_paths(vi) sig_off() else: boost_weighted_graph_from_sage_graph(&g_boost_und, g, weight_function) + vi = v_to_int[start] sig_on() - result = g_boost_und.bellman_ford_shortest_paths(v_to_int[start]) + result = g_boost_und.bellman_ford_shortest_paths(vi) sig_off() if result.distances.size() == 0: - raise ValueError("The graph contains a negative cycle."); + raise ValueError("the graph contains a negative cycle") elif algorithm in ['Dijkstra', 'Dijkstra_Boost']: if g.is_directed(): boost_weighted_graph_from_sage_graph(&g_boost_dir, g, weight_function) + vi = v_to_int[start] sig_on() - result = g_boost_dir.dijkstra_shortest_paths(v_to_int[start]) + result = g_boost_dir.dijkstra_shortest_paths(vi) sig_off() else: boost_weighted_graph_from_sage_graph(&g_boost_und, g, weight_function) + vi = v_to_int[start] sig_on() - result = g_boost_und.dijkstra_shortest_paths(v_to_int[start]) + result = g_boost_und.dijkstra_shortest_paths(vi) sig_off() if result.distances.size() == 0: - raise ValueError("Dijkstra algorithm does not work with negative weights. Please, use Bellman-Ford."); + raise RuntimeError("Dijkstra algorithm does not work with negative weights. Use Bellman-Ford instead") else: - raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) - + raise ValueError(f"unknown algorithm {algorithm!r}") dist = {} pred = {} @@ -857,9 +859,10 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): w = int_to_v[v] dist[w] = correct_type(result.distances[v]) pred[w] = int_to_v[result.predecessors[v]] if result.predecessors[v] != v else None - return [dist, pred] + return (dist, pred) -cpdef johnson_shortest_paths(g, weight_function = None): + +cpdef johnson_shortest_paths(g, weight_function=None): r""" Uses Johnson algorithm to solve the all-pairs-shortest-paths. @@ -913,7 +916,7 @@ cpdef johnson_shortest_paths(g, weight_function = None): sage: johnson_shortest_paths("I am not a graph!") Traceback (most recent call last): ... - ValueError: The input g must be a Sage Graph or DiGraph. + TypeError: the input must be a Sage graph If there is a negative cycle:: @@ -921,15 +924,13 @@ cpdef johnson_shortest_paths(g, weight_function = None): sage: johnson_shortest_paths(g) Traceback (most recent call last): ... - ValueError: The graph contains a negative cycle. - + ValueError: the graph contains a negative cycle """ from sage.graphs.generic_graph import GenericGraph if not isinstance(g, GenericGraph): - raise ValueError("The input g must be a Sage Graph or DiGraph.") + raise TypeError("the input must be a Sage graph") elif g.num_edges() == 0: - from sage.rings.infinity import Infinity return {v:{v:0} for v in g.vertices()} # These variables are automatically deleted when the function terminates. cdef dict v_to_int = {v:i for i,v in enumerate(g.vertices())} @@ -951,7 +952,7 @@ cpdef johnson_shortest_paths(g, weight_function = None): sig_off() if result.size() == 0: - raise ValueError("The graph contains a negative cycle.") + raise ValueError("the graph contains a negative cycle") if weight_function is not None: correct_type = type(weight_function(next(g.edge_iterator()))) @@ -969,7 +970,8 @@ cpdef johnson_shortest_paths(g, weight_function = None): for w in range(N) if result[v][w] != sys.float_info.max} for v in range(N)} -cpdef johnson_closeness_centrality(g, weight_function = None): + +cpdef johnson_closeness_centrality(g, weight_function=None): r""" Uses Johnson algorithm to compute the closeness centrality of all vertices. @@ -1016,7 +1018,7 @@ cpdef johnson_closeness_centrality(g, weight_function = None): sage: johnson_closeness_centrality("I am not a graph!") Traceback (most recent call last): ... - ValueError: The input g must be a Sage Graph or DiGraph. + TypeError: the input must be a Sage graph If there is a negative cycle:: @@ -1025,17 +1027,14 @@ cpdef johnson_closeness_centrality(g, weight_function = None): sage: johnson_closeness_centrality(g) Traceback (most recent call last): ... - ValueError: The graph contains a negative cycle. - + ValueError: the graph contains a negative cycle """ from sage.graphs.generic_graph import GenericGraph if not isinstance(g, GenericGraph): - raise ValueError("The input g must be a Sage Graph or DiGraph.") + raise TypeError("the input must be a Sage graph") elif g.num_edges() == 0: - from sage.rings.infinity import Infinity return {} - sig_on() # These variables are automatically deleted when the function terminates. cdef BoostVecWeightedDiGraphU g_boost_dir cdef BoostVecWeightedGraph g_boost_und @@ -1047,14 +1046,17 @@ cpdef johnson_closeness_centrality(g, weight_function = None): if g.is_directed(): boost_weighted_graph_from_sage_graph(&g_boost_dir, g, weight_function) + sig_on() result = g_boost_dir.johnson_shortest_paths() + sig_off() else: boost_weighted_graph_from_sage_graph(&g_boost_und, g, weight_function) + sig_on() result = g_boost_und.johnson_shortest_paths() + sig_off() if result.size() == 0: - sig_off() - raise ValueError("The graph contains a negative cycle.") + raise ValueError("the graph contains a negative cycle") import sys for i in range(N): @@ -1068,5 +1070,5 @@ cpdef johnson_closeness_centrality(g, weight_function = None): closeness.push_back((reach-1) * (reach-1) / ((N-1) * farness)) else: closeness.push_back(sys.float_info.max) - sig_off() + sig_check() return {v: closeness[i] for i,v in enumerate(g.vertices()) if closeness[i] != sys.float_info.max} diff --git a/src/sage/graphs/base/boost_interface.cpp b/src/sage/graphs/base/boost_interface.cpp index 65f4d2e9d84..81cf793960d 100644 --- a/src/sage/graphs/base/boost_interface.cpp +++ b/src/sage/graphs/base/boost_interface.cpp @@ -105,7 +105,7 @@ class BoostGraph std::back_insert_iterator inserter(disconnecting_set); to_return.ec = boost::edge_connectivity(graph, inserter); - for (v_index i = 0; i < disconnecting_set.size(); i++) { + for (size_t i = 0; i < disconnecting_set.size(); i++) { edge_descriptor edge = disconnecting_set[i]; to_return.edges.push_back(index[boost::source(edge, graph)]); to_return.edges.push_back(index[boost::target(edge, graph)]); @@ -156,7 +156,7 @@ class BoostGraph boost::king_ordering(graph, inv_perm.rbegin()); } - for (int i = 0; i < inv_perm.size(); i++) { + for (size_t i = 0; i < inv_perm.size(); i++) { to_return.push_back(index[inv_perm[i]]); } return to_return; diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index b10f70d0eae..d81b59debad 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -2075,9 +2075,9 @@ cdef class CGraphBackend(GenericGraphBackend): self.vertex_ints = new_vx_ints self.vertex_labels = new_vx_labels - def shortest_path(self, x, y): + def shortest_path(self, x, y, distance_flag=False): r""" - Returns the shortest path between ``x`` and ``y``. + Returns the shortest path or distance from ``x`` to ``y``. INPUT: @@ -2086,15 +2086,24 @@ cdef class CGraphBackend(GenericGraphBackend): - ``y`` -- the end vertex in the shortest path from ``x`` to ``y``. + - ``distance_flag`` -- boolean (default: ``False``). When set to + ``True``, the shortest path distance from ``x`` to ``y`` is + returned instead of the path. + OUTPUT: - - A list of vertices in the shortest path from ``x`` to ``y``. + - A list of vertices in the shortest path from ``x`` to ``y`` or + distance from ``x`` to ``y`` is returned depending upon the value + of parameter ``distance_flag`` EXAMPLES:: sage: G = Graph(graphs.PetersenGraph(), implementation="c_graph") sage: G.shortest_path(0, 1) [0, 1] + sage: G.shortest_path_length(0, 1) + 1 + """ if x == y: return 0 @@ -2166,13 +2175,16 @@ cdef class CGraphBackend(GenericGraphBackend): # to the list. if v not in dist_current: dist_current[v] = dist_current[u] + 1 - pred_current[v] = u + if not distance_flag: + pred_current[v] = u next_current.append(v) # If the new neighbor is already known by the other # side ... if v in dist_other: # build the shortest path and returns in. + if distance_flag: + return dist_other[v] + dist_current[v] w = v while w != x_int: @@ -2199,11 +2211,15 @@ cdef class CGraphBackend(GenericGraphBackend): next_current, next_other = next_other, next_current out = -out + if distance_flag: + from sage.rings.infinity import Infinity + return Infinity return [] - def bidirectional_dijkstra(self, x, y, weight_function=None): + def bidirectional_dijkstra(self, x, y, weight_function=None, + distance_flag=False): r""" - Returns the shortest path between ``x`` and ``y`` using a + Returns the shortest path or distance from ``x`` to ``y`` using a bidirectional version of Dijkstra's algorithm. INPUT: @@ -2217,9 +2233,14 @@ cdef class CGraphBackend(GenericGraphBackend): ``(u, v, l)`` and outputs its weight. If ``None``, we use the edge label ``l`` as a weight. + - ``distance_flag`` -- boolean (default: ``False``). When set to ``True``, + the shortest path distance from ``x`` to ``y`` is returned instead of the path. + OUTPUT: - - A list of vertices in the shortest path from ``x`` to ``y``. + - A list of vertices in the shortest path from ``x`` to ``y`` or + distance from ``x`` to ``y`` is returned depending upon the value + of parameter ``distance_flag`` EXAMPLES:: @@ -2228,9 +2249,13 @@ cdef class CGraphBackend(GenericGraphBackend): ....: G.set_edge_label(u,v,1) sage: G.shortest_path(0, 1, by_weight=True) [0, 1] + sage: G.shortest_path_length(0, 1, by_weight=True) + 1 sage: G = DiGraph([(1,2,{'weight':1}), (1,3,{'weight':5}), (2,3,{'weight':1})]) sage: G.shortest_path(1, 3, weight_function=lambda e:e[2]['weight']) [1, 2, 3] + sage: G.shortest_path_length(1, 3, weight_function=lambda e:e[2]['weight']) + 2 TEST: @@ -2259,10 +2284,7 @@ cdef class CGraphBackend(GenericGraphBackend): cdef int v = 0 cdef int w = 0 cdef int pred - cdef float distance - cdef float edge_label cdef int side - cdef float f_tmp # Each vertex knows its predecessors in the search, for each side cdef dict pred_x = {} @@ -2289,7 +2311,6 @@ cdef class CGraphBackend(GenericGraphBackend): # which defines the shortest path found # (of length shortest_path_length). cdef int meeting_vertex = -1 - cdef float shortest_path_length if weight_function is None: weight_function = lambda e:e[2] @@ -2308,7 +2329,8 @@ cdef class CGraphBackend(GenericGraphBackend): pred_current, pred_other = pred_y, pred_x if v not in dist_current: - pred_current[v] = pred + if not distance_flag: + pred_current[v] = pred dist_current[v] = distance if v in dist_other: @@ -2336,9 +2358,14 @@ cdef class CGraphBackend(GenericGraphBackend): # No meeting point has been found if meeting_vertex == -1: + if distance_flag: + from sage.rings.infinity import Infinity + return Infinity return [] else: # build the shortest path and returns it. + if distance_flag: + return shortest_path_length w = meeting_vertex while w != x_int: @@ -2359,9 +2386,11 @@ cdef class CGraphBackend(GenericGraphBackend): return shortest_path - def shortest_path_all_vertices(self, v, cutoff=None): + def shortest_path_all_vertices(self, v, cutoff=None, + distance_flag=False): r""" - Returns for each vertex ``u`` a shortest ``v-u`` path. + Returns for each vertex ``u`` a shortest ``v-u`` path or distance from + ``v`` to ``u``. INPUT: @@ -2369,10 +2398,16 @@ cdef class CGraphBackend(GenericGraphBackend): - ``cutoff`` -- maximal distance. Longer paths will not be returned. + - ``distance_flag`` -- boolean (default: ``False``). When set to + ``True``, each vertex ``u`` connected to ``v`` is mapped to shortest path + distance from ``v`` to ``u`` instead of the shortest path in the output + dictionary. + OUTPUT: - - A list which associates to each vertex ``u`` the shortest path - between ``u`` and ``v`` if there is one. + - A dictionary which maps each vertex ``u`` connected to ``v`` to the + shortest path list or distance from ``v`` to ``u`` depending upon the value + of parameter ``distance_flag`` .. NOTE:: @@ -2390,6 +2425,8 @@ cdef class CGraphBackend(GenericGraphBackend): sage: paths = g._backend.shortest_path_all_vertices(0) sage: all([ len(paths[v]) == 0 or len(paths[v])-1 == g.distance(0,v) for v in g]) True + sage: g._backend.shortest_path_all_vertices(0, distance_flag=True) + {0: 0, 1: 1, 2: 2, 3: 2, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 2} On a disconnected graph :: @@ -2410,8 +2447,7 @@ cdef class CGraphBackend(GenericGraphBackend): cdef bitset_t seen cdef int v_int cdef int u_int - cdef dict distances_int - cdef dict distance + cdef dict distances cdef int d distances = {} @@ -2428,23 +2464,27 @@ cdef class CGraphBackend(GenericGraphBackend): current_layer = [(u_int, v_int) for u_int in self._cg.out_neighbors(v_int)] next_layer = [] - distances[v] = [v] + + distances[v] = 0 if distance_flag else [v] while current_layer: if cutoff is not None and d >= cutoff: break + d += 1 while current_layer: v_int, u_int = current_layer.pop() if bitset_not_in(seen, v_int): bitset_add(seen, v_int) - distances[self.vertex_label(v_int)] = distances[self.vertex_label(u_int)] + [self.vertex_label(v_int)] + if distance_flag: + distances[self.vertex_label(v_int)] = d + else: + distances[self.vertex_label(v_int)] = distances[self.vertex_label(u_int)] + [self.vertex_label(v_int)] next_layer.extend([(u_int, v_int) for u_int in self._cg.out_neighbors(v_int)]) current_layer = next_layer next_layer = [] - d += 1 # If the graph is not connected, vertices which have not been # seen should be associated to the empty path diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 71d5a6b8906..a5f62c73cb4 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -36,6 +36,8 @@ Classes and methods """ from __future__ import print_function +from cysignals.memory cimport check_calloc, sig_free + from sage.graphs.base.static_sparse_graph cimport (init_short_digraph, init_reverse, out_degree, @@ -46,7 +48,6 @@ from .c_graph cimport CGraphBackend from sage.data_structures.bitset cimport FrozenBitset from libc.stdint cimport uint32_t include 'sage/data_structures/bitset.pxi' -include "cysignals/memory.pxi" cdef class StaticSparseCGraph(CGraph): """ diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 33fdded6c31..b3aa2f73dff 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -185,12 +185,14 @@ include "sage/data_structures/bitset.pxi" cimport cpython from libc.string cimport memset from libc.limits cimport INT_MAX +from libcpp.vector cimport vector +from cysignals.memory cimport check_allocarray, check_calloc, sig_free +from cysignals.signals cimport sig_on, sig_off + from sage.graphs.base.c_graph cimport CGraph from .static_sparse_backend cimport StaticSparseCGraph from .static_sparse_backend cimport StaticSparseBackend from sage.ext.memory_allocator cimport MemoryAllocator -include "cysignals/memory.pxi" -from libcpp.vector cimport vector cdef extern from "fenv.h": int FE_TONEAREST @@ -237,13 +239,8 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1 for i, v in enumerate(vertices): v_to_id[v] = i - g.edges = sig_malloc(n_edges*sizeof(uint32_t)) - if g.edges == NULL: - raise ValueError("Problem while allocating memory (edges)") - - g.neighbors = sig_malloc((1+g.n)*sizeof(uint32_t *)) - if g.neighbors == NULL: - raise ValueError("Problem while allocating memory (neighbors)") + g.edges = check_allocarray(n_edges, sizeof(uint32_t)) + g.neighbors = check_allocarray(1 + g.n, sizeof(uint32_t *)) # Initializing the value of neighbors g.neighbors[0] = g.edges @@ -314,13 +311,8 @@ cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1: dst.edge_labels = NULL cdef list edge_labels - dst.edges = sig_malloc(n_edges(src)*sizeof(uint32_t)) - if dst.edges == NULL: - raise ValueError("Problem while allocating memory (edges)") - - dst.neighbors = sig_malloc((src.n+1)*sizeof(uint32_t *)) - if dst.neighbors == NULL: - raise ValueError("Problem while allocating memory (neighbors)") + dst.edges = check_allocarray(n_edges(src), sizeof(uint32_t)) + dst.neighbors = check_allocarray(src.n + 1, sizeof(uint32_t *)) if src.edge_labels != NULL: edge_labels = [None]*n_edges(src) @@ -342,12 +334,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: # vector. With this information, we can initialize dst.neighbors to its # correct value. The content of dst.edges is not touched at this level. - cdef int * in_degree = sig_malloc(src.n*sizeof(int)) - if in_degree == NULL: - raise ValueError("Problem while allocating memory (in_degree)") - - # Counting the degrees - memset(in_degree, 0, src.n*sizeof(int)) + cdef int * in_degree = check_calloc(src.n, sizeof(int)) for i in range(n_edges(src)): in_degree[src.edges[i]] += 1 @@ -413,9 +400,7 @@ cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except # We will be doing a Depth-First Search. We allocate the stack we need for # that, and put "src" on top of it. - cdef int * stack = sig_malloc(g.n*sizeof(int)) - if stack == NULL: - raise ValueError("Problem while allocating memory (stack)") + cdef int * stack = check_allocarray(g.n, sizeof(int)) stack[0] = src cdef int stack_size = 1 @@ -630,18 +615,18 @@ def tarjan_strongly_connected_components(G): if not isinstance(G, DiGraph): raise ValueError("G must be a DiGraph.") - sig_on() cdef MemoryAllocator mem = MemoryAllocator() cdef short_digraph g init_short_digraph(g, G) cdef int * scc = mem.malloc(g.n * sizeof(int)) + sig_on() cdef int nscc = tarjan_strongly_connected_components_C(g, scc) + sig_off() cdef int i - cdef list output = list(list() for i in range(nscc)) # We cannot use [] here + cdef list output = [[] for i in range(nscc)] for i,v in enumerate(G.vertices()): output[scc[i]].append(v) - sig_off() return output cdef void strongly_connected_components_digraph_C(short_digraph g, int nscc, int *scc, short_digraph output): @@ -778,14 +763,9 @@ cdef strongly_connected_component_containing_vertex(short_digraph g, short_digra bitset_intersection(scc, scc, scc_reversed) cdef void free_short_digraph(short_digraph g): - if g.edges != NULL: - sig_free(g.edges) - - if g.neighbors != NULL: - sig_free(g.neighbors) - - if g.edge_labels != NULL: - cpython.Py_XDECREF(g.edge_labels) + sig_free(g.edges) + sig_free(g.neighbors) + cpython.Py_XDECREF(g.edge_labels) def triangles_count(G): r""" @@ -973,13 +953,12 @@ def spectral_radius(G, prec=1e-10): cdef long m = g.m cdef uint32_t ** neighbors = g.neighbors - cdef double * v1 = sig_malloc(n * sizeof(double)) - cdef double * v2 = sig_malloc(n * sizeof(double)) + # v1 and v2 are two arrays of length n, allocated as one array + # of length 2n for efficiency. + cdef double * vmem = check_allocarray(2*n, sizeof(double)) + cdef double * v1 = vmem + cdef double * v2 = vmem + n cdef double * v3 - if v1 == NULL or v2 == NULL: - sig_free(v1) - sig_free(v2) - raise MemoryError cdef size_t i cdef uint32_t *p @@ -1046,7 +1025,6 @@ def spectral_radius(G, prec=1e-10): fesetround(old_rounding) # and that the memory is freed - sig_free(v1) - sig_free(v2) + sig_free(vmem) return (e_min, e_max) diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index a3c61a5f3c0..ec42d610c4a 100644 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -16,22 +16,22 @@ Functions """ from __future__ import print_function -include "sage/data_structures/bitset.pxi" -include "cysignals/signals.pxi" - -from sage.graphs.base.static_sparse_graph cimport * from libc.string cimport memset from libc.stdint cimport uint32_t +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_check + +include "sage/data_structures/bitset.pxi" +from sage.graphs.base.static_sparse_graph cimport * from sage.libs.gmp.mpq cimport * from sage.rings.rational cimport Rational -include "cysignals/memory.pxi" from sage.ext.memory_allocator cimport MemoryAllocator ctypedef fused numerical_type: mpq_t double -import cython +cimport cython def centrality_betweenness(G, exact=False, normalize=True): r""" @@ -181,11 +181,11 @@ cdef dict centrality_betweenness_C(G, numerical_type _, normalize=True): init_short_digraph(g, G, edge_labelled = False) init_reverse(bfs_dag, g) - queue = check_malloc(n*sizeof(uint32_t)) - degrees = check_malloc(n*sizeof(uint32_t)) - n_paths_from_source = check_malloc(n*sizeof(numerical_type)) - betweenness_source = check_malloc(n*sizeof(numerical_type)) - betweenness = check_malloc(n*sizeof(numerical_type)) + queue = check_allocarray(n, sizeof(uint32_t)) + degrees = check_allocarray(n, sizeof(uint32_t)) + n_paths_from_source = check_allocarray(n, sizeof(numerical_type)) + betweenness_source = check_allocarray(n, sizeof(numerical_type)) + betweenness = check_allocarray(n, sizeof(numerical_type)) bitset_init(seen,n) bitset_init(next_layer,n) @@ -675,7 +675,6 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): if G.num_verts()==0 or G.num_verts()==1: return [] - sig_on() cdef MemoryAllocator mem = MemoryAllocator() cdef short_digraph sd # Copying the whole graph to obtain the list of neighbors quicker than by @@ -716,6 +715,8 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): _sort_vertices_degree(sd, sorted_vert) for x in sorted_vert[:n]: + sig_check() + if out_degree(sd, x) == 0: break # We start a BFSCut from x: @@ -741,6 +742,7 @@ def centrality_closeness_top_k(G, int k=1, int verbose=0): # The graph is explored layer by layer. while layer_current_beginning (sd.neighbors[sd.n]-sd.edges)))) - sig_off() if verbose > 0: print("Final performance ratio: {}".format(visited / (n * (sd.neighbors[sd.n]-sd.edges)))) diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index e05dc858ff7..c5027ae830b 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -23,12 +23,13 @@ REFERENCE: # http://www.gnu.org/licenses/ #***************************************************************************** +from cysignals.signals cimport sig_check + from sage.libs.gmp.mpz cimport * from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.ext.memory_allocator cimport MemoryAllocator from sage.misc.all import prod -include "cysignals/signals.pxi" def chromatic_polynomial(G, return_tree_basis=False): @@ -173,11 +174,7 @@ def chromatic_polynomial(G, return_tree_basis=False): chords2[i] = j i -= 1 try: - sig_on() - try: - contract_and_count(chords1, chords2, num_chords, nverts, tot, parent) - finally: - sig_off() + contract_and_count(chords1, chords2, num_chords, nverts, tot, parent) except BaseException: for i in range(nverts): mpz_clear(tot[i]) @@ -221,8 +218,9 @@ def chromatic_polynomial(G, return_tree_basis=False): return f -cdef int contract_and_count(int *chords1, int *chords2, int num_chords, int nverts, \ - mpz_t *tot, int *parent): + +cdef int contract_and_count(int *chords1, int *chords2, int num_chords, int nverts, + mpz_t *tot, int *parent) except -1: if num_chords == 0: mpz_add_ui(tot[nverts], tot[nverts], 1) return 0 @@ -232,7 +230,9 @@ cdef int contract_and_count(int *chords1, int *chords2, int num_chords, int nver cdef int *ins_list1 = mem.allocarray(num_chords, sizeof(int)) cdef int *ins_list2 = mem.allocarray(num_chords, sizeof(int)) cdef int i, j, k, x1, xj, z, num, insnum, parent_checked - for i from 0 <= i < num_chords: + for i in range(num_chords): + sig_check() + # contract chord i, and recurse z = chords1[i] x1 = chords2[i] diff --git a/src/sage/graphs/cliquer.pyx b/src/sage/graphs/cliquer.pyx index f2d6c2b1a44..1e78c3837d9 100644 --- a/src/sage/graphs/cliquer.pyx +++ b/src/sage/graphs/cliquer.pyx @@ -34,9 +34,8 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** - -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" +from cysignals.memory cimport sig_free +from cysignals.signals cimport sig_on, sig_off cdef extern from "sage/graphs/cliquer/cl.c": diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index 99e41afcfaa..5d7ec382f85 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -211,7 +211,7 @@ Methods #***************************************************************************** from __future__ import print_function -include "cysignals/memory.pxi" +from cysignals.memory cimport sig_free from copy import copy diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 28994797321..96ae4bfa087 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -244,6 +244,164 @@ def RandomBipartite(n1, n2, p): return g +def RandomBlockGraph(m, k, kmax=None, incidence_structure=False): + r""" + Return a Random Block Graph. + + A block graph is a connected graph in which every biconnected component + (block) is a clique. + + .. SEEALSO:: + + - :wikipedia:`Block_graph` for more details on these graphs + - :meth:`~sage.graphs.graph.Graph.is_block_graph` -- test if a graph is a block graph + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree` + - :meth:`~sage.combinat.designs.incidence_structures.IncidenceStructure` + + INPUT: + + - ``m`` -- integer; number of blocks (at least one). + + - ``k`` -- integer; minimum number of vertices of a block (at least two). + + - ``kmax`` -- integer (default: ``None``) By default, each block has `k` + vertices. When the parameter `kmax` is specified (with `kmax \geq k`), the + number of vertices of each block is randomly chosen between `k` and + `kmax`. + + - ``incidence_structure`` -- boolean (default: ``False``) when set to + ``True``, the incidence structure of the graphs is returned instead of the + graph itself, that is the list of the lists of vertices in each + block. This is useful for the creation of some hypergraphs. + + OUTPUT: + + A Graph when ``incidence_structure==False`` (default), and otherwise an + incidence structure. + + EXAMPLES: + + A block graph with a single block is a clique:: + + sage: B = graphs.RandomBlockGraph(1, 4) + sage: B.is_clique() + True + + A block graph with blocks of order 2 is a tree:: + + sage: B = graphs.RandomBlockGraph(10, 2) + sage: B.is_tree() + True + + Every biconnected component of a block graph is a clique:: + + sage: B = graphs.RandomBlockGraph(5, 3, kmax=6) + sage: blocks,cuts = B.blocks_and_cut_vertices() + sage: all(B.is_clique(block) for block in blocks) + True + + A block graph with blocks of order `k` has `m*(k-1)+1` vertices:: + + sage: m, k = 6, 4 + sage: B = graphs.RandomBlockGraph(m, k) + sage: B.order() == m*(k-1)+1 + True + + Test recognition methods:: + + sage: B = graphs.RandomBlockGraph(6, 2, kmax=6) + sage: B.is_block_graph() + True + sage: B in graph_classes.Block + True + + Asking for the incidence structure:: + + sage: m, k = 6, 4 + sage: IS = graphs.RandomBlockGraph(m, k, incidence_structure=True) + sage: from sage.combinat.designs.incidence_structures import IncidenceStructure + sage: IncidenceStructure(IS) + Incidence structure with 19 points and 6 blocks + sage: m*(k-1)+1 + 19 + + TESTS: + + A block graph has at least one block, so `m\geq 1`:: + + sage: B = graphs.RandomBlockGraph(0, 1) + Traceback (most recent call last): + ... + ValueError: the number `m` of blocks must be >= 1 + + A block has at least 2 vertices, so `k\geq 2`:: + + sage: B = graphs.RandomBlockGraph(1, 1) + Traceback (most recent call last): + ... + ValueError: the minimum number `k` of vertices in a block must be >= 2 + + The maximum size of a block is at least its minimum size, so `k\leq kmax`:: + + sage: B = graphs.RandomBlockGraph(1, 3, kmax=2) + Traceback (most recent call last): + ... + ValueError: the maximum number `kmax` of vertices in a block must be >= `k` + """ + import itertools + from sage.misc.prandom import choice + from sage.sets.disjoint_set import DisjointSet + + if m < 1: + raise ValueError("the number `m` of blocks must be >= 1") + if k < 2: + raise ValueError("the minimum number `k` of vertices in a block must be >= 2") + if kmax is None: + kmax = k + elif kmax < k: + raise ValueError("the maximum number `kmax` of vertices in a block must be >= `k`") + + if m == 1: + # A block graph with a single block is a clique + IS = [ list(range(randint(k, kmax))) ] + + elif kmax == 2: + # A block graph with blocks of order 2 is a tree + IS = [ list(e) for e in RandomTree(m+1).edges(labels=False) ] + + else: + # We start with a random tree of order m + T = RandomTree(m) + + # We create a block of order in range [k,kmax] per vertex of the tree + B = {u:[(u,i) for i in range(randint(k, kmax))] for u in T} + + # For each edge of the tree, we choose 1 vertex in each of the + # corresponding blocks and we merge them. We use a disjoint set data + # structure to keep a unique identifier per merged vertices + DS = DisjointSet([i for u in B for i in B[u]]) + for u,v in T.edges(labels=0): + DS.union(choice(B[u]), choice(B[v])) + + # We relabel vertices in the range [0, m*(k-1)] and build the incidence + # structure + new_label = {root:i for i,root in enumerate(DS.root_to_elements_dict())} + IS = [ [new_label[DS.find(v)] for v in B[u]] for u in B ] + + if incidence_structure: + return IS + + # We finally build the block graph + if k == kmax: + BG = Graph(name = "Random Block Graph with {} blocks of order {}".format(m, k)) + else: + BG = Graph(name = "Random Block Graph with {} blocks of order {} to {}".format(m, k, kmax)) + for block in IS: + BG.add_clique( block ) + return BG + + def RandomBoundedToleranceGraph(n): r""" Returns a random bounded tolerance graph. diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 9f810cf4c1b..0428f69ee5b 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -4793,14 +4793,16 @@ def _EllipticLinesProjectivePlaneScheme(k): from sage.matrix.constructor import matrix from itertools import product q = 2**k - g0 = libgap.GeneralOrthogonalGroup(3,q) # invariant form x0^2+x1*x2 - g = libgap.Group(libgap.List(g0.GeneratorsOfGroup(),libgap.TransposedMat)) + g0 = libgap.GeneralOrthogonalGroup(3,q) # invariant form x0^2+x1*x2 + g = libgap.Group(libgap.List(g0.GeneratorsOfGroup(), libgap.TransposedMat)) W = libgap.FullRowSpace(libgap.GF(q), 3) - l=sum(libgap.Elements(libgap.Basis(W))) - gp = libgap.Action(g,libgap.Orbit(g,l,libgap.OnLines),libgap.OnLines) - orbitals = gp.Orbits(list(product(gp.Orbit(1),gp.Orbit(1))),libgap.OnTuples) - mats = map(lambda o: map(lambda x: (int(x[0])-1,int(x[1])-1), o), orbitals) - return map(lambda x: matrix(q*(q-1)/2, lambda i,j: 1 if (i,j) in x else 0), mats) + l = sum(libgap.Elements(libgap.Basis(W))) + gp = libgap.Action(g, libgap.Orbit(g, l, libgap.OnLines), libgap.OnLines) + orbitals = gp.Orbits(list(product(gp.Orbit(1), gp.Orbit(1))), + libgap.OnTuples) + mats = map(lambda o: [(int(x[0]) - 1, int(x[1]) - 1) for x in o], orbitals) + return [matrix(q * (q - 1) / 2, lambda i, j: 1 if (i, j) in x else 0) + for x in mats] def MathonStronglyRegularGraph(t): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index a3fec74f674..9898000473a 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -3716,7 +3716,7 @@ def min_spanning_tree(self, sage: g.min_spanning_tree(algorithm="Prim_Boost") Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, 'a'). + ValueError: could not convert string to float: a sage: g.min_spanning_tree(algorithm="Prim_fringe") Traceback (most recent call last): ... @@ -3732,7 +3732,7 @@ def min_spanning_tree(self, sage: g.min_spanning_tree(algorithm="Kruskal_Boost") Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, 'a'). + ValueError: could not convert string to float: a sage: g.min_spanning_tree(algorithm="NetworkX") Traceback (most recent call last): ... @@ -3743,7 +3743,7 @@ def min_spanning_tree(self, sage: g.min_spanning_tree(algorithm="Prim_Boost") Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, [1, 2, 3]). + TypeError: float() argument must be a string or a number sage: g.min_spanning_tree(algorithm="Prim_fringe") Traceback (most recent call last): ... @@ -3759,7 +3759,7 @@ def min_spanning_tree(self, sage: g.min_spanning_tree(algorithm="Kruskal_Boost") Traceback (most recent call last): ... - ValueError: The weight function cannot find the weight of (1, 2, [1, 2, 3]). + TypeError: float() argument must be a string or a number sage: g.min_spanning_tree(algorithm="NetworkX") Traceback (most recent call last): ... @@ -13661,7 +13661,7 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, sage: G.eccentricity(algorithm = 'boh') Traceback (most recent call last): ... - ValueError: Algorithm boh not yet implemented. Please, contribute! + ValueError: unknown algorithm "boh" An algorithm that does not work with edge weights:: @@ -14971,7 +14971,7 @@ def triangles_count(self, algorithm=None): sage: G.triangles_count(algorithm='tip top') Traceback (most recent call last): ... - ValueError: Algorithm 'tip top' not yet implemented. Please contribute. + ValueError: unknown algorithm "tip top" sage: digraphs.Path(5).triangles_count(algorithm="sparse_copy") Traceback (most recent call last): ... @@ -15008,7 +15008,7 @@ def triangles_count(self, algorithm=None): elif algorithm=='matrix': return (self.adjacency_matrix()**3).trace() // 6 else: - raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) + raise ValueError('unknown algorithm "{}"'.format(algorithm)) def shortest_path(self, u, v, by_weight=False, algorithm=None, weight_function=None, check_weight=True, @@ -15110,7 +15110,7 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, sage: G.shortest_path(0, 3, by_weight=True, algorithm='tip top') Traceback (most recent call last): ... - ValueError: Algorithm 'tip top' not yet implemented. + ValueError: unknown algorithm "tip top" BFS on weighted graphs:: @@ -15122,7 +15122,23 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, Traceback (most recent call last): ... ValueError: The 'BFS_Bid' algorithm does not work on weighted graphs. + + If vertex is not in the graph:: + + sage: G.shortest_path(0, 5) + Traceback (most recent call last): + ... + ValueError: vertex '5' is not in the (di)graph + sage: G.shortest_path(6, 5) + Traceback (most recent call last): + ... + ValueError: vertex '6' is not in the (di)graph """ # TODO- multiple edges?? + if not self.has_vertex(u): + raise ValueError("vertex '{}' is not in the (di)graph".format(u)) + if not self.has_vertex(v): + raise ValueError("vertex '{}' is not in the (di)graph".format(v)) + if weight_function is not None: by_weight = True @@ -15165,7 +15181,7 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, elif algorithm=="BFS_Bid": return self._backend.shortest_path(u,v) else: - raise ValueError("Algorithm '" + algorithm + "' not yet implemented.") + raise ValueError('unknown algorithm "{}"'.format(algorithm)) def shortest_path_length(self, u, v, by_weight=False, algorithm=None, weight_function=None, check_weight=True, @@ -15286,17 +15302,70 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, -1000 sage: G.shortest_path_length(0, 2, by_weight=True) 2 + + TESTS: + + If vertex is not in the graph:: + + sage: G.shortest_path(0, 5) + Traceback (most recent call last): + ... + ValueError: vertex '5' is not in the (di)graph + sage: G.shortest_path(6, 5) + Traceback (most recent call last): + ... + ValueError: vertex '6' is not in the (di)graph """ + if not self.has_vertex(u): + raise ValueError("vertex '{}' is not in the (di)graph".format(u)) + if not self.has_vertex(v): + raise ValueError("vertex '{}' is not in the (di)graph".format(v)) + if weight_sum is not None: deprecation(18938, "Now weight_sum is replaced by by_weight.") - path = self.shortest_path(u, v, by_weight=by_weight, - weight_function=weight_function, - algorithm=algorithm, - check_weight=check_weight, - bidirectional=bidirectional) - return self._path_length(path, by_weight, weight_function) + if u == v: # to avoid a NetworkX bug + return 0 + + if weight_function is not None: + by_weight = True + + if algorithm is None: + algorithm = 'Dijkstra_Bid' if by_weight else 'BFS_Bid' + + if weight_function is None and by_weight: + weight_function = lambda e:e[2] + if algorithm in ['BFS', 'Dijkstra_NetworkX', 'Bellman-Ford_Boost']: + return self.shortest_path_lengths(u, by_weight, algorithm, weight_function, check_weight)[v] + + if bidirectional is not None: + deprecation(18938, "Variable 'bidirectional' is deprecated and " + + "replaced by 'algorithm'.") + + if by_weight: + if algorithm == 'BFS_Bid': + raise ValueError("the 'BFS_Bid' algorithm does not " + + "work on weighted graphs") + if check_weight: + self._check_weight_function(weight_function) + else: + weight_function = lambda e:1 + + if algorithm == "Dijkstra_Bid": + return self._backend.bidirectional_dijkstra(u, v, weight_function, distance_flag=True) + elif algorithm == "Dijkstra_Bid_NetworkX": + import networkx + if self.is_directed(): + G = networkx.DiGraph([(e[0], e[1], dict(weight=weight_function(e))) for e in self.edge_iterator()]) + else: + G = networkx.Graph([(e[0], e[1], dict(weight=weight_function(e))) for e in self.edge_iterator()]) + G.add_nodes_from(self.vertices()) + return networkx.bidirectional_dijkstra(G, u, v)[0] + elif algorithm == "BFS_Bid": + return self._backend.shortest_path(u, v, distance_flag=True) + else: + raise ValueError('unknown algorithm "{}"'.format(algorithm)) def _check_weight_function(self, weight_function=None): r""" @@ -15454,7 +15523,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, sage: D.shortest_paths(0, by_weight=True) Traceback (most recent call last): ... - ValueError: The graph contains a negative cycle. + ValueError: the graph contains a negative cycle TESTS: @@ -15464,7 +15533,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, sage: D.shortest_paths(0, algorithm='tip top') Traceback (most recent call last): ... - ValueError: Algorithm tip top not yet implemented. Please, contribute! + ValueError: unknown algorithm "tip top" If we ask for BFS in a weighted graph:: @@ -15479,7 +15548,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, sage: D.shortest_paths(0, algorithm='Dijkstra_Boost', by_weight=True) Traceback (most recent call last): ... - ValueError: Dijkstra algorithm does not work with negative weights. Please, use Bellman-Ford. + RuntimeError: Dijkstra algorithm does not work with negative weights. Use Bellman-Ford instead sage: D.shortest_paths(0, algorithm='Dijkstra_NetworkX', by_weight=True) Traceback (most recent call last): ... @@ -15507,7 +15576,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, elif algorithm=='Dijkstra_NetworkX': import networkx # If this is not present, an error might be raised by NetworkX - if self.num_verts()==1 and self.vertices()[0]==u: + if self.order() == 1 and self.has_vertex(u): return {u:[u]} if by_weight: if self.is_directed(): @@ -15538,8 +15607,7 @@ def shortest_paths(self, u, by_weight=False, algorithm=None, return paths else: - raise ValueError("Algorithm " + algorithm + " not yet " + - "implemented. Please, contribute!") + raise ValueError('unknown algorithm "{}"'.format(algorithm)) def _path_length(self, path, by_weight=False, weight_function=None): r""" @@ -15697,7 +15765,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: D.shortest_path_lengths(0, weight_function=weight_function) Traceback (most recent call last): ... - ValueError: The graph contains a negative cycle. + ValueError: the graph contains a negative cycle Checking that distances are equal regardless of the algorithm used:: @@ -15712,18 +15780,51 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, if weight_sums is not None: deprecation(18938, "Now weight_sums is replaced by by_weight.") - if algorithm in ['Dijkstra_Boost', 'Bellman-Ford_Boost'] or (algorithm is None and weight_function is None and by_weight): - if weight_function is None and not by_weight: - weight_function = lambda e:1 + if weight_function is not None: + by_weight = True + elif by_weight: + weight_function = lambda e:e[2] + else: + weight_function = lambda e:1 + + if algorithm is None and not by_weight: + algorithm = 'BFS' + + if by_weight and check_weight: + self._check_weight_function(weight_function) + + if algorithm == 'BFS': + if by_weight: + raise ValueError("the 'BFS' algorithm does not work on " + + "weighted graphs") + return self._backend.shortest_path_all_vertices(u, cutoff=None, distance_flag=True) + + elif algorithm == 'Dijkstra_NetworkX': + import networkx + # If this is not present, an error might be raised by NetworkX + if self.num_verts()==1 and self.vertices()[0]==u: + return {u:[u]} + if by_weight: + if self.is_directed(): + G = networkx.DiGraph([(e[0], e[1], dict(weight=weight_function(e))) for e in self.edge_iterator()]) + else: + G = networkx.Graph([(e[0], e[1], dict(weight=weight_function(e))) for e in self.edge_iterator()]) + else: + # Needed to remove labels. + if self.is_directed(): + G = networkx.DiGraph(self.edges(labels=False)) + else: + G = networkx.Graph(self.edges(labels=False)) + G.add_nodes_from(self.vertices()) + return networkx.single_source_dijkstra_path_length(G, u) + + elif algorithm in ['Dijkstra_Boost', 'Bellman-Ford_Boost', None]: self.weighted(True) from sage.graphs.base.boost_graph import shortest_paths return shortest_paths(self, u, weight_function, algorithm)[0] - paths = self.shortest_paths(u, by_weight=by_weight, algorithm=algorithm, - weight_function=weight_function) - return {v:self._path_length(p, by_weight, weight_function) - for v,p in iteritems(paths)} - + else: + raise ValueError('unknown algorithm "{}"'.format(algorithm)) def shortest_path_all_pairs(self, by_weight=False, algorithm=None, weight_function=None, check_weight=True, @@ -15968,7 +16069,7 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: g.shortest_path_all_pairs(algorithm="Bob") Traceback (most recent call last): ... - ValueError: Algorithm Bob is not implemented. + ValueError: unknown algorithm "Bob" Algorithms that do not work with weights:: @@ -15988,8 +16089,7 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: g.shortest_path_all_pairs(algorithm="Dijkstra_Boost", by_weight=True) Traceback (most recent call last): ... - ValueError: Dijkstra algorithm does not work with negative weights. Please, use Bellman-Ford. - + RuntimeError: Dijkstra algorithm does not work with negative weights. Use Bellman-Ford instead """ if default_weight is not None: deprecation(18938, "Variable default_weight is deprecated: hence," + @@ -16058,7 +16158,7 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, return dist, pred elif algorithm != "Floyd-Warshall-Python": - raise ValueError("Algorithm " + algorithm + " is not implemented.") + raise ValueError('unknown algorithm "{}"'.format(algorithm)) from sage.rings.infinity import Infinity diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 6847af92c46..0605ee7203c 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -22,8 +22,9 @@ AUTHORS: from __future__ import absolute_import, print_function -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off + include "sage/data_structures/binary_matrix.pxi" from libc.math cimport sqrt from libc.string cimport memset diff --git a/src/sage/graphs/genus.pyx b/src/sage/graphs/genus.pyx index 8408c2a06ab..2d86e0b0fdd 100644 --- a/src/sage/graphs/genus.pyx +++ b/src/sage/graphs/genus.pyx @@ -39,6 +39,8 @@ described throughout the file. from __future__ import print_function from libc.string cimport memcpy +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off cimport sage.combinat.permutation_cython @@ -48,10 +50,6 @@ from sage.graphs.base.dense_graph cimport DenseGraph from sage.graphs.graph import Graph -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" - - cdef inline int edge_map(int i): """ We might as well make the edge map nice, since the vertex map diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 5e6c7ef4178..6ce60c0e9b4 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1833,6 +1833,48 @@ def is_biconnected(self): return False return True + @doc_index("Graph properties") + def is_block_graph(self): + r""" + Return whether this graph is a block graph. + + A block graph is a connected graph in which every biconnected component + (block) is a clique. + + .. SEEALSO:: + + - :wikipedia:`Block_graph` for more details on these graphs + - :meth:`~sage.graphs.graph_generators.GraphGenerators.RandomBlockGraph` + -- generator of random block graphs + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree` + + + EXAMPLES:: + + sage: G = graphs.RandomBlockGraph(6, 2, kmax=4) + sage: G.is_block_graph() + True + sage: from sage.graphs.isgci import graph_classes + sage: G in graph_classes.Block + True + sage: graphs.CompleteGraph(4).is_block_graph() + True + sage: graphs.RandomTree(6).is_block_graph() + True + sage: graphs.PetersenGraph().is_block_graph() + False + sage: Graph(4).is_block_graph() + False + """ + if not self.is_connected(): + return False + if self.is_clique(): + return True + + B,C = self.blocks_and_cut_vertices() + return all(self.is_clique(vertices=block) for block in B) + @doc_index("Graph properties") def is_apex(self): """ diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx index 00f1f506021..f1ee2198e23 100644 --- a/src/sage/graphs/graph_decompositions/bandwidth.pyx +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -100,15 +100,20 @@ This module contains the following methods Functions --------- """ + #***************************************************************************** -# Copyright (C) 2015 Nathann Cohen +# Copyright (C) 2015 Nathann Cohen # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" from libc.stdint cimport uint16_t +from cysignals.signals cimport sig_check + from sage.graphs.distances_all_pairs cimport all_pairs_shortest_path_BFS from sage.graphs.base.boost_graph import bandwidth_heuristics from sage.ext.memory_allocator cimport MemoryAllocator @@ -263,7 +268,6 @@ def bandwidth(G, k=None): for i in range(n): left_to_order[i] = i - sig_on() if k is None: for kk in range((n-1)//G.diameter(),n): if bandwidth_C(n,kk,d,current,ordering,left_to_order,index_array_tmp,ith_range_array,range_array_tmp): @@ -275,13 +279,12 @@ def bandwidth(G, k=None): if ans: order = [int_to_vertex[ordering[i]] for i in range(n)] - sig_off() - if ans: ans = (kk, order) if k is None else order return ans + cdef bint bandwidth_C(int n, int k, unsigned short ** d, index_t * current, # choice of vertex for the current position @@ -304,6 +307,7 @@ cdef bint bandwidth_C(int n, int k, i = 0 while True: + sig_check() # There are (n-i) choices for vertex i, as i-1 have already been # determined. Thus, i<=current[i] +# Copyright (C) 2011 Nathann Cohen # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import print_function -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport * from libc.string cimport memset @@ -313,7 +317,7 @@ def mkgraph(int num_vertices): from sage.graphs.graph import Graph g = Graph() - cdef subset_t * tab = sig_malloc(sizeof(subset_t) * (2*num_vertices -1)) + cdef subset_t * tab = check_allocarray(2*num_vertices - 1, sizeof(subset_t)) tab[0] = 0x7ffffffful >> (31 - num_vertices) cdef int beg = 0 diff --git a/src/sage/graphs/graph_decompositions/tdlib.pyx b/src/sage/graphs/graph_decompositions/tdlib.pyx index 968f3c3149e..252b35fa585 100644 --- a/src/sage/graphs/graph_decompositions/tdlib.pyx +++ b/src/sage/graphs/graph_decompositions/tdlib.pyx @@ -65,12 +65,11 @@ Methods """ from libcpp.vector cimport vector +from cysignals.signals cimport sig_on, sig_off from sage.sets.set import Set from sage.graphs.graph import Graph -include "cysignals/signals.pxi" - cdef extern from "tdlib/sage_tdlib.cpp": int sage_exact_decomposition(vector[unsigned int] &V_G, vector[unsigned int] &E_G, vector[vector[int]] &V_T, vector[unsigned int] &E_T, int lb) diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index de8bd6cdfa3..4683b242a17 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -283,9 +283,10 @@ Methods from __future__ import absolute_import, print_function -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" from libc.string cimport memset +from cysignals.memory cimport check_malloc, sig_malloc, sig_free +from cysignals.signals cimport sig_check, sig_on, sig_off + from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, compute_out_neighborhood_cardinality, popcount32 from libc.stdint cimport uint8_t, int8_t include "sage/data_structures/binary_matrix.pxi" @@ -355,7 +356,7 @@ def lower_bound(G): cdef int n = FD.n # minimums[i] is means to store the value of c'_{i+1} - minimums = sig_malloc(sizeof(uint8_t)* n) + minimums = check_malloc(n) cdef unsigned int i # They are initialized to n @@ -956,14 +957,8 @@ def vertex_separation_exp(G, verbose = False): print("Memory allocation") g.print_adjacency_matrix() - sig_on() - cdef unsigned int mem = 1 << g.n - cdef uint8_t * neighborhoods = sig_malloc(mem) - - if neighborhoods == NULL: - sig_off() - raise MemoryError("Error allocating memory. I just tried to allocate "+str(mem>>10)+"MB, could that be too much ?") + cdef uint8_t * neighborhoods = check_malloc(mem) memset(neighborhoods, -1, mem) @@ -972,6 +967,7 @@ def vertex_separation_exp(G, verbose = False): if verbose: print("Looking for a strategy of cost", str(k)) + sig_check() if exists(g, neighborhoods, 0, k) <= k: break @@ -982,7 +978,6 @@ def vertex_separation_exp(G, verbose = False): cdef list order = find_order(g, neighborhoods, k) sig_free(neighborhoods) - sig_off() return k, list( g.int_to_vertices[i] for i in order ) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 161234dbaa5..3cb6e4c879b 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -303,6 +303,7 @@ def __append_to_doc(methods): ["RandomBarabasiAlbert", "RandomBicubicPlanar", "RandomBipartite", + "RandomBlockGraph", "RandomBoundedToleranceGraph", "RandomGNM", "RandomGNP", @@ -2080,6 +2081,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None RandomBarabasiAlbert = staticmethod(sage.graphs.generators.random.RandomBarabasiAlbert) RandomBipartite = staticmethod(sage.graphs.generators.random.RandomBipartite) RandomBicubicPlanar = staticmethod(sage.graphs.generators.random.RandomBicubicPlanar) + RandomBlockGraph = staticmethod(sage.graphs.generators.random.RandomBlockGraph) RandomBoundedToleranceGraph = staticmethod(sage.graphs.generators.random.RandomBoundedToleranceGraph) RandomGNM = staticmethod(sage.graphs.generators.random.RandomGNM) RandomGNP = staticmethod(sage.graphs.generators.random.RandomGNP) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 9569be77aa5..4ef8b6b3892 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -171,8 +171,10 @@ Methods #***************************************************************************** from __future__ import print_function -# imports from libc.string cimport memset +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off + from sage.graphs.graph import Graph from sage.graphs.distances_all_pairs cimport c_distances_all_pairs from sage.arith.all import binomial @@ -180,13 +182,11 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.functions.other import floor from sage.data_structures.bitset import Bitset -include "cysignals/memory.pxi" from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from libc.stdint cimport uint16_t, uint32_t, uint64_t -include "cysignals/signals.pxi" include "sage/data_structures/bitset.pxi" @@ -535,7 +535,7 @@ cdef inline pair** sort_pairs(uint32_t N, position k a pointer to the first included pair (i,j) such that values[i][j] = k. """ - # pairs_of_length[d] is the list of pairs of vertices at distance d + # pairs_of_length[d] is the list of pairs of vertices at distance d cdef pair ** pairs_of_length = check_allocarray(D+1, sizeof(pair *)) cdef unsigned short *p_to_include cdef uint32_t i,j,k @@ -557,22 +557,11 @@ cdef inline pair** sort_pairs(uint32_t N, nb_p[0] += 1 nb_pairs_of_length[ values[i][j] ] += 1 - if pairs_of_length != NULL: - pairs_of_length[0] = check_allocarray(nb_p[0], sizeof(pair)) + pairs_of_length[0] = check_allocarray(nb_p[0], sizeof(pair)) # temporary variable used to fill pairs_of_length cdef uint32_t * cpt_pairs = check_calloc(D+1, sizeof(uint32_t)) - if (pairs_of_length == NULL or - pairs_of_length[0] == NULL or - cpt_pairs == NULL): - if pairs_of_length != NULL: - sig_free(pairs_of_length[0]) - sig_free(nb_pairs_of_length) - sig_free(pairs_of_length) - sig_free(cpt_pairs) - raise MemoryError - # ==> Defines pairs_of_length[d] for all d for i from 1 <= i <= D: pairs_of_length[i] = pairs_of_length[i-1] + nb_pairs_of_length[i-1] @@ -1406,12 +1395,6 @@ def hyperbolicity(G, _distances_ = check_allocarray(N * N, sizeof(unsigned short)) _far_apart_pairs_ = check_allocarray(N * N, sizeof(unsigned short)) far_apart_pairs = check_allocarray(N, sizeof(unsigned short *)) - if _distances_ == NULL or _far_apart_pairs_ == NULL or far_apart_pairs == NULL: - sig_free(_distances_) - sig_free(distances) - sig_free(_far_apart_pairs_) - sig_free(far_apart_pairs) - raise MemoryError("Unable to allocate array '_distances_' or '_far_apart_pairs_'.") distances_and_far_apart_pairs(G, _distances_, _far_apart_pairs_) diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index c00c24ca6c6..09554dc9a16 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -149,6 +149,21 @@ * - Class - Related methods + * - Apex + + - :meth:`~Graph.is_apex()`, + :meth:`~Graph.apex_vertices()` + + * - AT_free + + - :meth:`~Graph.is_asteroidal_triple_free` + + * - Biconnected + + - :meth:`~Graph.is_biconnected`, + :meth:`~GenericGraph.blocks_and_cut_vertices`, + :meth:`~GenericGraph.blocks_and_cuts_tree` + * - BinaryTrees - :meth:`~sage.graphs.graph_generators.GraphGenerators.BalancedTree`, @@ -161,7 +176,9 @@ * - Block - - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` + - :meth:`~sage.graphs.graph.Graph.is_block_graph`, + :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices`, + :meth:`~sage.graphs.graph_generators.GraphGenerators.RandomBlockGraph` * - Chordal @@ -1043,7 +1060,7 @@ def _XML_to_dict(root): graph_classes.Biconnected = GraphClass("Biconnected", "gc_771", recognition_function = lambda x:x.is_biconnected()) graph_classes.BinaryTrees = GraphClass("BinaryTrees", "gc_847") graph_classes.Bipartite = GraphClass("Bipartite", "gc_69", recognition_function = lambda x:x.is_bipartite()) -graph_classes.Block = GraphClass("Block", "gc_93") +graph_classes.Block = GraphClass("Block", "gc_93", recognition_function = lambda x:x.is_block_graph()) graph_classes.Chordal = GraphClass("Chordal", "gc_32", recognition_function = lambda x:x.is_chordal()) graph_classes.ClawFree = GraphClass("Claw-free", "gc_62") graph_classes.Comparability = GraphClass("Comparability", "gc_72", recognition_function = lambda x: __import__('sage').graphs.comparability.is_comparability) diff --git a/src/sage/graphs/matchpoly.pyx b/src/sage/graphs/matchpoly.pyx index dc3f0b7b9b8..8c6b87fca3c 100644 --- a/src/sage/graphs/matchpoly.pyx +++ b/src/sage/graphs/matchpoly.pyx @@ -25,18 +25,22 @@ Methods """ #***************************************************************************** -# Copyright (C) 2010 Robert Miller +# Copyright (C) 2010 Robert Miller # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off + from sage.rings.polynomial.polynomial_ring import polygen from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer from sage.misc.all import prod -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * @@ -250,26 +254,20 @@ def matching_polynomial(G, complement=True, name=None): # The edges_mem table is of size (2 * nedges * nedges), and is to be read as # nedges blocks of size (2 * nedges). These blocks of size (2 * nedges) are # themselves to be read as two blocks of length nedges, addressed as edges1 - # and edges2 - - # Only the first block of size (2*nedges) is here filled. The function + # and edges2. + # + # Only the first block of size (2 * nedges) is here filled. The function # delete_and_add will need the rest of the memory. fmpz_poly_init(pol) # sets to zero - edges_mem = sig_malloc(2 * nedges * nedges * sizeof(int)) - edges = sig_malloc(2 * nedges * sizeof(int *)) - if edges_mem is NULL or edges is NULL: - if edges_mem is not NULL: - sig_free(edges_mem) - if edges is not NULL: - sig_free(edges) - raise MemoryError("Error allocating memory for matchpoly.") - - for i from 0 <= i < 2 * nedges: + edges = check_allocarray(2 * nedges, sizeof(int *)) + edges_mem = check_allocarray(2 * nedges * nedges, sizeof(int)) + + for i in range(2 * nedges): edges[i] = edges_mem + i * nedges - edges1 = edges[0] - edges2 = edges[1] + edges1 = edges_mem # edges[0] + edges2 = edges_mem + nedges # edges[1] cur = 0 for i, j in sorted(map(sorted, G.edges(labels=False))): @@ -285,12 +283,12 @@ def matching_polynomial(G, complement=True, name=None): # Building the actual matching polynomial - coeffs_ZZ = [] + cdef list coeffs_ZZ = [] cdef Integer c_ZZ - for i from 0 <= i <= nverts: + for i in range(nverts + 1): c_ZZ = Integer(0) fmpz_poly_get_coeff_mpz(c_ZZ.value, pol, i) - coeffs_ZZ.append(c_ZZ * (-1)**((nverts - i) / 2)) + coeffs_ZZ.append(c_ZZ * (-1)**((nverts - i) // 2)) f = x.parent()(coeffs_ZZ) sig_free(edges_mem) diff --git a/src/sage/graphs/modular_decomposition.pyx b/src/sage/graphs/modular_decomposition.pyx index c822b5685e4..03150f1491a 100644 --- a/src/sage/graphs/modular_decomposition.pyx +++ b/src/sage/graphs/modular_decomposition.pyx @@ -2,9 +2,8 @@ r""" Modular decomposition """ -include "cysignals/memory.pxi" +from cysignals.memory cimport check_calloc, check_malloc -from libc.string cimport memset ##################################################### # The following code is mainly a Cythonized @@ -120,9 +119,7 @@ cpdef modular_decomposition(g): label_id[label] = id G.n = g.order() - G.G = sig_malloc(G.n*sizeof(c_adj *)) - - memset( G.G, 0, G.n*sizeof(c_adj *)) + G.G = check_calloc(G.n, sizeof(c_adj *)) # Creating the graph structure for u,v in g.edges(labels = False): @@ -130,12 +127,12 @@ cpdef modular_decomposition(g): i = label_id[u] j = label_id[v] - a= sig_malloc(sizeof(c_adj)) + a = check_malloc(sizeof(c_adj)) a.s = j a.suiv = G.G[i] G.G[i] = a - a= sig_malloc(sizeof(c_adj)) + a = check_malloc(sizeof(c_adj)) a.s = i a.suiv = G.G[j] G.G[j] = a diff --git a/src/sage/graphs/trees.pyx b/src/sage/graphs/trees.pyx index dcc21f1b1c3..3ad0de3fb42 100644 --- a/src/sage/graphs/trees.pyx +++ b/src/sage/graphs/trees.pyx @@ -16,10 +16,8 @@ REFERENCES: """ from __future__ import print_function -cdef extern from "limits.h": - cdef int INT_MAX - -include "cysignals/memory.pxi" +from libc.limits cimport INT_MAX +from cysignals.memory cimport check_allocarray, sig_free # from networkx import MultiGraph @@ -85,12 +83,8 @@ cdef class TreeIterator: sage: t = TreeIterator(100) sage: t = None # indirect doctest """ - if self.l != NULL: - sig_free(self.l) - self.l = NULL - if self.current_level_sequence != NULL: - sig_free(self.current_level_sequence) - self.current_level_sequence = NULL + sig_free(self.l) + sig_free(self.current_level_sequence) def __str__(self): r""" @@ -147,11 +141,8 @@ cdef class TreeIterator: self.first_time = 0 self.q = 0 else: - self.l = sig_malloc(self.vertices * sizeof(int)) - self.current_level_sequence = sig_malloc(self.vertices * sizeof(int)) - - if self.l == NULL or self.current_level_sequence == NULL: - raise MemoryError + self.l = check_allocarray(self.vertices, sizeof(int)) + self.current_level_sequence = check_allocarray(self.vertices, sizeof(int)) self.generate_first_level_sequence() self.first_time = 0 diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 4416858946c..fa2639ba424 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -76,7 +76,7 @@ from sage.sets.set import Set from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement from sage.misc.package import PackageNotFoundError -from sage.structure.sage_object import richcmp, rich_to_bool +from sage.structure.richcmp import richcmp, rich_to_bool class Braid(FinitelyPresentedGroupElement): diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index f31236bf74d..8f76df86089 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -65,7 +65,7 @@ from sage.rings.integer_ring import IntegerRing from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject from sage.structure.element cimport Element -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp class ParentLibGAP(SageObject): diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx index 6dec90b407a..dca3f3ff963 100644 --- a/src/sage/groups/matrix_gps/group_element.pyx +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -78,7 +78,7 @@ from __future__ import print_function from sage.structure.element cimport MultiplicativeGroupElement, Element, MonoidElement, Matrix from sage.structure.parent cimport Parent -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from sage.libs.gap.element cimport GapElement, GapElement_List from sage.groups.libgap_wrapper cimport ElementLibGAP diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 4e91c4b23bf..9337c45f7ec 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -75,7 +75,7 @@ from sage.interfaces.all import gap from sage.interfaces.gap import is_GapElement from sage.sets.finite_enumerated_set import FiniteEnumeratedSet import sage.structure.coerce as coerce -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool import operator diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index bd5f04b703a..e463db7179b 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -16,7 +16,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element import ModuleElement, parent -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.categories.category_types import ChainComplexes from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule diff --git a/src/sage/interfaces/gap3.py b/src/sage/interfaces/gap3.py index af057778810..f4cac6c3ee5 100644 --- a/src/sage/interfaces/gap3.py +++ b/src/sage/interfaces/gap3.py @@ -311,7 +311,7 @@ def __init__(self, command=gap3_cmd): Expect.__init__(self, name='gap3', prompt='gap> ', - command=self.__gap3_command_string + " -p -y 500", + command=self.__gap3_command_string + " -p -b -y 500", server=None, ulimit=None, script_subdirectory=None, @@ -327,6 +327,8 @@ def __init__(self, command=gap3_cmd): def _start(self): r""" + Initialize the interface and start gap3. + EXAMPLES:: sage: gap3 = Gap3() #optional - gap3 @@ -335,6 +337,11 @@ def _start(self): sage: gap3._start() #optional - gap3 sage: gap3.is_running() #optional - gap3 True + + Check that :trac:`23142` is fixed:: + + sage: gap3.eval("1+1") #optional - gap3 + '2' sage: gap3.quit() #optional - gap3 """ Expect._start(self) @@ -345,6 +352,7 @@ def _start(self): '@p\d+\.','@@','@[A-Z]','@[123456!"#$%&][^+]*\+', '@e','@c', '@f','@h','@i','@m','@n','@r','@s\d','@w.*\+','@x','@z']) self._compiled_small_pattern = self._expect.compile_pattern_list('@J') + self._expect.expect("@i") def _object_class(self): r""" @@ -557,7 +565,7 @@ def _install_hints(self): sage: gap3('3+2') Traceback (most recent call last): ... - TypeError: unable to start gap3 because the command '/wrongpath/gap3 -p -y 500' failed: The command was not found or was not executable: /wrongpath/gap3. + TypeError: unable to start gap3 because the command '/wrongpath/gap3 ...' failed: The command was not found or was not executable: /wrongpath/gap3. Your attempt to start GAP3 failed, either because you do not have have GAP3 installed, or because it is not configured correctly. diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 016ba0ca732..ae6737445ea 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -79,7 +79,8 @@ from __future__ import absolute_import from libc.string cimport memcpy from cysignals.signals cimport sig_on, sig_off -from sage.structure.sage_object cimport SageObject, richcmp +from sage.structure.sage_object cimport SageObject +from sage.structure.richcmp cimport richcmp from sage.rings.integer cimport Integer diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 8e68c5fb0b1..8488fa8a387 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -21,7 +21,7 @@ cdef extern from *: # hack to get at cython macro int unlikely(int) int likely(int) -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from sage.libs.singular.decl cimport ideal, ring, poly, currRing from sage.libs.singular.decl cimport rChangeCurrRing from sage.libs.singular.decl cimport new_skStrategy, delete_skStrategy, id_RankFreeModule diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index b9c35be1aed..ecfa78bcd39 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -2364,8 +2364,11 @@ cdef class Matrix(matrix0.Matrix): Full MatrixSpace of 2 by 3 dense matrices over Real Field with 53 bits of precision """ - if self._nrows == nrows and self._ncols == ncols and (sparse is None or self.is_sparse() == sparse): - return self._parent(entries=entries, coerce=coerce, copy=copy) + if (sparse is None or self.is_sparse() == sparse): + if self._nrows == nrows and self._ncols == ncols: + return self._parent(entries=entries, coerce=coerce, copy=copy) + elif self._nrows == ncols and self._ncols == nrows: + return self._parent.transposed(entries=entries, coerce=coerce, copy=copy) return self.matrix_space(nrows, ncols, sparse=sparse)(entries=entries, coerce=coerce, copy=copy) def block_sum(self, Matrix other): diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index d780a22a548..c5fe5a6b07c 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -3689,7 +3689,7 @@ cdef class Matrix(matrix1.Matrix): sage: matrix(Integers(6), 2, 2).right_kernel_matrix(algorithm='generic') Traceback (most recent call last): ... - ValueError: 'generic' matrix kernel algorithm only available over a field, not over Ring of integers modulo 6 + NotImplementedError: Echelon form not implemented over 'Ring of integers modulo 6'. sage: matrix(QQ, 2, 2).right_kernel_matrix(algorithm='pluq') Traceback (most recent call last): ... @@ -8059,10 +8059,36 @@ cdef class Matrix(matrix1.Matrix): Traceback (most recent call last): ... TypeError: tensor product requires a second matrix, not junk + + TESTS: + + Check that `m \times 0` and `0 \times m` matrices work + (:trac:`22769`):: + + sage: m1 = matrix(QQ, 1, 0, []) + sage: m2 = matrix(QQ, 2, 2, [1, 2, 3, 4]) + sage: m1.tensor_product(m2).dimensions() + (2, 0) + sage: m2.tensor_product(m1).dimensions() + (2, 0) + sage: m3 = matrix(QQ, 0, 3, []) + sage: m3.tensor_product(m2).dimensions() + (0, 6) + sage: m2.tensor_product(m3).dimensions() + (0, 6) + + sage: m1 = MatrixSpace(GF(5), 3, 2).an_element() + sage: m2 = MatrixSpace(GF(5), 0, 4).an_element() + sage: m1.tensor_product(m2).parent() + Full MatrixSpace of 0 by 8 dense matrices over Finite Field of size 5 """ - from sage.matrix.constructor import block_matrix if not isinstance(A, Matrix): raise TypeError('tensor product requires a second matrix, not {0}'.format(A)) + from sage.matrix.constructor import block_matrix + # Special case when one of the matrices is 0 \times m or m \times 0 + if self.nrows() == 0 or self.ncols() == 0 or A.nrows() == 0 or A.ncols() == 0: + return self.matrix_space(self.nrows()*A.nrows(), + self.ncols()*A.ncols()).zero_matrix().__copy__() return block_matrix(self.nrows(), self.ncols(), [x * A for x in self.list()], subdivide=subdivide) diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index e598561961c..00e24f008c9 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1911,6 +1911,21 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): sage: subdiv = super(type(Mp),Mp).tensor_product(Np).subdivisions() sage: Mp.tensor_product(Np).subdivisions() == subdiv True + + Check that `m \times 0` and `0 \times m` matrices work + (:trac:`22769`):: + + sage: m1 = matrix(C, 1, 0, []) + sage: m2 = matrix(C, 2, 2, [1, 2, 3, 4]) + sage: m1.tensor_product(m2).dimensions() + (2, 0) + sage: m2.tensor_product(m1).dimensions() + (2, 0) + sage: m3 = matrix(C, 0, 3, []) + sage: m3.tensor_product(m2).dimensions() + (0, 6) + sage: m2.tensor_product(m3).dimensions() + (0, 6) """ if not isinstance(A, Matrix): raise TypeError('tensor product requires a second matrix, not {0}'.format(A)) diff --git a/src/sage/matrix/matrix_dense.pyx b/src/sage/matrix/matrix_dense.pyx index 7f71a383565..0a6b864c665 100644 --- a/src/sage/matrix/matrix_dense.pyx +++ b/src/sage/matrix/matrix_dense.pyx @@ -13,7 +13,7 @@ from __future__ import print_function cimport sage.matrix.matrix as matrix from sage.structure.element cimport Element, RingElement -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool import sage.matrix.matrix_space import sage.structure.sequence diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 5b12c64c133..b697f6ac0a5 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -407,15 +407,13 @@ cpdef __matrix_from_rows_of_matrices(X): ## v = sum([y.list() for y in X],[]) ## return matrix(K, len(X), X[0].nrows()*X[0].ncols(), v) - from matrix_space import MatrixSpace - cdef Matrix_modn_dense_template A, T + cdef Matrix_modn_dense_template T cdef Py_ssize_t i, n, m n = len(X) T = X[0] m = T._nrows * T._ncols - A = T.__class__.__new__(T.__class__, MatrixSpace(X[0].base_ring(), n, m), 0, 0, 0) - A.p = T.p + cdef Matrix_modn_dense_template A = T.new_matrix(nrows = n, ncols = m) for i from 0 <= i < n: T = X[i] @@ -569,7 +567,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): raise IndexError("The vector of entries has the wrong length.") k = 0 - cdef celement n cdef long tmp for i in range(self._nrows): @@ -590,7 +587,7 @@ cdef class Matrix_modn_dense_template(Matrix_dense): elif coerce: v[j] = R(entries[k]) else: - v[j] = (entries[k]) + v[j] = (entries[k]) k = k + 1 def __hash__(self): @@ -2099,6 +2096,108 @@ cdef class Matrix_modn_dense_template(Matrix_dense): self.cache('pivots', tuple(pivots)) self.cache('in_echelon_form',True) + def right_kernel_matrix(self, algorithm='linbox', basis='echelon'): + r""" + Returns a matrix whose rows form a basis for the right kernel + of ``self``, where ``self`` is a matrix over a (small) finite field. + + INPUT: + + - ``algorithm`` -- (default: ``'linbox'``) a parameter that is + passed on to ``self.echelon_form``, if computation of an echelon + form is required; see that routine for allowable values + + - ``basis`` -- (default: ``'echelon'``) a keyword that describes the + format of the basis returned, allowable values are: + + - ``'echelon'``: the basis matrix is in echelon form + - ``'pivot'``: the basis matrix is such that the submatrix obtained + by taking the columns that in ``self`` contain no pivots, is the + identity matrix + - ``'computed'``: no work is done to transform the basis; in + the current implementation the result is the negative of + that returned by ``'pivot'`` + + OUTPUT: + + A matrix ``X`` whose rows are a basis for the right kernel of + ``self``. This means that ``self * X.transpose()`` is a zero matrix. + + The result is not cached, but the routine benefits when ``self`` is + known to be in echelon form already. + + EXAMPLES:: + + sage: M = matrix(GF(5),6,6,range(36)) + sage: M.right_kernel_matrix(basis='computed') + [4 2 4 0 0 0] + [3 3 0 4 0 0] + [2 4 0 0 4 0] + [1 0 0 0 0 4] + sage: M.right_kernel_matrix(basis='pivot') + [1 3 1 0 0 0] + [2 2 0 1 0 0] + [3 1 0 0 1 0] + [4 0 0 0 0 1] + sage: M.right_kernel_matrix() + [1 0 0 0 0 4] + [0 1 0 0 1 3] + [0 0 1 0 2 2] + [0 0 0 1 3 1] + sage: M * M.right_kernel_matrix().transpose() + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + """ + if self.fetch('in_echelon_form') is None: + self = self.echelon_form(algorithm=algorithm) + + cdef Py_ssize_t r = self.rank() + cdef Py_ssize_t nrows = self._nrows + cdef Py_ssize_t ncols = self._ncols + cdef Py_ssize_t i, j, k + + cdef Py_ssize_t* nonpivots = sig_malloc(sizeof(Py_ssize_t)*(ncols-r)) + cdef Py_ssize_t* pivots = sig_malloc(sizeof(Py_ssize_t)*(r)) + cdef tuple pivot_tuple = self.pivots() + + for i in range(r): + pivots[i] = pivot_tuple[i] + j = 0 + k = 0 + for i in range(ncols): + if j < r and i == pivots[j]: + j += 1 + else: + nonpivots[k] = i + k += 1 + + cdef Matrix_modn_dense_template M = self.new_matrix(nrows=ncols-r, ncols=ncols) + cdef celement pm1 = self.p - 1 + + k = 0 + for i in range(ncols-r): + for j in range(ncols-r): + M._entries[nonpivots[i]+j*ncols] = 0 + M._entries[nonpivots[i]+k*ncols] = pm1 + k += 1 + for j in range(r): + M._entries[i*ncols+pivots[j]] = self._entries[nonpivots[i]+j*ncols] + + sig_free(pivots) + sig_free(nonpivots) + if basis == 'computed': + return M + elif basis == 'pivot': + return -M + elif basis != 'echelon': + raise ValueError("matrix kernel basis format not recognized") + M.echelonize(algorithm=algorithm) + return M + def hessenbergize(self): """ Transforms self in place to its Hessenberg form. @@ -2976,6 +3075,259 @@ cdef class Matrix_modn_dense_template(Matrix_dense): L.subdivide(self.subdivisions()) return L + def transpose(self): + """ + Return the transpose of ``self``, without changing ``self``. + + EXAMPLES: + + We create a matrix, compute its transpose, and note that + the original matrix is not changed. :: + + sage: M = MatrixSpace(GF(41), 2) + sage: A = M([1,2,3,4]) + sage: B = A.transpose() + sage: print B + [1 3] + [2 4] + sage: print A + [1 2] + [3 4] + + ``.T`` is a convenient shortcut for the transpose:: + + sage: A.T + [1 3] + [2 4] + + :: + + sage: A.subdivide(None, 1); A + [1|2] + [3|4] + sage: A.transpose() + [1 3] + [---] + [2 4] + """ + cdef Py_ssize_t nrows = self._nrows + cdef Py_ssize_t ncols = self._ncols + + cdef Matrix_modn_dense_template M = self.new_matrix(nrows = ncols, ncols = nrows) + cdef Py_ssize_t i,j + + for i from 0 <= i < ncols: + for j from 0 <= j < nrows: + M._entries[j+i*nrows] = self._entries[i+j*ncols] + + if self._subdivisions is not None: + row_divs, col_divs = self.subdivisions() + M.subdivide(col_divs, row_divs) + + return M + + cdef _stack_impl(self, bottom): + r""" + Implementation of :meth:`stack` by returning a new matrix + formed by appending the matrix ``bottom`` beneath ``self``. + + Assume that ``self`` and ``other`` are compatible in the sense + that they have the same base ring and that both are dense. + + INPUT: + + - ``bottom`` -- a matrix compatible with ``self`` + + EXAMPLES: + + Stacking with a matrix:: + + sage: A = matrix(GF(41), 4, 3, range(12)) + sage: B = matrix(GF(41), 3, 3, range(9)) + sage: A.stack(B) + [ 0 1 2] + [ 3 4 5] + [ 6 7 8] + [ 9 10 11] + [ 0 1 2] + [ 3 4 5] + [ 6 7 8] + + Stacking with a vector:: + + sage: A = matrix(GF(41), 3, 2, [0, 2, 4, 6, 8, 10]) + sage: v = vector(GF(41), 2, [100, 200]) + sage: A.stack(v) + [ 0 2] + [ 4 6] + [ 8 10] + [18 36] + + Errors are raised if the sizes are incompatible:: + + sage: A = matrix(GF(41), [[1, 2],[3, 4]]) + sage: B = matrix(GF(41), [[10, 20, 30], [40, 50, 60]]) + sage: A.stack(B) + Traceback (most recent call last): + ... + TypeError: number of columns must be the same, not 2 and 3 + + sage: v = vector(GF(41), [100, 200, 300]) + sage: A.stack(v) + Traceback (most recent call last): + ... + TypeError: number of columns must be the same, not 2 and 3 + + Setting ``subdivide`` to ``True`` will, in its simplest form, + add a subdivision between ``self`` and ``bottom``:: + + sage: A = matrix(GF(41), 2, 5, range(10)) + sage: B = matrix(GF(41), 3, 5, range(15)) + sage: A.stack(B, subdivide=True) + [ 0 1 2 3 4] + [ 5 6 7 8 9] + [--------------] + [ 0 1 2 3 4] + [ 5 6 7 8 9] + [10 11 12 13 14] + + Row subdivisions are preserved by stacking, and enriched, + if subdivisions are requested. (So multiple stackings can + be recorded.) :: + + sage: A = matrix(GF(41), 2, 4, range(8)) + sage: A.subdivide([1], None) + sage: B = matrix(GF(41), 3, 4, range(12)) + sage: B.subdivide([2], None) + sage: A.stack(B, subdivide=True) + [ 0 1 2 3] + [-----------] + [ 4 5 6 7] + [-----------] + [ 0 1 2 3] + [ 4 5 6 7] + [-----------] + [ 8 9 10 11] + + Column subdivisions can be preserved, but only if they are identical. + Otherwise, this information is discarded and must be managed + separately. :: + + sage: A = matrix(GF(41), 2, 5, range(10)) + sage: A.subdivide(None, [2,4]) + sage: B = matrix(GF(41), 3, 5, range(15)) + sage: B.subdivide(None, [2,4]) + sage: A.stack(B, subdivide=True) + [ 0 1| 2 3| 4] + [ 5 6| 7 8| 9] + [-----+-----+--] + [ 0 1| 2 3| 4] + [ 5 6| 7 8| 9] + [10 11|12 13|14] + + sage: A.subdivide(None, [1,2]) + sage: A.stack(B, subdivide=True) + [ 0 1 2 3 4] + [ 5 6 7 8 9] + [--------------] + [ 0 1 2 3 4] + [ 5 6 7 8 9] + [10 11 12 13 14] + + The result retains the base ring of ``self`` by coercing + the elements of ``bottom`` into the base ring of ``self``:: + + sage: A = matrix(GF(41), 1, 2, [1,2]) + sage: B = matrix(ZZ, 1, 2, [100, 100]) + sage: C = A.stack(B); C + [ 1 2] + [18 18] + + sage: C.parent() + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 41 + + sage: D = B.stack(A); D + [18 18] + [ 1 2] + + sage: D.parent() + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 41 + """ + cdef Matrix_modn_dense_template other = bottom + cdef Matrix_modn_dense_template M = self.new_matrix(nrows=self._nrows+other._nrows, + ncols=self._ncols) + cdef Py_ssize_t selfsize = self._ncols * self._nrows + memcpy(M._entries, self._entries, sizeof(celement)*selfsize) + memcpy(M._entries+selfsize, other._entries, sizeof(celement)*other._ncols*other._nrows) + return M + + def submatrix(self, Py_ssize_t row=0, Py_ssize_t col=0, + Py_ssize_t nrows=-1, Py_ssize_t ncols=-1): + r""" + Return the matrix constructed from self using the specified + range of rows and columns. + + INPUT: + + - ``row``, ``col`` -- index of the starting row and column. + Indices start at zero + + - ``nrows``, ``ncols`` -- (optional) number of rows and columns to + take. If not provided, take all rows below and all columns to + the right of the starting entry + + .. SEEALSO:: + + The functions :func:`matrix_from_rows`, + :func:`matrix_from_columns`, and + :func:`matrix_from_rows_and_columns` allow one to select + arbitrary subsets of rows and/or columns. + + EXAMPLES: + + Take the `3 \times 3` submatrix starting from entry `(1,1)` in a + `4 \times 4` matrix:: + + sage: m = matrix(GF(17),4, [1..16]) + sage: m.submatrix(1, 1) + [ 6 7 8] + [10 11 12] + [14 15 16] + + Same thing, except take only two rows:: + + sage: m.submatrix(1, 1, 2) + [ 6 7 8] + [10 11 12] + + And now take only one column:: + + sage: m.submatrix(1, 1, 2, 1) + [ 6] + [10] + + You can take zero rows or columns if you want:: + + sage: m.submatrix(0, 0, 0) + [] + sage: parent(m.submatrix(0, 0, 0)) + Full MatrixSpace of 0 by 4 dense matrices over Finite Field of size 17 + """ + if ncols == -1: + ncols = self._ncols - col + + if nrows == -1: + nrows = self._nrows - row + + if col != 0 or ncols != self._ncols: + return self.matrix_from_rows_and_columns(range(row, row+nrows), range(col, col+ncols)) + + if nrows < 0 or row < 0 or row + nrows > self._nrows: + raise IndexError("rows out of range") + + cdef Matrix_modn_dense_template M = self.new_matrix(nrows=nrows, ncols=self._ncols) + memcpy(M._entries, self._entries+row*ncols, sizeof(celement)*ncols*nrows) + return M def _matrices_from_rows(self, Py_ssize_t nrows, Py_ssize_t ncols): """ @@ -3010,19 +3362,12 @@ cdef class Matrix_modn_dense_template(Matrix_dense): if nrows * ncols != self._ncols: raise ValueError("nrows * ncols must equal self's number of columns") - from matrix_space import MatrixSpace - F = self.base_ring() - MS = MatrixSpace(F, nrows, ncols) - cdef Matrix_modn_dense_template M cdef Py_ssize_t i cdef Py_ssize_t n = nrows * ncols ans = [] for i from 0 <= i < self._nrows: - # Quickly construct a new mod-p matrix - M = self.__class__.__new__(self.__class__, MS, 0,0,0) - M.p = self.p - # Set the entries + M = self.new_matrix(nrows = nrows, ncols = ncols) memcpy(M._entries, self._entries+i*n, sizeof(celement)*n) ans.append(M) return ans @@ -3064,6 +3409,3 @@ cdef class Matrix_modn_dense_template(Matrix_dense): cdef celement *_from = self._entries+(i*self._ncols) for j in range(self._ncols): to[j] = _from[j] - - - diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index e2f79d33f40..8b6c6c4093f 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -381,6 +381,24 @@ def full_category_initialisation(self): " as a matrix space now has its category" " systematically fully initialized") + @lazy_attribute + def transposed(self): + """ + The transposed matrix space, having the same base ring and sparseness, + but number of columns and rows is swapped. + + EXAMPLES:: + + sage: MS = MatrixSpace(GF(3), 7, 10) + sage: MS.transposed + Full MatrixSpace of 10 by 7 dense matrices over Finite Field of size 3 + sage: MS = MatrixSpace(GF(3), 7, 7) + sage: MS.transposed is MS + True + + """ + return MatrixSpace(self._base, self.__ncols, self.__nrows, self.__is_sparse) + @lazy_attribute def _copy_zero(self): """ diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index 4cd99ca1788..27c27c29f06 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -16,7 +16,7 @@ cimport cython cimport sage.matrix.matrix as matrix cimport sage.matrix.matrix0 as matrix0 from sage.structure.element cimport Element, RingElement, ModuleElement, Vector -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from sage.rings.ring import is_Ring from sage.misc.misc import verbose diff --git a/src/sage/misc/bindable_class.py b/src/sage/misc/bindable_class.py index 84d4d2cd6cc..065893f43d4 100644 --- a/src/sage/misc/bindable_class.py +++ b/src/sage/misc/bindable_class.py @@ -117,7 +117,7 @@ class BindableClass(six.with_metaclass(ClasscallMetaclass)): sage: type(outer.Inner).mro() [, , - ] + <... 'object'>] Still, documentation works as usual:: diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 19c9ac3792d..e9fabf1754d 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -1041,7 +1041,7 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): sage: x.cls sage: x.cls.mro() - [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ] + [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , <... 'object'>] """ @staticmethod def __classcall__(cls, value, succ, key = None): @@ -1307,12 +1307,12 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): sage: x.cls sage: x.cls.mro() - [, ] + [, <... 'object'>] sage: x = HierarchyElement(30, P) sage: x.cls sage: x.cls.mro() - [, , , , , , , , ] + [, , , , , , , , <... 'object'>] """ super_classes = tuple(self._from_value(base).cls for base in self._bases_controlled) if not super_classes: diff --git a/src/sage/misc/constant_function.pyx b/src/sage/misc/constant_function.pyx index d4129f2a9aa..09a1f4ce888 100644 --- a/src/sage/misc/constant_function.pyx +++ b/src/sage/misc/constant_function.pyx @@ -1,13 +1,19 @@ r""" Constant functions """ + #***************************************************************************** -# Copyright (C) 2009 Nicolas M. Thiery +# Copyright (C) 2009 Nicolas M. Thiery # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -#****************************************************************************** -from sage.structure.sage_object cimport SageObject, richcmp +#***************************************************************************** + +from sage.structure.richcmp cimport richcmp +from sage.structure.sage_object cimport SageObject cdef class ConstantFunction(SageObject): diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 2902e2f8c6f..78338478bf3 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -46,6 +46,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import from six import iteritems import collections diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 7731e153e5c..3cdfcfcfe4d 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -35,12 +35,8 @@ cblas_library_dirs = list(cblas_pc['library_dirs']) cblas_include_dirs = list(cblas_pc['include_dirs']) -# TODO: Remove Cygwin hack by installing a suitable cblas.pc -if os.path.exists('/usr/lib/libblas.dll.a'): - cblas_libs = 'gslcblas' - standard_libs = [ - 'mpfr', 'gmp', 'gmpxx', 'stdc++', 'pari', 'm', + 'mpfr', 'gmp', 'gmpxx', 'stdc++', 'pari', 'm', 'ec', 'gsl', ] + cblas_libs + [ 'ntl'] diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 34b8500048b..9274984b707 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -14,7 +14,7 @@ # http://www.gnu.org/licenses/ #****************************************************************************** from __future__ import absolute_import -from six import iteritems +from six import iteritems, string_types def runsnake(command): @@ -519,10 +519,10 @@ def import_statements(*objects, **kwds): name = None # the name of the object # 1. if obj is a string, we look for an object that has that name - if isinstance(obj, str): + if isinstance(obj, string_types): name = obj obj = find_objects_from_name(name, 'sage') - if len(obj) == 0: + if not obj: obj = find_objects_from_name(name) # remove lazy imported objects from list obj diff --git a/src/sage/misc/inline_fortran.py b/src/sage/misc/inline_fortran.py index 2701a6f0533..faf115d0099 100644 --- a/src/sage/misc/inline_fortran.py +++ b/src/sage/misc/inline_fortran.py @@ -1,7 +1,9 @@ """ Fortran compiler """ +from __future__ import absolute_import from six import iteritems + import os import imp import shutil diff --git a/src/sage/misc/mathml.py b/src/sage/misc/mathml.py index aa987c45cbb..d584a481900 100644 --- a/src/sage/misc/mathml.py +++ b/src/sage/misc/mathml.py @@ -22,6 +22,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import from six import iteritems, integer_types diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index fcd31c823ab..1e85b4c6b0f 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -571,12 +571,17 @@ def generic_cmp(x,y): return 0 return 1 + def cmp_props(left, right, props): + from sage.misc.superseded import deprecation + deprecation(23149, "cmp_props is deprecated") for a in props: c = cmp(left.__getattribute__(a)(), right.__getattribute__(a)()) - if c: return c + if c: + return c return 0 + def union(x, y=None): """ Return the union of x and y, as a list. The resulting list need not diff --git a/src/sage/misc/nested_class_test.py b/src/sage/misc/nested_class_test.py index a60bf68831c..8bba13b90ab 100644 --- a/src/sage/misc/nested_class_test.py +++ b/src/sage/misc/nested_class_test.py @@ -44,7 +44,7 @@ # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #****************************************************************************** -from __future__ import print_function +from __future__ import print_function, absolute_import from six import add_metaclass __all__ = [] # Don't document any parents diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index e2593b116ae..ebe1015364f 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -41,6 +41,8 @@ from __future__ import print_function from __future__ import absolute_import +from six import string_types + import os, re, sys import pydoc from sage.misc.temporary_file import tmp_dir @@ -635,7 +637,7 @@ def format(s, embedded=False): ... """ - if not isinstance(s, str): + if not isinstance(s, string_types): raise TypeError("s must be a string") # Leading empty lines must be removed, since we search for directives @@ -717,7 +719,7 @@ def format_src(s): sage: format_src('<<>>')[5:15] 'Sq(*nums):' """ - if not isinstance(s, str): + if not isinstance(s, string_types): raise TypeError("s must be a string") docs = set([]) import sage.all diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 91254e18395..18b54c91795 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -114,7 +114,8 @@ def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return """ from __future__ import print_function, absolute_import from six.moves import range -from six import iteritems, string_types, class_types +from six import iteritems, string_types, class_types, text_type +from sage.misc.six import u import ast import inspect @@ -354,9 +355,9 @@ def _extract_source(lines, lineno): raise ValueError("Line numbering starts at 1! (tried to extract line {})".format(lineno)) lineno -= 1 - if isinstance(lines, str): + if isinstance(lines, string_types): lines = lines.splitlines(True) # true keeps the '\n' - if len(lines) > 0: + if len(lines): # Fixes an issue with getblock lines[-1] += '\n' @@ -1635,7 +1636,7 @@ def _sage_getdoc_unformatted(obj): # not a 'getset_descriptor' or similar. if not isinstance(r, string_types): return '' - elif isinstance(r, unicode): + elif isinstance(r, text_type): # unicode (py2) = str (py3) return r.encode('utf-8', 'ignore') else: return r @@ -2115,7 +2116,11 @@ class Element: pos = _extract_embedded_position(d) if pos is None: try: + # BEWARE HERE + # inspect gives str (=bytes) in python2 + # and str (=unicode) in python3 return inspect.getsourcelines(obj) + except (IOError, TypeError) as err: try: objinit = obj.__init__ diff --git a/src/sage/misc/session.pyx b/src/sage/misc/session.pyx index 856aa73095e..fe70a1b3de0 100644 --- a/src/sage/misc/session.pyx +++ b/src/sage/misc/session.pyx @@ -215,7 +215,7 @@ def show_identifiers(hidden=False): ['__', '_i', '_6', '_4', '_3', '_1', '_ii', '__doc__', '__builtins__', '___', '_9', '__name__', '_', 'a', '_i12', '_i14', 'factor', '__file__', '_hello', '_i13', '_i11', '_i10', '_i15', '_i5', '_13', '_10', '_iii', '_i9', '_i8', '_i7', '_i6', '_i4', '_i3', '_i2', '_i1', '_init_cmdline', '_14'] """ state = caller_locals() - return [x for x, v in state.iteritems() if _is_new_var(x, v, hidden)] + return sorted([x for x, v in state.iteritems() if _is_new_var(x, v, hidden)]) def save_session(name='sage_session', verbose=False): r""" @@ -288,6 +288,7 @@ def save_session(name='sage_session', verbose=False): sage: g = cython_lambda('double x', 'x*x + 1.5') sage: save_session(tmp_f, verbose=True) + Saving... Not saving g: g is a function, method, class or type ... """ diff --git a/src/sage/misc/six.py b/src/sage/misc/six.py index 37295c7aeca..92ea494324d 100644 --- a/src/sage/misc/six.py +++ b/src/sage/misc/six.py @@ -108,6 +108,18 @@ def u(x): r""" Convert `x` to unicode, assuming UTF-8 encoding. + Python2 behaviour: + + If input is unicode, returns the input. + + If input is str (assumed to be utf-8 encoded), convert to unicode. + + Python3 behaviour: + + If input is str, returns the input. + + If input is bytes (assumed to be utf-8 encoded), convert to unicode. + EXAMPLES:: sage: from sage.misc.six import u @@ -116,6 +128,8 @@ def u(x): sage: u(u"500 \u20ac") u'500 \u20ac' """ - if isinstance(x, unicode): + if isinstance(x, text_type): # py2 unicode and py3 str return x - return str(x).decode("utf-8") + if isinstance(x, bytes): + return x.decode("utf-8") + raise TypeError('input has no conversion to unicode') diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 7cb1c225d46..2628bde18be 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -22,7 +22,7 @@ # # http://www.gnu.org/licenses/ ######################################################################## -from __future__ import print_function +from __future__ import print_function, absolute_import from six import iteritems from warnings import warn diff --git a/src/sage/misc/test_class_pickling.py b/src/sage/misc/test_class_pickling.py index f43c09cb3c1..3ff5b9cff50 100644 --- a/src/sage/misc/test_class_pickling.py +++ b/src/sage/misc/test_class_pickling.py @@ -24,7 +24,7 @@ def metaclass(name, bases): sage: type(c) sage: c.__bases__ - (, ) + (<... 'object'>, ) """ print("constructing class") @@ -63,7 +63,7 @@ def __eq__(self, other): def __reduce__(self): """ - Implements the pickle protocol for classes in this metaclass + Implement the pickle protocol for classes in this metaclass (not for the instances of this class!!!) EXAMPLES:: @@ -73,7 +73,7 @@ def __reduce__(self): constructing class sage: c.__class__.__reduce__(c) reducing a class - (, ('foo3', (, ))) + (, ('foo3', (<... 'object'>, ))) """ print("reducing a class") return (metaclass, self.reduce_args) diff --git a/src/sage/modular/arithgroup/arithgroup_element.pyx b/src/sage/modular/arithgroup/arithgroup_element.pyx index 7cae7d26067..dd80644c965 100644 --- a/src/sage/modular/arithgroup/arithgroup_element.pyx +++ b/src/sage/modular/arithgroup/arithgroup_element.pyx @@ -16,7 +16,7 @@ Elements of Arithmetic Subgroups from __future__ import absolute_import from sage.structure.element cimport MultiplicativeGroupElement, MonoidElement, Element -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from sage.rings.all import ZZ from sage.modular.cusps import Cusp diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index 577c23369f4..4133d24c602 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -502,7 +502,7 @@ class ArithmeticSubgroup_Permutation_class(ArithmeticSubgroup): sage: TestSuite(G).run() """ - def __cmp__(self, other): + def __eq__(self, other): r""" Equality test. @@ -525,17 +525,40 @@ def __cmp__(self, other): True """ if isinstance(other, ArithmeticSubgroup_Permutation_class): - - return (cmp(self.is_odd(), other.is_odd()) or - cmp(self.index(), other.index()) or - cmp(self.relabel(inplace=False)._S2, other.relabel(inplace=False)._S2) or - cmp(self.relabel(inplace=False)._S3, other.relabel(inplace=False)._S3)) + return (self.is_odd() == other.is_odd() and + self.index() == other.index() and + self.relabel(inplace=False)._S2 == other.relabel(inplace=False)._S2 and + self.relabel(inplace=False)._S3 == other.relabel(inplace=False)._S3) elif isinstance(other, ArithmeticSubgroup): - return cmp(self, other.as_permutation_group()) + return self == other.as_permutation_group() else: - return cmp(type(self), type(other)) + return False + + def __ne__(self, other): + """ + Check that ``self`` is not equal to ``other``. + + TESTS:: + + sage: G2 = Gamma(2) + sage: G3 = Gamma(3) + sage: H = ArithmeticSubgroup_Permutation(S2="(1,4)(2,6)(3,5)",S3="(1,2,3)(4,5,6)") + sage: (G2 != H) or (H != G2) + False + sage: (G3 != H) and (H != G3) + True + + sage: G2 = Gamma1(2) + sage: G3 = Gamma1(3) + sage: H = ArithmeticSubgroup_Permutation(S2="(1,6,4,3)(2,7,5,8)",S3="(1,2,3,4,5,6)(7,8)") + sage: (G2 != H) and (H != G2) + True + sage: (G3 != H) or (H != G3) + False + """ + return not (self == other) def __hash__(self): r""" diff --git a/src/sage/modular/arithgroup/congroup_generic.py b/src/sage/modular/arithgroup/congroup_generic.py index 5439bab7f9e..3c89d9d0cad 100644 --- a/src/sage/modular/arithgroup/congroup_generic.py +++ b/src/sage/modular/arithgroup/congroup_generic.py @@ -9,8 +9,6 @@ - William Stein - David Loeffler (2009, 10) -- modifications to work with more general arithmetic subgroups """ -from __future__ import absolute_import - ################################################################################ # # Copyright (C) 2004, 2006 William Stein @@ -22,6 +20,7 @@ # http://www.gnu.org/licenses/ # ################################################################################ +from __future__ import absolute_import from sage.rings.all import QQ, ZZ, Zmod from sage.arith.all import gcd @@ -199,8 +198,10 @@ def level(self): """ return self.__level - def __cmp__(self, other): + def __eq__(self, other): r""" + Check that ``self`` is equal to ``other``. + EXAMPLES:: sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) == Gamma1(3) @@ -218,24 +219,40 @@ def __cmp__(self, other): # Note that lazy_import doesn't work here, because it doesn't play # nicely with isinstance(). if not isinstance(other, ArithmeticSubgroup): - return cmp(type(self), type(other)) + return False elif is_CongruenceSubgroup(other): - t = cmp(self.level(), other.level()) - if t: return t - if self.level() == 1: return 0 # shouldn't come up except with pickling/unpickling - t = cmp(self.index(), other.index()) - if t: return t - return cmp(self.image_mod_n(),other.image_mod_n()) + if self.level() == other.level() == 1: + return True + # shouldn't come up except with pickling/unpickling + return (self.level() == other.level() and + self.index() == other.index() and + self.image_mod_n() == other.image_mod_n()) from sage.modular.arithgroup.arithgroup_perm import ArithmeticSubgroup_Permutation_class if isinstance(other, ArithmeticSubgroup_Permutation_class): - return cmp(self.as_permutation_group(), other) + return self.as_permutation_group() == other else: # we shouldn't ever get here raise NotImplementedError + def __ne__(self, other): + """ + Check that ``self`` is not equal to ``other``. + + EXAMPLES:: + + sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) != Gamma1(3) + False + sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) != Gamma(3) + True + sage: CongruenceSubgroup(3,[ [1,1,0,1] ]) != QQ + True + """ + return not (self == other) + + class CongruenceSubgroupFromGroup(CongruenceSubgroupBase): r""" A congruence subgroup, defined by the data of an integer `N` and a subgroup diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index 7df08d4017a..cf946ecee82 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -47,7 +47,7 @@ from sage.plot.all import hyperbolic_arc, hyperbolic_triangle, text from sage.misc.latex import latex from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method -from sage.structure.sage_object cimport richcmp_not_equal +from sage.structure.richcmp cimport richcmp_not_equal cdef extern from "sage/modular/arithgroup/sl2z.hpp": diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 937be4bc8f1..1a318b2578a 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -51,7 +51,7 @@ from sage.modular.btquotients.btquotient import DoubleCosetReduction from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object import op_EQ, op_NE +from sage.structure.richcmp import op_EQ, op_NE from sage.matrix.matrix_space import MatrixSpace from sage.structure.element import ModuleElement diff --git a/src/sage/modular/cusps.py b/src/sage/modular/cusps.py index 15cd18de80f..99d2f2b9e1f 100644 --- a/src/sage/modular/cusps.py +++ b/src/sage/modular/cusps.py @@ -35,7 +35,7 @@ from sage.structure.parent_base import ParentWithBase from sage.structure.element import Element, is_InfinityElement -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.modular.modsym.p1list import lift_to_sl2z_llong from sage.matrix.matrix import is_Matrix @@ -67,9 +67,9 @@ def __init__(self): """ ParentWithBase.__init__(self, self) - def __cmp__(self, right): + def __eq__(self, right): """ - Return equality only if right is the set of cusps. + Return equality only if ``right`` is the set of cusps. EXAMPLES:: @@ -78,7 +78,20 @@ def __cmp__(self, right): sage: Cusps == QQ False """ - return cmp(type(self), type(right)) + return isinstance(right, Cusps_class) + + def __ne__(self, right): + """ + Check that ``self`` is not equal to ``right``. + + EXAMPLES:: + + sage: Cusps != Cusps + False + sage: Cusps != QQ + True + """ + return not (self == right) def _repr_(self): """ @@ -162,16 +175,15 @@ def zero(self): """ Return the zero cusp. - NOTE: + .. NOTE:: - The existence of this method is assumed by some - parts of Sage's coercion model. + The existence of this method is assumed by some + parts of Sage's coercion model. EXAMPLES:: sage: Cusps.zero() 0 - """ return Cusp(0, parent=self) @@ -373,7 +385,6 @@ def __init__(self, a, b=None, parent=None, check=True): self.__a = r.numer() self.__b = r.denom() - def __hash__(self): """ EXAMPLES: diff --git a/src/sage/modular/cusps_nf.py b/src/sage/modular/cusps_nf.py index 31d68826b12..0c40a6ed2b6 100644 --- a/src/sage/modular/cusps_nf.py +++ b/src/sage/modular/cusps_nf.py @@ -86,7 +86,7 @@ from sage.structure.parent_base import ParentWithBase from sage.structure.element import Element, is_InfinityElement -from sage.structure.sage_object import richcmp, rich_to_bool +from sage.structure.richcmp import richcmp, rich_to_bool from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecated_function_alias @@ -269,13 +269,10 @@ def __init__(self, number_field): self.__number_field = number_field ParentWithBase.__init__(self, self) - def __cmp__(self, right): + def __eq__(self, right): """ Return equality only if right is the set of cusps for the same field. - Comparing sets of cusps for two different fields gives the same - result as comparing the two fields. - EXAMPLES:: sage: k. = NumberField(x^2 + 5) @@ -290,13 +287,31 @@ def __cmp__(self, right): True sage: LCusps == kCusps False + """ + if not isinstance(right, NFCuspsSpace): + return False + return self.number_field() == right.number_field() + def __ne__(self, right): """ - t = cmp(type(self), type(right)) - if t: - return t - else: - return cmp(self.number_field(), right.number_field()) + Check that ``self`` is not equal to ``right``. + + EXAMPLES:: + + sage: k. = NumberField(x^2 + 5) + sage: L. = NumberField(x^2 + 23) + sage: kCusps = NFCusps(k); kCusps + Set of all cusps of Number Field in a with defining polynomial x^2 + 5 + sage: LCusps = NFCusps(L); LCusps + Set of all cusps of Number Field in a with defining polynomial x^2 + 23 + sage: kCusps != NFCusps(k) + False + sage: LCusps != NFCusps(L) + False + sage: LCusps != kCusps + True + """ + return not (self == right) def _repr_(self): """ diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index c1f9c64615f..99f427abde3 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -80,7 +80,7 @@ from sage.structure.parent import Parent from sage.structure.sequence import Sequence from sage.structure.factory import UniqueFactory -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.arith.all import (binomial, bernoulli, kronecker, factor, gcd, lcm, fundamental_discriminant, euler_phi, factorial, valuation) diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 15025a39d15..930ae4a675d 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -108,25 +108,39 @@ def __reduce__(self): """ return (EtaGroup, (self.level(),)) - def __cmp__(self, other): + def __eq__(self, other): r""" - Compare self to other. If other is not an EtaGroup, compare by - type; otherwise compare by level. EtaGroups of the same level + Check that ``self`` is equal to ``other``. + + If other is not an EtaGroup, return ``False``. + + Otherwise compare the levels. EtaGroups of the same level compare as identical. EXAMPLES:: - sage: EtaGroup(12) == 12 - False - sage: EtaGroup(12) < EtaGroup(13) - True sage: EtaGroup(12) == EtaGroup(12) True + sage: EtaGroup(12) == EtaGroup(13) + False """ if not isinstance(other, EtaGroup_class): - return cmp(type(self), type(other)) + return False else: - return cmp(self.level(), other.level()) + return self.level() == other.level() + + def __ne__(self, other): + """ + Check that ``self`` is not equal to ``other``. + + EXAMPLES:: + + sage: EtaGroup(12) != EtaGroup(12) + False + sage: EtaGroup(12) != EtaGroup(13) + True + """ + return not (self == other) def _repr_(self): r""" diff --git a/src/sage/modular/hecke/element.py b/src/sage/modular/hecke/element.py index 4d7aa4317aa..f8af57a63d4 100644 --- a/src/sage/modular/hecke/element.py +++ b/src/sage/modular/hecke/element.py @@ -23,7 +23,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.structure.sage_object import richcmp, op_NE +from sage.structure.richcmp import richcmp, op_NE from sage.structure.element import ModuleElement def is_HeckeModuleElement(x): diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index b90249e604f..fbf1d7abaca 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -24,7 +24,7 @@ from sage.symbolic.all import pi from sage.structure.parent_gens import localvars -from sage.structure.sage_object import op_NE, op_EQ +from sage.structure.richcmp import op_NE, op_EQ from sage.structure.element import CommutativeAlgebraElement from sage.structure.unique_representation import UniqueRepresentation diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index af89067356e..41dbd9a8e3a 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -182,27 +182,42 @@ def __init__(self, group, weight, sign, base_ring, hecke.AmbientHeckeModule.__init__(self, base_ring, rank, group.level(), weight, category=category) - def __cmp__(self, other): + def __eq__(self, other): """ - Standard comparison function. + Check that ``self`` is equal to ``other``. EXAMPLES:: - sage: ModularSymbols(11,2) == ModularSymbols(11,2) # indirect doctest + sage: ModularSymbols(11,2) == ModularSymbols(11,2) True - sage: ModularSymbols(11,2) == ModularSymbols(11,4) # indirect doctest + sage: ModularSymbols(11,2) == ModularSymbols(11,4) False - """ if not isinstance(other, ModularSymbolsSpace): - return cmp(type(self), type(other)) + return False + if isinstance(other, ModularSymbolsAmbient): - return misc.cmp_props(self, other, ['group', 'weight', 'sign', 'base_ring', 'character']) - c = cmp(self, other.ambient_hecke_module()) - if c: return c - if self.free_module() == other.free_module(): - return 0 - return -1 + return (self.group() == other.group() and + self.weight() == other.weight() and + self.sign() == other.sign() and + self.base_ring() == other.base_ring() and + self.character() == other.character()) + + return (self == other.ambient_hecke_module() and + self.free_module() == other.free_module()) + + def __ne__(self, other): + """ + Check that ``self`` is not equal to ``other``. + + EXAMPLES:: + + sage: ModularSymbols(11,2) != ModularSymbols(11,2) + False + sage: ModularSymbols(11,2) != ModularSymbols(11,4) + True + """ + return not (self == other) def new_submodule(self, p=None): r""" diff --git a/src/sage/modular/modsym/manin_symbol.pyx b/src/sage/modular/modsym/manin_symbol.pyx index be67254638d..d9a7c8b1f1a 100644 --- a/src/sage/modular/modsym/manin_symbol.pyx +++ b/src/sage/modular/modsym/manin_symbol.pyx @@ -27,7 +27,7 @@ from sage.rings.all import Infinity, ZZ from sage.rings.integer cimport Integer from sage.structure.element cimport Element from sage.structure.sage_object import register_unpickle_override -from sage.structure.sage_object cimport richcmp_not_equal, richcmp +from sage.structure.richcmp cimport richcmp_not_equal, richcmp def is_ManinSymbol(x): diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index fdb40b8f418..0a94eaaa462 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -28,8 +28,8 @@ REFERENCES: # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from sage.structure.sage_object import SageObject -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.sage_object cimport SageObject +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.power_series_ring import PowerSeriesRing diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index 8730832a9c4..06340fd6857 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -40,7 +40,7 @@ from __future__ import absolute_import import operator from sage.structure.element import ModuleElement -from sage.structure.sage_object import op_EQ, op_NE +from sage.structure.richcmp import op_EQ, op_NE from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.misc.cachefunc import cached_method diff --git a/src/sage/modular/pollack_stevens/sigma0.py b/src/sage/modular/pollack_stevens/sigma0.py index ea74d2ba1cf..e2a4f65074d 100644 --- a/src/sage/modular/pollack_stevens/sigma0.py +++ b/src/sage/modular/pollack_stevens/sigma0.py @@ -51,7 +51,7 @@ from sage.misc.abstract_method import abstract_method from sage.structure.factory import UniqueFactory from sage.structure.element import MonoidElement -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.categories.monoids import Monoids from sage.categories.morphism import Morphism from sage.structure.parent import Parent diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 9ec32b92192..b99f81cb043 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -207,7 +207,7 @@ class if `I=aJ` for some `a \in A^*`. (Left `\mathcal{O}`-ideals are from sage.modular.dirichlet import TrivialCharacter from sage.matrix.all import MatrixSpace, matrix from sage.misc.mrange import cartesian_product_iterator - +from sage.structure.richcmp import richcmp from sage.misc.cachefunc import cached_method from copy import copy @@ -1504,7 +1504,7 @@ def __init__(self, parent, x): x = x.element() HeckeModuleElement.__init__(self, parent, parent.free_module()(x)) - def __cmp__(self, other): + def _richcmp_(self, other, op): """ EXAMPLES:: @@ -1522,12 +1522,7 @@ def __cmp__(self, other): sage: loads(dumps(B.0)) == B.0 True """ - if not isinstance(other, BrandtModuleElement): - other = self.parent()(other) - else: - c = cmp(self.parent(), other.parent()) - if c: return c - return cmp(self.element(), other.element()) + return richcmp(self.element(), other.element(), op) def monodromy_pairing(self, x): """ diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 1fd2a399652..4dd63a9ed2b 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -16,7 +16,7 @@ #***************************************************************************** from sage.structure.element import ModuleElement -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp # This adds extra maybe-not-necessary checks in the code, but could # slow things down. It can impact what happens in more than just this diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 91873f2a4ae..a244724e491 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -112,7 +112,7 @@ from cpython.slice cimport PySlice_GetIndicesEx from sage.structure.sequence import Sequence from sage.structure.element cimport Element, ModuleElement, RingElement, Vector from sage.structure.element import canonical_coercion -from sage.structure.sage_object cimport richcmp_not_equal, richcmp, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, richcmp, rich_to_bool from sage.rings.ring import is_Ring from sage.rings.infinity import Infinity, AnInfinity diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 7e93a8bc522..aa272b92a65 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -22,7 +22,7 @@ from __future__ import print_function from six import iteritems from sage.structure.element cimport parent -from sage.structure.sage_object cimport richcmp, richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp, richcmp_not_equal, rich_to_bool from cpython.object cimport Py_NE, Py_EQ from sage.misc.misc import repr_lincomb diff --git a/src/sage/modules/with_basis/morphism.py b/src/sage/modules/with_basis/morphism.py index a4558d90161..4590627ac0a 100644 --- a/src/sage/modules/with_basis/morphism.py +++ b/src/sage/modules/with_basis/morphism.py @@ -119,7 +119,7 @@ from sage.categories.sets_cat import Sets from sage.categories.sets_with_partial_maps import SetsWithPartialMaps from sage.structure.element import parent -from sage.structure.sage_object import op_EQ, op_NE +from sage.structure.richcmp import op_EQ, op_NE from sage.matrix.matrix import is_Matrix class ModuleMorphism(Morphism): diff --git a/src/sage/monoids/free_abelian_monoid_element.py b/src/sage/monoids/free_abelian_monoid_element.py index 0c7d859068d..64168e86254 100644 --- a/src/sage/monoids/free_abelian_monoid_element.py +++ b/src/sage/monoids/free_abelian_monoid_element.py @@ -35,7 +35,7 @@ #***************************************************************************** from six import integer_types -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.rings.integer import Integer from sage.structure.element import MonoidElement diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index a8df61db4a2..06c8b6a678a 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -21,7 +21,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import MonoidElement from sage.structure.indexed_generators import IndexedGenerators -from sage.structure.sage_object import op_EQ, op_NE, richcmp, rich_to_bool +from sage.structure.richcmp import op_EQ, op_NE, richcmp, rich_to_bool import sage.data_structures.blas_dict as blas from sage.categories.monoids import Monoids diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index a4d46bb38e9..e4bf95f1314 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -26,7 +26,7 @@ from sage.rings.integer import Integer from sage.rings.all import RealField from .free_monoid_element import FreeMonoidElement -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp def is_StringMonoidElement(x): diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx new file mode 100644 index 00000000000..858d6ae3194 --- /dev/null +++ b/src/sage/numerical/gauss_legendre.pyx @@ -0,0 +1,240 @@ +r""" +Gauss-Legendre integration for vector-valued functions + +Routine to perform Gauss-Legendre integration for vector-functions. + +AUTHORS: + + - Nils Bruin (2017-06-06): initial version + +EXAMPLES:: + +NOTE: + +The code here is directly based on mpmath (see http://mpmath.org), but has a highly +optimized routine to compute the nodes. +""" + +#***************************************************************************** +# Copyright (C) 2017 Nils Bruin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +#as it turns out, computing the nodes can easily turn out to be more +#expensive than computing the integrals. So it's worth optimizing this. +#making the function into a cython routine helps a little bit. If we really +#want to we can optimize this further, probably to a point where +#we don't have to bother with node computation routines that have a better order +#than this naive approach (which is quadratic) +from __future__ import absolute_import, division, print_function +from sage.libs.mpfr cimport * +import math +from sage.rings.real_mpfr import RealField +from sage.misc.cachefunc import cached_function +from sage.rings.real_mpfr cimport RealNumber, RealField_class + +@cached_function +def nodes(degree,prec): + r""" + Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme. + + INPUT: + + - ``degree`` -- integer. The number of nodes. Must be 3 or even. + + - ``prec`` -- integer (minimal value 53). Binary precision with which the nodes and weights are computed. + + OUTPUT: + + A list of (node,weight) pairs. + + EXAMPLE: + + The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. + The weights can be computed by a straightforward formula (note that evaluating + a derivative of a Legendre polynomial isn't particularly numerically stable, so the results + from this routine are actually more accurate than what the values the closed formula produces):: + + sage: from sage.numerical.gauss_legendre import nodes + sage: L1=nodes(24,53) + sage: P=RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) + sage: Pdif=P.diff() + sage: L2=[( (r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] + sage: all((a[0]-b[0]).abs() < 10^-15 and (a[1]-b[1]).abs() < 10^-9 for a,b in zip(L1,L2)) + True + """ + cdef long j,j1,n + cdef RealNumber r,t1,t2,t3,t4,a,w + cdef mpfr_t u,v + cdef RealField_class R + if prec < 53: + prec = 53 + if degree !=3 and degree % 2 !=0: + raise ValueError("degree=%s not supported (degree must be 3 or even)"%degree) + R = RealField(int(prec*3/2)) + Rout = RealField(prec) + mpfr_init2(u,R.__prec) + mpfr_init2(v,R.__prec) + ZERO = R.zero() + ONE = R.one() + HALF = ONE/2 + TWO = 2*ONE + rnd = R.rnd + epsilon = R(1)>>(prec+8) + if degree == 1: + x = (R(3)/5).sqrt() + w = R(5)/18 + nodes = [((1-x)/2,w),(HALF,R(4)/9),((1+x)/2,w)] + else: + nodes = [] + n = degree + upto = n//2+1 + for j in xrange(1,upto): + r = R(math.cos(math.pi*(j-0.25)/(n+0.5))) + while True: + t1,t2=ONE,ZERO + for j1 in xrange(1,n+1): + mpfr_mul(u,r.value,t1.value,rnd) + mpfr_mul_si(u,u,2*j1-1,rnd) + mpfr_mul_si(v,t2.value,j1-1,rnd) + mpfr_sub(u,u,v,rnd) + mpfr_div_si(u,u,j1,rnd) + t2=t1 + t1=R._new() + mpfr_set(t1.value,u,rnd) + t4 = R(n)*(r*t1-t2)/(r**2-ONE) + a = t1/t4 + r = r-a + if a.abs() 3: + err = estimate_error(results,prec,epsilon) + if err <= epsilon: + return I + #double the degree to double expected precision + degree *= 2 diff --git a/src/sage/plot/plot3d/examples.py b/src/sage/plot/plot3d/examples.py deleted file mode 100644 index 798fa8ad375..00000000000 --- a/src/sage/plot/plot3d/examples.py +++ /dev/null @@ -1,13 +0,0 @@ -r""" -Introduction - -EXAMPLES:: - - sage: x, y = var('x y') - sage: W = plot3d(sin(pi*((x)^2+(y)^2))/2,(x,-1,1),(y,-1,1), frame=False, color='purple', opacity=0.8) - sage: S = sphere((0,0,0),size=0.3, color='red', aspect_ratio=[1,1,1]) - sage: show(W + S, figsize=8) -""" - - - diff --git a/src/sage/plot/plot3d/introduction.py b/src/sage/plot/plot3d/introduction.py new file mode 100644 index 00000000000..1222aec1684 --- /dev/null +++ b/src/sage/plot/plot3d/introduction.py @@ -0,0 +1,131 @@ +r""" +Introduction + +Sage has a wide support for 3D graphics, from basic shapes to implicit and +parametric plots. + +The following graphics functions are supported: + + +- :func:`~plot3d` - plot a 3d function + +- :func:`~sage.plot.plot3d.parametric_plot3d.parametric_plot3d` - a parametric three-dimensional space curve or surface + +- :func:`~sage.plot.plot3d.revolution_plot3d.revolution_plot3d` - a plot of a revolved curve + +- :func:`~sage.plot.plot3d.plot_field3d.plot_vector_field3d` - a plot of a 3d vector field + +- :func:`~sage.plot.plot3d.implicit_plot3d.implicit_plot3d` - a plot of an isosurface of a function + +- :func:`~sage.plot.plot3d.list_plot3d.list_plot3d`- a 3-dimensional plot of a surface defined by a list of points in 3-dimensional space + +- :func:`~sage.plot.plot3d.list_plot3d.list_plot3d_matrix` - a 3-dimensional plot of a surface defined by a matrix defining points in 3-dimensional space + +- :func:`~sage.plot.plot3d.list_plot3d.list_plot3d_array_of_arrays`- A 3-dimensional plot of a surface defined by a list of lists defining points in 3-dimensional space + +- :func:`~sage.plot.plot3d.list_plot3d.list_plot3d_tuples` - a 3-dimensional plot of a surface defined by a list of points in 3-dimensional space + +The following classes for basic shapes are supported: + + +- :class:`~sage.plot.plot3d.shapes.Box` - a box given its three magnitudes + +- :class:`~sage.plot.plot3d.shapes.Cone` - a cone, with base in the xy-plane pointing up the z-axis + +- :class:`~sage.plot.plot3d.shapes.Cylinder` - a cylinder, with base in the xy-plane pointing up the z-axis + +- :class:`~sage.plot.plot3d.shapes2.Line` - a 3d line joining a sequence of points + +- :class:`~sage.plot.plot3d.shapes.Sphere` - a sphere centered at the origin + +- :class:`~sage.plot.plot3d.shapes.Text` - a text label attached to a point in 3d space + +- :class:`~sage.plot.plot3d.shapes.Torus` - a 3d torus + +- :class:`~sage.plot.plot3d.shapes2.Point` - a position in 3d, represented by a sphere of fixed size + + +The following plotting funtions for basic shapes are supported + + +- :func:`~sage.plot.plot3d.shapes.ColorCube` - a cube with given size and sides with given colors + +- :func:`~sage.plot.plot3d.shapes.LineSegment` - a line segment, which is drawn as a cylinder from start to end with radius radius + +- :func:`~sage.plot.plot3d.shapes2.line3d` - a 3d line joining a sequence of points + +- :func:`~sage.plot.plot3d.shapes.arrow3d` - a 3d arrow + +- :func:`~sage.plot.plot3d.shapes2.point3d` - a point or list of points in 3d space + +- :func:`~sage.plot.plot3d.shapes2.bezier3d` - a 3d bezier path + +- :func:`~sage.plot.plot3d.shapes2.frame3d` - a frame in 3d + +- :func:`~sage.plot.plot3d.shapes2.frame_labels` - labels for a given frame in 3d + +- :func:`~sage.plot.plot3d.shapes2.polygon3d` - draw a polygon in 3d + +- :func:`~sage.plot.plot3d.shapes2.polygons3d` - draw the union of several polygons in 3d + +- :func:`~sage.plot.plot3d.shapes2.ruler` - draw a ruler in 3d, with major and minor ticks + +- :func:`~sage.plot.plot3d.shapes2.ruler_frame` - draw a frame made of 3d rulers, with major and minor ticks + +- :func:`~sage.plot.plot3d.shapes2.sphere` - plot of a sphere given center and radius + +- :func:`~sage.plot.plot3d.shapes2.text3d` - 3d text + +Sage also supports platonic solids with the following functions: + + +- :func:`~sage.plot.plot3d.platonic.tetrahedron` + +- :func:`~sage.plot.plot3d.platonic.cube` + +- :func:`~sage.plot.plot3d.platonic.octahedron` + +- :func:`~sage.plot.plot3d.platonic.dodecahedron` + +- :func:`~sage.plot.plot3d.platonic.icosahedron` + +Different viewers are supported: jmol, a web-based interactive viewer +using the Three.js JavaScript library and a raytraced representation. +The viewer is invoked by adding the keyword argument +``viewer='jmol'`` (respectively ``'tachyon'`` or ``'threejs'``) +to the command ``show()`` on any three-dimensional graphic + + +- :class:`~sage.plot.plot3d.tachyon.Tachyon` - create a scene the can be rendered using the Tachyon ray tracer + +- :class:`~sage.plot.plot3d.tachyon.Axis_aligned_box` - box with axis-aligned edges with the given min and max coordinates + +- :class:`~sage.plot.plot3d.tachyon.Cylinder` - an infinite cylinder + +- :class:`~sage.plot.plot3d.tachyon.FCylinder` - a finite cylinder + +- :class:`~sage.plot.plot3d.tachyon.FractalLandscape`- axis-aligned fractal landscape + +- :class:`~sage.plot.plot3d.tachyon.Light` - represents lighting objects + +- :class:`~sage.plot.plot3d.tachyon.ParametricPlot` - parametric plot routines + +- :class:`~sage.plot.plot3d.tachyon.Plane` - an infinite plane + +- :class:`~sage.plot.plot3d.tachyon.Ring` - an annulus of zero thickness + +- :class:`~sage.plot.plot3d.tachyon.Sphere`- a sphere + +- :class:`~sage.plot.plot3d.tachyon.TachyonSmoothTriangle` - a triangle along with a normal vector, which is used for smoothing + +- :class:`~sage.plot.plot3d.tachyon.TachyonTriangle` - basic triangle class + +- :class:`~sage.plot.plot3d.tachyon.TachyonTriangleFactory` - class to produce triangles of various rendering types + +- :class:`~sage.plot.plot3d.tachyon.Texfunc` - creates a texture function + +- :class:`~sage.plot.plot3d.tachyon.Texture` - stores texture information + +- :func:`~sage.plot.plot3d.tachyon.tostr` - converts vector information to a space-separated string + +""" diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 097551ea9c7..ac1071de8c5 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -1,19 +1,34 @@ r""" Plotting Functions + EXAMPLES:: + sage: x, y = var('x y') + sage: W = plot3d(sin(pi*((x)^2+(y)^2))/2,(x,-1,1),(y,-1,1), frame=False, color='purple', opacity=0.8) + sage: S = sphere((0,0,0),size=0.3, color='red', aspect_ratio=[1,1,1]) + sage: show(W + S, figsize=8) + +.. PLOT:: + + x, y = var('x y') + W = plot3d(sin(pi*((x)**2+(y)**2))/2,(x,-1,1),(y,-1,1), frame=False, color='purple', opacity=0.8) + S = sphere((0,0,0),size=0.3, color='red', aspect_ratio=[1,1,1]) + sphinx_plot(W + S) + +:: + sage: def f(x,y): - ....: return math.sin(y*y+x*x)/math.sqrt(x*x+y*y+.0001) + ....: return math.sin(y^2+x^2)/math.sqrt(x^2+y^2+0.0001) sage: P = plot3d(f,(-3,3),(-3,3), adaptive=True, color=rainbow(60, 'rgbtuple'), max_bend=.1, max_depth=15) sage: P.show() .. PLOT:: - - def f(x,y): return math.sin(y*y+x*x)/math.sqrt(x*x+y*y+.0001) + + def f(x,y): return math.sin(y*y+x*x)/math.sqrt(x*x+y*y+0.0001) P = plot3d(f,(-3,3),(-3,3), adaptive=True, color=rainbow(60, 'rgbtuple'), max_bend=.1, max_depth=15) sphinx_plot(P) - + :: sage: def f(x,y): @@ -742,6 +757,8 @@ def smooth_triangle(self, a, b, c, da, db, dc, color = None): from . import parametric_plot3d def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): """ + Plots a function in 3d. + INPUT: diff --git a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py index 5198e68e6d7..66de9a5a702 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py +++ b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py @@ -41,7 +41,7 @@ def rational_diagonal_form(self, return_matrix=False): INPUT: - ``return_matrix`` -- (boolean, default: False) also return the - transformation matrix. + transformation matrix OUTPUT: either ``D`` (if ``return_matrix`` is false) or ``(D,T)`` (if ``return_matrix`` is true) where @@ -388,12 +388,9 @@ def signature(self): return p - n - - - def hasse_invariant(self, p): """ - Computes the Hasse invariant at a prime `p`, as given on p55 of + Computes the Hasse invariant at a prime `p` or at infinity, as given on p55 of Cassels's book. If Q is diagonal with coefficients `a_i`, then the (Cassels) Hasse invariant is given by @@ -405,21 +402,24 @@ def hasse_invariant(self, p): quadratic form must be non-degenerate over `Q_p` for this to make sense. - WARNING: This is different from the O'Meara Hasse invariant, which - allows `i <= j` in the product. That is given by the method - hasse_invariant__OMeara(p). + .. WARNING:: - NOTE: We should really rename this hasse_invariant__Cassels(), and - set hasse_invariant() as a front-end to it. + This is different from the O'Meara Hasse invariant, which + allows `i <= j` in the product. That is given by the method + hasse_invariant__OMeara(p). + .. NOTE:: + + We should really rename this hasse_invariant__Cassels(), and + set hasse_invariant() as a front-end to it. INPUT: - `p` -- a prime number > 0 + - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: - 1 or -1 + 1 or -1 EXAMPLES:: @@ -484,8 +484,10 @@ def hasse_invariant(self, p): def hasse_invariant__OMeara(self, p): """ - Computes the O'Meara Hasse invariant at a prime `p`, as given on - p167 of O'Meara's book. If Q is diagonal with coefficients `a_i`, + Compute the O'Meara Hasse invariant at a prime `p`. + + This is defined on + p167 of O'Meara's book. If Q is diagonal with coefficients `a_i`, then the (Cassels) Hasse invariant is given by .. MATH:: @@ -494,18 +496,19 @@ def hasse_invariant__OMeara(self, p): where `(a,b)_p` is the Hilbert symbol at `p`. - WARNING: This is different from the (Cassels) Hasse invariant, which - only allows `i < j` in the product. That is given by the method - hasse_invariant(p). + .. WARNING:: + This is different from the (Cassels) Hasse invariant, which + only allows `i < j` in the product. That is given by the method + hasse_invariant(p). INPUT: - `p` -- a prime number > 0 + - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: - 1 or -1 + 1 or -1 EXAMPLES:: @@ -529,7 +532,7 @@ def hasse_invariant__OMeara(self, p): :: - sage: Q=DiagonalQuadraticForm(ZZ,[1,-1,-1]) + sage: Q = DiagonalQuadraticForm(ZZ,[1,-1,-1]) sage: [Q.hasse_invariant(p) for p in prime_range(20)] [-1, 1, 1, 1, 1, 1, 1, 1] sage: [Q.hasse_invariant__OMeara(p) for p in prime_range(20)] @@ -538,10 +541,9 @@ def hasse_invariant__OMeara(self, p): :: sage: K.=NumberField(x^2-23) - sage: Q=DiagonalQuadraticForm(K,[-a,a+2]) + sage: Q = DiagonalQuadraticForm(K,[-a,a+2]) sage: [Q.hasse_invariant__OMeara(p) for p in K.primes_above(19)] [1, 1] - """ ## TO DO: Need to deal with the case n=1 separately somewhere! @@ -567,31 +569,30 @@ def hasse_invariant__OMeara(self, p): return hasse_temp - - def is_hyperbolic(self, p): - """ - Checks if the quadratic form is a sum of hyperbolic planes over - the p-adic numbers Q_p. + r""" + Check if the quadratic form is a sum of hyperbolic planes over + the `p`-adic numbers `\QQ_p` or over the real numbers `\RR`. REFERENCES: - This criteria follows from Cassels's "Rational Quadratic Forms": - - local invariants for hyperbolic plane (Lemma 2.4, p58) - - direct sum formulas (Lemma 2.3 on p58) + This criteria follows from Cassels's "Rational Quadratic Forms": + + - local invariants for hyperbolic plane (Lemma 2.4, p58) + - direct sum formulas (Lemma 2.3, p58) INPUT: - `p` -- a prime number > 0 + - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: - boolean + boolean EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1]) - sage: Q.is_hyperbolic("infinity") + sage: Q.is_hyperbolic(-1) False sage: Q.is_hyperbolic(2) False @@ -603,42 +604,42 @@ def is_hyperbolic(self, p): False sage: Q.is_hyperbolic(13) ## Here -1 is a square, so it's true. True - """ ## False for odd-dim'l forms - if self.dim() % 2 != 0: + if self.dim() % 2: return False ## True for the zero form - if self.dim == 0: + if not self.dim(): return True ## Compare local invariants - ## (Note: since the dimension is even, the extra powers of 2 in - ## self.det() := Det(2*Q) don't affect the answer!) + ## Note: since the dimension is even, the extra powers of 2 in + ## self.det() := Det(2*Q) don't affect the answer! m = ZZ(self.dim() // 2) - if p == "infinity": - return (self.signature() == 0) - - elif p == 2: - return QQ(self.det() * (-1)**m).is_padic_square(p) and (self.hasse_invariant(p) == (-1)**m) ## Actually, this -1 is the Hilbert symbol (-1,-1)_p + if p == -1: + return self.signature() == 0 - else: - return QQ(self.det() * (-1)**m).is_padic_square(p) and (self.hasse_invariant(p) == 1) + if p == 2: + return (QQ(self.det() * (-1) ** m).is_padic_square(p) and + self.hasse_invariant(p) == + (-1) ** m.binomial(2)) # here -1 is hilbert_symbol(-1,-1,2) + return (QQ(self.det() * (-1) ** m).is_padic_square(p) and + self.hasse_invariant(p) == 1) def is_anisotropic(self, p): - """ - Checks if the quadratic form is anisotropic over the p-adic numbers `Q_p`. + r""" + Check if the quadratic form is anisotropic over the p-adic numbers `\QQ_p` or `\RR`. INPUT: - `p` -- a prime number > 0 + - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: - boolean + boolean EXAMPLES:: @@ -669,42 +670,44 @@ def is_anisotropic(self, p): sage: [DiagonalQuadraticForm(ZZ, [1, -least_quadratic_nonresidue(p), p, -p*least_quadratic_nonresidue(p)]).is_anisotropic(p) for p in prime_range(3, 30)] [True, True, True, True, True, True, True, True, True] - """ + ## TO DO: Should check that p is prime + if p == -1: + return self.is_definite() + n = self.dim() D = self.det() - ## TO DO: Should check that p is prime - - if (n >= 5): - return False; + if n >= 5: + return False - if (n == 4): - return ( QQ(D).is_padic_square(p) and (self.hasse_invariant(p) == - hilbert_symbol(-1,-1,p)) ) + if n == 4: + return (QQ(D).is_padic_square(p) and + (self.hasse_invariant(p) == - hilbert_symbol(-1, -1, p))) - if (n == 3): - return (self.hasse_invariant(p) != hilbert_symbol(-1, -D, p)) + if n == 3: + return self.hasse_invariant(p) != hilbert_symbol(-1, -D, p) - if (n == 2): - return (not QQ(-D).is_padic_square(p)) + if n == 2: + return not QQ(-D).is_padic_square(p) - if (n == 1): - return (self[0,0] != 0) + if n == 1: + return self[0, 0] != 0 raise NotImplementedError("Oops! We haven't established a convention for 0-dim'l quadratic forms... =(") def is_isotropic(self, p): """ - Checks if Q is isotropic over the p-adic numbers `Q_p`. + Checks if Q is isotropic over the p-adic numbers `Q_p` or `RR`. INPUT: - `p` -- a prime number > 0 + - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: - boolean + boolean EXAMPLES:: @@ -742,54 +745,28 @@ def is_isotropic(self, p): def anisotropic_primes(self): """ - Returns a list with all of the anisotropic primes of the quadratic form. - - - INPUT: + Return a list with all of the anisotropic primes of the quadratic form. - None - - OUTPUT: - - Returns a list of prime numbers >0. + The infinite place is denoted by `-1`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) sage: Q.anisotropic_primes() - [2] - - :: + [2, -1] sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.anisotropic_primes() - [2] - - :: + [2, -1] sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1,1]) sage: Q.anisotropic_primes() - [] - + [-1] """ - - ## Look at all prime divisors of 2 * Det(Q) to find the anisotropic primes... - possible_primes = prime_divisors(2 * self.det()) - AnisoPrimes = [] - - ## DIAGNSOTIC - #print " Possible anisotropic primes are: " + str(possible_primes) - - for p in possible_primes: - if (self.is_anisotropic(p)): - AnisoPrimes += [p] - - ## DIAGNSOTIC - #print " leaving anisotropic_primes..." - - return AnisoPrimes - - + # Look at all prime divisors of 2 * Det(Q) to find the + # anisotropic primes... + possible_primes = prime_divisors(2 * self.det()) + [-1] + return [p for p in possible_primes if self.is_anisotropic(p)] def compute_definiteness(self): @@ -862,13 +839,11 @@ def compute_definiteness(self): n = self.dim() M = self.matrix() - ## Deal with the zero-diml form if n == 0: self.__definiteness_string = "zero" return - sig_pos, sig_neg, sig_zer = self.signature_vector() ## Determine and cache the definiteness string diff --git a/src/sage/quivers/algebra_elements.pxi b/src/sage/quivers/algebra_elements.pxi index 64f5950bd54..6b5ff47a8fd 100644 --- a/src/sage/quivers/algebra_elements.pxi +++ b/src/sage/quivers/algebra_elements.pxi @@ -23,7 +23,7 @@ include "sage/data_structures/bitset.pxi" from cpython.ref cimport * from cython.operator cimport predecrement as predec, postincrement as postinc -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.libs.gmp.mpn cimport mpn_cmp from libc.stdlib cimport free diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index 289044eaa22..65c51ce19f2 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -20,7 +20,7 @@ from __future__ import division, print_function include "algebra_elements.pxi" from sage.misc.cachefunc import cached_method from sage.misc.misc import repr_lincomb -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool cdef class PathAlgebraElement(RingElement): diff --git a/src/sage/quivers/paths.pyx b/src/sage/quivers/paths.pyx index 78ec103b3c3..4e30ea3a4c5 100644 --- a/src/sage/quivers/paths.pyx +++ b/src/sage/quivers/paths.pyx @@ -23,7 +23,7 @@ from cysignals.signals cimport sig_check, sig_on, sig_off from sage.data_structures.bounded_integer_sequences cimport * from cpython.slice cimport PySlice_GetIndicesEx -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool include "sage/data_structures/bitset.pxi" diff --git a/src/sage/rings/asymptotic/misc.py b/src/sage/rings/asymptotic/misc.py index 6c0eba0b9cb..2782fd92f50 100644 --- a/src/sage/rings/asymptotic/misc.py +++ b/src/sage/rings/asymptotic/misc.py @@ -820,7 +820,7 @@ def richcmp_by_eq_and_lt(left, right, op): sage: z < x, x < z, z > x, x > z, z <= x, x <= z, z >= x, x >= z (False, True, True, False, False, True, True, False) """ - from sage.structure.sage_object import (rich_to_bool, + from sage.structure.richcmp import (rich_to_bool, op_NE, op_EQ, op_LT, op_LE, op_GT, op_GE) diff --git a/src/sage/rings/bernmm.pyx b/src/sage/rings/bernmm.pyx index 9538e3d16c3..03246461e87 100644 --- a/src/sage/rings/bernmm.pyx +++ b/src/sage/rings/bernmm.pyx @@ -14,7 +14,8 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off + from sage.libs.gmp.types cimport mpq_t diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index b949b249621..6cb9a338f74 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -128,9 +128,9 @@ Classes and Methods # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" import operator +from cysignals.signals cimport sig_on, sig_str, sig_off, sig_error import sage.categories.fields diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 4133bc1ab23..2a27b435a95 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -61,16 +61,16 @@ AUTHORS: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function + +from __future__ import absolute_import, print_function import operator from cpython.object cimport Py_NE +from cysignals.signals cimport sig_on, sig_off from sage.misc.randstate cimport randstate, current_randstate from cypari2.paridecl cimport * -include "cysignals/signals.pxi" from sage.libs.gsl.complex cimport * @@ -84,7 +84,7 @@ cimport sage.rings.integer from sage.structure.element cimport RingElement, Element, ModuleElement, FieldElement from sage.structure.parent cimport Parent from sage.structure.parent_gens import ParentWithGens -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool from sage.categories.morphism cimport Morphism from sage.structure.coerce cimport is_numpy_type diff --git a/src/sage/rings/complex_field.py b/src/sage/rings/complex_field.py index 1796fcccbcb..57a80cb0c84 100644 --- a/src/sage/rings/complex_field.py +++ b/src/sage/rings/complex_field.py @@ -333,6 +333,8 @@ def __call__(self, x=None, im=None): sage: CC(QQ[I].gen()) 1.00000000000000*I sage: CC.gen() + QQ[I].gen() + 2.00000000000000*I + sage: CC.gen() + QQ.extension(x^2 + 1, 'I', embedding=None).gen() Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for +: 'Complex Field with 53 bits of precision' and 'Number Field in I with defining polynomial x^2 + 1' diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index c0b40e11ff7..81039d378c8 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -41,7 +41,8 @@ heavily modified: from __future__ import absolute_import, print_function -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off + from sage.libs.gmp.mpz cimport mpz_sgn, mpz_cmpabs_ui from sage.libs.flint.fmpz cimport * from cypari2.gen cimport Gen as pari_gen @@ -1478,7 +1479,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): elif op == Py_GE: return real_diff > 0 or (real_diff == 0 and imag_diff >= 0) - cpdef int _cmp_(left, right) except -2: + def lexico_cmp(left, right): """ Intervals are compared lexicographically on the 4-tuple: ``(x.real().lower(), x.real().upper(), @@ -1489,15 +1490,15 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: a = CIF(RIF(0,1), RIF(0,1)) sage: b = CIF(RIF(0,1), RIF(0,2)) sage: c = CIF(RIF(0,2), RIF(0,2)) - sage: cmp(a, b) + sage: a.lexico_cmp(b) -1 - sage: cmp(b, c) + sage: b.lexico_cmp(c) -1 - sage: cmp(a, c) + sage: a.lexico_cmp(c) -1 - sage: cmp(a, a) + sage: a.lexico_cmp(a) 0 - sage: cmp(b, a) + sage: b.lexico_cmp(a) 1 TESTS:: @@ -1510,7 +1511,12 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): ....: tests.append((CIF(RIF(rl, ru), RIF(il, iu)), (rl, ru, il, iu))) sage: for (i1, t1) in tests: ....: for (i2, t2) in tests: - ....: assert(cmp(i1, i2) == cmp(t1, t2)) + ....: if t1 == t2: + ....: assert(i1.lexico_cmp(i2) == 0) + ....: elif t1 < t2: + ....: assert(i1.lexico_cmp(i2) == -1) + ....: elif t1 > t2: + ....: assert(i1.lexico_cmp(i2) == 1) """ cdef int a, b a = mpfi_nan_p(left.__re) @@ -1541,6 +1547,22 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): return 1 return 0 + cpdef int _cmp_(self, other) except -2: + """ + Deprecated method (:trac:`23133`) + + EXAMPLES:: + + sage: a = CIF(RIF(0,1), RIF(0,1)) + sage: a._cmp_(a) + doctest:...: DeprecationWarning: for CIF elements, do not use cmp + See http://trac.sagemath.org/23133 for details. + 0 + """ + from sage.misc.superseded import deprecation + deprecation(23133, 'for CIF elements, do not use cmp') + return self.lexico_cmp(other) + ######################################################################## # Transcendental (and other) functions ######################################################################## diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index f5ea8a909bb..d9e5af767d3 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -430,6 +430,18 @@ def __call__(self, x, im=None): 3.141592653589794? + 2.718281828459046?*I sage: ComplexIntervalField(100)(CIF(RIF(2,3))) 3.? + + sage: QQi. = QuadraticField(-1) + sage: CIF(i) + 1*I + sage: QQi. = QuadraticField(-1, embedding=CC(0,-1)) + sage: CIF(i) + -1*I + sage: QQi. = QuadraticField(-1, embedding=None) + sage: CIF(i) + Traceback (most recent call last): + ... + ValueError: can not convert complex algebraic number to real interval """ if im is None: if isinstance(x, complex_interval.ComplexIntervalFieldElement): @@ -446,9 +458,14 @@ def __call__(self, x, im=None): sage_eval(x.replace(' ',''), locals={"I":self.gen(),"i":self.gen()})) late_import() - if isinstance(x, NumberFieldElement_quadratic) and list(x.parent().polynomial()) == [1, 0, 1]: - (re, im) = list(x) - return complex_interval.ComplexIntervalFieldElement(self, re, im) + if isinstance(x, NumberFieldElement_quadratic): + parent = x.parent() + if (list(parent.polynomial()) == [1, 0, 1] and + parent.coerce_embedding() is not None): + (re, im) = list(x) + if not parent._standard_embedding: + im = -im + return complex_interval.ComplexIntervalFieldElement(self, re, im) try: return x._complex_mpfi_( self ) diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 2c1b137d57e..abd46a33100 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -77,7 +77,7 @@ from sage.misc.randstate cimport randstate, current_randstate from sage.misc.superseded import deprecated_function_alias from .real_mpfr cimport RealField_class, RealNumber from .real_mpfr import mpfr_prec_min, mpfr_prec_max -from sage.structure.sage_object cimport rich_to_bool, richcmp +from sage.structure.richcmp cimport rich_to_bool, richcmp NumberFieldElement_quadratic = None AlgebraicNumber_base = None diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 743ac9fd5a3..071e9e8ce23 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -49,10 +49,11 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function -include "cysignals/signals.pxi" +from __future__ import absolute_import, print_function + +from cysignals.signals cimport sig_on, sig_off + include "sage/libs/ntl/decl.pxi" from cypari2.paridecl cimport * @@ -61,7 +62,8 @@ from sage.rings.finite_rings.finite_field_base cimport FiniteField from sage.rings.ring cimport Ring from .element_ext_pari import FiniteField_ext_pariElement from .element_pari_ffelt cimport FiniteFieldElement_pari_ffelt -from sage.structure.sage_object cimport SageObject, richcmp +from sage.structure.richcmp cimport richcmp +from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, ModuleElement, RingElement import operator import sage.arith.all diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index c46207d8021..10ef55177fb 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -20,13 +20,15 @@ AUTHORS: #***************************************************************************** from __future__ import absolute_import -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" +from cysignals.memory cimport check_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off + include "sage/libs/ntl/decl.pxi" from cypari2.paridecl cimport * -from sage.structure.sage_object cimport (SageObject, richcmp, - richcmp_not_equal, rich_to_bool) +from sage.structure.richcmp cimport (richcmp, + richcmp_not_equal, rich_to_bool) +from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, ModuleElement, RingElement from sage.structure.parent cimport Parent @@ -426,9 +428,6 @@ cdef class Cache_ntl_gf2e(SageObject): self.F.restore() - cdef unsigned char *p - cdef int i - if number < 0 or number >= self.order(): raise TypeError("n must be between 0 and self.order()") @@ -443,8 +442,9 @@ cdef class Cache_ntl_gf2e(SageObject): else: raise TypeError("number %s is not an integer" % number) - p = sig_malloc(n) - for i from 0 <= i < n: + cdef unsigned char* p = check_malloc(n) + cdef long i + for i in range(n): p[i] = (number%256) number = number >> 8 GF2XFromBytes(_a, p, n) diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 2d633b81117..a6ef1adaa8f 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -17,9 +17,9 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from cysignals.memory cimport sig_free +from cysignals.signals cimport sig_on, sig_off -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" from cypari2.paridecl cimport * from cypari2.paripriv cimport * from sage.libs.pari.convert_gmp cimport _new_GEN_from_mpz_t diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index 7a585027c57..ca389e173ae 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -172,7 +172,7 @@ from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.structure.factory import UniqueFactory from sage.structure.element cimport parent -from sage.structure.sage_object cimport richcmp, richcmp_not_equal +from sage.structure.richcmp cimport richcmp, richcmp_not_equal class ResidueFieldFactory(UniqueFactory): diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index fdb885b48c1..685d97b1650 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -3,7 +3,7 @@ from __future__ import print_function import sys -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport * from sage.rings.all import GF diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 463a1042b89..0ee2314ce61 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -40,7 +40,7 @@ import operator from sage.structure.element cimport (FieldElement, ModuleElement, RingElement, Element) from sage.structure.element import parent -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp from . import integer_ring from .integer_ring import ZZ diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 6190ed360d6..852b66d5e2a 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1427,6 +1427,9 @@ def __init__(self, constant_field, names, self._ring = R self._field = R.fraction_field() self._populate_coercion_lists_(coerce_list=[self._field]) + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps + from sage.categories.morphism import SetMorphism + R.register_conversion(SetMorphism(self.Hom(R, SetsWithPartialMaps()), self._to_polynomial)) self._gen = self(R.gen()) def __reduce__(self): @@ -1562,6 +1565,25 @@ def _to_constant_base_field(self, f): return K(f.numerator()) / K(f.denominator()) raise ValueError("only constants can be converted into the constant base field but %r is not a constant"%(f,)) + def _to_polynomial(self, f): + """ + If ``f`` is integral, return it as a polynomial. + + INPUT: + + - ``f`` -- an element of this rational function field whose denominator is a constant. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K._ring(x) # indirect doctest + x + """ + K = f.parent().constant_base_field() + if f.denominator() in K: + return f.numerator()/K(f.denominator()) + raise ValueError("Only polynomials can be converted to the underlying polynomial ring") + def _to_bivariate_polynomial(self, f): """ Convert ``f`` from a univariate polynomial over the rational function diff --git a/src/sage/rings/function_field/function_field_element.pyx b/src/sage/rings/function_field/function_field_element.pyx index 802390328a1..7b5fb6f4afc 100644 --- a/src/sage/rings/function_field/function_field_element.pyx +++ b/src/sage/rings/function_field/function_field_element.pyx @@ -25,7 +25,7 @@ AUTHORS: from sage.structure.element cimport FieldElement, RingElement, ModuleElement, Element -from sage.structure.sage_object cimport richcmp, richcmp_not_equal +from sage.structure.richcmp cimport richcmp, richcmp_not_equal def is_FunctionFieldElement(x): diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 3812cf6aaea..ad01c8ab666 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -339,7 +339,8 @@ cdef _digits_internal(mpz_t v,l,int offset,int power_index,power_list,digits): mpz_clear(mpz_quot) mpz_clear(mpz_res) -from sage.structure.sage_object cimport SageObject, rich_to_bool_sgn +from sage.structure.sage_object cimport SageObject +from sage.structure.richcmp cimport rich_to_bool_sgn from sage.structure.element cimport EuclideanDomainElement, ModuleElement, Element from sage.structure.element import bin_op from sage.structure.coerce_exceptions import CoercionException diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 14639f74a08..1e4d05ed360 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -64,7 +64,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.structure.coerce cimport is_numpy_type from sage.structure.parent_gens import ParentWithGens from sage.structure.parent cimport Parent -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool from sage.structure.sequence import Sequence from sage.misc.misc_c import prod diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 52c87e0f774..5c20340f0db 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -63,7 +63,7 @@ from sage.rings.integer import Integer from sage.rings.polynomial.laurent_polynomial import LaurentPolynomial_univariate from .power_series_ring_element cimport PowerSeries from sage.structure.element cimport Element, ModuleElement, RingElement, AlgebraElement -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.misc.derivative import multi_derivative diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 223879b0f28..c81ca1e14e7 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -357,8 +357,8 @@ from __future__ import print_function import ideal import homset from cpython.object cimport Py_EQ, Py_NE -from sage.structure.sage_object cimport (richcmp, rich_to_bool, - richcmp_not_equal) +from sage.structure.richcmp cimport (richcmp, rich_to_bool, + richcmp_not_equal) def is_RingHomomorphism(phi): diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index be398ae74e9..45d67d50e75 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -157,7 +157,7 @@ #***************************************************************************** from six import iteritems, integer_types -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.rings.infinity import infinity, is_Infinite diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index d2324ab8f41..dee50786d18 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -1618,6 +1618,12 @@ def _element_constructor_(self, x, check=True): sage: L(gap(tau)^3) # indirect doctest 2 + Check that :trac:`22202` is fixed:: + + sage: y = QQ['y'].gen() + sage: R = QQ.extension(y^2-2,'a')['x'] + sage: R("a*x").factor() + (a) * x """ if isinstance(x, number_field_element.NumberFieldElement): K = x.parent() @@ -1644,7 +1650,8 @@ def _element_constructor_(self, x, check=True): if x.type() in ["t_INT", "t_FRAC"]: pass elif x.type() == "t_POL": - if check and self.pari_polynomial() != self.absolute_polynomial().monic(): + var = self.absolute_polynomial().variable_name() + if check and self.pari_polynomial(var) != self.absolute_polynomial().monic(): from warnings import warn warn("interpreting PARI polynomial %s relative to the defining polynomial %s of the PARI number field" % (x, self.pari_polynomial())) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 772ceba3f95..4aaeb6b60f2 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -44,7 +44,7 @@ from sage.libs.mpfi cimport mpfi_t, mpfi_init, mpfi_set, mpfi_clear, mpfi_div_z, from sage.libs.mpfr cimport mpfr_less_p, mpfr_greater_p, mpfr_greaterequal_p from sage.libs.ntl.error import NTLError from cpython.object cimport Py_EQ, Py_NE, Py_LT, Py_GT, Py_LE, Py_GE -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool import sage.rings.infinity import sage.rings.polynomial.polynomial_element diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 6c92df0a73d..db54c3c5220 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -42,7 +42,7 @@ from sage.libs.mpfi cimport mpfi_set_z, mpfi_set_q, mpfi_sqrt, mpfi_add_z, mpfi_ from sage.structure.parent_base cimport ParentWithBase from sage.structure.element cimport Element, ModuleElement, RingElement -from sage.structure.sage_object cimport rich_to_bool_sgn +from sage.structure.richcmp cimport rich_to_bool_sgn from sage.rings.rational cimport Rational from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 81023469a66..10f73166a47 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -25,8 +25,8 @@ AUTHORS: from __future__ import absolute_import, print_function -include "cysignals/memory.pxi" from libc.math cimport sqrt +from cysignals.memory cimport sig_malloc, sig_free from sage.arith.all import binomial, gcd from sage.libs.gmp.mpz cimport * diff --git a/src/sage/rings/padics/FP_template.pxi b/src/sage/rings/padics/FP_template.pxi new file mode 100644 index 00000000000..022638e17a2 --- /dev/null +++ b/src/sage/rings/padics/FP_template.pxi @@ -0,0 +1,1798 @@ +""" +Floating point template for complete discrete valuation rings. + +In order to use this template you need to write a linkage file and +gluing file. For an example see mpz_linkage.pxi (linkage file) and +padic_floating_point_element.pyx (gluing file). + +The linkage file implements a common API that is then used in the +class FPElement defined here. See sage/libs/linkages/padics/API.pxi +for the functions needed. + +The gluing file does the following: + +- ctypedef's celement to be the appropriate type (e.g. mpz_t) +- includes the linkage file +- includes this template +- defines a concrete class inheriting from FPElement, and implements + any desired extra methods + +AUTHORS: + +- David Roe (2016-03-21) -- initial version +""" + +#***************************************************************************** +# Copyright (C) 2007-2016 David Roe +# William Stein +# +# 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.ext.stdsage cimport PY_NEW +include "padic_template_element.pxi" +from cpython.int cimport * + +from sage.structure.element cimport Element +from sage.rings.padics.common_conversion cimport comb_prec, _process_args_and_kwds +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.categories.sets_cat import Sets +from sage.categories.sets_with_partial_maps import SetsWithPartialMaps +from sage.categories.homset import Hom + +cdef inline bint overunderflow(long* ordp, celement unit, PowComputer_ prime_pow): + """ + Check for over and underflow. If detected, sets ordp and unit + appropriately, and returns True. If not, returns False. + """ + if ordp[0] >= maxordp: + ordp[0] = maxordp + csetzero(unit, prime_pow) + elif ordp[0] <= minusmaxordp: + ordp[0] = minusmaxordp + csetone(unit, prime_pow) + else: + return False + return True + +cdef inline bint overunderflow_mpz(long* ordp, mpz_t ordp_mpz, celement unit, PowComputer_ prime_pow): + """ + Check for over and underflow with an mpz_t ordp. If detected, sets ordp and unit + appropriately, and returns True. If not, returns False. + """ + if mpz_fits_slong_p(ordp_mpz) == 0 or mpz_cmp_si(ordp_mpz, maxordp) >= 0 or mpz_cmp_si(ordp_mpz, minusmaxordp) <= 0: + if mpz_sgn(ordp_mpz) > 0: + ordp[0] = maxordp + csetzero(unit, prime_pow) + else: + ordp[0] = minusmaxordp + csetone(unit, prime_pow) + return True + return False + +cdef inline bint very_pos_val(long ordp): + return ordp >= maxordp + +cdef inline bint very_neg_val(long ordp): + return ordp <= minusmaxordp + +cdef inline bint huge_val(long ordp): + return very_pos_val(ordp) or very_neg_val(ordp) + +cdef class FPElement(pAdicTemplateElement): + cdef int _set(self, x, long val, long xprec, absprec, relprec) except -1: + """ + Sets the value of this element from given defining data. + + This function is intended for use in conversion, and should + not be called on an element created with :meth:`_new_c`. + + INPUT: + + - ``x`` -- data defining a `p`-adic element: int, long, + Integer, Rational, other `p`-adic element... + + - ``val`` -- the valuation of the resulting element (unused; + for compatibility with other `p`-adic precision modes) + + - ``xprec -- an inherent precision of ``x`` (unused; for + compatibility with other `p`-adic precision modes) + + - ``absprec`` -- an absolute precision cap for this element + (unused; for compatibility with other `p`-adic precision + modes) + + - ``relprec`` -- a relative precision cap for this element + (unused; for compatibility with other `p`-adic precision + modes) + + TESTS:: + + sage: R = ZpFP(5) + sage: a = R(17,5); a #indirect doctest + 2 + 3*5 + sage: R(15) #indirect doctest + 3*5 + + sage: R = ZpFP(5,5) + sage: a = R(25/9); a #indirect doctest + 4*5^2 + 2*5^3 + 5^5 + 2*5^6 + sage: R(ZpCR(5)(25/9)) == a + True + sage: R(5) - R(5) + 0 + """ + cconstruct(self.unit, self.prime_pow) + if very_pos_val(val): + self._set_exact_zero() + elif very_neg_val(val): + self._set_infinity() + else: + self.ordp = val + if isinstance(x,FPElement) and x.parent() is self.parent(): + ccopy(self.unit, (x).unit, self.prime_pow) + else: + cconv(self.unit, x, self.prime_pow.prec_cap, val, self.prime_pow) + + cdef int _set_exact_zero(self) except -1: + """ + Sets this element to zero. + + TESTS:: + + sage: R = Zp(5); R(0) #indirect doctest + 0 + """ + csetzero(self.unit, self.prime_pow) + self.ordp = maxordp + + cdef int _set_infinity(self) except -1: + """ + Sets this element to zero. + + TESTS:: + + sage: R = Zp(5); R(0) #indirect doctest + 0 + """ + csetone(self.unit, self.prime_pow) + self.ordp = minusmaxordp + + cdef FPElement _new_c(self): + """ + Creates a new element with the same basic info. + + TESTS:: + + sage: R = ZpFP(5); R(6) * R(7) #indirect doctest + 2 + 3*5 + 5^2 + """ + cdef type t = type(self) + cdef FPElement ans = t.__new__(t) + ans._parent = self._parent + ans.prime_pow = self.prime_pow + cconstruct(ans.unit, ans.prime_pow) + return ans + + cdef int check_preccap(self) except -1: + """ + Check that the precision of this element does not exceed the + precision cap. Does nothing for floating point elements. + + TESTS:: + + sage: ZpFP(5)(1).lift_to_precision(30) # indirect doctest + 1 + """ + pass + + def __copy__(self): + """ + Return a copy of this element. + + EXAMPLES:: + + sage: a = ZpFP(5,6)(17); b = copy(a) + sage: a == b + True + sage: a is b + False + """ + cdef FPElement ans = self._new_c() + ans.ordp = self.ordp + ccopy(ans.unit, self.unit, ans.prime_pow) + return ans + + cdef int _normalize(self) except -1: + """ + Normalizes this element, so that ``self.ordp`` is correct. + + TESTS:: + + sage: R = ZpFP(5) + sage: R(6) + R(4) #indirect doctest + 2*5 + """ + cdef long diff + cdef bint is_zero + if very_pos_val(self.ordp): + self._set_exact_zero() + elif very_neg_val(self.ordp): + self._set_infinity() + else: + is_zero = creduce(self.unit, self.unit, self.prime_pow.prec_cap, self.prime_pow) + if is_zero: + self.ordp = maxordp + else: + diff = cremove(self.unit, self.unit, self.prime_pow.prec_cap, self.prime_pow) + self.ordp += diff + if very_pos_val(self.ordp): + self._set_exact_zero() + + def __dealloc__(self): + """ + Deallocate the underlying data structure. + + TESTS:: + + sage: R = ZpFP(5) + sage: a = R(17) + sage: del(a) + """ + cdestruct(self.unit, self.prime_pow) + + def __reduce__(self): + """ + Return a tuple of a function and data that can be used to unpickle this + element. + + EXAMPLES:: + + sage: a = ZpFP(5)(-3) + sage: type(a) + + sage: loads(dumps(a)) == a + True + """ + return unpickle_fpe_v2, (self.__class__, self.parent(), cpickle(self.unit, self.prime_pow), self.ordp) + +# def __richcmp__(self, right, int op): +# """ +# Compare this element to ``right`` using the comparison operator ``op``. +# +# TESTS:: +# +# sage: R = ZpFP(5) +# sage: a = R(17) +# sage: b = R(21) +# sage: a == b +# False +# sage: a < b +# True +# """ +# return (self)._richcmp(right, op) + + cpdef _neg_(self): + r""" + Return the additive inverse of this element. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: -R(7) #indirect doctest + 6*7 + 6*7^2 + 6*7^3 + 6*7^4 + """ + cdef FPElement ans = self._new_c() + ans.ordp = self.ordp + if huge_val(self.ordp): # zero or infinity + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + cneg(ans.unit, self.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce_small(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + cpdef _add_(self, _right): + r""" + Return the sum of this element and ``_right``. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: x = R(1721); x + 6 + 5*7^3 + sage: y = R(1373); y + 1 + 4*7^3 + sage: x + y #indirect doctest + 7 + 2*7^3 + """ + cdef FPElement ans + cdef FPElement right = _right + cdef long tmpL + if self.ordp == right.ordp: + ans = self._new_c() + ans.ordp = self.ordp + if huge_val(ans.ordp): + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + cadd(ans.unit, self.unit, right.unit, ans.prime_pow.prec_cap, ans.prime_pow) + ans._normalize() # safer than trying to leave unnormalized + else: + if self.ordp > right.ordp: + # Addition is commutative, swap so self.ordp < right.ordp + ans = right; right = self; self = ans + tmpL = right.ordp - self.ordp + if tmpL > self.prime_pow.prec_cap: + return self + ans = self._new_c() + ans.ordp = self.ordp + if huge_val(ans.ordp): + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + cshift(ans.unit, right.unit, tmpL, ans.prime_pow.prec_cap, ans.prime_pow, False) + cadd(ans.unit, ans.unit, self.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + cpdef _sub_(self, _right): + r""" + Return the difference of this element and ``_right``. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: x = R(1721); x + 6 + 5*7^3 + sage: y = R(1373); y + 1 + 4*7^3 + sage: x - y #indirect doctest + 5 + 7^3 + """ + cdef FPElement ans + cdef FPElement right = _right + cdef long tmpL + if self.ordp == right.ordp: + ans = self._new_c() + ans.ordp = self.ordp + if huge_val(ans.ordp): + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + csub(ans.unit, self.unit, right.unit, ans.prime_pow.prec_cap, ans.prime_pow) + ans._normalize() # safer than trying to leave unnormalized + elif self.ordp < right.ordp: + tmpL = right.ordp - self.ordp + if tmpL > self.prime_pow.prec_cap: + return self + ans = self._new_c() + ans.ordp = self.ordp + if huge_val(ans.ordp): + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + cshift(ans.unit, right.unit, tmpL, ans.prime_pow.prec_cap, ans.prime_pow, False) + csub(ans.unit, self.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + else: + tmpL = self.ordp - right.ordp + if tmpL > self.prime_pow.prec_cap: + return right._neg_() + ans = self._new_c() + ans.ordp = right.ordp + if huge_val(ans.ordp): + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + cshift(ans.unit, self.unit, tmpL, ans.prime_pow.prec_cap, ans.prime_pow, False) + csub(ans.unit, ans.unit, right.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + def __invert__(self): + r""" + Returns multiplicative inverse of this element. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: ~R(2) + 4 + 3*7 + 3*7^2 + 3*7^3 + sage: ~R(0) + infinity + sage: ~R(7) + 7^-1 + """ + # Input should be normalized! + cdef FPElement ans = self._new_c() + if ans.prime_pow.in_field == 0: + ans._parent = self._parent.fraction_field() + ans.prime_pow = ans._parent.prime_pow + ans.ordp = -self.ordp + if very_pos_val(ans.ordp): + csetone(ans.unit, ans.prime_pow) + elif very_neg_val(ans.ordp): + csetzero(ans.unit, ans.prime_pow) + else: + cinvert(ans.unit, self.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + cpdef _mul_(self, _right): + r""" + Return the product of this element and ``_right``. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: R(3) * R(2) #indirect doctest + 6 + sage: R(1/2) * R(2) + 1 + """ + cdef FPElement right = _right + if very_pos_val(self.ordp): + if very_neg_val(right.ordp): + raise ZeroDivisionError("Cannot multipy 0 by infinity") + return self + elif very_pos_val(right.ordp): + if very_neg_val(self.ordp): + raise ZeroDivisionError("Cannot multiply 0 by infinity") + return right + elif very_neg_val(self.ordp): + return self + elif very_neg_val(right.ordp): + return right + cdef FPElement ans = self._new_c() + ans.ordp = self.ordp + right.ordp + if overunderflow(&ans.ordp, ans.unit, ans.prime_pow): + return ans + cmul(ans.unit, self.unit, right.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + cpdef _div_(self, _right): + r""" + Return the quotient of this element and ``right``. + + EXAMPLES:: + + sage: R = Zp(7, 4, 'floating-point', 'series') + sage: R(3) / R(2) #indirect doctest + 5 + 3*7 + 3*7^2 + 3*7^3 + sage: R(5) / R(0) + infinity + sage: R(7) / R(49) + 7^-1 + """ + # Input should be normalized! + cdef FPElement right = _right + cdef FPElement ans = self._new_c() + if ans.prime_pow.in_field == 0: + ans._parent = self._parent.fraction_field() + ans.prime_pow = ans._parent.prime_pow + if very_pos_val(self.ordp): + if very_pos_val(right.ordp): + raise ZeroDivisionError("Cannot divide 0 by 0") + ans._set_exact_zero() + elif very_neg_val(right.ordp): + if very_neg_val(self.ordp): + raise ZeroDivisionError("Cannot divide infinity by infinity") + ans._set_exact_zero() + elif very_neg_val(self.ordp) or very_pos_val(right.ordp): + ans._set_infinity() + else: + ans.ordp = self.ordp - right.ordp + if overunderflow(&ans.ordp, ans.unit, ans.prime_pow): + return ans + cdivunit(ans.unit, self.unit, right.unit, ans.prime_pow.prec_cap, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.prime_pow.prec_cap, ans.prime_pow) + return ans + + def __pow__(FPElement self, _right, dummy): # NOTE: dummy ignored, always use self.prime_pow.prec_cap + """ + Exponentiation by an integer + + EXAMPLES:: + + sage: R = ZpFP(11, 5) + sage: R(1/2)^5 + 10 + 7*11 + 11^2 + 5*11^3 + 4*11^4 + sage: R(1/32) + 10 + 7*11 + 11^2 + 5*11^3 + 4*11^4 + sage: R(1/2)^5 == R(1/32) + True + sage: R(3)^1000 #indirect doctest + 1 + 4*11^2 + 3*11^3 + 7*11^4 + sage: R(11)^-1 + 11^-1 + """ + cdef long dummyL + cdef mpz_t tmp + cdef Integer right + cdef FPElement base, pright, ans + cdef bint exact_exp + if isinstance(_right, (Integer, int, long, Rational)): + if _right < 0: + self = ~self + _right = -_right + exact_exp = True + elif self.parent() is _right.parent(): + ## For extension elements, we need to switch to the + ## fraction field sometimes in highly ramified extensions. + exact_exp = False + pright = _right + else: + self, _right = canonical_coercion(self, _right) + return self.__pow__(_right, dummy) + if exact_exp and _right == 0: + ans = self._new_c() + ans.ordp = 0 + csetone(ans.unit, ans.prime_pow) + return ans + if huge_val(self.ordp): + if exact_exp: + # We may assume from above that right > 0 + return self + else: + # log(0) and log(infinity) not defined + raise ValueError("0^x and inf^x not defined for p-adic x") + ans = self._new_c() + if exact_exp: + # exact_pow_helper is defined in padic_template_element.pxi + right = exact_pow_helper(&dummyL, self.prime_pow.prec_cap, _right, self.prime_pow) + mpz_init(tmp) + try: + mpz_mul_si(tmp, right.value, self.ordp) + if overunderflow_mpz(&ans.ordp, tmp, ans.unit, ans.prime_pow): + return ans + else: + ans.ordp = mpz_get_si(tmp) + finally: + mpz_clear(tmp) + cpow(ans.unit, self.unit, right.value, ans.prime_pow.prec_cap, ans.prime_pow) + else: + # padic_pow_helper is defined in padic_template_element.pxi + dummyL = padic_pow_helper(ans.unit, self.unit, self.ordp, self.prime_pow.prec_cap, + pright.unit, pright.ordp, pright.prime_pow.prec_cap, self.prime_pow) + ans.ordp = 0 + return ans + + cdef pAdicTemplateElement _lshift_c(self, long shift): + """ + Multiplies self by `\pi^{shift}`. + + Negative shifts may truncate the result if the parent is not a + field. + + EXAMPLES: + + We create a floating point ring:: + + sage: R = ZpFP(5, 20); a = R(1000); a + 3*5^3 + 5^4 + + Shifting to the right is the same as dividing by a power of + the uniformizer `\pi` of the `p`-adic ring.:: + + sage: a >> 1 + 3*5^2 + 5^3 + + Shifting to the left is the same as multiplying by a power of + `\pi`:: + + sage: a << 2 + 3*5^5 + 5^6 + sage: a*5^2 + 3*5^5 + 5^6 + + Shifting by a negative integer to the left is the same as + right shifting by the absolute value:: + + sage: a << -3 + 3 + 5 + sage: a >> 3 + 3 + 5 + """ + if shift < 0: + return self._rshift_c(-shift) + elif shift == 0: + return self + cdef FPElement ans = self._new_c() + # check both in case of overflow in sum; this case also includes self.ordp = maxordp + if very_pos_val(shift) or very_pos_val(self.ordp + shift): + # need to check that we're not shifting infinity + if very_neg_val(self.ordp): + raise ZeroDivisionError("Cannot multiply zero by infinity") + ans.ordp = maxordp + csetzero(ans.unit, ans.prime_pow) + else: + ans.ordp = self.ordp + shift + ccopy(ans.unit, self.unit, ans.prime_pow) + return ans + + cdef pAdicTemplateElement _rshift_c(self, long shift): + """ + Divides by `\pi^{shift}`. + + Positive shifts may truncate the result if the parent is not a + field. + + EXAMPLES:: + + sage: R = ZpFP(997, 7); a = R(123456878908); a + 964*997 + 572*997^2 + 124*997^3 + + Shifting to the right divides by a power of `\pi`, but + dropping terms with negative valuation:: + + sage: a >> 3 + 124 + + A negative shift multiplies by that power of `\pi`:: + + sage: a >> -3 + 964*997^4 + 572*997^5 + 124*997^6 + """ + if shift == 0: + return self + elif very_pos_val(self.ordp): + if very_pos_val(shift): + raise ZeroDivisionError("Cannot divide zero by zero") + return self + elif very_neg_val(self.ordp): + if very_neg_val(shift): + raise ZeroDivisionError("Cannot divide infinity by infinity") + return self + cdef FPElement ans = self._new_c() + cdef long diff + if self.prime_pow.in_field == 1 or shift <= self.ordp: + if very_pos_val(shift): + ans._set_infinity() + elif very_neg_val(shift): + ans._set_exact_zero() + else: + ans.ordp = self.ordp - shift + ccopy(ans.unit, self.unit, ans.prime_pow) + else: + diff = shift - self.ordp + if diff >= self.prime_pow.prec_cap: + ans._set_exact_zero() + else: + ans.ordp = 0 + cshift(ans.unit, self.unit, -diff, ans.prime_pow.prec_cap, ans.prime_pow, False) + ans._normalize() + return ans + + def _repr_(self, mode=None, do_latex=False): + """ + Returns a string representation of this element. + + INPUT: + + - ``mode`` -- allows one to override the default print mode of + the parent (default: ``None``). + + - ``do_latex`` -- whether to return a latex representation or + a normal one. + + EXAMPLES:: + + sage: ZpFP(5,5)(1/3) # indirect doctest + 2 + 3*5 + 5^2 + 3*5^3 + 5^4 + sage: ~QpFP(5,5)(0) + infinity + """ + if very_neg_val(self.ordp): + return "infinity" + return self.parent()._printer.repr_gen(self, do_latex, mode=mode) + + def add_bigoh(self, absprec): + """ + Returns a new element truncated modulo `\pi^{\mbox{absprec}}`. + + INPUT: + + - ``absprec`` -- an integer + + OUTPUT: + + - a new element truncated modulo `\pi^{\mbox{absprec}}`. + + EXAMPLES:: + + sage: R = Zp(7,4,'floating-point','series'); a = R(8); a.add_bigoh(1) + 1 + """ + cdef long aprec, newprec + if absprec is infinity or very_neg_val(self.ordp): + return self + elif isinstance(absprec, int): + aprec = absprec + else: + if not isinstance(absprec, Integer): + absprec = Integer(absprec) + if mpz_fits_slong_p((absprec).value) == 0: + if mpz_sgn((absprec).value) > 0: + return self + aprec = minusmaxordp + else: + aprec = mpz_get_si((absprec).value) + if aprec >= self.ordp + self.prime_pow.prec_cap: + return self + cdef FPElement ans = self._new_c() + if aprec <= self.ordp: + ans._set_exact_zero() + else: + ans.ordp = self.ordp + creduce(ans.unit, self.unit, aprec - self.ordp, ans.prime_pow) + return ans + + cpdef bint _is_exact_zero(self) except -1: + """ + Tests whether this element is exactly zero. + + EXAMPLES:: + + sage: R = Zp(7,4,'floating-point','series'); a = R(8); a._is_exact_zero() + False + sage: b = R(0); b._is_exact_zero() + True + """ + return very_pos_val(self.ordp) + + cpdef bint _is_inexact_zero(self) except -1: + """ + Returns True if self is indistinguishable from zero. + + EXAMPLES:: + + sage: R = ZpFP(7, 5) + sage: R(14)._is_inexact_zero() + False + sage: R(0)._is_inexact_zero() + True + """ + return very_pos_val(self.ordp) + + def is_zero(self, absprec = None): + r""" + Returns whether self is zero modulo `\pi^{\mbox{absprec}}`. + + INPUT: + + - ``absprec`` -- an integer + + EXAMPLES:: + + sage: R = ZpFP(17, 6) + sage: R(0).is_zero() + True + sage: R(17^6).is_zero() + False + sage: R(17^2).is_zero(absprec=2) + True + """ + if absprec is None: + return very_pos_val(self.ordp) + if very_pos_val(self.ordp): + return True + if absprec is infinity: + return False + if isinstance(absprec, int): + return self.ordp >= absprec + if not isinstance(absprec, Integer): + absprec = Integer(absprec) + return mpz_cmp_si((absprec).value, self.ordp) <= 0 + + def __nonzero__(self): + """ + Returns True if this element is distinguishable from zero. + + For most applications, explicitly specifying the power of p + modulo which the element is supposed to be nonzero is + preferrable. + + EXAMPLES:: + + sage: R = ZpFP(5); a = R(0); b = R(75) + sage: bool(a), bool(b) # indirect doctest + (False, True) + """ + return not very_pos_val(self.ordp) + + def is_equal_to(self, _right, absprec=None): + r""" + Returns whether this element is equal to ``right`` modulo `p^{\mbox{absprec}}`. + + If ``absprec`` is ``None``, determines whether self and right + have the same value. + + INPUT: + + - ``right`` -- a p-adic element with the same parent + - ``absprec`` -- a positive integer or ``None`` (default: ``None``) + + EXAMPLES:: + + sage: R = ZpFP(2, 6) + sage: R(13).is_equal_to(R(13)) + True + sage: R(13).is_equal_to(R(13+2^10)) + True + sage: R(13).is_equal_to(R(17), 2) + True + sage: R(13).is_equal_to(R(17), 5) + False + """ + cdef FPElement right + cdef long aprec, rprec + if self.parent() is _right.parent(): + right = _right + else: + right = self.parent().coerce(_right) + if very_neg_val(self.ordp): + if very_neg_val(right.ordp): + return True + return False + elif very_neg_val(right.ordp): + return False + if absprec is None or absprec is infinity: + return ((self.ordp == right.ordp) and + (ccmp(self.unit, right.unit, self.prime_pow.prec_cap, False, False, self.prime_pow) == 0)) + if not isinstance(absprec, Integer): + absprec = Integer(absprec) + if mpz_cmp_si((absprec).value, self.ordp) <= 0: + if mpz_cmp_si((absprec).value, right.ordp) <= 0: + return True + return False + elif mpz_cmp_si((absprec).value, right.ordp) <= 0: + return False + if self.ordp != right.ordp: + return False + if mpz_cmp_si((absprec).value, maxordp) >= 0: + return ccmp(self.unit, right.unit, self.prime_pow.prec_cap, False, False, self.prime_pow) == 0 + aprec = mpz_get_si((absprec).value) + rprec = aprec - self.ordp + if rprec > self.prime_pow.prec_cap: + rprec = self.prime_pow.prec_cap + return ccmp(self.unit, + right.unit, + rprec, + rprec < self.prime_pow.prec_cap, + rprec < right.prime_pow.prec_cap, + self.prime_pow) == 0 + + cdef int _cmp_units(self, pAdicGenericElement _right) except -2: + """ + Comparison of units, used in equality testing. + + EXAMPLES:: + + sage: R = ZpFP(5) + sage: a = R(17); b = R(0,3); c = R(85,7); d = R(2, 1) + sage: any([a == b, a == c, b == c, b == d, c == d, a == d]) # indirect doctest + False + sage: all([a == a, b == b, c == c, d == d]) + True + """ + cdef FPElement right = _right + return ccmp(self.unit, right.unit, self.prime_pow.prec_cap, False, False, self.prime_pow) + + cdef pAdicTemplateElement lift_to_precision_c(self, long absprec): + """ + Lifts this element to another with precision at least absprec. + + Since floating point elements don't track precision, this + function just returns the same element. + + EXAMPLES:: + + sage: R = ZpFP(5); + sage: a = R(77, 2); a + 2 + sage: a.lift_to_precision(17) # indirect doctest + 2 + """ + return self + + def list(self, lift_mode = 'simple', start_val = None): + r""" + Returns a list of coefficients in a power series expansion of + this element in terms of `\pi`. If this is a field element, + they start at `\pi^{\mbox{valuation}}`, if a ring element at `\pi^0`. + + For each lift mode, this function returns a list of `a_i` so + that this element can be expressed as + + .. MATH:: + + \pi^v \cdot \sum_{i=0}^\infty a_i \pi^i + + where `v` is the valuation of this element when the parent is + a field, and `v = 0` otherwise. + + Different lift modes affect the choice of `a_i`. When + ``lift_mode`` is ``'simple'``, the resulting `a_i` will be + non-negative: if the residue field is `\mathbb{F}_p` then they + will be integers with `0 \le a_i < p`; otherwise they will be + a list of integers in the same range giving the coefficients + of a polynomial in the indeterminant representing the maximal + unramified subextension. + + Choosing ``lift_mode`` as ``'smallest'`` is similar to + ``'simple'``, but uses a balanced representation `-p/2 < a_i + \le p/2`. + + Finally, setting ``lift_mode = 'teichmuller'`` will yield + Teichmuller representatives for the `a_i`: `a_i^q = a_i`. In + this case the `a_i` will also be `p`-adic elements. + + INPUT: + + - ``lift_mode`` -- ``'simple'``, ``'smallest'`` or + ``'teichmuller'`` (default: ``'simple'``) + + - ``start_val`` -- start at this valuation rather than the + default (`0` or the valuation of this element). If + ``start_val`` is larger than the valuation of this element + a ``ValueError`` is raised. + + OUTPUT: + + - the list of coefficients of this element. For base elements + these will be integers if ``lift_mode`` is ``'simple'`` or + ``'smallest'``, and elements of ``self.parent()`` if + ``lift_mode`` is ``'teichmuller'``. + + .. NOTE:: + + Use slice operators to get a particular range. + + EXAMPLES:: + + sage: R = ZpFP(7,6); a = R(12837162817); a + 3 + 4*7 + 4*7^2 + 4*7^4 + sage: L = a.list(); L + [3, 4, 4, 0, 4] + sage: sum([L[i] * 7^i for i in range(len(L))]) == a + True + sage: L = a.list('smallest'); L + [3, -3, -2, 1, -3, 1] + sage: sum([L[i] * 7^i for i in range(len(L))]) == a + True + sage: L = a.list('teichmuller'); L + [3 + 4*7 + 6*7^2 + 3*7^3 + 2*7^5, + 0, + 5 + 2*7 + 3*7^3 + 6*7^4 + 4*7^5, + 1, + 3 + 4*7 + 6*7^2 + 3*7^3 + 2*7^5, + 5 + 2*7 + 3*7^3 + 6*7^4 + 4*7^5] + sage: sum([L[i] * 7^i for i in range(len(L))]) + 3 + 4*7 + 4*7^2 + 4*7^4 + + sage: R(0).list() + [] + + sage: R = QpFP(7,4); a = R(6*7+7**2); a.list() + [6, 1] + sage: a.list('smallest') + [-1, 2] + sage: a.list('teichmuller') + [6 + 6*7 + 6*7^2 + 6*7^3, + 2 + 4*7 + 6*7^2 + 3*7^3, + 3 + 4*7 + 6*7^2 + 3*7^3, + 3 + 4*7 + 6*7^2 + 3*7^3] + """ + R = self.parent() + if start_val is not None and start_val > self.ordp: + raise ValueError("starting valuation must be smaller than the element's valuation. See slice()") + if very_pos_val(self.ordp): + return [] + elif very_neg_val(self.ordp): + if lift_mode == 'teichmuller': + return [R(1)] + elif R.f() == 1: + return [ZZ(1)] + else: + return [[ZZ(1)]] + if lift_mode == 'teichmuller': + ulist = self.teichmuller_list() + elif lift_mode == 'simple': + ulist = clist(self.unit, self.prime_pow.prec_cap, True, self.prime_pow) + elif lift_mode == 'smallest': + ulist = clist(self.unit, self.prime_pow.prec_cap, False, self.prime_pow) + else: + raise ValueError("unknown lift_mode") + if (self.prime_pow.in_field == 0 and self.ordp > 0) or start_val is not None: + if lift_mode == 'teichmuller': + zero = R(0) + else: + # needs to be defined in the linkage file. + zero = _list_zero + if start_val is None: + v = self.ordp + else: + v = self.ordp - start_val + ulist = [zero] * v + ulist + return ulist + + def teichmuller_list(self): + r""" + Returns a list [`a_0`, `a_1`,..., `a_n`] such that + + - `a_i^q = a_i` + + - self.unit_part() = `\sum_{i = 0}^n a_i \pi^i` + + EXAMPLES:: + + sage: R = ZpFP(5,5); R(14).list('teichmuller') #indirect doctest + [4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4, + 3 + 3*5 + 2*5^2 + 3*5^3 + 5^4, + 2 + 5 + 2*5^2 + 5^3 + 3*5^4, + 1, + 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4] + """ + cdef FPElement list_elt + ans = PyList_New(0) + if very_pos_val(self.ordp): + return ans + if very_neg_val(self.ordp): + list_elt = self._new_c() + csetone(list_elt.unit, self.prime_pow) + list_elt.ordp = 0 + PyList_Append(ans, list_elt) + return ans + cdef long prec_cap = self.prime_pow.prec_cap + cdef long curpower = prec_cap + cdef FPElement tmp = self._new_c() + ccopy(tmp.unit, self.unit, self.prime_pow) + while not ciszero(tmp.unit, tmp.prime_pow) and curpower > 0: + list_elt = self._new_c() + cteichmuller(list_elt.unit, tmp.unit, prec_cap, self.prime_pow) + if ciszero(list_elt.unit, self.prime_pow): + list_elt.ordp = maxordp + cshift_notrunc(tmp.unit, tmp.unit, -1, prec_cap, self.prime_pow) + else: + list_elt.ordp = 0 + csub(tmp.unit, tmp.unit, list_elt.unit, prec_cap, self.prime_pow) + cshift_notrunc(tmp.unit, tmp.unit, -1, prec_cap, self.prime_pow) + creduce(tmp.unit, tmp.unit, prec_cap, self.prime_pow) + curpower -= 1 + PyList_Append(ans, list_elt) + return ans + + def _teichmuller_set_unsafe(self): + """ + Sets this element to the Teichmuller representative with the + same residue. + + .. WARNING:: + + This function modifies the element, which is not safe. + Elements are supposed to be immutable. + + EXAMPLES:: + + sage: R = ZpFP(17,5); a = R(11) + sage: a + 11 + sage: a._teichmuller_set_unsafe(); a + 11 + 14*17 + 2*17^2 + 12*17^3 + 15*17^4 + sage: a.list('teichmuller') + [11 + 14*17 + 2*17^2 + 12*17^3 + 15*17^4] + + Note that if you set an element which is congruent to 0 you + get 0 to maximum precision:: + + sage: b = R(17*5); b + 5*17 + sage: b._teichmuller_set_unsafe(); b + 0 + """ + if self.ordp > 0: + self._set_exact_zero() + elif self.ordp < 0: + raise ValueError("cannot set negative valuation element to Teichmuller representative.") + else: + cteichmuller(self.unit, self.unit, self.prime_pow.prec_cap, self.prime_pow) + + def precision_absolute(self): + """ + The absolute precision of this element. + + EXAMPLES:: + + sage: R = Zp(7,4,'floating-point'); a = R(7); a.precision_absolute() + 5 + sage: R(0).precision_absolute() + +Infinity + sage: (~R(0)).precision_absolute() + -Infinity + """ + if very_pos_val(self.ordp): + return infinity + elif very_neg_val(self.ordp): + return -infinity + cdef Integer ans = PY_NEW(Integer) + mpz_set_si(ans.value, self.ordp + self.prime_pow.prec_cap) + return ans + + def precision_relative(self): + r""" + The relative precision of this element. + + EXAMPLES:: + + sage: R = Zp(7,4,'floating-point'); a = R(7); a.precision_relative() + 4 + sage: R(0).precision_relative() + 0 + sage: (~R(0)).precision_relative() + 0 + """ + cdef Integer ans = PY_NEW(Integer) + if huge_val(self.ordp): + mpz_set_si(ans.value, 0) + else: + mpz_set_si(ans.value, self.prime_pow.prec_cap) + return ans + + cpdef pAdicTemplateElement unit_part(FPElement self): + r""" + Returns the unit part of this element. + + If the valuation of this element is positive, then the high + digits of the result will be zero. + + EXAMPLES:: + + sage: R = Zp(17, 4, 'floating-point') + sage: R(5).unit_part() + 5 + sage: R(18*17).unit_part() + 1 + 17 + sage: R(0).unit_part() + Traceback (most recent call last): + ... + ValueError: unit part of 0 and infinity not defined + sage: type(R(5).unit_part()) + + sage: R = ZpFP(5, 5); a = R(75); a.unit_part() + 3 + """ + if huge_val(self.ordp): + raise ValueError("unit part of 0 and infinity not defined") + cdef FPElement ans = (self)._new_c() + ans.ordp = 0 + ccopy(ans.unit, (self).unit, ans.prime_pow) + return ans + + cdef long valuation_c(self): + """ + Returns the valuation of this element. + + If this element is an exact zero, returns ``maxordp``, which is defined as + ``(1L << (sizeof(long) * 8 - 2))-1``. + + If this element is infinity, returns ``-maxordp``. + + TESTS:: + + sage: R = ZpFP(5, 5); R(1).valuation() #indirect doctest + 0 + sage: R = Zp(17, 4,'floating-point') + sage: a = R(2*17^2) + sage: a.valuation() + 2 + sage: R = Zp(5, 4,'floating-point') + sage: R(0).valuation() + +Infinity + sage: (~R(0)).valuation() + -Infinity + sage: R(1).valuation() + 0 + sage: R(2).valuation() + 0 + sage: R(5).valuation() + 1 + sage: R(10).valuation() + 1 + sage: R(25).valuation() + 2 + sage: R(50).valuation() + 2 + """ + return self.ordp + + cpdef val_unit(self, p=None): + """ + Returns a 2-tuple, the first element set to the valuation of + this element, and the second to the unit part. + + If this element is either zero or infinity, raises an error. + + EXAMPLES:: + + sage: R = ZpFP(5,5) + sage: a = R(75); b = a - a + sage: a.val_unit() + (2, 3) + sage: b.val_unit() + Traceback (most recent call last): + ... + ValueError: unit part of 0 and infinity not defined + """ + if p is not None and p != self.parent().prime(): + raise ValueError('Ring (%s) residue field of the wrong characteristic.'%self.parent()) + if huge_val(self.ordp): + raise ValueError("unit part of 0 and infinity not defined") + cdef Integer valuation = PY_NEW(Integer) + mpz_set_si(valuation.value, self.ordp) + cdef FPElement unit = self._new_c() + unit.ordp = 0 + ccopy(unit.unit, self.unit, unit.prime_pow) + return valuation, unit + + def __hash__(self): + """ + Hashing. + + EXAMPLES:: + + sage: R = ZpFP(11, 5) + sage: hash(R(3)) == hash(3) + True + """ + if very_pos_val(self.ordp): + return 0 + if very_neg_val(self.ordp): + return 314159 + return chash(self.unit, self.ordp, self.prime_pow.prec_cap, self.prime_pow) ^ self.ordp + +cdef class pAdicCoercion_ZZ_FP(RingHomomorphism_coercion): + """ + The canonical inclusion from the integer ring to a floating point ring. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ); f + Ring Coercion morphism: + From: Integer Ring + To: 5-adic Ring with floating precision 20 + """ + def __init__(self, R): + """ + Initialization. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ); type(f) + + """ + RingHomomorphism_coercion.__init__(self, ZZ.Hom(R), check=False) + self._zero = R._element_constructor(R, 0) + self._section = pAdicConvert_FP_ZZ(R) + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ) + sage: g = copy(f) # indirect doctest + sage: g == f + True + sage: g(6) + 1 + 5 + sage: g(6) == f(6) + True + """ + _slots['_zero'] = self._zero + _slots['_section'] = self._section + return RingHomomorphism_coercion._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ) + sage: g = copy(f) # indirect doctest + sage: g == f + True + sage: g(6) + 1 + 5 + sage: g(6) == f(6) + True + """ + self._zero = _slots['_zero'] + self._section = _slots['_section'] + RingHomomorphism_coercion._update_slots(self, _slots) + + cpdef Element _call_(self, x): + """ + Evaluation. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ) + sage: f(0).parent() + 5-adic Ring with floating precision 20 + sage: f(5) + 5 + """ + if mpz_sgn((x).value) == 0: + return self._zero + cdef FPElement ans = self._zero._new_c() + ans.ordp = cconv_mpz_t(ans.unit, (x).value, ans.prime_pow.prec_cap, False, ans.prime_pow) + return ans + + cpdef Element _call_with_args(self, x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in (relative or absolute or both). + + INPUT: + + - ``x`` -- an Integer + + - ``absprec``, or the first positional argument -- the maximum + absolute precision (unused for floating point elements). + + - ``relprec``, or the second positional argument -- the + maximum relative precision (unused for floating point + elements) + + EXAMPLES:: + + sage: R = ZpFP(5,4) + sage: type(R(10,2)) + + sage: R(30,2) + 5 + sage: R(30,3,2) + 5 + 5^2 + sage: R(30,absprec=2) + 5 + sage: R(30,relprec=2) + 5 + 5^2 + sage: R(30,absprec=1) + 0 + sage: R(30,empty=True) + 0 + """ + cdef long val, aprec, rprec + if mpz_sgn((x).value) == 0: + return self._zero + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, self._zero.prime_pow) + val = get_ordp(x, self._zero.prime_pow) + if aprec - val < rprec: + rprec = aprec - val + if rprec <= 0: + return self._zero + cdef FPElement ans = self._zero._new_c() + ans.ordp = cconv_mpz_t(ans.unit, (x).value, rprec, False, ans.prime_pow) + return ans + + def section(self): + """ + Returns a map back to ZZ that approximates an element of this + `p`-adic ring by an integer. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ).section() + sage: f(ZpFP(5)(-1)) - 5^20 + -1 + """ + return self._section + +cdef class pAdicConvert_FP_ZZ(RingMap): + """ + The map from a floating point ring back to ZZ that returns the the smallest + non-negative integer approximation to its input which is accurate up to the precision. + + If the input is not in the closure of the image of ZZ, raises a ValueError. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ).section(); f + Set-theoretic ring morphism: + From: 5-adic Ring with floating precision 20 + To: Integer Ring + """ + def __init__(self, R): + """ + Initialization. + + EXAMPLES:: + + sage: f = ZpFP(5).coerce_map_from(ZZ).section(); type(f) + + sage: f.category() + Category of homsets of sets + """ + if R.degree() > 1 or R.characteristic() != 0 or R.residue_characteristic() == 0: + RingMap.__init__(self, Hom(R, ZZ, SetsWithPartialMaps())) + else: + RingMap.__init__(self, Hom(R, ZZ, Sets())) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(ZZ).section() + sage: f(QpFP(5)(-1)) - 5^20 + -1 + sage: f(QpFP(5)(5)) + 5 + sage: f(QpFP(5)(0)) + 0 + sage: f(~QpFP(5)(5)) + Traceback (most recent call last): + ... + ValueError: negative valuation + sage: f(~QpFP(5)(0)) + Traceback (most recent call last): + ... + ValueError: Infinity cannot be converted to a rational + """ + cdef Integer ans = PY_NEW(Integer) + cdef FPElement x = _x + if very_pos_val(x.ordp): + mpz_set_ui(ans.value, 0) + elif very_neg_val(x.ordp): + raise ValueError("Infinity cannot be converted to a rational") + else: + cconv_mpz_t_out(ans.value, x.unit, x.ordp, x.prime_pow.prec_cap, x.prime_pow) + return ans + +cdef class pAdicCoercion_QQ_FP(RingHomomorphism_coercion): + """ + The canonical inclusion from the rationals to a floating point field. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ); f + Ring Coercion morphism: + From: Rational Field + To: 5-adic Field with floating precision 20 + """ + def __init__(self, R): + """ + Initialization. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ); type(f) + + """ + RingHomomorphism_coercion.__init__(self, QQ.Hom(R), check=False) + self._zero = R._element_constructor(R, 0) + self._section = pAdicConvert_FP_QQ(R) + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Rational Field + To: 5-adic Field with floating precision 20 + sage: g == f + True + sage: g is f + False + sage: g(6) + 1 + 5 + sage: g(6) == f(6) + True + """ + _slots['_zero'] = self._zero + _slots['_section'] = self._section + return RingHomomorphism_coercion._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Rational Field + To: 5-adic Field with floating precision 20 + sage: g == f + True + sage: g is f + False + sage: g(6) + 1 + 5 + sage: g(6) == f(6) + True + """ + self._zero = _slots['_zero'] + self._section = _slots['_section'] + RingHomomorphism_coercion._update_slots(self, _slots) + + cpdef Element _call_(self, x): + """ + Evaluation. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ) + sage: f(0).parent() + 5-adic Field with floating precision 20 + sage: f(1/5) + 5^-1 + sage: f(1/4) + 4 + 3*5 + 3*5^2 + 3*5^3 + 3*5^4 + 3*5^5 + 3*5^6 + 3*5^7 + 3*5^8 + 3*5^9 + 3*5^10 + 3*5^11 + 3*5^12 + 3*5^13 + 3*5^14 + 3*5^15 + 3*5^16 + 3*5^17 + 3*5^18 + 3*5^19 + sage: f(1/4, 5) + 4 + 3*5 + 3*5^2 + 3*5^3 + 3*5^4 + """ + if mpq_sgn((x).value) == 0: + return self._zero + cdef FPElement ans = self._zero._new_c() + ans.ordp = cconv_mpq_t(ans.unit, (x).value, ans.prime_pow.prec_cap, False, self._zero.prime_pow) + return ans + + cpdef Element _call_with_args(self, x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in + (relative or absolute or both). + + See the documentation for + :meth:`pAdicCappedRelativeElement.__init__` for more details. + + EXAMPLES:: + + sage: R = QpFP(5,4) + sage: type(R(10/3,2)) + + sage: R(10/3,2) + 4*5 + sage: R(10/3,3,1) + 4*5 + sage: R(10/3,absprec=2) + 4*5 + sage: R(10/3,relprec=2) + 4*5 + 5^2 + sage: R(10/3,absprec=1) + 0 + sage: R(3/100,absprec=-1) + 2*5^-2 + """ + cdef long val, aprec, rprec + cdef FPElement ans + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, self._zero.prime_pow) + if mpq_sgn((x).value) == 0: + return self._zero + val = get_ordp(x, self._zero.prime_pow) + if aprec <= val: + return self._zero + ans = self._zero._new_c() + rprec = min(rprec, aprec - val) + ans.ordp = cconv_mpq_t(ans.unit, (x).value, rprec, False, self._zero.prime_pow) + return ans + + def section(self): + """ + Returns a map back to the rationals that approximates an element by + a rational number. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ).section() + sage: f(QpFP(5)(1/4)) + 1/4 + sage: f(QpFP(5)(1/5)) + 1/5 + """ + return self._section + +cdef class pAdicConvert_FP_QQ(RingMap): + """ + The map from the floating point ring back to the rationals that returns a + rational approximation of its input. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ).section(); f + Set-theoretic ring morphism: + From: 5-adic Field with floating precision 20 + To: Rational Field + """ + def __init__(self, R): + """ + Initialization. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ).section(); type(f) + + sage: f.category() + Category of homsets of sets with partial maps + """ + RingMap.__init__(self, Hom(R, QQ, SetsWithPartialMaps())) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: f = QpFP(5).coerce_map_from(QQ).section() + sage: f(QpFP(5)(-1)) + -1 + sage: f(QpFP(5)(0)) + 0 + sage: f(QpFP(5)(1/5)) + 1/5 + """ + cdef Rational ans = Rational.__new__(Rational) + cdef FPElement x = _x + if very_pos_val(x.ordp): + mpq_set_ui(ans.value, 0, 1) + elif very_neg_val(x.ordp): + raise ValueError("Infinity cannot be converted to a rational") + else: + cconv_mpq_t_out(ans.value, x.unit, x.ordp, x.prime_pow.prec_cap, x.prime_pow) + return ans + +cdef class pAdicConvert_QQ_FP(Morphism): + """ + The inclusion map from QQ to a floating point ring. + + EXAMPLES:: + + sage: f = ZpFP(5).convert_map_from(QQ); f + Generic morphism: + From: Rational Field + To: 5-adic Ring with floating precision 20 + """ + def __init__(self, R): + """ + Initialization. + + EXAMPLES:: + + sage: f = ZpFP(5).convert_map_from(QQ); type(f) + + """ + Morphism.__init__(self, Hom(QQ, R, SetsWithPartialMaps())) + self._zero = R._element_constructor(R, 0) + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = ZpFP(5).convert_map_from(QQ) + sage: g = copy(f) # indirect doctest + sage: g == f # todo: comparison not implemented + True + sage: g(1/6) + 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + sage: g(1/6) == f(1/6) + True + """ + _slots['_zero'] = self._zero + return Morphism._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: f = ZpFP(5).convert_map_from(QQ) + sage: g = copy(f) # indirect doctest + sage: g == f # todo: comparison not implemented + True + sage: g(1/6) + 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + sage: g(1/6) == f(1/6) + True + """ + self._zero = _slots['_zero'] + Morphism._update_slots(self, _slots) + + cpdef Element _call_(self, x): + """ + Evaluation. + + EXAMPLES:: + + sage: f = ZpFP(5,4).convert_map_from(QQ) + sage: f(1/7) + 3 + 3*5 + 2*5^3 + sage: f(0/1) + 0 + """ + if mpq_sgn((x).value) == 0: + return self._zero + cdef FPElement ans = self._zero._new_c() + ans.ordp = cconv_mpq_t(ans.unit, (x).value, ans.prime_pow.prec_cap, False, ans.prime_pow) + if ans.ordp < 0: + raise ValueError("p divides the denominator") + return ans + + cpdef Element _call_with_args(self, x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in (relative or absolute or both). + + INPUT: + + - ``x`` -- a Rational + + - ``absprec``, or the first positional argument -- the maximum + absolute precision (unused for floating point elements). + + - ``relprec``, or the second positional argument -- the + maximum relative precision (unused for floating point + elements) + + EXAMPLES:: + + sage: R = ZpFP(5,4) + sage: type(R(1/7,2)) + + sage: R(1/7,2) + 3 + 3*5 + sage: R(1/7,3,2) + 3 + 3*5 + sage: R(1/7,absprec=2) + 3 + 3*5 + sage: R(5/7,relprec=2) + 3*5 + 3*5^2 + sage: R(1/7,absprec=1) + 3 + sage: R(1/7,empty=True) + 0 + """ + cdef long val, aprec, rprec + if mpq_sgn((x).value) == 0: + return self._zero + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, self._zero.prime_pow) + val = get_ordp(x, self._zero.prime_pow) + rprec = min(rprec, aprec - val) + if rprec <= 0: + return self._zero + cdef FPElement ans = self._zero._new_c() + ans.ordp = cconv_mpq_t(ans.unit, (x).value, rprec, False, ans.prime_pow) + if ans.ordp < 0: + raise ValueError("p divides the denominator") + return ans + +def unpickle_fpe_v2(cls, parent, unit, ordp): + """ + Unpickles a floating point element. + + EXAMPLES:: + + sage: from sage.rings.padics.padic_floating_point_element import pAdicFloatingPointElement, unpickle_fpe_v2 + sage: R = ZpFP(5) + sage: a = unpickle_fpe_v2(pAdicFloatingPointElement, R, 17, 2); a + 2*5^2 + 3*5^3 + sage: a.parent() is R + True + """ + cdef FPElement ans = cls.__new__(cls) + ans._parent = parent + ans.prime_pow = parent.prime_pow + cconstruct(ans.unit, ans.prime_pow) + cunpickle(ans.unit, unit, ans.prime_pow) + ans.ordp = ordp + return ans diff --git a/src/sage/rings/padics/FP_template_header.pxi b/src/sage/rings/padics/FP_template_header.pxi new file mode 100644 index 00000000000..6a991d3c85a --- /dev/null +++ b/src/sage/rings/padics/FP_template_header.pxi @@ -0,0 +1,55 @@ +""" +This file provides the declaration for ``FPElement`` and the morphisms +to and from the integers and rationals. + +It is included in the .pxd files associated to gluing files, such as +padic_floating_point_element.pxd. + +AUTHORS: + +- David Roe (2012-03-01) -- initial version +""" + +#***************************************************************************** +# Copyright (C) 2012 David Roe +# William Stein +# +# 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/ +#***************************************************************************** + +# includes the header for pAdicTemplateElement +include "padic_template_element_header.pxi" + +from sage.categories.morphism cimport Morphism +from sage.rings.morphism cimport RingHomomorphism_coercion, RingMap + +cdef class FPElement(pAdicTemplateElement): + cdef celement unit + cdef long ordp + + cdef FPElement _new_c(self) + cdef int _normalize(self) except -1 + cdef int _set_infinity(self) except -1 + +cdef class pAdicCoercion_ZZ_FP(RingHomomorphism_coercion): + cdef FPElement _zero + cdef RingMap _section +cdef class pAdicConvert_FP_ZZ(RingMap): + pass +cdef class pAdicCoercion_QQ_FP(RingHomomorphism_coercion): + cdef FPElement _zero + cdef RingMap _section +cdef class pAdicConvert_FP_QQ(RingMap): + pass +cdef class pAdicConvert_QQ_FP(Morphism): + cdef FPElement _zero + cdef RingMap _section +cdef class pAdicCoercion_FP_frac_field(RingHomomorphism_coercion): + cdef FPElement _zero + cdef Morphism _section +cdef class pAdicConvert_FP_frac_field(Morphism): + cdef FPElement _zero diff --git a/src/sage/rings/padics/all.py b/src/sage/rings/padics/all.py index 7fe2a73483d..809b4aafe9f 100644 --- a/src/sage/rings/padics/all.py +++ b/src/sage/rings/padics/all.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from .generic_nodes import is_pAdicField, is_pAdicRing -from .factory import Zp, Zq, Zp as pAdicRing, ZpCR, ZpCA, ZpFM, ZqCR, ZqCA, ZqFM #, ZpL, ZqL -from .factory import Qp, Qq, Qp as pAdicField, QpCR, QqCR #, QpL, QqL +from .factory import Zp, Zq, Zp as pAdicRing, ZpCR, ZpCA, ZpFM, ZpFP, ZqCR, ZqCA, ZqFM, ZqFP #, ZpL, ZqL +from .factory import Qp, Qq, Qp as pAdicField, QpCR, QpFP, QqCR, QqFP #, QpL, QqL from .factory import pAdicExtension from .padic_generic import local_print_mode from .pow_computer import PowComputer diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 3e2bd5b8ac3..41fc26b10c3 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -30,7 +30,9 @@ from .padic_base_leaves import (pAdicRingCappedRelative, pAdicRingCappedAbsolute, pAdicRingFixedMod, - pAdicFieldCappedRelative) + pAdicRingFloatingPoint, + pAdicFieldCappedRelative, + pAdicFieldFloatingPoint) from . import padic_printing ###################################################### @@ -49,6 +51,8 @@ ext_table['e', pAdicRingCappedAbsolute] = EisensteinExtensionRingCappedAbsolute ext_table['e', pAdicRingCappedRelative] = EisensteinExtensionRingCappedRelative ext_table['e', pAdicRingFixedMod] = EisensteinExtensionRingFixedMod +#ext_table['e', pAdicRingFloatingPoint] = EisensteinExtensionRingFloatingPoint +#ext_table['e', pAdicFieldFloatingPoint] = EisensteinExtensionFieldFloatingPoint #ext_table['e', pAdicRingLazy] = EisensteinExtensionRingLazy #ext_table['p', pAdicFieldCappedRelative] = pAdicGeneralExtensionFieldCappedRelative #ext_table['p', pAdicFieldLazy] = pAdicGeneralExtensionFieldLazy @@ -61,6 +65,8 @@ ext_table['u', pAdicRingCappedAbsolute] = UnramifiedExtensionRingCappedAbsolute ext_table['u', pAdicRingCappedRelative] = UnramifiedExtensionRingCappedRelative ext_table['u', pAdicRingFixedMod] = UnramifiedExtensionRingFixedMod +ext_table['u', pAdicRingFloatingPoint] = UnramifiedExtensionRingFloatingPoint +ext_table['u', pAdicFieldFloatingPoint] = UnramifiedExtensionFieldFloatingPoint #ext_table['u', pAdicRingLazy] = UnramifiedExtensionRingLazy @@ -185,7 +191,7 @@ class Qp_class(UniqueFactory): TYPES and PRECISION below. - ``type`` -- string (default: ``'capped-rel'``) Valid types are - ``'capped-rel'`` and ``'lazy'`` (though ``'lazy'`` currently + ``'capped-rel'``, ``'floating-point'`` and ``'lazy'`` (though ``'lazy'`` currently doesn't work). See TYPES and PRECISION below - ``print_mode`` -- string (default: ``None``). Valid modes are 'series', @@ -236,8 +242,8 @@ class Qp_class(UniqueFactory): sage: a.precision_absolute() 22 - There are two types of `p`-adic fields: capped relative fields and - lazy fields. + There are three types of `p`-adic fields: capped relative fields, + floating point fields and lazy fields. In the capped relative case, the relative precision of an element is restricted to be at most a certain value, specified at the @@ -254,6 +260,10 @@ class Qp_class(UniqueFactory): sage: a + b 1 + 5 + 5^2 + 4*5^3 + 2*5^4 + O(5^5) + In the floating point case, elements do not track their + precision, but the relative precision of elements is truncated + during arithmetic to the precision cap of the field. + The lazy case will eventually support elements that can increase their precision upon request. It is not currently implemented. @@ -472,7 +482,7 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N sage: Qp.create_key(5,40) (5, 40, 'capped-rel', 'series', '5', True, '|', (), -1) """ - return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, check, ['capped-rel']) + return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, check, ['capped-rel', 'floating-point']) def create_object(self, version, key): """ @@ -514,6 +524,13 @@ def create_object(self, version, key): else: return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + elif type == 'floating-point': + if print_mode == 'terse': + return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_terse_terms': print_max_terms, 'show_prec': False}, name) + else: + return pAdicFieldFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': False}, name) else: raise ValueError("unexpected type") @@ -545,8 +562,8 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, TYPES and PRECISION below. - ``type`` -- string (default: ``'capped-rel'``) Valid types are - ``'capped-rel'`` and ``'lazy'`` (though ``'lazy'`` doesn't currently work). - See TYPES and PRECISION below + ``'capped-rel'``, ``'floating-point'`` and ``'lazy'`` (though ``'lazy'`` + doesn't currently work). See TYPES and PRECISION below - ``modulus`` -- polynomial (default ``None``) A polynomial defining an unramified extension of `\mathbb{Q}_p`. See MODULUS below. @@ -608,8 +625,8 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: b.precision_absolute() 22 - There are two types of unramified `p`-adic fields: capped relative - fields and lazy fields. + There are three types of unramified `p`-adic fields: capped relative + fields, floating point fields and lazy fields. In the capped relative case, the relative precision of an element is restricted to be at most a certain value, specified at the @@ -626,6 +643,10 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: b + c 2 + (2*a + 2)*3 + (2*a + 2)*3^2 + 3^4 + O(3^5) + In the floating point case, elements do not track their + precision, but the relative precision of elements is truncated + during arithmetic to the precision cap of the field. + The lazy case will eventually support elements that can increase their precision upon request. It is not currently implemented. @@ -1043,6 +1064,23 @@ def QpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, type = 'capped-rel') +def QpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_terms = None, check=True): + """ + A shortcut function to create floating point `p`-adic fields. + + Same functionality as ``Qp``. See documentation for ``Qp`` for a + description of the input parameters. + + EXAMPLES:: + + sage: QpFP(5, 40) + 5-adic Field with floating precision 40 + """ + return Qp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, + print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, + type = 'floating-point') + #def QpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): # """ @@ -1079,6 +1117,27 @@ def QqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, check=check, implementation=implementation, type = 'capped-rel') +def QqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, + print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_ram_terms = None, + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + """ + A shortcut function to create floating point unramified `p`-adic + fields. + + Same functionality as ``Qq``. See documentation for ``Qq`` for a + description of the input parameters. + + EXAMPLES:: + + sage: R. = QqFP(25, 40); R + Unramified Extension of 5-adic Field with floating precision 40 in a defined by x^2 + 4*x + 2 + """ + return Qq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, + halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'floating-point') + #def QqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_ram_terms = None, @@ -1119,9 +1178,9 @@ class Zp_class(UniqueFactory): below. - ``type`` -- string (default: ``'capped-rel'``) Valid types are - ``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'`` and - ``'lazy'`` (though lazy is not yet implemented). See TYPES and - PRECISION below + ``'capped-rel'``, ``'capped-abs'``, ``'fixed-mod'``, + ``'floating-point'`` and ``'lazy'`` (though lazy is not yet + implemented). See TYPES and PRECISION below - ``print_mode`` -- string (default: ``None``). Valid modes are ``'series'``, ``'val-unit'``, ``'terse'``, ``'digits'``, and @@ -1170,9 +1229,10 @@ class Zp_class(UniqueFactory): sage: a.precision_absolute() 22 - There are four types of `p`-adic rings: capped relative rings + There are five types of `p`-adic rings: capped relative rings (type= ``'capped-rel'``), capped absolute rings - (type= ``'capped-abs'``), fixed modulus ring (type= ``'fixed-mod'``) + (type= ``'capped-abs'``), fixed modulus rings (type= ``'fixed-mod'``), + floating point rings (type=``'floating-point'``), and lazy rings (type= ``'lazy'``). In the capped relative case, the relative precision of an element @@ -1216,6 +1276,10 @@ class Zp_class(UniqueFactory): sage: a // 5 1 + 2*5^2 + 5^3 + O(5^5) + The floating point case is similar to the fixed modulus type + in that elements do not trac their own precision. However, relative + precision is truncated with each operation rather than absolute precision. + The lazy case will eventually support elements that can increase their precision upon request. It is not currently implemented. @@ -1471,7 +1535,7 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1) """ return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, - print_max_terms, check, ['capped-rel', 'fixed-mod', 'capped-abs']) + print_max_terms, check, ['capped-rel', 'fixed-mod', 'capped-abs', 'floating-point']) def create_object(self, version, key): """ @@ -1515,6 +1579,9 @@ def create_object(self, version, key): elif type == 'capped-abs': return pAdicRingCappedAbsolute(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + elif type == 'floating-point': + return pAdicRingFloatingPoint(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms, 'show_prec': False}, name) else: raise ValueError("unexpected type") @@ -1543,8 +1610,9 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, field. Individual elements keep track of their own precision. See TYPES and PRECISION below. - - ``type`` -- string (default: ``'capped-rel'``) Valid types are - ``'capped-rel'`` and ``'lazy'`` (though ``'lazy'`` doesn't + - ``type`` -- string (default: ``'capped-abs'``) Valid types are + ``'capped-abs'``, ``'capped-rel'``, ``'fixed-mod'``, + ``'floating-point'`` and ``'lazy'`` (though ``'lazy'`` doesn't currently work). See TYPES and PRECISION below - modulus -- polynomial (default None) A polynomial defining an @@ -1611,8 +1679,11 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: b.precision_absolute() 22 - There are four types of unramified `p`-adic rings: capped relative - rings, capped absolute rings, fixed modulus rings, and lazy rings. + There are five types of `p`-adic rings: capped relative rings + (type= ``'capped-rel'``), capped absolute rings + (type= ``'capped-abs'``), fixed modulus rings (type= ``'fixed-mod'``), + floating point rings (type=``'floating-point'``), + and lazy rings (type= ``'lazy'``). In the capped relative case, the relative precision of an element is restricted to be at most a certain value, specified at the @@ -1661,6 +1732,10 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, sage: b*c >> 1 2*3^2 + (2*a + 2)*3^3 + O(3^5) + The floating point case is similar to the fixed modulus type + in that elements do not trac their own precision. However, relative + precision is truncated with each operation rather than absolute precision. + The lazy case will eventually support elements that can increase their precision upon request. It is not currently implemented. @@ -1984,6 +2059,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, True sage: Zq(125.factor(), names="alpha") is R True + """ if check: if isinstance(q, Factorization) or isinstance(q, (list, tuple)): @@ -2095,6 +2171,23 @@ def ZpFM(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, type = 'fixed-mod') +def ZpFP(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_terms = None, check=True): + """ + A shortcut function to create floating point `p`-adic rings. + + Same functionality as ``Zp``. See documentation for ``Zp`` for a + description of the input parameters. + + EXAMPLES:: + + sage: ZpFP(5, 40) + 5-adic Ring with floating precision 40 + """ + return Zp(p=p, prec=prec, print_mode=print_mode, halt=halt, check=check, names=names, + print_pos=print_pos, print_sep=print_sep, print_alphabet=print_alphabet, print_max_terms=print_max_terms, + type = 'floating-point') + #def ZpL(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_terms = None, check=True): # """ @@ -2167,6 +2260,26 @@ def ZqFM(q, prec = DEFAULT_PREC, modulus = None, names=None, print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, check=check, implementation=implementation, type = 'fixed-mod') +def ZqFP(q, prec = DEFAULT_PREC, modulus = None, names=None, + print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_ram_terms = None, + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): + """ + A shortcut function to create floating point unramified `p`-adic rings. + + Same functionality as ``Zq``. See documentation for ``Zq`` for a + description of the input parameters. + + EXAMPLES:: + + sage: R. = ZqFP(25, 40); R + Unramified Extension of 5-adic Ring with floating precision 40 in a defined by x^2 + 4*x + 2 + """ + return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, + halt=halt, ram_name=ram_name, print_pos=print_pos, print_max_ram_terms=print_max_ram_terms, + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'floating-point') + #def ZqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, # print_sep = None, print_alphabet = None, print_max_ram_terms = None, @@ -2392,10 +2505,11 @@ def create_object(self, version, key, shift_seed): if polytype == 'u' or polytype == 'e': (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, implementation) = key + show_prec = base._printer._show_prec() T = ext_table[polytype, type(base.ground_ring_of_tower()).__base__] return T(premodulus, modulus, prec, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, - 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, + 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms, 'show_prec': show_prec}, shift_seed, names, implementation) elif polytype == 'p': (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 6f941a9f997..de24571fb81 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -27,6 +27,7 @@ from sage.rings.padics.padic_base_generic import pAdicBaseGeneric from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ +from sage.rings.infinity import infinity, SignError class CappedAbsoluteGeneric(LocalGeneric): def is_capped_absolute(self): @@ -140,6 +141,122 @@ def _prec_type(self): """ return 'fixed-mod' +class FloatingPointGeneric(LocalGeneric): + def is_floating_point(self): + """ + Returns whether this `p`-adic ring uses a floating point precision model. + + Elements in the floating point model are stored by giving a + valuation and a unit part. Arithmetic is done where the unit + part is truncated modulo a fixed power of the uniformizer, + stored in the precision cap of the parent. + + EXAMPLES:: + + sage: R = ZpFP(5,15) + sage: R.is_floating_point() + True + sage: R(5^7,absprec=9) + 5^7 + sage: S = ZpCR(5,15) + sage: S.is_floating_point() + False + sage: S(5^7,absprec=9) + 5^7 + O(5^9) + """ + return True + + def _prec_type(self): + """ + Returns the precision handling type. + + EXAMPLES:: + + sage: ZpFP(5)._prec_type() + 'floating-point' + """ + return 'floating-point' + + def _test_distributivity(self, **options): + r""" + Test the distributivity of `*` on `+` on (not necessarily + all) elements of this set. + + p-adic floating point rings only satisfy distributivity + up to a precision that depends on the elements. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + By default, this method runs the tests only on the + elements returned by ``self.some_elements()``:: + + sage: R = ZpFP(5,3) + sage: R.some_elements() + [0, 1, 5, 1 + 3*5 + 3*5^2, 5 + 4*5^2 + 4*5^3] + sage: R._test_distributivity() + + However, the elements tested can be customized with the + ``elements`` keyword argument:: + + sage: R._test_distributivity(elements=[R(0),~R(0),R(42)]) + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + S = tester.some_elements() + from sage.misc.misc import some_tuples + for x,y,z in some_tuples(S, 3, tester._max_runs): + yz_prec = min(y.precision_absolute(), z.precision_absolute()) + yz_val = (y + z).valuation() + try: + prec = min(x.valuation() + yz_val + min(x.precision_relative(), yz_prec - yz_val), + x.valuation() + y.valuation() + (x * y).precision_relative(), + x.valuation() + z.valuation() + (x * z).precision_relative()) + except SignError: + pass + else: + if prec > -infinity: + # only check left distributivity, since multiplication commutative + tester.assert_((x * (y + z)).is_equal_to((x * y) + (x * z),prec)) + + def _test_additive_associativity(self, **options): + r""" + Test associativity for (not necessarily all) elements of this + additive semigroup. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + By default, this method tests only the elements returned by + ``self.some_elements()``:: + + sage: R = QpFP(7,3) + sage: R._test_additive_associativity() + + However, the elements tested can be customized with the + ``elements`` keyword argument:: + + sage: R._test_additive_associativity(elements = [R(0), ~R(0), R(42)]) + + See the documentation for :class:`TestSuite` for more information. + """ + tester = self._tester(**options) + S = tester.some_elements() + from sage.misc.misc import some_tuples + for x,y,z in some_tuples(S, 3, tester._max_runs): + tester.assert_(((x + y) + z).is_equal_to(x + (y + z), min(x.precision_absolute(), y.precision_absolute(), z.precision_absolute()))) + +class FloatingPointRingGeneric(FloatingPointGeneric): + pass +class FloatingPointFieldGeneric(FloatingPointGeneric):#, sage.rings.ring.Field): + pass class CappedRelativeRingGeneric(CappedRelativeGeneric): pass class CappedRelativeFieldGeneric(CappedRelativeGeneric):#, sage.rings.ring.Field): @@ -247,6 +364,10 @@ class pAdicCappedRelativeRingGeneric(pAdicRingGeneric, CappedRelativeRingGeneric pass class pAdicCappedRelativeFieldGeneric(pAdicFieldGeneric, CappedRelativeFieldGeneric): pass +class pAdicFloatingPointRingGeneric(pAdicRingGeneric, FloatingPointRingGeneric): + pass +class pAdicFloatingPointFieldGeneric(pAdicFieldGeneric, FloatingPointFieldGeneric): + pass class pAdicRingBaseGeneric(pAdicBaseGeneric, pAdicRingGeneric): def construction(self): diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 8fbaef3d9d0..cd9cfb749c4 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -138,6 +138,31 @@ def is_fixed_mod(self): """ return False + def is_floating_point(self): + """ + Returns whether this `p`-adic ring bounds precision in a floating + point fashion. + + The relative precision of an element is the power of `p` + modulo which the unit part of that element is defined. In a + floating point ring, elements do not store precision, but arithmetic + operations truncate to a relative precision depending on the ring. + + EXAMPLES:: + + sage: R = ZpCR(5, 15) + sage: R.is_floating_point() + False + sage: R(5^7) + 5^7 + O(5^22) + sage: S = ZpFP(5, 15) + sage: S.is_floating_point() + True + sage: S(5^7) + 5^7 + """ + return False + def is_lazy(self): """ Returns whether this `p`-adic ring bounds precision in a lazy diff --git a/src/sage/rings/padics/morphism.pyx b/src/sage/rings/padics/morphism.pyx index 3e33397c04d..7accf763fd8 100644 --- a/src/sage/rings/padics/morphism.pyx +++ b/src/sage/rings/padics/morphism.pyx @@ -18,8 +18,8 @@ from sage.rings.infinity import Infinity from sage.rings.ring import CommutativeRing from sage.categories.homset import Hom from sage.structure.element cimport Element -from sage.structure.sage_object cimport (richcmp, rich_to_bool, - richcmp_not_equal) +from sage.structure.richcmp cimport (richcmp, rich_to_bool, + richcmp_not_equal) from sage.rings.morphism cimport RingHomomorphism from padic_generic import pAdicGeneric 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 ffce738e1c2..1c13338a9f0 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -159,8 +159,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from cysignals.signals cimport sig_on, sig_off from sage.ext.stdsage cimport PY_NEW -include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" from sage.rings.integer cimport Integer 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 bc4c5e68068..a9250cc8661 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -183,8 +183,8 @@ AUTHORS: #***************************************************************************** from __future__ import print_function +from cysignals.signals cimport sig_on, sig_off from sage.ext.stdsage cimport PY_NEW -include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" from sage.rings.integer cimport Integer diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index ab38b2d93a7..8f61546db70 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -25,6 +25,7 @@ from sage.rings.padics.padic_capped_relative_element import pAdicCoercion_ZZ_CR, pAdicCoercion_QQ_CR, pAdicConvert_QQ_CR from sage.rings.padics.padic_capped_absolute_element import pAdicCoercion_ZZ_CA, pAdicConvert_QQ_CA from sage.rings.padics.padic_fixed_mod_element import pAdicCoercion_ZZ_FM, pAdicConvert_QQ_FM +from sage.rings.padics.padic_floating_point_element import pAdicCoercion_ZZ_FP, pAdicCoercion_QQ_FP, pAdicConvert_QQ_FP class pAdicBaseGeneric(pAdicGeneric): _implementation = 'GMP' @@ -39,8 +40,12 @@ def __init__(self, p, prec, print_mode, names, element_class): self.prime_pow = PowComputer(p, max(min(prec - 1, 30), 1), prec, self.is_field(), self._prec_type()) pAdicGeneric.__init__(self, self, p, prec, print_mode, names, element_class) if self.is_field(): - coerce_list = [pAdicCoercion_ZZ_CR(self), pAdicCoercion_QQ_CR(self)] - convert_list = [] + if self.is_capped_relative(): + coerce_list = [pAdicCoercion_ZZ_CR(self), pAdicCoercion_QQ_CR(self)] + convert_list = [] + else: + coerce_list = [pAdicCoercion_ZZ_FP(self), pAdicCoercion_QQ_FP(self)] + convert_list = [] elif self.is_capped_relative(): coerce_list = [pAdicCoercion_ZZ_CR(self)] convert_list = [pAdicConvert_QQ_CR(self)] @@ -50,6 +55,9 @@ def __init__(self, p, prec, print_mode, names, element_class): elif self.is_fixed_mod(): coerce_list = [pAdicCoercion_ZZ_FM(self)] convert_list = [pAdicConvert_QQ_FM(self)] + elif self.is_floating_point(): + coerce_list = [pAdicCoercion_ZZ_FP(self)] + convert_list = [pAdicConvert_QQ_FP(self)] else: raise RuntimeError self._populate_coercion_lists_(coerce_list=coerce_list, convert_list=convert_list, element_constructor=element_class) @@ -78,7 +86,11 @@ def fraction_field(self, print_mode=None): if self.is_field() and print_mode is None: return self from sage.rings.padics.factory import Qp - return Qp(self.prime(), self.precision_cap(), 'capped-rel', print_mode=self._modified_print_mode(print_mode), names=self._uniformizer_print()) + if self.is_floating_point(): + mode = 'floating-point' + else: + mode = 'capped-rel' + return Qp(self.prime(), self.precision_cap(), mode, print_mode=self._modified_print_mode(print_mode), names=self._uniformizer_print()) def integer_ring(self, print_mode=None): r""" diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 376c85919a7..968d6e593bb 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -188,17 +188,20 @@ class names.:: # # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.structure.sage_object import op_LE +from sage.structure.richcmp import op_LE from .generic_nodes import pAdicFieldBaseGeneric, \ pAdicCappedRelativeFieldGeneric, \ pAdicRingBaseGeneric, \ pAdicCappedRelativeRingGeneric, \ pAdicFixedModRingGeneric, \ - pAdicCappedAbsoluteRingGeneric + pAdicCappedAbsoluteRingGeneric, \ + pAdicFloatingPointRingGeneric, \ + pAdicFloatingPointFieldGeneric from .padic_capped_relative_element import pAdicCappedRelativeElement from .padic_capped_absolute_element import pAdicCappedAbsoluteElement from .padic_fixed_mod_element import pAdicFixedModElement +from .padic_floating_point_element import pAdicFloatingPointElement from sage.rings.integer_ring import ZZ class pAdicRingCappedRelative(pAdicRingBaseGeneric, pAdicCappedRelativeRingGeneric): @@ -384,6 +387,94 @@ def _coerce_map_from_(self, R): self._printer.richcmp_modes(R._printer, op_LE)): return True +class pAdicRingFloatingPoint(pAdicRingBaseGeneric, pAdicFloatingPointRingGeneric): + r""" + An implementation of the `p`-adic integers with floating point + precision. + """ + def __init__(self, p, prec, print_mode, names): + """ + Initialization. + + INPUT: + + - ``p`` -- prime + - ``prec`` -- precision cap + - ``print_mode`` -- dictionary with print options. + - ``names`` -- how to print the prime. + + EXAMPLES:: + + sage: R = ZpFP(next_prime(10^60)) #indirect doctest + sage: type(R) + + + TESTS:: + + sage: R = ZpFP(2) + sage: TestSuite(R).run() + sage: TestSuite(R).run(elements = [R.random_element() for i in range(2^10)], max_runs = 2^12) # long time + + sage: R = ZpFP(3, 1) + sage: TestSuite(R).run(elements = [R.random_element() for i in range(3^3)]) + + sage: R = ZpFP(3, 2) + sage: TestSuite(R).run(elements = [R.random_element() for i in range(3^6)]) # long time + + sage: R = ZpFP(next_prime(10^60)) + sage: TestSuite(R).run() + sage: TestSuite(R).run(elements = [R.random_element() for i in range(2^4)], max_runs = 2^6) # long time + + """ + pAdicRingBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicFloatingPointElement) + + def _coerce_map_from_(self, R): + """ + Returns ``True`` if there is a coerce map from ``R`` to ``self``. + + EXAMPLES:: + + sage: K = ZpFP(17) + sage: K(1) + 1 #indirect doctest + 2 + sage: K.has_coerce_map_from(ZZ) + True + sage: K.has_coerce_map_from(int) + True + sage: K.has_coerce_map_from(QQ) + False + sage: K.has_coerce_map_from(RR) + False + sage: K.has_coerce_map_from(Qp(7)) + False + sage: K.has_coerce_map_from(Zp(17,40)) + False + sage: K.has_coerce_map_from(Zp(17,10)) + False + sage: K.has_coerce_map_from(ZpCA(17,40)) + False + """ + if isinstance(R, pAdicRingFloatingPoint) and R.prime() == self.prime(): + if R.precision_cap() > self.precision_cap(): + return True + elif R.precision_cap() == self.precision_cap() and self._printer.richcmp_modes(R._printer, op_LE): + return True + + def _repr_(self, do_latex=False): + r""" + Print representation. + + EXAMPLES:: + + sage: K = ZpFP(17); K #indirect doctest + 17-adic Ring with floating precision 20 + sage: latex(K) + \ZZ_{17} + """ + if do_latex: + return "\\ZZ_{%s}" % self.prime() + return "%s-adic Ring with floating precision %s"%(self.prime(), self.precision_cap()) + class pAdicRingFixedMod(pAdicRingBaseGeneric, pAdicFixedModRingGeneric): r""" An implementation of the `p`-adic integers using fixed modulus. @@ -620,3 +711,93 @@ def random_element(self, algorithm='default'): return self(self.prime()**k * a, absprec = k + self.precision_cap()) else: raise NotImplementedError("Don't know %s algorithm"%algorithm) + +class pAdicFieldFloatingPoint(pAdicFieldBaseGeneric, pAdicFloatingPointFieldGeneric): + r""" + An implementation of the `p`-adic rationals with floating point + precision. + """ + def __init__(self, p, prec, print_mode, names): + """ + Initialization. + + INPUT: + + - ``p`` -- prime + - ``prec`` -- precision cap + - ``print_mode`` -- dictionary with print options. + - ``names`` -- how to print the prime. + + EXAMPLES:: + + sage: R = QpFP(next_prime(10^60)) #indirect doctest + sage: type(R) + + + TESTS:: + + sage: R = QpFP(2) + sage: TestSuite(R).run() + sage: TestSuite(R).run(elements = [R.random_element() for i in range(2^10)], max_runs = 2^12) # long time + + sage: R = QpFP(3, 1) + sage: TestSuite(R).run(elements = [R.random_element() for i in range(3^3)]) + + sage: R = QpFP(3, 2) + sage: TestSuite(R).run(elements = [R.random_element() for i in range(3^6)]) # long time + + sage: R = QpFP(next_prime(10^60)) + sage: TestSuite(R).run() + sage: TestSuite(R).run(elements = [R.random_element() for i in range(2^4)], max_runs = 2^6) # long time + + """ + pAdicFieldBaseGeneric.__init__(self, p, prec, print_mode, names, pAdicFloatingPointElement) + + def _coerce_map_from_(self, R): + """ + Returns ``True`` if there is a coerce map from ``R`` to ``self``. + + EXAMPLES:: + + sage: K = QpFP(17) + sage: K(1) + 1 #indirect doctest + 2 + sage: K.has_coerce_map_from(ZZ) + True + sage: K.has_coerce_map_from(int) + True + sage: K.has_coerce_map_from(QQ) + True + sage: K.has_coerce_map_from(RR) + False + sage: K.has_coerce_map_from(Qp(7)) + False + sage: K.has_coerce_map_from(Zp(17,40)) + False + sage: K.has_coerce_map_from(Qp(17,10)) + False + sage: K.has_coerce_map_from(ZpFP(17)) + True + sage: K.has_coerce_map_from(ZpCA(17,40)) + False + """ + if isinstance(R, (pAdicRingFloatingPoint, pAdicFieldFloatingPoint)) and R.prime() == self.prime(): + if R.precision_cap() > self.precision_cap(): + return True + elif R.precision_cap() == self.precision_cap() and self._printer.richcmp_modes(R._printer, op_LE): + return True + + def _repr_(self, do_latex=False): + r""" + Print representation. + + EXAMPLES:: + + sage: K = QpFP(17); K #indirect doctest + 17-adic Field with floating precision 20 + sage: latex(K) + \QQ_{17} + """ + if do_latex: + return "\\QQ_{%s}" % self.prime() + return "%s-adic Field with floating precision %s"%(self.prime(), self.precision_cap()) diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index c5454415af8..09b616f9d0b 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -27,6 +27,9 @@ include "CA_template.pxi" from sage.libs.pari.convert_gmp cimport new_gen_from_padic from sage.rings.finite_rings.integer_mod import Mod +cdef extern from "sage/rings/padics/transcendantal.c": + cdef void padiclog(mpz_t ans, const mpz_t a, unsigned long p, unsigned long prec, const mpz_t modulo) + cdef class PowComputer_(PowComputer_base): """ A PowComputer for a capped-absolute padic ring. @@ -280,6 +283,86 @@ cdef class pAdicCappedAbsoluteElement(CAElement): mpz_clear(ppow_minus_one) return infinity + def _log_binary_splitting(self, aprec, mina=0): + r""" + Return ``\log(self)`` for ``self`` equal to 1 in the residue field + + This is a helper method for :meth:`log`. + It uses a fast binary splitting algorithm. + + INPUT: + + - ``aprec`` -- an integer, the precision to which the result is + correct. ``aprec`` must not exceed the precision cap of the ring over + which this element is defined. + - ``mina`` -- an integer (default: 0), the series will check `n` up to + this valuation (and beyond) to see if they can contribute to the + series. + + NOTE:: + + The function does not check that its argument ``self`` is + 1 in the residue field. If this assumption is not fullfiled + the behaviour of the function is not specified. + + ALGORITHM: + + 1. Raise `u` to the power `p^v` for a suitable `v` in order + to make it closer to 1. (`v` is chosen such that `p^v` is + close to the precision.) + + 2. Write + + .. MATH:: + + u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) + + with `0 \leq a_i < p^{(v+1)*2^i}` and compute + `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion + + .. MATH:: + + \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + + together with a binary splitting method. + + 3. Divide the result by `p^v` + + The complexity of this algorithm is quasi-linear. + + EXAMPLES:: + + sage: r = Qp(5,prec=4)(6) + sage: r._log_binary_splitting(2) + 5 + O(5^2) + sage: r._log_binary_splitting(4) + 5 + 2*5^2 + 4*5^3 + O(5^4) + sage: r._log_binary_splitting(100) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + sage: r = Zp(5,prec=4,type='fixed-mod')(6) + sage: r._log_binary_splitting(5) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + """ + cdef unsigned long p + cdef unsigned long prec = min(aprec, self.absprec) + cdef pAdicCappedAbsoluteElement ans, unit + + if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: + raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + p = self.prime_pow.prime + + ans = self._new_c() + ans.absprec = prec + unit = self.unit_part() + sig_on() + padiclog(ans.value, unit.value, p, prec, self.prime_pow.pow_mpz_t_tmp(prec)) + sig_off() + + return ans + + def make_pAdicCappedAbsoluteElement(parent, x, absprec): """ Unpickles a capped absolute element. diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index 60e8c5d0391..8206656f334 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -29,6 +29,9 @@ from sage.libs.pari.convert_gmp cimport new_gen_from_padic from sage.rings.finite_rings.integer_mod import Mod from sage.rings.padics.pow_computer cimport PowComputer_class +cdef extern from "sage/rings/padics/transcendantal.c": + cdef void padiclog(mpz_t ans, const mpz_t a, unsigned long p, unsigned long prec, const mpz_t modulo) + cdef class PowComputer_(PowComputer_base): """ A PowComputer for a capped-relative padic ring or field. @@ -332,6 +335,86 @@ cdef class pAdicCappedRelativeElement(CRElement): mpz_mul(selfvalue.value, self.prime_pow.pow_mpz_t_tmp(self.ordp), self.unit) return Mod(selfvalue, modulus) + def _log_binary_splitting(self, aprec, mina=0): + r""" + Return ``\log(self)`` for ``self`` equal to 1 in the residue field + + This is a helper method for :meth:`log`. + It uses a fast binary splitting algorithm. + + INPUT: + + - ``aprec`` -- an integer, the precision to which the result is + correct. ``aprec`` must not exceed the precision cap of the ring over + which this element is defined. + - ``mina`` -- an integer (default: 0), the series will check `n` up to + this valuation (and beyond) to see if they can contribute to the + series. + + NOTE:: + + The function does not check that its argument ``self`` is + 1 in the residue field. If this assumption is not fullfiled + the behaviour of the function is not specified. + + ALGORITHM: + + 1. Raise `u` to the power `p^v` for a suitable `v` in order + to make it closer to 1. (`v` is chosen such that `p^v` is + close to the precision.) + + 2. Write + + .. MATH:: + + u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) + + with `0 \leq a_i < p^{(v+1)*2^i}` and compute + `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion + + .. MATH:: + + \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + + together with a binary splitting method. + + 3. Divide the result by `p^v` + + The complexity of this algorithm is quasi-linear. + + EXAMPLES:: + + sage: r = Qp(5,prec=4)(6) + sage: r._log_binary_splitting(2) + 5 + O(5^2) + sage: r._log_binary_splitting(4) + 5 + 2*5^2 + 4*5^3 + O(5^4) + sage: r._log_binary_splitting(100) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + sage: r = Zp(5,prec=4,type='fixed-mod')(6) + sage: r._log_binary_splitting(5) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + """ + cdef unsigned long p + cdef unsigned long prec = min(aprec, self.relprec) + cdef pAdicCappedRelativeElement ans + + if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: + raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + p = self.prime_pow.prime + + ans = self._new_c() + ans.ordp = 0 + ans.relprec = prec + sig_on() + padiclog(ans.unit, self.unit, p, prec, self.prime_pow.pow_mpz_t_tmp(prec)) + sig_off() + ans._normalize() + + return ans + def unpickle_pcre_v1(R, unit, ordp, relprec): """ Unpickles a capped relative element. diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index d1c36d69e6e..cd904fd0010 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -22,7 +22,7 @@ from .padic_generic import pAdicGeneric from .padic_base_generic import pAdicBaseGeneric -from sage.structure.sage_object import op_EQ +from sage.structure.richcmp import op_EQ from functools import reduce diff --git a/src/sage/rings/padics/padic_extension_leaves.py b/src/sage/rings/padics/padic_extension_leaves.py index 90fbc31a8a3..bc75a58aeff 100644 --- a/src/sage/rings/padics/padic_extension_leaves.py +++ b/src/sage/rings/padics/padic_extension_leaves.py @@ -35,7 +35,9 @@ from .generic_nodes import pAdicCappedRelativeRingGeneric, \ pAdicCappedRelativeFieldGeneric, \ pAdicCappedAbsoluteRingGeneric, \ - pAdicFixedModRingGeneric + pAdicFixedModRingGeneric, \ + pAdicFloatingPointRingGeneric, \ + pAdicFloatingPointFieldGeneric #from unramified_extension_absolute_element import UnramifiedExtensionAbsoluteElement #from unramified_extension_capped_relative_element import UnramifiedExtensionCappedRelativeElement @@ -53,6 +55,7 @@ from .qadic_flint_CR import qAdicCappedRelativeElement from .qadic_flint_CA import qAdicCappedAbsoluteElement from .qadic_flint_FM import qAdicFixedModElement +from .qadic_flint_FP import qAdicFloatingPointElement def _make_integral_poly(prepoly, p, prec): """ @@ -95,9 +98,9 @@ class UnramifiedExtensionRingCappedRelative(UnramifiedExtensionGeneric, pAdicCap TESTS:: sage: R. = ZqCR(27,10000) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): """ A capped relative representation of Zq. @@ -155,9 +158,9 @@ class UnramifiedExtensionFieldCappedRelative(UnramifiedExtensionGeneric, pAdicCa TESTS:: sage: R. = QqCR(27,10000) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): """ A representation of Qq. @@ -226,9 +229,9 @@ class UnramifiedExtensionRingCappedAbsolute(UnramifiedExtensionGeneric, pAdicCap TESTS:: sage: R. = ZqCA(27,10000) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): """ A capped absolute representation of Zq. @@ -287,9 +290,9 @@ class UnramifiedExtensionRingFixedMod(UnramifiedExtensionGeneric, pAdicFixedModR TESTS:: sage: R. = ZqFM(27,10000) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): """ A fixed modulus representation of Zq. @@ -347,13 +350,116 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, imp # return Morphism_ZpFM_UnrFM(S, self) # return None +class UnramifiedExtensionRingFloatingPoint(UnramifiedExtensionGeneric, pAdicFloatingPointRingGeneric): + """ + TESTS:: + + sage: R. = ZqFP(27,10000); R == loads(dumps(R)) + True + """ + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): + """ + A floating point representation of Zq. + + INPUT: + + - prepoly -- The original polynomial defining the + extension. This could be a polynomial with integer + coefficients, for example, while poly has coefficients + in Zp. + + - poly -- The polynomial with coefficients in + self.base_ring() defining this extension. + + - prec -- The precision cap of this ring. + + - halt -- unused + + - print_mode -- A dictionary of print options. + + - shift_seed -- unused + + - names -- a 4-tuple, (variable_name, residue_name, unramified_subextension_variable_name, uniformizer_name) + + EXAMPLES:: + + sage: R. = ZqFP(27,10000); R #indirect doctest + Unramified Extension of 3-adic Ring with floating precision 10000 in a defined by x^3 + 2*x + 1 + sage: R. = ZqFP(next_prime(10^30)^3, 3); R.prime() + 1000000000000000000000000000057 + """ + self._shift_seed = None + self._pre_poly = prepoly + self._implementation = implementation + if implementation == 'NTL': + raise NotImplementedError + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = min(prec, 30) + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, True, Zpoly, prec_type='floating-point') + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, qAdicFloatingPointElement) + from .qadic_flint_FP import pAdicCoercion_ZZ_FP, pAdicConvert_QQ_FP + self.register_coercion(pAdicCoercion_ZZ_FP(self)) + self.register_conversion(pAdicConvert_QQ_FP(self)) + +class UnramifiedExtensionFieldFloatingPoint(UnramifiedExtensionGeneric, pAdicFloatingPointFieldGeneric): + """ + TESTS:: + + sage: R. = QqFP(27,10000); R == loads(dumps(R)) + True + """ + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='FLINT'): + """ + A representation of Qq. + + INPUT: + + - prepoly -- The original polynomial defining the + extension. This could be a polynomial with integer + coefficients, for example, while poly has coefficients + in Qp. + + - poly -- The polynomial with coefficients in + self.base_ring() defining this extension. + + - prec -- The precision cap of this ring. + + - halt -- unused + + - print_mode -- A dictionary of print options. + + - shift_seed -- unused + + - names -- a 4-tuple, (variable_name, residue_name, unramified_subextension_variable_name, uniformizer_name) + + EXAMPLES:: + + sage: R. = QqFP(27,10000); R #indirect doctest + Unramified Extension of 3-adic Field with floating precision 10000 in a defined by x^3 + 2*x + 1 + sage: R. = Qq(next_prime(10^30)^3, 3); R.prime() + 1000000000000000000000000000057 + """ + # Currently doesn't support polynomials with non-integral coefficients + self._shift_seed = None + self._pre_poly = prepoly + self._implementation = implementation + if implementation == 'NTL': + raise NotImplementedError + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = min(prec, 30) + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, True, Zpoly, prec_type='floating-point') + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, qAdicFloatingPointElement) + from .qadic_flint_FP import pAdicCoercion_ZZ_FP, pAdicCoercion_QQ_FP + self.register_coercion(pAdicCoercion_ZZ_FP(self)) + self.register_coercion(pAdicCoercion_QQ_FP(self)) + class EisensteinExtensionRingCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeRingGeneric): """ TESTS:: sage: R = Zp(3, 10000, print_pos=False); S. = ZZ[]; f = x^3 + 9*x - 3 sage: W. = R.ext(f) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ @@ -411,7 +517,7 @@ class EisensteinExtensionFieldCappedRelative(EisensteinExtensionGeneric, pAdicCa sage: R = Qp(3, 10000, print_pos=False); S. = ZZ[]; f = x^3 + 9*x - 3 sage: W. = R.ext(f) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ @@ -480,7 +586,7 @@ class EisensteinExtensionRingCappedAbsolute(EisensteinExtensionGeneric, pAdicCap sage: R = ZpCA(3, 10000, print_pos=False); S. = ZZ[]; f = x^3 + 9*x - 3 sage: W. = R.ext(f) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation): """ @@ -538,7 +644,7 @@ class EisensteinExtensionRingFixedMod(EisensteinExtensionGeneric, pAdicFixedModR sage: R = ZpFM(3, 10000, print_pos=False); S. = ZZ[]; f = x^3 + 9*x - 3 sage: W. = R.ext(f) - sage: TestSuite(R).run() + sage: TestSuite(R).run(skip='_test_log',max_runs=4) """ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index f0df5623903..d97c9fa350e 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -27,6 +27,9 @@ include "FM_template.pxi" from sage.libs.pari.convert_gmp cimport new_gen_from_padic from sage.rings.finite_rings.integer_mod import Mod +cdef extern from "sage/rings/padics/transcendantal.c": + cdef void padiclog(mpz_t ans, const mpz_t a, unsigned long p, unsigned long prec, const mpz_t modulo) + cdef class PowComputer_(PowComputer_base): """ A PowComputer for a fixed-modulus padic ring. @@ -354,6 +357,83 @@ cdef class pAdicFixedModElement(FMElement): mpz_clear(tmp) return infinity + def _log_binary_splitting(self, aprec, mina=0): + r""" + Return ``\log(self)`` for ``self`` equal to 1 in the residue field + + This is a helper method for :meth:`log`. + It uses a fast binary splitting algorithm. + + INPUT: + + - ``aprec`` -- an integer, the precision to which the result is + correct. ``aprec`` must not exceed the precision cap of the ring over + which this element is defined. + - ``mina`` -- an integer (default: 0), the series will check `n` up to + this valuation (and beyond) to see if they can contribute to the + series. + + NOTE:: + + The function does not check that its argument ``self`` is + 1 in the residue field. If this assumption is not fullfiled + the behaviour of the function is not specified. + + ALGORITHM: + + 1. Raise `u` to the power `p^v` for a suitable `v` in order + to make it closer to 1. (`v` is chosen such that `p^v` is + close to the precision.) + + 2. Write + + .. MATH:: + + u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) + + with `0 \leq a_i < p^{(v+1)*2^i}` and compute + `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion + + .. MATH:: + + \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + + together with a binary splitting method. + + 3. Divide the result by `p^v` + + The complexity of this algorithm is quasi-linear. + + EXAMPLES:: + + sage: r = Qp(5,prec=4)(6) + sage: r._log_binary_splitting(2) + 5 + O(5^2) + sage: r._log_binary_splitting(4) + 5 + 2*5^2 + 4*5^3 + O(5^4) + sage: r._log_binary_splitting(100) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + sage: r = Zp(5,prec=4,type='fixed-mod')(6) + sage: r._log_binary_splitting(5) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + """ + cdef unsigned long p + cdef unsigned long prec = min(aprec, self.prime_pow.prec_cap) + cdef pAdicFixedModElement ans + + if mpz_fits_slong_p(self.prime_pow.prime.value) == 0: + raise NotImplementedError("The prime %s does not fit in a long" % self.prime_pow.prime) + p = self.prime_pow.prime + + ans = self._new_c() + sig_on() + padiclog(ans.value, self.value, p, prec, self.prime_pow.pow_mpz_t_tmp(prec)) + sig_off() + return ans + + def make_pAdicFixedModElement(parent, value): """ Unpickles a fixed modulus element. diff --git a/src/sage/rings/padics/padic_floating_point_element.pxd b/src/sage/rings/padics/padic_floating_point_element.pxd new file mode 100644 index 00000000000..721c471d253 --- /dev/null +++ b/src/sage/rings/padics/padic_floating_point_element.pxd @@ -0,0 +1,13 @@ +from sage.libs.gmp.types cimport mpz_t +from cypari2.gen cimport Gen as pari_gen + +ctypedef mpz_t celement +include "FP_template_header.pxi" + +cdef class pAdicFloatingPointElement(FPElement): + cdef lift_c(self) + cdef pari_gen _to_gen(self) + +from sage.rings.padics.pow_computer cimport PowComputer_base +cdef class PowComputer_(PowComputer_base): + pass diff --git a/src/sage/rings/padics/padic_floating_point_element.pyx b/src/sage/rings/padics/padic_floating_point_element.pyx new file mode 100644 index 00000000000..7de6486c476 --- /dev/null +++ b/src/sage/rings/padics/padic_floating_point_element.pyx @@ -0,0 +1,304 @@ +""" +`p`-Adic Floating Point Elements + +Elements of `p`-Adic Rings with Floating Point Precision + +AUTHORS: + +- David Roe: initial version (2016-12-6) +""" + +#***************************************************************************** +# Copyright (C) 2016 David Roe +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# http://www.gnu.org/licenses/ +#***************************************************************************** + +include "sage/libs/linkages/padics/mpz.pxi" +include "FP_template.pxi" + +from sage.libs.pari.all import pari +from sage.libs.pari.convert_gmp cimport new_gen_from_padic +from sage.rings.finite_rings.integer_mod import Mod + +cdef class PowComputer_(PowComputer_base): + """ + A PowComputer for a floating-point padic ring or field. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field): + """ + Initialization. + + EXAMPLES:: + + sage: R = ZpFP(5) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'floating-point' + """ + self._prec_type = 'floating-point' + PowComputer_base.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field) + +cdef class pAdicFloatingPointElement(FPElement): + """ + Constructs new element with given parent and value. + + INPUT: + + - ``x`` -- value to coerce into a floating point ring or field + + - ``absprec`` -- maximum number of digits of absolute precision + + - ``relprec`` -- maximum number of digits of relative precision + + EXAMPLES:: + + sage: R = Zp(5, 10, 'floating-point') + + Construct from integers:: + + sage: R(3) + 3 + sage: R(75) + 3*5^2 + sage: R(0) + 0 + sage: R(-1) + 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + sage: R(-5) + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + sage: R(-7*25) + 3*5^2 + 3*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + 4*5^11 + + Construct from rationals:: + + sage: R(1/2) + 3 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + sage: R(-7875/874) + 3*5^3 + 2*5^4 + 2*5^5 + 5^6 + 3*5^7 + 2*5^8 + 3*5^10 + 3*5^11 + 3*5^12 + sage: R(15/425) + Traceback (most recent call last): + ... + ValueError: p divides the denominator + + Construct from IntegerMod:: + + sage: R(Integers(125)(3)) + 3 + sage: R(Integers(5)(3)) + 3 + sage: R(Integers(5^30)(3)) + 3 + sage: R(Integers(5^30)(1+5^23)) + 1 + sage: R(Integers(49)(3)) + Traceback (most recent call last): + ... + TypeError: p does not divide modulus 49 + + :: + + sage: R(Integers(48)(3)) + Traceback (most recent call last): + ... + TypeError: p does not divide modulus 48 + + Some other conversions:: + + sage: R(R(5)) + 5 + + Construct from Pari objects:: + + sage: R = ZpFP(5) + sage: x = pari(123123) ; R(x) + 3 + 4*5 + 4*5^2 + 4*5^3 + 5^4 + 4*5^5 + 2*5^6 + 5^7 + sage: R(pari(R(5252))) + 2 + 2*5^3 + 3*5^4 + 5^5 + sage: R = ZpFP(5,prec=5) + sage: R(pari(-1)) + 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + sage: pari(R(-1)) + 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + O(5^5) + sage: pari(R(0)) + 0 + sage: R(pari(R(0,5))) + 0 + + # todo: doctests for converting from other types of p-adic rings + + """ + def lift(self): + """ + Return an integer or rational congruent to ``self`` modulo ``self``'s + precision. If a rational is returned, its denominator will equal + ``p^ordp(self)``. + + This method will raise a ValueError when this element is infinity. + + EXAMPLES:: + + sage: R = Zp(7,4,'floating-point'); a = R(8); a.lift() + 8 + sage: R = QpFP(7,4); a = R(8); a.lift() + 8 + sage: R = QpFP(7,4); a = R(8/7); a.lift() + 8/7 + """ + return self.lift_c() + + cdef lift_c(self): + """ + Implementation of lift. + + TESTS:: + + sage: ZpFP(5)(0).lift() #indirect doctest + 0 + sage: R = QpFP(5); R(0).lift() + 0 + sage: R(5/9).lift() + 264909532335070 + sage: R(9/5).lift() + 9/5 + """ + cdef Integer ans + cdef Rational ansr + if self.ordp >= 0: + ans = PY_NEW(Integer) + if very_pos_val(self.ordp): + mpz_set_ui(ans.value, 0) + else: + mpz_set(ans.value, self.unit) + mpz_mul(ans.value, ans.value, self.prime_pow.pow_mpz_t_tmp(self.ordp)) + return ans + else: + ansr = Rational.__new__(Rational) + if very_neg_val(self.ordp): + raise ValueError("infinity cannot be lifted to an integer or rational") + mpz_set(mpq_numref(ansr.value), self.unit) + mpz_set(mpq_denref(ansr.value), self.prime_pow.pow_mpz_t_tmp(-self.ordp)) + return ansr + + def __pari__(self): + """ + Converts this element to an equivalent pari element. + + EXAMPLES:: + + sage: R = ZpFP(17, 10); a = ~R(14); pari(a) #indirect doctest + 11 + 3*17 + 17^2 + 6*17^3 + 13*17^4 + 15*17^5 + 10*17^6 + 3*17^7 + 17^8 + 6*17^9 + O(17^10) + sage: pari(R(0)) + 0 + """ + return self._to_gen() + + cdef pari_gen _to_gen(self): + """ + Converts this element to an equivalent pari element. + + EXAMPLES:: + + sage: R = ZpFP(5, 10); a = R(17); pari(a) #indirect doctest + 2 + 3*5 + O(5^10) + sage: pari(R(0)) + 0 + """ + if very_pos_val(self.ordp): + return pari.zero() + elif very_neg_val(self.ordp): + raise ValueError("no analogue of p-adic infinity in pari") + else: + return new_gen_from_padic(self.ordp, self.prime_pow.prec_cap, + self.prime_pow.prime.value, + self.prime_pow.pow_mpz_t_top(), + self.unit) + def _integer_(self, Z=None): + """ + Returns an integer congruent to this element modulo + ``p^self.absolute_precision()``. + + EXAMPLES:: + + sage: R = ZpFP(5); a = R(-1); a._integer_() + 95367431640624 + """ + if self.ordp < 0: + raise ValueError("Cannot form an integer out of a p-adic field element with negative valuation") + return self.lift_c() + + def residue(self, absprec=1): + """ + Reduces this element modulo `p^{\mathrm{absprec}}`. + + INPUT: + + - ``absprec`` - a non-negative integer (default: ``1``) + + OUTPUT: + + This element reduced modulo `p^\mathrm{absprec}` as an element of + `\ZZ/p^\mathrm{absprec}\ZZ` + + EXAMPLES:: + + sage: R = ZpFP(7,4) + sage: a = R(8) + sage: a.residue(1) + 1 + sage: a.residue(2) + 8 + + sage: K = QpFP(7,4) + sage: a = K(8) + sage: a.residue(1) + 1 + sage: a.residue(2) + 8 + sage: b = K(1/7) + sage: b.residue() + Traceback (most recent call last): + ... + ValueError: element must have non-negative valuation in order to compute residue. + + TESTS:: + + sage: R = ZpFP(7,4) + sage: a = R(8) + sage: a.residue(0) + 0 + sage: a.residue(-1) + Traceback (most recent call last): + ... + ValueError: cannot reduce modulo a negative power of p. + sage: a.residue(5) + Traceback (most recent call last): + ... + PrecisionError: not enough precision known in order to compute residue. + + """ + cdef Integer selfvalue, modulus + cdef long aprec + if not isinstance(absprec, Integer): + absprec = Integer(absprec) + if mpz_cmp_ui((absprec).value, self.prime_pow.prec_cap) > 0: + raise PrecisionError("not enough precision known in order to compute residue.") + elif mpz_sgn((absprec).value) < 0: + raise ValueError("cannot reduce modulo a negative power of p.") + aprec = mpz_get_ui((absprec).value) + if self.ordp < 0: + raise ValueError("element must have non-negative valuation in order to compute residue.") + modulus = PY_NEW(Integer) + mpz_set(modulus.value, self.prime_pow.pow_mpz_t_tmp(aprec)) + selfvalue = PY_NEW(Integer) + if very_pos_val(self.ordp): + mpz_set_ui(selfvalue.value, 0) + else: + # Need to do this better. + mpz_mul(selfvalue.value, self.prime_pow.pow_mpz_t_tmp(self.ordp), self.unit) + return Mod(selfvalue, modulus) diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 079d2cd6cba..a246f3e6365 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -38,7 +38,7 @@ from sage.rings.padics.padic_printing import pAdicPrinter from sage.rings.padics.precision_error import PrecisionError from sage.misc.cachefunc import cached_method -from sage.structure.sage_object import richcmp_not_equal +from sage.structure.richcmp import richcmp_not_equal class pAdicGeneric(PrincipalIdealDomain, LocalGeneric): @@ -543,12 +543,14 @@ def _test_add(self, **options): for x,y in some_tuples(elements, 2, tester._max_runs): z = x + y tester.assertIs(z.parent(), self) - tester.assertEqual(z.precision_absolute(), min(x.precision_absolute(), y.precision_absolute())) + zprec = min(x.precision_absolute(), y.precision_absolute()) + if not self.is_floating_point(): + tester.assertEqual(z.precision_absolute(), zprec) tester.assertGreaterEqual(z.valuation(), min(x.valuation(),y.valuation())) if x.valuation() != y.valuation(): tester.assertEqual(z.valuation(), min(x.valuation(),y.valuation())) - tester.assertEqual(z - x, y) - tester.assertEqual(z - y, x) + tester.assert_(y.is_equal_to(z-x,zprec)) + tester.assert_(x.is_equal_to(z-y,zprec)) def _test_sub(self, **options): """ @@ -579,12 +581,14 @@ def _test_sub(self, **options): for x,y in some_tuples(elements, 2, tester._max_runs): z = x - y tester.assertIs(z.parent(), self) - tester.assertEqual(z.precision_absolute(), min(x.precision_absolute(), y.precision_absolute())) + zprec = min(x.precision_absolute(), y.precision_absolute()) + if not self.is_floating_point(): + tester.assertEqual(z.precision_absolute(), zprec) tester.assertGreaterEqual(z.valuation(), min(x.valuation(),y.valuation())) if x.valuation() != y.valuation(): tester.assertEqual(z.valuation(), min(x.valuation(),y.valuation())) - tester.assertEqual(z - x, -y) - tester.assertEqual(z + y, x) + tester.assert_((-y).is_equal_to(z - x,zprec)) + tester.assert_(x.is_equal_to(z + y,zprec)) def _test_invert(self, **options): """ @@ -613,13 +617,16 @@ def _test_invert(self, **options): tester.assertFalse(x.is_unit()) if not self.is_fixed_mod(): tester.assertTrue(x.is_zero()) else: - e = y * x - - tester.assertFalse(x.is_zero()) - tester.assertIs(y.parent(), self if self.is_fixed_mod() else self.fraction_field()) - tester.assertTrue(e.is_one()) - tester.assertEqual(e.precision_relative(), x.precision_relative()) - tester.assertEqual(y.valuation(), -x.valuation()) + try: + e = y * x + except ZeroDivisionError: + tester.assertTrue(self.is_floating_point() and (x.is_zero() or y.is_zero())) + else: + tester.assertFalse(x.is_zero()) + tester.assertIs(y.parent(), self if self.is_fixed_mod() else self.fraction_field()) + tester.assertTrue(e.is_one()) + tester.assertEqual(e.precision_relative(), x.precision_relative()) + tester.assertEqual(y.valuation(), -x.valuation()) def _test_mul(self, **options): """ @@ -644,7 +651,10 @@ def _test_mul(self, **options): for x,y in some_tuples(elements, 2, tester._max_runs): z = x * y tester.assertIs(z.parent(), self) - tester.assertLessEqual(z.precision_relative(), min(x.precision_relative(), y.precision_relative())) + if self.is_capped_relative() or self.is_floating_point(): + tester.assertEqual(z.precision_relative(), min(x.precision_relative(), y.precision_relative())) + else: + tester.assertLessEqual(z.precision_relative(), min(x.precision_relative(), y.precision_relative())) if not z.is_zero(): tester.assertEqual(z.valuation(), x.valuation() + y.valuation()) @@ -675,10 +685,16 @@ def _test_div(self, **options): if self.is_fixed_mod(): tester.assertFalse(y.is_unit()) else: tester.assertTrue(y.is_zero()) else: - tester.assertFalse(y.is_zero()) - tester.assertIs(z.parent(), self if self.is_fixed_mod() else self.fraction_field()) - tester.assertEqual(z.precision_relative(), min(x.precision_relative(), y.precision_relative())) - tester.assertEqual(z.valuation(), x.valuation() - y.valuation()) + try: + xx = z*y + except ZeroDivisionError: + tester.assertTrue(self.is_floating_point() and (z.is_zero() or y.is_zero())) + else: + tester.assertFalse(y.is_zero()) + tester.assertIs(z.parent(), self if self.is_fixed_mod() else self.fraction_field()) + tester.assertEqual(z.precision_relative(), min(x.precision_relative(), y.precision_relative())) + tester.assertEqual(z.valuation(), x.valuation() - y.valuation()) + tester.assertEqual(xx, x) def _test_neg(self, **options): """ @@ -707,6 +723,52 @@ def _test_neg(self, **options): tester.assertEqual(x.is_zero(),y.is_zero()) tester.assertEqual(x.is_unit(),y.is_unit()) + def _test_log(self, **options): + """ + Test the log operator on elements of this ring. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + + EXAMPLES:: + + sage: Zp(3)._test_log() + + .. SEEALSO:: + + :class:`TestSuite` + """ + tester = self._tester(**options) + for x in tester.some_elements(): + if x.is_zero(): continue + l = x.log(p_branch=0) + tester.assertIs(l.parent(), self) + tester.assertGreater(l.valuation(), 0) + if self.is_capped_absolute() or self.is_capped_relative(): + tester.assertEqual(x.precision_relative(), l.precision_absolute()) + + if self.is_capped_absolute() or self.is_capped_relative(): + # In the fixed modulus setting, rounding errors may occur + elements = list(tester.some_elements()) + for x, y, b in some_tuples(elements, 3, tester._max_runs): + if x.is_zero() or y.is_zero(): continue + r1 = x.log(pi_branch=b) + y.log(pi_branch=b) + r2 = (x*y).log(pi_branch=b) + tester.assertEqual(r1, r2) + + p = self.prime() + for x in tester.some_elements(): + if x.is_zero(): continue + if p == 2: + a = 4 * x.unit_part() + else: + a = p * x.unit_part() + b = a.exp().log() + c = (1+a).log().exp() + tester.assertEqual(a, b) + tester.assertEqual(1+a, c) + def _test_teichmuller(self, **options): """ Test Teichmuller lifts. diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 8fa3c441f79..a48ef7e5972 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -485,7 +485,7 @@ cdef class pAdicGenericElement(LocalGenericElement): def _repr_(self, mode=None, do_latex=False): """ - Returns a string representation of self. + Returns a string representation of this element. INPUT: @@ -1418,6 +1418,8 @@ cdef class pAdicGenericElement(LocalGenericElement): cdef long v = self.valuation_c() if v == maxordp: return infinity + if v == -maxordp: + return -infinity cdef Integer ans = PY_NEW(Integer) mpz_set_si(ans.value, v) return ans @@ -1521,9 +1523,9 @@ cdef class pAdicGenericElement(LocalGenericElement): r = rational_reconstruction(alpha, m) return (Rational(p)**self.valuation())*r - def _shifted_log(self, aprec, mina=0): + def _log_generic(self, aprec, mina=0): r""" - Return ``-\log(1-self)`` for elements of positive valuation. + Return ``\log(self)`` for ``self`` equal to 1 in the residue field This is a helper method for :meth:`log`. @@ -1550,28 +1552,28 @@ cdef class pAdicGenericElement(LocalGenericElement): EXAMPLES:: - sage: r = Qp(5,prec=4)(5) - sage: r._shifted_log(2) + sage: r = Qp(5,prec=4)(6) + sage: r._log_generic(2) 5 + O(5^2) - sage: r._shifted_log(4) - 5 + 3*5^2 + 4*5^3 + O(5^4) - sage: r._shifted_log(100) - 5 + 3*5^2 + 4*5^3 + O(5^5) + sage: r._log_generic(4) + 5 + 2*5^2 + 4*5^3 + O(5^4) + sage: r._log_generic(100) + 5 + 2*5^2 + 4*5^3 + O(5^4) - sage: r = Zp(5,prec=4,type='fixed-mod')(5) - sage: r._shifted_log(5) - 5 + 3*5^2 + 4*5^3 + O(5^4) + sage: r = Zp(5,prec=4,type='fixed-mod')(6) + sage: r._log_generic(5) + 5 + 2*5^2 + 4*5^3 + O(5^4) - Only implemented for elements of positive valuation:: + Only implemented for elements congruent to 1 modulo the maximal ideal:: - sage: r = Zp(5,prec=4,type='fixed-mod')(1) - sage: r._shifted_log(5) + sage: r = Zp(5,prec=4,type='fixed-mod')(2) + sage: r._log_generic(5) Traceback (most recent call last): ... - ValueError: Input value (=1 + O(5^4)) must have strictly positive valuation + ValueError: Input value (=2 + O(5^4)) must be 1 in the residue field """ - x = self + x = 1-self R = self.parent() # to get the precision right over capped-absolute rings, we have to # work over the capped-relative fraction field @@ -1581,7 +1583,7 @@ cdef class pAdicGenericElement(LocalGenericElement): alpha=x.valuation() if alpha<=0: - raise ValueError('Input value (=%s) must have strictly positive valuation' % self) + raise ValueError('Input value (=%s) must be 1 in the residue field' % self) e=R.ramification_index() p=R.prime() @@ -1590,7 +1592,10 @@ cdef class pAdicGenericElement(LocalGenericElement): total=R.zero() # pre-compute x^p/p into x2p_p - if R.is_capped_relative(): + if mina == 0 and alpha*p - e > aprec: + # The value of x^p/p is not needed in that case + x2p_p = R(0) + elif R.is_capped_relative(): if p*alpha >= e: x2p_p = x**p/p else: @@ -1610,44 +1615,113 @@ cdef class pAdicGenericElement(LocalGenericElement): # Two sum over these terms, we run two nested loops, the outer one # iterates over the possible values for a, the inner one iterates over # the possible values for u. - a=0 - p2a=1 # p^a - x2pa = x # x^(p^a) + upper_u = (aprec/alpha).floor() + if mina > 0 or upper_u > 0: + a=0 + p2a=1 # p^a + x2pa = x # x^(p^a) - while True: - upper_u = ((aprec+a*e)/(alpha*p2a)).floor() # In the unramified case, we can stop summing terms as soon as # there are no u for a given a to sum over. In the ramified case, # it can happen that for some initial a there are no such u but # later in the series there are such u again. mina can be set to # take care of this by summing at least to a=mina-1 - if a >= mina and upper_u<=0: - break - # we compute the sum for the possible values for u using Horner's method - inner_sum = R.zero() - for u in xrange(upper_u,0,-1): - # We want u to be a p-adic unit - if u%p==0: - new_term = R.zero() - else: - new_term = ~R(u) - - # This hack is to deal with rings that don't lift to fields - if u>1 or x2p_p.is_zero(): - inner_sum = (inner_sum+new_term)*x2pa - else: - inner_sum = (inner_sum+new_term)*(x2p_p**a)*(x**(p2a-a*p)) - - total += inner_sum - - # Now increase the power of p - a += 1 - p2a = p2a*p - x2pa = x2pa**p + while True: + # we compute the sum for the possible values for u using Horner's method + inner_sum = R.zero() + for u in xrange(upper_u,0,-1): + # We want u to be a p-adic unit + if u%p==0: + new_term = R.zero() + else: + new_term = ~R(u) + + # This hack is to deal with rings that don't lift to fields + if u > 1 or x2p_p.is_zero(): + inner_sum = (inner_sum+new_term)*x2pa + else: + inner_sum = (inner_sum+new_term)*(x2p_p**a)*(x**(p2a-a*p)) + + total -= inner_sum + + # Now increase a and check if a new iteration of the loop is needed + a += 1 + p2a = p2a*p + upper_u = ((aprec+a*e)/(alpha*p2a)).floor() + if a >= mina and upper_u <= 0: break + + # We perform this last operation after the test + # because it is costly and may raise OverflowError + x2pa = x2pa**p return total.add_bigoh(aprec) - def log(self, p_branch=None, pi_branch=None, aprec=None, change_frac=False): + def _log_binary_splitting(self, aprec, mina=0): + r""" + Return ``\log(self)`` for ``self`` equal to 1 in the residue field + + This is a helper method for :meth:`log`. + It uses a fast binary splitting algorithm. + + INPUT: + + - ``aprec`` -- an integer, the precision to which the result is + correct. ``aprec`` must not exceed the precision cap of the ring over + which this element is defined. + + - ``mina`` -- an integer (default: 0), the series will check `n` up to + this valuation (and beyond) to see if they can contribute to the + series. + + NOTE:: + + The function does not check that its argument ``self`` is + 1 in the residue field. If this assumption is not fullfiled + the behaviour of the function is not specified. + + ALGORITHM: + + 1. Raise `u` to the power `p^v` for a suitable `v` in order + to make it closer to 1. (`v` is chosen such that `p^v` is + close to the precision.) + + 2. Write + + .. MATH:: + + u^{p-1} = \prod_{i=1}^\infty (1 - a_i p^{(v+1)*2^i}) + + with `0 \leq a_i < p^{(v+1)*2^i}` and compute + `\log(1 - a_i p^{(v+1)*2^i})` using the standard Taylor expansion + + .. MATH:: + + \log(1 - x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + + together with a binary splitting method. + + 3. Divide the result by `p^v` + + The complexity of this algorithm is quasi-linear. + + EXAMPLES:: + + sage: r = Qp(5,prec=4)(6) + sage: r._log_binary_splitting(2) + 5 + O(5^2) + sage: r._log_binary_splitting(4) + 5 + 2*5^2 + 4*5^3 + O(5^4) + sage: r._log_binary_splitting(100) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + sage: r = Zp(5,prec=4,type='fixed-mod')(6) + sage: r._log_binary_splitting(5) + 5 + 2*5^2 + 4*5^3 + O(5^4) + + """ + raise NotImplementedError + + def log(self, p_branch=None, pi_branch=None, aprec=None, change_frac=False, algorithm=None): r""" Compute the `p`-adic logarithm of this element. @@ -1685,6 +1759,21 @@ cdef class pAdicGenericElement(LocalGenericElement): the same as the input (default) or if it should change to the fraction field of the input. + - ``algorithm`` -- ``generic``, ``binary_splitting`` or ``None`` (default) + The generic algorithm evaluates naively the series defining the log, + namely + + .. MATH:: + + \log(1-x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots + + Its binary complexity is quadratic with respect to the precision. + + The binary splitting algorithm is faster, it has a quasi-linear + complexity. + By default, we use the binary splitting if it is available. Otherwise + we switch to the generic algorithm. + NOTES: What some other systems do: @@ -1700,53 +1789,30 @@ cdef class pAdicGenericElement(LocalGenericElement): by Dan Berstein at http://cr.yp.to/lineartime/multapps-20041007.pdf - ALGORITHM: - - 1. Take the unit part `u` of the input. - - 2. Raise `u` to `q-1` where `q` is the inertia degree of the ring - extension, to obtain a 1-unit. - - 3. Use the series expansion - - .. MATH:: - - \log(1-x) = -x - 1/2 x^2 - 1/3 x^3 - 1/4 x^4 - 1/5 x^5 - \cdots - - to compute the logarithm `\log(u)`. - - 4. Divide the result by ``q-1`` and multiply by ``self.valuation()*log(pi)`` - EXAMPLES:: sage: Z13 = Zp(13, 10) sage: a = Z13(14); a 1 + 13 + O(13^10) - - Note that the relative precision decreases when we take log -- it is - the absolute precision that is preserved:: - sage: a.log() 13 + 6*13^2 + 2*13^3 + 5*13^4 + 10*13^6 + 13^7 + 11*13^8 + 8*13^9 + O(13^10) + sage: Q13 = Qp(13, 10) sage: a = Q13(14); a 1 + 13 + O(13^10) sage: a.log() 13 + 6*13^2 + 2*13^3 + 5*13^4 + 10*13^6 + 13^7 + 11*13^8 + 8*13^9 + O(13^10) - The next few examples illustrate precision when computing `p`-adic - logarithms:: + Note that the relative precision decreases when we take log. + Precisely the absolute precision on ``\log(a)`` agrees with the relative + precision on ``a`` thanks to the relation ``d\log(a) = da/a``. - sage: R = Zp(5,10) - sage: e = R(389); e - 4 + 2*5 + 3*5^3 + O(5^10) - sage: e.log() - 2*5 + 2*5^2 + 4*5^3 + 3*5^4 + 5^5 + 3*5^7 + 2*5^8 + 4*5^9 + O(5^10) - sage: K = Qp(5,10) - sage: e = K(389); e - 4 + 2*5 + 3*5^3 + O(5^10) - sage: e.log() - 2*5 + 2*5^2 + 4*5^3 + 3*5^4 + 5^5 + 3*5^7 + 2*5^8 + 4*5^9 + O(5^10) + The call `log(a)` works as well:: + + sage: log(a) + 13 + 6*13^2 + 2*13^3 + 5*13^4 + 10*13^6 + 13^7 + 11*13^8 + 8*13^9 + O(13^10) + sage: log(a) == a.log() + True The logarithm is not only defined for 1-units:: @@ -1875,6 +1941,57 @@ cdef class pAdicGenericElement(LocalGenericElement): TESTS: + Check that the generic algorithm and the binary splitting algorithm + returns the same answers:: + + sage: p = 17 + sage: R = Zp(p) + sage: a = 1 + p*R.random_element() + sage: l1 = a.log(algorithm='generic') + sage: l2 = a.log(algorithm='binary_splitting') + sage: l1 == l2 + True + sage: l1.precision_absolute() == l2.precision_absolute() + True + + Check multiplicativity:: + + sage: p = 11 + sage: R = Zp(p, prec=1000) + + sage: x = 1 + p*R.random_element() + sage: y = 1 + p*R.random_element() + sage: log(x*y) == log(x) + log(y) + True + + sage: x = y = 0 + sage: while x == 0: + ....: x = R.random_element() + sage: while y == 0: + ....: y = R.random_element() + sage: branch = R.random_element() + sage: (x*y).log(p_branch=branch) == x.log(p_branch=branch) + y.log(p_branch=branch) + True + + Note that multiplicativity may fail in the fixed modulus setting + due to rounding errors:: + + sage: R = ZpFM(2, prec=5) + sage: R(180).log(p_branch=0) == R(30).log(p_branch=0) + R(6).log(p_branch=0) + False + + Check that log is the inverse of exp:: + + sage: p = 11 + sage: R = Zp(p, prec=1000) + sage: a = 1 + p*R.random_element() + sage: exp(log(a)) == a + True + + sage: a = p*R.random_element() + sage: log(exp(a)) == a + True + Check that results are consistent over a range of precision:: sage: max_prec = 40 @@ -1934,6 +2051,12 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: z.log().precision_relative() 250 + Performances:: + + sage: R = Zp(17, prec=10^6) + sage: a = R.random_element() + sage: b = a.log(p_branch=0) # should be rather fast + AUTHORS: - William Stein: initial version @@ -1947,12 +2070,15 @@ cdef class pAdicGenericElement(LocalGenericElement): generic `p`-adic rings. - Soroosh Yazdani (2013-02-1): Fixed a precision issue in - :meth:`_shifted_log`. This should really fix the issue with + :meth:`_log_generic`. This should really fix the issue with divisions. - Julian Rueth (2013-02-14): Added doctests, some changes for capped-absolute implementations. + - Xavier Caruso (2017-06): Added binary splitting type algorithms + over Qp + """ if self.is_zero(): raise ValueError('logarithm is not defined at zero') @@ -2008,7 +2134,20 @@ cdef class pAdicGenericElement(LocalGenericElement): if aprec is None or aprec > minaprec: aprec=minaprec - retval = total - x._shifted_log(aprec, minn)*R(denom).inverse_of_unit() + if algorithm is None: + try: + # The binary splitting algorithm is supposed to be faster + log_unit = y._log_binary_splitting(aprec, minn) + except NotImplementedError: + log_unit = y._log_generic(aprec, minn) + elif algorithm == "generic": + log_unit = y._log_generic(aprec, minn) + elif algorithm == "binary_splitting": + log_unit = y._log_binary_splitting(aprec, minn) + else: + raise ValueError("Algorithm must be either 'generic', 'binary_splitting' or None") + + retval = total + log_unit*R(denom).inverse_of_unit() if not change_frac: if retval.valuation() < 0 and not R.is_field(): raise ValueError("logarithm is not integral, use change_frac=True to obtain a result in the fraction field") diff --git a/src/sage/rings/padics/padic_printing.pxd b/src/sage/rings/padics/padic_printing.pxd index b3f02ef006d..d7f22b372e8 100644 --- a/src/sage/rings/padics/padic_printing.pxd +++ b/src/sage/rings/padics/padic_printing.pxd @@ -17,6 +17,7 @@ cdef class pAdicPrinter_class(SageObject): cdef long max_ram_terms cdef long max_unram_terms cdef long max_terse_terms + cdef object show_prec cdef base_p_list(self, value, bint pos) cdef _repr_gen(self, pAdicGenericElement elt, bint do_latex, bint pos, int mode, pname) diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index bd19a160f09..7e38ea705bc 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -25,7 +25,7 @@ from __future__ import print_function from cpython.list cimport * from sage.libs.gmp.mpz cimport * -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool import sys @@ -59,7 +59,7 @@ def pAdicPrinter(ring, options={}): sage: pAdicPrinter(R, {'sep': '&'}) series printer for 5-adic Ring with capped relative precision 20 """ - for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet']: + for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec']: if option not in options: options[option] = None return pAdicPrinter_class(ring, **options) @@ -296,7 +296,7 @@ cdef class pAdicPrinter_class(SageObject): or field, and uses these to compute the representations of elements. """ - def __init__(self, ring, mode, pos, ram_name, unram_name, var_name, max_ram_terms, max_unram_terms, max_terse_terms, sep, alphabet): + def __init__(self, ring, mode, pos, ram_name, unram_name, var_name, max_ram_terms, max_unram_terms, max_terse_terms, sep, alphabet, show_prec): """ Initializes a pAdicPrinter. @@ -366,6 +366,9 @@ cdef class pAdicPrinter_class(SageObject): p-adic digits into strings (so that no separator need be used in 'digits' mode). + - show_prec -- Determines whether `` + O(p^prec)`` is added. + If None, uses defaults determined by print mode. + TESTS:: sage: R = Qp(7, print_mode='bars', print_sep='&') #indirect doctest @@ -436,6 +439,7 @@ cdef class pAdicPrinter_class(SageObject): raise ValueError("max_terse_terms must be positive and fit in a long") else: self.max_terse_terms = _printer_defaults._max_terse_terms + self.show_prec = show_prec def __reduce__(self): """ @@ -452,15 +456,16 @@ cdef class pAdicPrinter_class(SageObject): return pAdicPrinter, (self.ring, \ {'mode': self._print_mode(), \ - 'pos': self.pos, \ - 'ram_name': self.ram_name, \ - 'unram_name': self.unram_name, \ - 'var_name': self.var_name, \ - 'max_ram_terms': self.max_ram_terms, \ - 'max_unram_terms': self.max_unram_terms, \ - 'max_terse_terms': self.max_terse_terms, \ - 'sep':self.sep, \ - 'alphabet': self.alphabet}) + 'pos': self.pos, \ + 'ram_name': self.ram_name, \ + 'unram_name': self.unram_name, \ + 'var_name': self.var_name, \ + 'max_ram_terms': self.max_ram_terms, \ + 'max_unram_terms': self.max_unram_terms, \ + 'max_terse_terms': self.max_terse_terms, \ + 'sep':self.sep, \ + 'alphabet': self.alphabet, \ + 'show_prec': self.show_prec}) def __richcmp__(self, other, op): """ @@ -567,6 +572,10 @@ cdef class pAdicPrinter_class(SageObject): rx = other.max_terse_terms if lx != rx: return richcmp_not_equal(lx, rx, op) + lx = self.show_prec + rx = other.show_prec + if lx != rx: + return richcmp_not_equal(lx, rx, op) return rich_to_bool(op, 0) @@ -605,7 +614,7 @@ cdef class pAdicPrinter_class(SageObject): sage: D = Zp(5)._printer.dict(); D['sep'] '|' """ - return {'mode': self._print_mode(), 'pos': self.pos, 'ram_name': self.ram_name, 'unram_name': self.unram_name, 'var_name': self.var_name, 'max_ram_terms': self.max_ram_terms, 'max_unram_terms': self.max_unram_terms, 'max_terse_terms': self.max_terse_terms, 'sep': self.sep, 'alphabet': self.alphabet} + return {'mode': self._print_mode(), 'pos': self.pos, 'ram_name': self.ram_name, 'unram_name': self.unram_name, 'var_name': self.var_name, 'max_ram_terms': self.max_ram_terms, 'max_unram_terms': self.max_unram_terms, 'max_terse_terms': self.max_terse_terms, 'sep': self.sep, 'alphabet': self.alphabet, 'show_prec': self.show_prec} def __exit__(self, type, value, traceback): """ @@ -689,6 +698,17 @@ cdef class pAdicPrinter_class(SageObject): """ return self.max_terse_terms + def _show_prec(self): + """ + Accesses self.show_prec. + + EXAMPLES:: + + sage: R = ZpFP(5); R._printer._show_prec() + False + """ + return self.show_prec + def _ring(self): """ Accesses self.ring. @@ -870,27 +890,34 @@ cdef class pAdicPrinter_class(SageObject): if elt._is_exact_zero(): return "0" if elt._is_inexact_zero(): + if self.show_prec is False: + return "0" if mode == val_unit or mode == series: s = "O(%s"%(ram_name) elif mode == terse: s = "0 + O(%s"%(ram_name) else: # mode == digits or bars - s = "..." + if self.show_prec is True: + s = "O(%s"%(ram_name) + else: + s = "..." elif mode == val_unit: if do_latex: if elt.valuation() == 0: - s = "%s + O(%s"%(self._repr_spec(elt, do_latex, pos, terse, 0, ram_name), ram_name) + s = self._repr_spec(elt, do_latex, pos, terse, 0, ram_name) elif elt.valuation() == 1: - s = "%s \\cdot %s + O(%s"%(ram_name, self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name), ram_name) + s = "%s \\cdot %s"%(ram_name, self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) else: - s = "%s^{%s} \\cdot %s + O(%s"%(ram_name, elt.valuation(), self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name), ram_name) + s = "%s^{%s} \\cdot %s"%(ram_name, elt.valuation(), self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) else: if elt.valuation() == 0: - s = "%s + O(%s"%(self._repr_spec(elt, do_latex, pos, terse, 0, ram_name), ram_name) + s = self._repr_spec(elt, do_latex, pos, terse, 0, ram_name) elif elt.valuation() == 1: - s = "%s * %s + O(%s"%(ram_name, self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name), ram_name) + s = "%s * %s"%(ram_name, self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) else: - s = "%s^%s * %s + O(%s"%(ram_name, elt.valuation(), self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name), ram_name) + s = "%s^%s * %s"%(ram_name, elt.valuation(), self._repr_spec(elt.unit_part(), do_latex, pos, terse, 1, ram_name)) + if not (self.show_prec is False): + s += " + O(%s"%(ram_name) elif mode == digits: n = elt.valuation() if self.base: @@ -909,7 +936,10 @@ cdef class pAdicPrinter_class(SageObject): L = ['?']*(1 - n - len(L)) + L L = L[:n] + ['.'] + L[n:] s = "".join(L) - s = "..." + s + if self.show_prec: + s += " + O(%s"%(ram_name) + else: + s = "..." + s elif mode == bars: n = elt.valuation() if self.base: @@ -937,13 +967,17 @@ cdef class pAdicPrinter_class(SageObject): L = ['0']*(min(-n, elt.precision_relative()) - len(L)) + L L = ['?']*(-n - len(L)) + L L = L[:n] + ['.'] + L[n:] - if L[0] == '.': + if self.show_prec: + s = "%s + O(%s"%(self.sep.join(L), ram_name) + elif L[0] == '.': s = "..." + self.sep + self.sep.join(L) else: s = "..." + self.sep.join(L) else: # mode == terse or series - s = "%s + O(%s"%(self._repr_spec(elt, do_latex, pos, mode, 0, ram_name), ram_name) - if mode != bars and mode != digits: + s = self._repr_spec(elt, do_latex, pos, mode, 0, ram_name) + if not (self.show_prec is False): + s += " + O(%s"%(ram_name) + if (mode == bars or mode == digits) and self.show_prec or (mode == terse or mode == series or mode == val_unit) and not (self.show_prec is False): if elt.precision_absolute() == 1: s += ")" else: diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index e182ea381ce..5c6073ee0e4 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -33,14 +33,15 @@ AUTHORS: #***************************************************************************** import weakref +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off + from sage.rings.infinity import infinity from sage.libs.gmp.mpz cimport * -from sage.structure.sage_object cimport richcmp_not_equal, richcmp +from sage.structure.richcmp cimport richcmp_not_equal, richcmp from cpython.object cimport Py_EQ, Py_NE from sage.ext.stdsage cimport PY_NEW -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" cdef long maxpreccap = (1L << (sizeof(long) * 8 - 2)) - 1 @@ -577,6 +578,8 @@ cdef PowComputer_base PowComputer_c(Integer m, Integer cache_limit, Integer prec from padic_capped_absolute_element import PowComputer_ as PC_class elif prec_type == 'fixed-mod': from padic_fixed_mod_element import PowComputer_ as PC_class + elif prec_type == 'floating-point': + from padic_floating_point_element import PowComputer_ as PC_class else: PC_class = PowComputer_base PC = PC_class(m, mpz_get_ui(cache_limit.value), mpz_get_ui(prec_cap.value), mpz_get_ui(prec_cap.value), in_field) diff --git a/src/sage/rings/padics/pow_computer_flint.pyx b/src/sage/rings/padics/pow_computer_flint.pyx index c8ddd3f2826..d5542626d5c 100644 --- a/src/sage/rings/padics/pow_computer_flint.pyx +++ b/src/sage/rings/padics/pow_computer_flint.pyx @@ -1,7 +1,7 @@ from __future__ import absolute_import -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport mpz_init, mpz_clear, mpz_pow_ui from sage.libs.flint.padic cimport * @@ -11,7 +11,7 @@ from sage.libs.flint.fmpz_vec cimport * from sage.libs.flint.fmpz cimport fmpz_init, fmpz_one, fmpz_mul, fmpz_set, fmpz_get_mpz, fmpz_clear, fmpz_pow_ui, fmpz_set_mpz, fmpz_fdiv_q_2exp from cpython.object cimport Py_EQ, Py_NE -from sage.structure.sage_object cimport richcmp_not_equal +from sage.structure.richcmp cimport richcmp_not_equal from sage.rings.integer cimport Integer from sage.rings.all import ZZ from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint @@ -629,6 +629,8 @@ def PowComputer_flint_maker(prime, cache_limit, prec_cap, ram_prec_cap, in_field from .qadic_flint_CA import PowComputer_ elif prec_type == 'fixed-mod': from .qadic_flint_FM import PowComputer_ + elif prec_type == 'floating-point': + from .qadic_flint_FP import PowComputer_ else: raise ValueError("unknown prec_type `%s`" % prec_type) return PowComputer_(prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) diff --git a/src/sage/rings/padics/qadic_flint_FP.pxd b/src/sage/rings/padics/qadic_flint_FP.pxd new file mode 100644 index 00000000000..1a25be2fc08 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_FP.pxd @@ -0,0 +1,11 @@ +from cypari2.gen cimport Gen as pari_gen +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint_unram +cdef class PowComputer_(PowComputer_flint_unram): + pass +ctypedef fmpz_poly_t celement + +include "FP_template_header.pxi" + +cdef class qAdicFloatingPointElement(FPElement): + pass diff --git a/src/sage/rings/padics/qadic_flint_FP.pyx b/src/sage/rings/padics/qadic_flint_FP.pyx new file mode 100644 index 00000000000..0137a9f2da6 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_FP.pyx @@ -0,0 +1,124 @@ +from types import MethodType + +include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" +include "sage/libs/linkages/padics/unram_shared.pxi" +include "FP_template.pxi" + +cdef class PowComputer_(PowComputer_flint_unram): + """ + A PowComputer for a floating-point unramified ring or field. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqFP(125) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'floating-point' + """ + self._prec_type = 'floating-point' + PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + +cdef class qAdicFloatingPointElement(FPElement): + frobenius = MethodType(frobenius_unram, None, qAdicFloatingPointElement) + trace = MethodType(trace_unram, None, qAdicFloatingPointElement) + norm = MethodType(norm_unram, None, qAdicFloatingPointElement) + + def matrix_mod_pn(self): + """ + Returns the matrix of right multiplication by the element on + the power basis `1, x, x^2, \ldots, x^{d-1}` for this + extension field. Thus the *rows* of this matrix give the + images of each of the `x^i`. The entries of the matrices are + IntegerMod elements, defined modulo ``p^(self.absprec() / e)``. + + Raises an error if ``self`` has negative valuation. + + EXAMPLES:: + + sage: R. = QqFP(5^5,5) + sage: b = (5 + 15*a)^3 + sage: b.matrix_mod_pn() + [ 125 1125 3375 3375 0] + [ 0 125 1125 3375 3375] + [380500 377125 125 1125 3375] + [380500 367000 377125 125 1125] + [387250 376000 367000 377125 125] + + sage: M = R(0,3).matrix_mod_pn(); M == 0 + True + sage: M.base_ring() + Integer Ring + + Check that :trac:`13617` has been fixed:: + + sage: R(0).matrix_mod_pn() + [0 0 0 0 0] + [0 0 0 0 0] + [0 0 0 0 0] + [0 0 0 0 0] + [0 0 0 0 0] + """ + if self.ordp < 0: + raise ValueError("self must be integral") + if very_pos_val(self.ordp): + from sage.matrix.all import matrix + return matrix(ZZ, self.prime_pow.deg, self.prime_pow.deg) + else: + return cmatrix_mod_pn(self.unit, self.ordp + self.prime_pow.prec_cap, self.ordp, self.prime_pow) + + def _flint_rep(self, var='x'): + """ + Replacement for _ntl_rep for use in printing and debugging. + + EXAMPLES:: + + sage: R. = QqFP(27, 4) + sage: (~(1+a))._flint_rep() + 41*x^2 + 40*x + 42 + sage: (1+a)*(41*a^2+40*a+42) + 1 + """ + return self.prime_pow._new_fmpz_poly(self.unit, var) + + def _flint_rep_abs(self, var='x'): + """ + Replacement for _ntl_rep_abs for use in printing and debugging. + + EXAMPLES:: + + sage: R. = QqFP(27, 4) + sage: (~(3+3*a))._flint_rep_abs() + (41*x^2 + 40*x + 42, -1) + sage: (3+3*a)*(41*a^2+40*a+42) + 3 + sage: (3+3*a)._flint_rep_abs() + (3*x + 3, 0) + """ + if self.ordp < 0: + return self._flint_rep(var), Integer(self.ordp) + cshift(self.prime_pow.poly_flint_rep, self.unit, self.ordp, self.ordp + self.prime_pow.prec_cap, self.prime_pow, False) + return self.prime_pow._new_fmpz_poly(self.prime_pow.poly_flint_rep, var), Integer(0) + + def __hash__(self): + r""" + Raise a ``TypeError`` since this element is not hashable + (:trac:`11895`.) + + TESTS:: + + sage: K. = QqFP(9) + sage: hash(a) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_FP.qAdicFloatingPointElement' + + """ + # Eventually, hashing will be disabled for all (non-fixed-mod) p-adic + # elements (#11895), until then, we only to this for types which did + # not support hashing before we switched some elements to FLINT + raise TypeError("unhashable type: 'sage.rings.padics.qadic_flint_FP.qAdicFloatingPointElement'") diff --git a/src/sage/rings/padics/transcendantal.c b/src/sage/rings/padics/transcendantal.c new file mode 100644 index 00000000000..3796cfb6e4c --- /dev/null +++ b/src/sage/rings/padics/transcendantal.c @@ -0,0 +1,158 @@ +/* + * C helper functions for the computation + * of p-adic transcendantal functions + * + *********************************************/ + +#include +#include +#include +#include /* cysignals library */ + +/* p-adic logarithm */ +void padiclog(mpz_t ans, const mpz_t a, unsigned long p, unsigned long prec, const mpz_t modulo) { + /* Compute the p-adic logarithm of a, + which is supposed to be congruent to 1 mod p + + Algorithm: + 1. we raise a at the power p^(v-1) (for a suitable v) in order + to make it closer to 1 + 2. we write the new a as a product + 1/a = (1 - a_0*p^v) (1 - a_1*p^(2*v) (1 - a_2*p^(4*v) ... + with 0 <= a_i < p^(v*2^i). + 3. we compute each log(1 - a_i*p^(v*2^i)) using Taylor expansion + and a binary spliting strategy. */ + + unsigned long i, v, e, N, saveN, Np, tmp, trunc, step; + double den = log(p); + mpz_t f, arg, trunc_mod, h, hpow, mpz_tmp, mpz_tmp2, d, inv, mod2; + mpz_t *num, *denom; + + mpz_init(mpz_tmp); + mpz_init(mpz_tmp2); + mpz_init(arg); + mpz_set_ui(ans, 0); + + mpz_fdiv_r_ui(mpz_tmp, a, p); + mpz_set(arg, a); + + /* First we make the argument closer to 1 by raising it to the p^(v-1) */ + if (prec < p) { + v = 0; e = 1; + } else { + v = (unsigned long)(log(prec)/den); // v here is v-1 + e = pow(p,v); + mpz_mul_ui(mpz_tmp, modulo, e); + mpz_powm_ui(arg, arg, e, mpz_tmp); + prec += v; + } + + /* Where do we need to truncate the Taylor expansion */ + N = prec+v; N /= ++v; // note the ++v + Np = N; + den *= v; + while(1) { + tmp = Np + (unsigned long)(log(N)/den); + if (tmp == N) break; + N = tmp; + } + + /* We allocate memory and initialize variables */ + mpz_init(f); mpz_init(mod2); + mpz_init(h); mpz_init(hpow); + mpz_init(d); mpz_init(inv); + sig_block(); + num = (mpz_t*)malloc(N*sizeof(mpz_t)); + denom = (mpz_t*)malloc(N*sizeof(mpz_t)); + sig_unblock(); + for (i = 0; i < N; i++) { + mpz_init(num[i]); + mpz_init(denom[i]); + } + + trunc = v << 1; + mpz_init(trunc_mod); + mpz_ui_pow_ui(trunc_mod, p, trunc); + while(1) { + /* We compute f = 1 - a_i*p^((v+1)*2^i) + trunc_mod is p^((v+1)*2^(i+1)) */ + mpz_fdiv_r(f, arg, trunc_mod); + + if (mpz_cmp_ui(f, 1) != 0) { + + mpz_ui_sub(f, 2, f); + mpz_mul(arg, arg, f); + + /* We compute the Taylor expansion of log(f) + For now, computations are carried out over the rationals */ + for (i = 0; i < N; i++) { + mpz_set_ui(num[i], 1); + mpz_set_ui(denom[i], i+1); + } + step = 1; + mpz_ui_sub(h, 1, f); // we write f = 1 - h, i.e. h = a_i*p^(2^i) + mpz_set(hpow, h); + while(step < N) { + for (i = 0; i < N - step; i += step << 1) { + mpz_mul(mpz_tmp2, hpow, num[i+step]); + mpz_mul(mpz_tmp, mpz_tmp2, denom[i]); + mpz_mul(num[i], num[i], denom[i+step]); + mpz_add(num[i], num[i], mpz_tmp); + mpz_mul(denom[i], denom[i], denom[i+step]); + } + step <<= 1; + mpz_mul(hpow, hpow, hpow); + } + + /* We simplify the fraction */ + Np = N; tmp = 0; + while(Np > 0) { Np /= p; tmp += Np; } + mpz_ui_pow_ui(d, p, tmp); + mpz_divexact(mpz_tmp, num[0], d); + mpz_divexact(denom[0], denom[0], d); + + mpz_divexact_ui(h, h, e); + mpz_mul(mpz_tmp, h, mpz_tmp); + + /* We coerce the result from Q to Zp */ + mpz_gcdext(d, inv, NULL, denom[0], modulo); + mpz_mul(mpz_tmp, mpz_tmp, inv); + + /* We add this contribution to log(f) */ + mpz_add(ans, ans, mpz_tmp); + + } + + if (trunc > prec) break; + + /* We update the variables for the next step */ + mpz_mul(trunc_mod, trunc_mod, trunc_mod); + trunc <<= 1; + for (i = N >> 1; i < N; i++) { + mpz_clear(num[i]); + mpz_clear(denom[i]); + } + N >>= 1; + } + + mpz_fdiv_r(ans, ans, modulo); + + /* We clear memory */ + mpz_clear(arg); + mpz_clear(f); + mpz_clear(trunc_mod); + mpz_clear(h); + mpz_clear(hpow); + mpz_clear(mpz_tmp); + mpz_clear(d); + mpz_clear(inv); + mpz_clear(mod2); + for (i = 0; i < N; i++) { + mpz_clear(num[i]); + mpz_clear(denom[i]); + } + sig_block(); + free(num); + free(denom); + sig_unblock(); +} diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index 3a9697f5ddb..9cacdc34c8f 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -6,10 +6,8 @@ - William Stein (2004): Initial version. - Simon King (2011-08-24): Use UniqueRepresentation, element_class and proper initialisation of elements. - """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2004 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -17,23 +15,22 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#***************************************************************************** - -import operator - +# **************************************************************************** import sage.libs.pari.all as pari import sage.rings.ring as ring from sage.structure.element import RingElement - +from sage.structure.richcmp import richcmp from sage.misc.fast_methods import Singleton + class Pari(RingElement): """ Element of Pari pseudo-ring. """ def __init__(self, x, parent=None): """ - EXAMPLES: + EXAMPLES:: + sage: R = PariRing() sage: f = R('x^3 + 1/2') sage: f @@ -141,17 +138,21 @@ def __invert__(self): """ return self.__class__(~self.__x, parent=_inst) - def __cmp__(self, other): + def _richcmp_(self, other, op): """ EXAMPLES:: sage: R = PariRing() sage: a = R(3) sage: b = R(11) - sage: cmp(a,b) - -1 + sage: a < b + True + sage: a == b + False + sage: a > b + False """ - return cmp(self.__x, other.__x) + return richcmp(self.__x, other.__x, op) def __int__(self): return int(self.__x) @@ -159,7 +160,8 @@ def __int__(self): class PariRing(Singleton, ring.Ring): """ - EXAMPLES: + EXAMPLES:: + sage: R = PariRing(); R Pseudoring of all PARI objects. sage: loads(R.dumps()) is R @@ -169,6 +171,7 @@ class PariRing(Singleton, ring.Ring): def __init__(self): ring.Ring.__init__(self, self) + def __repr__(self): return 'Pseudoring of all PARI objects.' @@ -177,20 +180,19 @@ def _element_constructor_(self, x): return x return self.element_class(x, parent=self) - def is_field(self, proof = True): + def is_field(self, proof=True): return False def characteristic(self): raise RuntimeError("Not defined.") - #return 0 def random_element(self, x=None, y=None, distribution=None): """ Return a random integer in Pari. - NOTE: + .. NOTE:: - The given arguments are passed to ``ZZ.random_element(...)``. + The given arguments are passed to ``ZZ.random_element(...)``. INPUT: @@ -214,7 +216,7 @@ def random_element(self, x=None, y=None, distribution=None): """ from sage.all import ZZ - return self(ZZ.random_element(x,y,distribution)) + return self(ZZ.random_element(x, y, distribution)) def zeta(self): """ @@ -229,4 +231,3 @@ def zeta(self): return self(-1) _inst = PariRing() - diff --git a/src/sage/rings/polynomial/complex_roots.py b/src/sage/rings/polynomial/complex_roots.py index 7ffab6741ae..529ff9277bb 100644 --- a/src/sage/rings/polynomial/complex_roots.py +++ b/src/sage/rings/polynomial/complex_roots.py @@ -186,7 +186,7 @@ def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): sage: v[1][0].imag() < 1e25 True - sage: K. = NumberField(x^2 + 1) + sage: K. = QuadraticField(-1) sage: eps = 1/2^100 sage: x = polygen(K) sage: p = (x-1)*(x-1-eps)*(x-1+eps)*(x-1-eps*im)*(x-1+eps*im) diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index e1ca22dcbe1..02083ce0b65 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -28,9 +28,8 @@ from __future__ import print_function import sys -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" -from libc.string cimport memset +from cysignals.memory cimport sig_malloc, check_calloc, sig_free +from cysignals.signals cimport sig_on, sig_off from sage.structure.element cimport parent @@ -148,12 +147,7 @@ def cyclotomic_coeffs(nn, sparse=None): d = prod(s) max_deg += n / d - if (max_deg)*sizeof(long) > sys.maxsize: - raise MemoryError("Not enough memory to calculate cyclotomic polynomial of %s" % n) - cdef long* coeffs = sig_malloc(sizeof(long) * (max_deg+1)) - if coeffs == NULL: - raise MemoryError("Not enough memory to calculate cyclotomic polynomial of %s" % n) - memset(coeffs, 0, sizeof(long) * (max_deg+1)) + cdef long* coeffs = check_calloc(max_deg+1, sizeof(long)) coeffs[0] = 1 cdef long k, dd, offset = 0, deg = 0 diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 1204e629147..0607c876c90 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -17,7 +17,7 @@ from sage.misc.misc import union from sage.structure.factorization import Factorization from sage.misc.derivative import multi_derivative from sage.rings.polynomial.polynomial_element import Polynomial -from sage.structure.sage_object cimport richcmp, rich_to_bool +from sage.structure.richcmp cimport richcmp, rich_to_bool cdef class LaurentPolynomial_generic(CommutativeAlgebraElement): diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx index 8c3754db3ad..14a33278563 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx @@ -48,8 +48,7 @@ Two examples from the Mathematica documentation (done in Sage): # http://www.gnu.org/licenses/ #***************************************************************************** - -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off from sage.libs.singular.decl cimport tHomog, number, IDELEMS, p_Copy, rChangeCurrRing from sage.libs.singular.decl cimport idInit, id_Delete, currRing, Sy_bit, OPT_REDSB @@ -57,7 +56,7 @@ from sage.libs.singular.decl cimport scKBase, poly, testHomog, idSkipZeroes, id_ from sage.libs.singular.decl cimport OPT_REDTAIL, singular_options, kInterRed, t_rep_gb, p_GetCoeff from sage.libs.singular.decl cimport pp_Mult_nn, p_Delete, n_Delete from sage.libs.singular.decl cimport rIsPluralRing -from sage.libs.singular.decl cimport n_unknown, n_Zp, n_Q, n_R, n_GF, n_long_R, n_algExt,n_transExt,n_long_C, n_Z, n_Zn, n_Znm, n_Z2m, n_CF +from sage.libs.singular.decl cimport n_unknown, n_Zp, n_Q, n_R, n_GF, n_long_R, n_algExt,n_transExt,n_long_C, n_Z, n_Zn, n_Znm, n_Z2m, n_CF from sage.rings.polynomial.multi_polynomial_libsingular cimport new_MP from sage.rings.polynomial.plural cimport new_NCP diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index ea51f00518e..1b896572940 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -174,10 +174,9 @@ from __future__ import absolute_import, print_function # * pNext and pIter don't need currRing # * p_Normalize apparently needs currRing -include "cysignals/memory.pxi" -include "cysignals/signals.pxi" - from cpython.object cimport Py_NE +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off # singular types from sage.libs.singular.decl cimport ring, poly, ideal, intvec, number, currRing @@ -247,7 +246,7 @@ from sage.structure.element cimport Element from sage.structure.element cimport CommutativeRingElement from sage.structure.element cimport coercion_model -from sage.structure.sage_object cimport rich_to_bool, richcmp +from sage.structure.richcmp cimport rich_to_bool, richcmp from sage.structure.factorization import Factorization from sage.structure.sequence import Sequence diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 2c38245b0fa..55d6ae35020 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -9,7 +9,7 @@ from sage.misc.latex import latex_variable_name from sage.misc.misc_c import prod from sage.structure.parent cimport Parent -from sage.structure.sage_object cimport rich_to_bool, richcmp +from sage.structure.richcmp cimport rich_to_bool, richcmp from cpython.object cimport Py_NE from sage.categories.commutative_rings import CommutativeRings diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index 1d3fc804c40..1235d282a48 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -183,9 +183,9 @@ REFERENCES: """ from __future__ import print_function -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" from cpython.object cimport Py_EQ, Py_NE +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off import operator @@ -211,7 +211,7 @@ from sage.structure.parent cimport Parent from sage.structure.sequence import Sequence from sage.structure.element import coerce_binop from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.sage_object cimport richcmp, richcmp_not_equal +from sage.structure.richcmp cimport richcmp, richcmp_not_equal from sage.categories.action cimport Action diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index d92f1172fbd..d66048ae902 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -104,7 +104,7 @@ TESTS:: """ from __future__ import print_function -include "cysignals/memory.pxi" +from cysignals.memory cimport sig_malloc, sig_free from sage.categories.algebras import Algebras diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index e91022c6ae5..0ffb14b6097 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -38,11 +38,11 @@ AUTHORS: #***************************************************************************** from __future__ import print_function -include "cysignals/memory.pxi" from libc.string cimport memcpy from cpython.dict cimport * from cpython.object cimport (PyObject_RichCompare, Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT, Py_GE) +from cysignals.memory cimport sig_malloc, sig_free import copy from functools import reduce diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx index 2e6539da4cd..7332dc213ea 100644 --- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx +++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx @@ -23,7 +23,7 @@ TESTS: """ -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off from sage.libs.arb.acb cimport * from sage.rings.integer cimport Integer, smallInteger diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 00e4f94176d..0db8d5e7834 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -67,7 +67,7 @@ from sage.misc.latex import latex from sage.misc.long cimport pyobject_to_long from sage.structure.factorization import Factorization from sage.structure.element import coerce_binop -from sage.structure.sage_object cimport (richcmp, richcmp_not_equal, +from sage.structure.richcmp cimport (richcmp, richcmp_not_equal, rich_to_bool, rich_to_bool_sgn) from sage.interfaces.singular import singular as singular_default, is_SingularElement @@ -7030,7 +7030,7 @@ cdef class Polynomial(CommutativeAlgebraElement): :: - sage: K. = NumberField(x^2 + 1) + sage: K. = QuadraticField(-1) sage: y = polygen(K) sage: p = y^4 - 2 - im sage: p.roots(ring=CC) @@ -8207,11 +8207,50 @@ cdef class Polynomial(CommutativeAlgebraElement): Traceback (most recent call last): ... TypeError: is_squarefree() is not defined for polynomials over Ring of integers modulo 9 + + TESTS: + + If the base ring implements `_is_squarefree_univariate_polynomial`, + then this method gets used instead of the generic algorithm in + :meth:`_is_squarefree_generic`:: + + sage: R. = QQbar[] + sage: (x^2).is_squarefree() + False + sage: hasattr(QQbar, '_is_squarefree_univariate_polynomial') + False + sage: QQbar._is_squarefree_univariate_polynomial = lambda self: True + sage: (x^2).is_squarefree() + True + sage: del(QQbar._is_squarefree_univariate_polynomial) + """ B = self.parent().base_ring() if B not in sage.categories.integral_domains.IntegralDomains(): raise TypeError("is_squarefree() is not defined for polynomials over {}".format(B)) + B = self.parent().base_ring() + if hasattr(B, '_is_squarefree_univariate_polynomial'): + return B._is_squarefree_univariate_polynomial(self) + + return self._is_squarefree_generic() + + def _is_squarefree_generic(self): + r""" + Return False if this polynomial is not square-free, i.e., if there is a + non-unit `g` in the polynomial ring such that `g^2` divides ``self``. + + EXAMPLES:: + + sage: R. = QQbar[] + sage: (x^2*(x + 1)).is_squarefree() # indirect doctest + False + sage: (x*(x+1)).is_squarefree() # indirect doctest + True + + """ + B = self.parent().base_ring() + # a square-free polynomial has a square-free content if not B.is_field(): content = self.content() diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index b99a3fb5726..8bf32464545 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -39,6 +39,7 @@ from sage.rings.polynomial.polynomial_singular_interface import Polynomial_singular_repr from sage.libs.pari.all import pari_gen +from sage.structure.richcmp import richcmp, richcmp_not_equal, rich_to_bool, rich_to_bool_sgn from sage.structure.element import coerce_binop from sage.rings.infinity import infinity, Infinity @@ -1415,8 +1416,7 @@ class Polynomial_generic_sparse_cdvf(Polynomial_generic_sparse_cdv, Polynomial_g # XXX: Ensures that the generic polynomials implemented in SAGE via PARI # # until at least until 4.5.0 unpickle correctly as polynomials implemented # # via FLINT. # -from sage.structure.sage_object import (register_unpickle_override, - richcmp, richcmp_not_equal, rich_to_bool, rich_to_bool_sgn) +from sage.structure.sage_object import register_unpickle_override from sage.rings.polynomial.polynomial_rational_flint import Polynomial_rational_flint register_unpickle_override( \ diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 457327c1666..3de0b0cb93b 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -28,6 +28,9 @@ AUTHORS: #***************************************************************************** from __future__ import absolute_import +from cysignals.memory cimport sig_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off + from sage.rings.polynomial.polynomial_element cimport Polynomial, _dict_to_list from sage.libs.all import pari, pari_gen @@ -56,8 +59,6 @@ from sage.libs.ntl.ZZ_pX cimport * def make_element(parent, args): return parent(*args) -include "cysignals/signals.pxi" - zz_p_max = NTL_SP_BOUND cdef class Polynomial_dense_mod_n(Polynomial): diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index f5858775d5c..5a9267732e5 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -23,8 +23,8 @@ Check that operations with numpy elements work well (see :trac:`18076` and """ from __future__ import absolute_import -include "cysignals/signals.pxi" -include "cysignals/memory.pxi" +from cysignals.memory cimport check_allocarray, check_reallocarray, sig_free +from cysignals.signals cimport sig_on, sig_off from cpython cimport PyInt_AS_LONG, PyFloat_AS_DOUBLE diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 1d6d2c25acc..6d32517e28f 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -17,7 +17,7 @@ Polynomial Template for C/C++ Library Interfaces from sage.rings.polynomial.polynomial_element cimport Polynomial from sage.structure.element cimport ModuleElement, Element, RingElement from sage.structure.element import coerce_binop, bin_op -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool from sage.rings.fraction_field_element import FractionFieldElement from sage.rings.integer cimport Integer from sage.libs.all import pari_gen diff --git a/src/sage/rings/polynomial/symmetric_reduction.pyx b/src/sage/rings/polynomial/symmetric_reduction.pyx index fa02df012b4..f3e1db1c6cf 100644 --- a/src/sage/rings/polynomial/symmetric_reduction.pyx +++ b/src/sage/rings/polynomial/symmetric_reduction.pyx @@ -121,7 +121,7 @@ from __future__ import print_function import copy import operator import sys -from sage.structure.sage_object cimport richcmp, Py_NE, Py_EQ +from sage.structure.richcmp cimport richcmp, Py_NE, Py_EQ cdef class SymmetricReductionStrategy: diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 5396768612b..28382e69c97 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -118,7 +118,7 @@ from sage.misc.derivative import multi_derivative Polynomial = sage.rings.polynomial.polynomial_element.Polynomial_generic_dense from sage.structure.element cimport AlgebraElement, RingElement, ModuleElement, Element -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp def is_PowerSeries(x): diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 2621249f1b5..8e453818c9c 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -498,6 +498,14 @@ sage: P = 1/(1+x^4) sage: P.partial_fraction_decomposition() (0, [(-0.3535533905932738?*x + 1/2)/(x^2 - 1.414213562373095?*x + 1), (0.3535533905932738?*x + 1/2)/(x^2 + 1.414213562373095?*x + 1)]) + +Check that :trac:`22202` is fixed:: + + sage: R1. = AA[]; R2. = QQbar[] + sage: v = QQbar.polynomial_root(x^2 - x + 1, CIF(0.5, RIF(-0.87, -0.85))) + sage: a = QQbar.polynomial_root((-4*v + 2)*s + (v - 1/2), CIF(RIF(0.24, 0.26), RIF(0))) + sage: QQ(a) + 1/4 """ from __future__ import absolute_import, print_function @@ -510,10 +518,11 @@ import sage.rings.ring from sage.misc.fast_methods import Singleton from sage.misc.cachefunc import cached_method -from sage.structure.sage_object import (SageObject, richcmp, - rich_to_bool, richcmp_not_equal, - op_EQ, op_NE, op_LE, op_LT, - op_GE, op_GT) +from sage.structure.sage_object import SageObject +from sage.structure.richcmp import (richcmp, + rich_to_bool, richcmp_not_equal, + op_EQ, op_NE, op_LE, op_LT, + op_GE, op_GT) from sage.rings.real_mpfr import RR from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement, RealIntervalField_class from sage.rings.complex_field import ComplexField diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 1ea5d3841d0..0dfc4f46d60 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -178,16 +178,18 @@ TESTS:: Classes and Methods =================== """ + #***************************************************************************** -# Copyright (C) 2014 Clemens Heuberger +# Copyright (C) 2014 Clemens Heuberger # -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_str, sig_off from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index ea23cb62861..949218d0894 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -68,7 +68,7 @@ from sage.rings.integer_ring import ZZ from sage.categories.morphism cimport Morphism from sage.structure.coerce cimport is_numpy_type from sage.misc.randstate cimport randstate, current_randstate -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool def is_RealDoubleField(x): diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 5e809974453..6c366e87314 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -29,7 +29,7 @@ from operator import add, sub, mul, div, pow, neg, inv cdef canonical_coercion from sage.structure.element import canonical_coercion from sage.structure.all import parent -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp import sage.categories.map from sage.categories.morphism cimport Morphism diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 3bad4a6f4ea..d8cf79777b9 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -247,16 +247,16 @@ import math # for log import sys import operator -include "cysignals/signals.pxi" from cpython.mem cimport * from cpython.object cimport Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT, Py_GE from libc.string cimport strlen +from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport * cimport sage.rings.ring cimport sage.structure.element from sage.structure.element cimport RingElement, Element, ModuleElement -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp cimport sage.rings.real_mpfr as real_mpfr from .real_mpfr cimport RealField_class, RealNumber, RealField @@ -3824,7 +3824,7 @@ cdef class RealIntervalFieldElement(RingElement): EXAMPLES:: sage: a = RIF(1) - sage: a.__cmp__(a) + sage: a._cmp_(a) doctest:...: DeprecationWarning: for RIF elements, do not use cmp See http://trac.sagemath.org/22907 for details. 0 diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index e7fc8a28eeb..101376bf34b 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -112,23 +112,22 @@ Make sure we don't have a new field for every new literal:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function + +from __future__ import absolute_import, print_function import math # for log import sys import re -include "cysignals/signals.pxi" - from cpython.object cimport Py_NE +from cysignals.signals cimport sig_on, sig_off from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.mpz cimport * from sage.misc.randstate cimport randstate, current_randstate from sage.structure.element cimport RingElement, Element, ModuleElement -from sage.structure.sage_object cimport rich_to_bool_sgn +from sage.structure.richcmp cimport rich_to_bool_sgn cdef bin_op from sage.structure.element import bin_op diff --git a/src/sage/rings/sum_of_squares.pyx b/src/sage/rings/sum_of_squares.pyx index 2a8bdb0036e..63d253eea73 100644 --- a/src/sage/rings/sum_of_squares.pyx +++ b/src/sage/rings/sum_of_squares.pyx @@ -16,13 +16,11 @@ AUTHORS: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function - -from libc.math cimport sqrt +from __future__ import absolute_import, print_function -include "cysignals/signals.pxi" +from libc.math cimport sqrt +from cysignals.signals cimport sig_on, sig_off cimport sage.rings.integer as integer from . import integer diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index a94f2c00603..aa8461bfa60 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -156,7 +156,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecated_function_alias -from sage.structure.sage_object import rich_to_bool +from sage.structure.richcmp import rich_to_bool from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import FieldElement, parent from sage.structure.coerce import py_scalar_to_element diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 47c9a0477af..f15ef68c216 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -84,7 +84,7 @@ from sage.schemes.elliptic_curves.weierstrass_morphism import WeierstrassIsomorphism, isomorphisms from sage.sets.set import Set -from sage.structure.sage_object import richcmp_not_equal, richcmp +from sage.structure.richcmp import richcmp_not_equal, richcmp from sage.misc.cachefunc import cached_function # diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 47958639a00..c30994653b8 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -3,10 +3,7 @@ This module defines the class ``EllipticCurve_field``, based on ``EllipticCurve_generic``, for elliptic curves over general fields. - """ -from __future__ import absolute_import - #***************************************************************************** # Copyright (C) 2006 William Stein # @@ -14,6 +11,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import from . import ell_generic import sage.rings.all as rings @@ -40,6 +38,23 @@ class EllipticCurve_field(ell_generic.EllipticCurve_generic): # j=0=1728, but I have never worked them out or seen them used! # + def genus(self): + """ + Return 1 for elliptic curves. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(3), [0, -1, 0, -346, 2652]) + sage: E.genus() + 1 + + sage: R = FractionField(QQ['z']) + sage: E = EllipticCurve(R, [0, -1, 0, -346, 2652]) + sage: E.genus() + 1 + """ + return rings.ZZ.one() + r""" Twists: rewritten by John Cremona as follows: @@ -755,7 +770,7 @@ def descend_to(self, K, f=None): def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True): r""" - Returns an elliptic curve isogeny from self. + Return an elliptic curve isogeny from self. The isogeny can be determined in two ways, either by a polynomial or a set of torsion points. The methods used are: @@ -868,7 +883,7 @@ def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True): def isogeny_codomain(self, kernel, degree=None): r""" - Returns the codomain of the isogeny from self with given + Return the codomain of the isogeny from self with given kernel. INPUT: @@ -1031,7 +1046,7 @@ def isogenies_prime_degree(self, l=None, max_l=31): def is_isogenous(self, other, field=None): """ - Returns whether or not self is isogenous to other. + Return whether or not self is isogenous to other. INPUT: @@ -1122,7 +1137,7 @@ def weierstrass_p(self, prec=20, algorithm=None): def hasse_invariant(self): r""" - Returns the Hasse invariant of this elliptic curve. + Return the Hasse invariant of this elliptic curve. OUTPUT: diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index e7aa33d73c0..dba93c7b1ee 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -134,7 +134,7 @@ from sage.libs.pari import pari from cypari2.pari_instance import prec_words_to_bits from sage.structure.sequence import Sequence -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.schemes.curves.projective_curve import Hasse_bounds from sage.schemes.projective.projective_point import (SchemeMorphism_point_projective_ring, diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 86648d76b5e..8e39abaf108 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -26,8 +26,8 @@ from sage.categories.morphism import Morphism from .constructor import EllipticCurve from sage.categories.homset import Hom -from sage.structure.sage_object import (richcmp, richcmp_not_equal, - op_NE, op_EQ, op_LT) +from sage.structure.richcmp import (richcmp, richcmp_not_equal, + op_NE, op_EQ, op_LT) class baseWI: diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index f0ac03a2c48..a07fb13f148 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -83,7 +83,7 @@ import operator from sage.structure.element import (AdditiveGroupElement, RingElement, Element, generic_power, parent, coercion_model) -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp from sage.structure.sequence import Sequence from sage.categories.homset import Homset, Hom, End from sage.categories.number_fields import NumberFields diff --git a/src/sage/schemes/generic/point.py b/src/sage/schemes/generic/point.py index 29bcb769056..0cadc10957e 100644 --- a/src/sage/schemes/generic/point.py +++ b/src/sage/schemes/generic/point.py @@ -9,7 +9,7 @@ #******************************************************************************* from sage.structure.element import Element -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp ######################################################## # Base class for points on a scheme, either topological diff --git a/src/sage/schemes/product_projective/point.py b/src/sage/schemes/product_projective/point.py index 8921e7a638e..f4eebd64385 100644 --- a/src/sage/schemes/product_projective/point.py +++ b/src/sage/schemes/product_projective/point.py @@ -27,7 +27,7 @@ from sage.schemes.generic.morphism import SchemeMorphism from sage.schemes.generic.morphism import SchemeMorphism_point from sage.structure.sequence import Sequence -from sage.structure.sage_object import richcmp +from sage.structure.richcmp import richcmp class ProductProjectiveSpaces_point_ring(SchemeMorphism_point): diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index da9a63ebe0d..575d616f3cd 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -56,7 +56,7 @@ SchemeMorphism_point) from sage.structure.element import AdditiveGroupElement from sage.structure.sequence import Sequence -from sage.structure.sage_object import rich_to_bool, richcmp, op_EQ, op_NE +from sage.structure.richcmp import rich_to_bool, richcmp, op_EQ, op_NE #******************************************************************* # Projective varieties diff --git a/src/sage/schemes/toric/morphism.py b/src/sage/schemes/toric/morphism.py index 268aecde9f1..04d47bc7b44 100644 --- a/src/sage/schemes/toric/morphism.py +++ b/src/sage/schemes/toric/morphism.py @@ -375,7 +375,7 @@ # https://groups.google.com/d/msg/sage-devel/qF4yU6Vdmao/wQlNrneSmWAJ from sage.categories.morphism import Morphism -from sage.structure.sage_object import richcmp_not_equal, richcmp +from sage.structure.richcmp import richcmp_not_equal, richcmp from sage.structure.sequence import Sequence from sage.rings.all import ZZ diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 7b4d17670c8..4fd3e40e821 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -85,7 +85,8 @@ import operator cdef dict operator_dict = operator.__dict__ from operator import add, sub, mul, div, truediv, iadd, isub, imul, idiv -from .sage_object cimport SageObject, rich_to_bool +from .richcmp cimport rich_to_bool +from .sage_object cimport SageObject from .parent cimport Set_PythonType, Parent_richcmp_element_without_coercion from .element cimport bin_op_exception, parent, Element from .coerce_actions import LeftModuleAction, RightModuleAction, IntegerMulAction @@ -278,7 +279,7 @@ cpdef bint is_numpy_type(t): sage: 1 + object() Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for +: 'Integer Ring' and '' + TypeError: unsupported operand parent(s) for +: 'Integer Ring' and '<... 'object'>' """ if not isinstance(t, type): return False @@ -1806,7 +1807,7 @@ cdef class CoercionModel_cache_maps(CoercionModel): EXAMPLES:: sage: from sage.structure.element import get_coercion_model - sage: from sage.structure.sage_object import op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE + sage: from sage.structure.richcmp import op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE sage: richcmp = get_coercion_model().richcmp sage: richcmp(None, None, op_EQ) True diff --git a/src/sage/structure/dynamic_class.py b/src/sage/structure/dynamic_class.py index a2f5ea0f574..2269bd78e39 100644 --- a/src/sage/structure/dynamic_class.py +++ b/src/sage/structure/dynamic_class.py @@ -198,13 +198,13 @@ def dynamic_class(name, bases, cls=None, reduction=None, doccls=None, '__main__' sage: Foo.__bases__ - (,) + (<... 'object'>,) sage: FooBar.__bases__ - (, ) + (<... 'object'>, ) sage: Foo.mro() - [, ] + [, <... 'object'>] sage: FooBar.mro() - [, , ] + [, <... 'object'>, ] .. RUBRIC:: Pickling @@ -466,7 +466,7 @@ def __reduce__(self): sage: C = sage.structure.dynamic_class.dynamic_class_internal("bla", (object,), Foo, doccls = DocClass) sage: type(C).__reduce__(C) (, - ('bla', (,), , None, )) + ('bla', (<... 'object'>,), , None, )) sage: C = sage.structure.dynamic_class.dynamic_class_internal("bla", (object,), Foo, doccls = DocClass, reduction = "blah") sage: type(C).__reduce__(C) 'blah' diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 0fe1b3d0408..0bd1729fe4f 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -293,7 +293,7 @@ cdef dict _coerce_op_symbols = dict( cdef MethodType from types import MethodType -from sage.structure.sage_object cimport rich_to_bool +from sage.structure.richcmp cimport rich_to_bool from sage.structure.coerce cimport py_scalar_to_element from sage.structure.parent cimport Parent from sage.structure.misc import is_extension_type @@ -1010,7 +1010,7 @@ cdef class Element(SageObject): and check that comparison works:: sage: cython(''' - ....: from sage.structure.sage_object cimport rich_to_bool + ....: from sage.structure.richcmp cimport rich_to_bool ....: from sage.structure.element cimport Element ....: cdef class FloatCmp(Element): ....: cdef float x diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index 4f7de4deb79..cff6398da22 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -153,7 +153,7 @@ import sage from sage.ext.stdsage cimport HAS_DICTIONARY from sage.structure.element cimport Element from sage.structure.parent cimport Parent -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp ############################################################################ ### Basic clone elements ### diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index a7ec72d0642..932aad89368 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -99,7 +99,8 @@ from sage.structure.element cimport parent, coercion_model cimport sage.categories.morphism as morphism cimport sage.categories.map as map from sage.structure.debug_options cimport debug -from sage.structure.sage_object cimport SageObject, rich_to_bool +from sage.structure.richcmp cimport rich_to_bool +from sage.structure.sage_object cimport SageObject from sage.structure.misc import is_extension_type from sage.misc.lazy_attribute import lazy_attribute from sage.categories.sets_cat import Sets, EmptySetError diff --git a/src/sage/structure/richcmp.pxd b/src/sage/structure/richcmp.pxd new file mode 100644 index 00000000000..ac7a6d4042e --- /dev/null +++ b/src/sage/structure/richcmp.pxd @@ -0,0 +1,146 @@ +from libc.stdint cimport uint32_t +from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE +from cpython.object cimport PyObject_RichCompare as richcmp + + +cpdef inline richcmp_not_equal(x, y, int op): + """ + Like ``richcmp(x, y, op)`` but assuming that `x` is not equal to `y`. + + INPUT: + + - ``op`` -- a rich comparison operation (e.g. ``Py_EQ``) + + OUTPUT: + + If ``op`` is not ``op_EQ`` or ``op_NE``, the result of + ``richcmp(x, y, op)``. If ``op`` is ``op_EQ``, return + ``False``. If ``op`` is ``op_NE``, return ``True``. + + This is useful to compare lazily two objects A and B according to 2 + (or more) different parameters, say width and height for example. + One could use:: + + return richcmp((A.width(), A.height()), (B.width(), B.height()), op) + + but this will compute both width and height in all cases, even if + A.width() and B.width() are enough to decide the comparison. + + Instead one can do:: + + wA = A.width() + wB = B.width() + if wA != wB: + return richcmp_not_equal(wA, wB, op) + return richcmp(A.height(), B.height(), op) + + The difference with ``richcmp`` is that ``richcmp_not_equal`` + assumes that its arguments are not equal, which is excluding the case + where the comparison cannot be decided so far, without + knowing the rest of the parameters. + + EXAMPLES:: + + sage: from sage.structure.richcmp import (richcmp_not_equal, + ....: op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE) + sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): + ....: print(richcmp_not_equal(3, 4, op)) + True + True + False + True + False + False + sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): + ....: print(richcmp_not_equal(5, 4, op)) + False + False + False + True + True + True + """ + if op == Py_EQ: + return False + elif op == Py_NE: + return True + return richcmp(x, y, op) + + +cpdef inline bint rich_to_bool(int op, int c): + """ + Return the corresponding ``True`` or ``False`` value for a rich + comparison, given the result of an old-style comparison. + + INPUT: + + - ``op`` -- a rich comparison operation (e.g. ``Py_EQ``) + + - ``c`` -- the result of an old-style comparison: -1, 0 or 1. + + OUTPUT: 1 or 0 (corresponding to ``True`` and ``False``) + + .. SEEALSO:: + + :func:`rich_to_bool_sgn` if ``c`` could be outside the + [-1, 0, 1] range. + + EXAMPLES:: + + sage: from sage.structure.richcmp import (rich_to_bool, + ....: op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE) + sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): + ....: for c in (-1,0,1): + ....: print(rich_to_bool(op, c)) + True False False + True True False + False True False + True False True + False False True + False True True + + Indirect tests using integers:: + + sage: 0 < 5, 5 < 5, 5 < -8 + (True, False, False) + sage: 0 <= 5, 5 <= 5, 5 <= -8 + (True, True, False) + sage: 0 >= 5, 5 >= 5, 5 >= -8 + (False, True, True) + sage: 0 > 5, 5 > 5, 5 > -8 + (False, False, True) + sage: 0 == 5, 5 == 5, 5 == -8 + (False, True, False) + sage: 0 != 5, 5 != 5, 5 != -8 + (True, False, True) + """ + # op is a value in [0,5], c a value in [-1,1]. We implement this + # function very efficienly using a bitfield. Note that the masking + # below implies we consider c mod 4, so c = -1 implicitly becomes + # c = 3. + + # The 4 lines below involve just constants, so the compiler should + # optimize them to just one constant value for "bits". + cdef uint32_t less_bits = (1 << Py_LT) + (1 << Py_LE) + (1 << Py_NE) + cdef uint32_t equal_bits = (1 << Py_LE) + (1 << Py_GE) + (1 << Py_EQ) + cdef uint32_t greater_bits = (1 << Py_GT) + (1 << Py_GE) + (1 << Py_NE) + cdef uint32_t bits = (less_bits << 24) + (equal_bits) + (greater_bits << 8) + + cdef int shift = 8*c + op + + # The shift masking (shift & 31) will likely be optimized away by + # the compiler since shift and bit test instructions implicitly + # mask their offset. + return (bits >> (shift & 31)) & 1 + + +cpdef inline bint rich_to_bool_sgn(int op, Py_ssize_t c): + """ + Same as ``rich_to_bool``, but allow any `c < 0` and `c > 0` + instead of only `-1` and `1`. + + .. NOTE:: + + This is in particular needed for ``mpz_cmp()``. + """ + return rich_to_bool(op, (c > 0) - (c < 0)) diff --git a/src/sage/structure/richcmp.pyx b/src/sage/structure/richcmp.pyx new file mode 100644 index 00000000000..7800ab30fe3 --- /dev/null +++ b/src/sage/structure/richcmp.pyx @@ -0,0 +1,89 @@ +r""" +Cython-like rich comparisons in Python + +With "rich comparisons", we mean the Python 3 comparisons which are +usually implemented in Python using methods like ``__eq__`` and +``__lt__``. Internally in Python, there is only one rich comparison +slot ``tp_richcompare``. The actual operator is passed as an integer +constant (defined in this module as +``op_LT``, ``op_LE``, ``op_EQ``, ``op_NE``, ``op_GT``, ``op_GE``). + +Cython exposes rich comparisons in ``cdef`` classes as the +``__richcmp__`` special method. The Sage coercion model also supports +rich comparisons this way: for two instances ``x`` and ``y`` +of :class:`~sage.structure.element.Element`, ``x._richcmp_(y, op)`` +is called when the user does something like ``x <= y`` +(possibly after coercion if ``x`` and ``y`` have different parents). + +Various helper functions exist to make it easier to implement rich +comparison: the most important one is the :func:`richcmp` function. +This is analogous to the Python 2 function ``cmp()`` but implements +rich comparison, with the comparison operator (e.g. ``op_GE``) as +third argument. There is also :func:`richcmp_not_equal` which is like +:func:`richcmp` but it is optimized assuming that the compared objects +are not equal. + +The functions :func:`rich_to_bool` and :func:`rich_to_bool_sgn` can be +used to convert results of ``cmp()`` (i.e. -1, 0 or 1) to a boolean +``True``/``False`` for rich comparisons. + +AUTHORS: + +- Jeroen Demeyer +""" + +from cpython.object cimport PyObject_RichCompare + +op_LT = Py_LT # operator < +op_LE = Py_LE # operator <= +op_EQ = Py_EQ # operator == +op_NE = Py_NE # operator != +op_GT = Py_GT # operator > +op_GE = Py_GE # operator >= + + +def richcmp(x, y, int op): + """ + Return the result of the rich comparison of ``x`` and ``y`` with + operator ``op``. + + INPUT: + + - ``x``, ``y`` -- arbitrary Python objects + + - ``op`` -- comparison operator (one of ``op_LT`, ``op_LE``, + ``op_EQ``, ``op_NE``, ``op_GT``, ``op_GE``). + + EXAMPLES:: + + sage: from sage.structure.richcmp import * + sage: richcmp(3, 4, op_LT) + True + sage: richcmp(x, x^2, op_EQ) + x == x^2 + + The two examples above are completely equivalent to ``3 < 4`` + and ``x == x^2``. For this reason, it only makes sense in practice + to call ``richcmp`` with a non-constant value for ``op``. + + We can write a custom ``Element`` class which shows a more + realistic example of how to use this:: + + sage: from sage.structure.element import Element + sage: class MyElement(Element): + ....: def __init__(self, parent, value): + ....: Element.__init__(self, parent) + ....: self.v = value + ....: def _richcmp_(self, other, op): + ....: return richcmp(self.v, other.v, op) + sage: P = Parent() + sage: x = MyElement(P, 3) + sage: y = MyElement(P, 3) + sage: x < y + False + sage: x == y + True + sage: x > y + False + """ + return PyObject_RichCompare(x, y, op) diff --git a/src/sage/structure/sage_object.pxd b/src/sage/structure/sage_object.pxd index 2461dce633c..5e3e442c896 100644 --- a/src/sage/structure/sage_object.pxd +++ b/src/sage/structure/sage_object.pxd @@ -1,161 +1,2 @@ -from libc.stdint cimport uint32_t -from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE - -# Export this for use by Python modules -cdef extern from "Python.h": - cpdef richcmp "PyObject_RichCompare"(object, object, int) - - cdef class SageObject: pass - - -cpdef inline richcmp_not_equal(x, y, int op): - """ - Like ``richcmp(x, y, op)`` but assuming that `x` is not equal to `y`. - - INPUT: - - - ``op`` -- a rich comparison operation (e.g. ``Py_EQ``) - - OUTPUT: - - If ``op`` is not ``op_EQ`` or ``op_NE``, the result of - ``richcmp(x, y, op)``. If ``op`` is ``op_EQ``, return - ``False``. If ``op`` is ``op_NE``, return ``True``. - - This is useful to compare lazily two objects A and B according to 2 - (or more) different parameters, say width and height for example. - One could use:: - - return richcmp((A.width(), A.height()), (B.width(), B.height()), op) - - but this will compute both width and height in all cases, even if - A.width() and B.width() are enough to decide the comparison. - - Instead one can do:: - - wA = A.width() - wB = B.width() - if wA != wB: - return richcmp_not_equal(wA, wB, op) - return richcmp(A.height(), B.height(), op) - - The difference with ``richcmp`` is that ``richcmp_not_equal`` - assumes that its arguments are not equal, which is excluding the case - where the comparison cannot be decided so far, without - knowing the rest of the parameters. - - EXAMPLES:: - - sage: from sage.structure.sage_object import (richcmp_not_equal, - ....: op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE) - sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): - ....: print(richcmp_not_equal(3, 4, op)) - True - True - False - True - False - False - sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): - ....: print(richcmp_not_equal(5, 4, op)) - False - False - False - True - True - True - """ - if op == Py_EQ: - return False - elif op == Py_NE: - return True - return richcmp(x, y, op) - - -cpdef inline bint rich_to_bool(int op, int c): - """ - Return the corresponding ``True`` or ``False`` value for a rich - comparison, given the result of an ordinary comparison. - - INPUT: - - - ``op`` -- a rich comparison operation (e.g. ``Py_EQ``) - - - ``c`` -- the result of an ordinary comparison: -1, 0 or 1. - - OUTPUT: 1 or 0 (corresponding to ``True`` and ``False``) - - .. SEEALSO:: - - ``rich_to_bool_sgn`` if ``c`` could be outside the [-1, 1] - range. - - EXAMPLES:: - - sage: from sage.structure.sage_object import (rich_to_bool, - ....: op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE) - sage: for op in (op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE): - ....: for c in (-1,0,1): - ....: print(rich_to_bool(op, c)) - True False False - True True False - False True False - True False True - False False True - False True True - - Indirect tests using integers:: - - sage: 0 < 5, 5 < 5, 5 < -8 - (True, False, False) - sage: 0 <= 5, 5 <= 5, 5 <= -8 - (True, True, False) - sage: 0 >= 5, 5 >= 5, 5 >= -8 - (False, True, True) - sage: 0 > 5, 5 > 5, 5 > -8 - (False, False, True) - sage: 0 == 5, 5 == 5, 5 == -8 - (False, True, False) - sage: 0 != 5, 5 != 5, 5 != -8 - (True, False, True) - - TESTS:: - - sage: from sage.structure.sage_object import py_rich_to_bool - sage: py_rich_to_bool(op_EQ, 0) - doctest:...: DeprecationWarning: py_rich_to_bool is deprecated. Please use sage.structure.sage_object.rich_to_bool instead. - See http://trac.sagemath.org/21128 for details. - True - """ - # op is a value in [0,5], c a value in [-1,1]. We implement this - # function very efficienly using a bitfield. Note that the masking - # below implies we consider c mod 4, so c = -1 implicitly becomes - # c = 3. - - # The 4 lines below involve just constants, so the compiler should - # optimize them to just one constant value for "bits". - cdef uint32_t less_bits = (1 << Py_LT) + (1 << Py_LE) + (1 << Py_NE) - cdef uint32_t equal_bits = (1 << Py_LE) + (1 << Py_GE) + (1 << Py_EQ) - cdef uint32_t greater_bits = (1 << Py_GT) + (1 << Py_GE) + (1 << Py_NE) - cdef uint32_t bits = (less_bits << 24) + (equal_bits) + (greater_bits << 8) - - cdef int shift = 8*c + op - - # The shift masking (shift & 31) will likely be optimized away by - # the compiler since shift and bit test instructions implicitly - # mask their offset. - return (bits >> (shift & 31)) & 1 - - -cpdef inline bint rich_to_bool_sgn(int op, Py_ssize_t c): - """ - Same as ``rich_to_bool``, but allow any `c < 0` and `c > 0` - instead of only `-1` and `1`. - - .. NOTE:: - - This is in particular needed for ``mpz_cmp()``. - """ - return rich_to_bool(op, (c > 0) - (c < 0)) diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index bb62a4a3410..b98e3a6383f 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -2,6 +2,49 @@ # cython: old_style_globals=True r""" Abstract base class for Sage objects + +TESTS: + +Test deprecations:: + + sage: from sage.structure.sage_object import ( + ....: richcmp, richcmp_not_equal, + ....: rich_to_bool, py_rich_to_bool, rich_to_bool_sgn, + ....: op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE) + sage: richcmp(2, 3, op_EQ) + doctest:...: DeprecationWarning: Importing richcmp from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + doctest:...: DeprecationWarning: Importing op_EQ from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + False + sage: richcmp_not_equal(2, 3, op_LT) + doctest:...: DeprecationWarning: Importing richcmp_not_equal from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + doctest:...: DeprecationWarning: Importing op_LT from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + True + sage: rich_to_bool(op_NE, 0) + doctest:...: DeprecationWarning: Importing rich_to_bool from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + doctest:...: DeprecationWarning: Importing op_NE from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + False + sage: py_rich_to_bool(op_GT, 1) + doctest:...: DeprecationWarning: Importing rich_to_bool from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/21128 for details. + doctest:...: DeprecationWarning: Importing op_GT from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + True + sage: rich_to_bool_sgn(op_LE, -123) + doctest:...: DeprecationWarning: Importing rich_to_bool_sgn from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + doctest:...: DeprecationWarning: Importing op_LE from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + True + sage: op_GE + doctest:...: DeprecationWarning: Importing op_GE from here is deprecated. If you need to use it, please import it directly from sage.structure.richcmp + See http://trac.sagemath.org/23103 for details. + 5 """ from __future__ import absolute_import, print_function @@ -19,16 +62,18 @@ sys_modules = sys.modules import zlib; comp = zlib import bz2; comp_other = bz2 -op_LT = Py_LT # operator < -op_LE = Py_LE # operator <= -op_EQ = Py_EQ # operator == -op_NE = Py_NE # operator != -op_GT = Py_GT # operator > -op_GE = Py_GE # operator >= - - -from sage.misc.superseded import deprecated_function_alias -py_rich_to_bool = deprecated_function_alias(21128, rich_to_bool) +from sage.misc.lazy_import import LazyImport +richcmp = LazyImport('sage.structure.richcmp', 'richcmp', deprecation=23103) +richcmp_not_equal = LazyImport('sage.structure.richcmp', 'richcmp_not_equal', deprecation=23103) +rich_to_bool = LazyImport('sage.structure.richcmp', 'rich_to_bool', deprecation=23103) +py_rich_to_bool = LazyImport('sage.structure.richcmp', 'rich_to_bool', deprecation=21128) +rich_to_bool_sgn = LazyImport('sage.structure.richcmp', 'rich_to_bool_sgn', deprecation=23103) +op_LT = LazyImport('sage.structure.richcmp', 'op_LT', deprecation=23103) +op_LE = LazyImport('sage.structure.richcmp', 'op_LE', deprecation=23103) +op_EQ = LazyImport('sage.structure.richcmp', 'op_EQ', deprecation=23103) +op_NE = LazyImport('sage.structure.richcmp', 'op_NE', deprecation=23103) +op_GT = LazyImport('sage.structure.richcmp', 'op_GT', deprecation=23103) +op_GE = LazyImport('sage.structure.richcmp', 'op_GE', deprecation=23103) cdef process(s): diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index d55bb20e73c..fddc82251e8 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -43,6 +43,18 @@ sage: assumptions() [x > 0] +Assumptions also affect operations that do not use Maxima:: + + sage: forget() + sage: assume(x, 'even') + sage: assume(x, 'real') + sage: (-1)^x + 1 + sage: (-gamma(pi))^x + gamma(pi)^x + sage: binomial(2*x, x).is_integer() + True + Assumptions are added and in some cases checked for consistency:: sage: assume(x>0) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index b0054f9938a..f5a15c4c2fc 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -2293,6 +2293,61 @@ cdef class Expression(CommutativeRingElement): """ return is_a_relational(self._gobj) + def is_exact(self): + """ + Return True if this expression only contains exact numerical coefficients. + + EXAMPLES:: + + sage: x, y = var('x, y') + sage: (x+y-1).is_exact() + True + sage: (x+y-1.9).is_exact() + False + sage: x.is_exact() + True + sage: pi.is_exact() + True + sage: (sqrt(x-y) - 2*x + 1).is_exact() + True + sage: ((x-y)^0.5 - 2*x + 1).is_exact() + False + + TESTS:: + + sage: (sin(x*cos(2*x*pi)) - 10*y^3 - 1/(x+4)).is_exact() + True + sage: (sin(x*cos(2.0*x*pi)) - 10*y^3 - 1/(x+4)).is_exact() + False + sage: SR(42).is_exact() + True + sage: SR(42.01).is_exact() + False + sage: SR(I).is_exact() + True + sage: (x-I).is_exact() + True + sage: (x-CC(0,1)).is_exact() + False + """ + # generator over all numerical elements in the subexpression tree of expr + def numelems_gen(expr): + if expr.is_numeric(): + yield expr + elif expr.operator() is not None: + for op in expr.operands(): + if op.is_numeric(): + yield op + else: + for opp in numelems_gen(op): + yield opp + # stop at the first inexact number in the subexpression tree of self, + # and if there is no such element, then self is exact + for nelem in numelems_gen(self): + if not nelem.pyobject().base_ring().is_exact(): + return False + return True + cpdef bint is_infinity(self): """ Return True if self is an infinite expression. @@ -4509,6 +4564,12 @@ cdef class Expression(CommutativeRingElement): sage: sin(x/2).expand_trig(half_angles=True) (-1)^floor(1/2*x/pi)*sqrt(-1/2*cos(x) + 1/2) + If the expression contains terms which are factored, we expand first:: + + sage: (x, k1, k2) = var('x, k1, k2') + sage: cos((k1-k2)*x).expand().expand_trig() + cos(k1*x)*cos(k2*x) + sin(k1*x)*sin(k2*x) + ALIASES: :meth:`trig_expand` and :meth:`expand_trig` are the same diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 1774b053587..66db6e940b4 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -1008,6 +1008,12 @@ def __init__(self, ex, base_ring=None, ring=None): Traceback (most recent call last): ... TypeError: y is not a variable of Univariate Polynomial Ring in x over Rational Field + + TESTS:: + + sage: t, x, z = SR.var('t,x,z') + sage: QQ[i]['x,y,z,t'](4*I*t + 2*x -12*z + 2) + 2*x - 12*z + (4*I)*t + 2 """ if not (ring is None or base_ring is None): raise TypeError("either base_ring or ring must be specified, but not both") diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 976960b7b02..4f863f67f1f 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -24,7 +24,7 @@ from ring import SR from sage.structure.coerce cimport py_scalar_to_element, is_numpy_type, is_mpmath_type from sage.structure.element cimport coercion_model -from sage.structure.sage_object cimport richcmp +from sage.structure.richcmp cimport richcmp # we keep a database of symbolic functions initialized in a session # this also makes the .operator() method of symbolic expressions work diff --git a/src/sage/tests/stl_vector.pyx b/src/sage/tests/stl_vector.pyx index 1b4a6ba1b90..7baccb6fec8 100644 --- a/src/sage/tests/stl_vector.pyx +++ b/src/sage/tests/stl_vector.pyx @@ -32,7 +32,7 @@ from sage.rings.integer cimport Integer from sage.libs.gmp.mpz cimport mpz_add_ui from libcpp.vector cimport vector from libcpp.string cimport string -from sage.structure.sage_object cimport richcmp_not_equal, rich_to_bool +from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool cdef class stl_int_vector(SageObject): diff --git a/src/sage/version.py b/src/sage/version.py index 767e570cba9..9d800484904 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.0.beta9' -date = '2017-05-31' +version = '8.0.beta10' +date = '2017-06-11' diff --git a/src/sage_setup/autogen/interpreters/__init__.py b/src/sage_setup/autogen/interpreters/__init__.py index bcb742da057..37d0108802d 100644 --- a/src/sage_setup/autogen/interpreters/__init__.py +++ b/src/sage_setup/autogen/interpreters/__init__.py @@ -183,6 +183,7 @@ def rebuild(dirname, force=False): sage: testdir = tmp_dir() sage: rebuild(testdir) Building interpreters for fast_callable + -> First build of interpreters sage: open(testdir + '/wrapper_el.pyx').readline() '# Automatically generated by ...\n' """ @@ -198,12 +199,28 @@ def rebuild(dirname, force=False): # Although multiple files are generated by this function, since # they are all generated at once it suffices to make sure if just # one of the generated files is older than the generator sources - src_file = __file__ - gen_file = os.path.join(dirname, '__init__.py') - - if os.path.exists(gen_file): - if getmtime(gen_file) > getmtime(src_file) and not force: - return + class NeedToRebuild(Exception): pass + try: + if force: + raise NeedToRebuild("-> Force rebuilding interpreters") + gen_file = os.path.join(dirname, '__init__.py') + if not os.path.isfile(gen_file): + raise NeedToRebuild("-> First build of interpreters") + + gen_timestamp = getmtime(gen_file) + src_dir = os.path.dirname(__file__) + for root, dirs, files in os.walk(src_dir): + for basename in files: + if basename.endswith(".py"): + src_file = os.path.join(root, basename) + src_timestamp = getmtime(src_file) + if src_timestamp > gen_timestamp: + raise NeedToRebuild("-> Rebuilding interpreters because {} changed".format(src_file)) + except NeedToRebuild as E: + # Rebuild + print(E) + else: + return # Up-to-date for interp in _INTERPRETERS: build_interp(interp(), dirname)