From 3f2878194efe7adb432c655c2afcda70c7e33e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Honrubia=20Gonz=C3=A1lez?= Date: Tue, 1 Aug 2017 18:47:38 +0200 Subject: [PATCH 001/924] Bug corrected. Some examples on perpendicular_bisector() modified as to pass tests --- .../hyperbolic_space/hyperbolic_geodesic.py | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index b88ae461ad5..32f5cfd6d17 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -888,14 +888,14 @@ def perpendicular_bisector(self): sage: PD = HyperbolicPlane().PD() sage: g = PD.get_geodesic(-0.3+0.4*I,+0.7-0.1*I) - sage: h = g.perpendicular_bisector() + sage: h = g.perpendicular_bisector().complete() sage: P = g.plot(color='blue')+h.plot(color='orange');P Graphics object consisting of 4 graphics primitives .. PLOT:: g = HyperbolicPlane().PD().get_geodesic(-0.3+0.4*I,+0.7-0.1*I) - h = g.perpendicular_bisector() + h = g.perpendicular_bisector().complete() sphinx_plot(g.plot(color='blue')+h.plot(color='orange')) Complete geodesics cannot be bisected:: @@ -909,7 +909,7 @@ def perpendicular_bisector(self): TESTS:: sage: g = HyperbolicPlane().PD().random_geodesic() - sage: h = g.perpendicular_bisector() + sage: h = g.perpendicular_bisector().complete() sage: bool(h.intersection(g)[0].coordinates() - g.midpoint().coordinates() < 10**-9) True @@ -1307,6 +1307,15 @@ def intersection(self, other): sage: g.intersection(h) [Point in UHP 2/3*sqrt(-2) + 13/3] + .. PLOT:: + + UHP = HyperbolicPlane().UHP() + g = UHP.get_geodesic(3, 5) + h = UHP.get_geodesic(4, 7) + P = g.intersection(h) + pict = g.plot(color="red")+h.plot(color="red") + sphinx_plot(pict) + If the given geodesics do not intersect, the function returns an empty list:: @@ -1315,6 +1324,13 @@ def intersection(self, other): sage: g.intersection(h) [] + .. PLOT:: + + UHP = HyperbolicPlane().UHP() + g = UHP.get_geodesic(4.0, 5.0) + h = UHP.get_geodesic(5.0, 7.0) + sphinx_plot(g.plot() + h.plot()) + If the given geodesics are identical, return that geodesic:: @@ -1324,6 +1340,13 @@ def intersection(self, other): [Boundary point in UHP -1/8*sqrt(114985) - 307/8, Boundary point in UHP 1/8*sqrt(114985) - 307/8] + TEST: + + sage: UHP = HyperbolicPlane().UHP() + sage: g1 = UHP.get_geodesic(2*QQbar.gen(), 5) + sage: g2 = UHP.get_geodesic(-1/2, Infinity) + sage: g1.intersection(g2) + [] """ start_1, end_1 = sorted(self.ideal_endpoints(), key=str) @@ -1339,7 +1362,24 @@ def intersection(self, other): C = A * B if C.classification() in ['hyperbolic', 'parabolic']: return [] - return C.fixed_point_set() + #Check wether the C fixed point lies in the geodesic segment + P = C.fixed_point_set() + p_real = P[0].coordinates().real() + s = self.start() + e = self.end() + if s.is_boundary(): + s_real = s.coordinates() + else: + s_real = s.coordinates().real() + if e.is_boundary(): + e_real = e.coordinates() + else: + e_real = e.coordinates().real() + if p_real>=s_real and p_real<=e_real: + return P + else: + #the point does not lie in the segment therefore return empty list + return[] def perpendicular_bisector(self): # UHP r""" @@ -1348,25 +1388,25 @@ def perpendicular_bisector(self): # UHP EXAMPLES:: - sage: UHP = HyperbolicPlane().UHP() - sage: g = UHP.random_geodesic() - sage: h = g.perpendicular_bisector() - sage: c = lambda x: x.coordinates() - sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) - True + sage: UHP = HyperbolicPlane().UHP() + sage: g = UHP.random_geodesic() + sage: h = g.perpendicular_bisector().complete() + sage: c = lambda x: x.coordinates() + sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) + True :: sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.get_geodesic(1+I,2+0.5*I) - sage: h = g.perpendicular_bisector() + sage: h = g.perpendicular_bisector().complete() sage: show(g.plot(color='blue')+h.plot(color='orange')) .. PLOT:: UHP = HyperbolicPlane().UHP() g = UHP.get_geodesic(1+I,2+0.5*I) - h = g.perpendicular_bisector() + h = g.perpendicular_bisector().complete() sphinx_plot(g.plot(color='blue')+h.plot(color='orange')) Infinite geodesics cannot be bisected:: From 8c220dbf9c335dbd91050743720604c25f42116b Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 7 Oct 2020 20:15:18 +0200 Subject: [PATCH 002/924] Refactor to break cyclic import in complex numbers --- src/sage/libs/mpmath/utils.pyx | 3 ++- src/sage/rings/complex_conversion.pyx | 23 +++++++++++++++++++++++ src/sage/rings/complex_double.pyx | 6 ++++-- src/sage/rings/complex_field.py | 2 +- src/sage/rings/complex_number.pyx | 21 --------------------- src/sage/rings/convert/mpfi.pyx | 2 +- 6 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 src/sage/rings/complex_conversion.pyx diff --git a/src/sage/libs/mpmath/utils.pyx b/src/sage/libs/mpmath/utils.pyx index f1a4740aed2..c50c703e236 100644 --- a/src/sage/libs/mpmath/utils.pyx +++ b/src/sage/libs/mpmath/utils.pyx @@ -14,7 +14,6 @@ from sage.structure.element cimport Element from sage.libs.mpfr cimport * from sage.libs.gmp.all cimport * -from sage.rings.complex_field import ComplexField from sage.rings.real_mpfr cimport RealField cpdef int bitcount(n): @@ -272,6 +271,7 @@ def mpmath_to_sage(x, prec): mpfr_from_mpfval(y.value, x._mpf_) return y elif hasattr(x, "_mpc_"): + from sage.rings.complex_field import ComplexField z = ComplexField(prec)(0) re, im = x._mpc_ mpfr_from_mpfval(z.__re, re) @@ -331,6 +331,7 @@ def sage_to_mpmath(x, prec): if isinstance(x, ComplexNumber): return x._mpmath_() else: + from sage.rings.complex_field import ComplexField x = ComplexField(prec)(x) return x._mpmath_() if isinstance(x, tuple) or isinstance(x, list): diff --git a/src/sage/rings/complex_conversion.pyx b/src/sage/rings/complex_conversion.pyx new file mode 100644 index 00000000000..2c0e5cc8f2e --- /dev/null +++ b/src/sage/rings/complex_conversion.pyx @@ -0,0 +1,23 @@ +from .complex_double cimport ComplexDoubleElement +from .complex_number cimport ComplexNumber +from sage.libs.mpfr cimport mpfr_get_d, MPFR_RNDN +from sage.structure.element cimport Element +from sage.categories.map cimport Map + +cdef class CCtoCDF(Map): + + cpdef Element _call_(self, x): + """ + EXAMPLES:: + + sage: from sage.rings.complex_conversion import CCtoCDF + sage: f = CCtoCDF(CC, CDF) # indirect doctest + sage: f(CC.0) + 1.0*I + sage: f(exp(pi*CC.0/4)) + 0.7071067811865476 + 0.7071067811865475*I + """ + z = ComplexDoubleElement.__new__(ComplexDoubleElement) + z._complex.real = mpfr_get_d((x).__re, MPFR_RNDN) + z._complex.imag = mpfr_get_d((x).__im, MPFR_RNDN) + return z \ No newline at end of file diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 9c454868c44..bc9a43b3d28 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -430,9 +430,11 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): elif RR.has_coerce_map_from(S): return FloatToCDF(RR) * RR._internal_coerce_map_from(S) elif isinstance(S, ComplexField_class) and S.prec() >= 53: - return complex_number.CCtoCDF(S, self) + from sage.rings.complex_conversion import CCtoCDF + return CCtoCDF(S, self) elif CC.has_coerce_map_from(S): - return complex_number.CCtoCDF(CC, self) * CC._internal_coerce_map_from(S) + from sage.rings.complex_conversion import CCtoCDF + return CCtoCDF(CC, self) * CC._internal_coerce_map_from(S) def _magma_init_(self, magma): r""" diff --git a/src/sage/rings/complex_field.py b/src/sage/rings/complex_field.py index 4d4c12b459c..538a967a445 100644 --- a/src/sage/rings/complex_field.py +++ b/src/sage/rings/complex_field.py @@ -21,7 +21,6 @@ from __future__ import absolute_import from .complex_number import ComplexNumber, RRtoCC -from .complex_double import ComplexDoubleElement from . import ring from .real_mpfr import RealNumber import weakref @@ -404,6 +403,7 @@ def _element_constructor_(self, x): """ if not isinstance(x, (RealNumber, tuple)): + from .complex_double import ComplexDoubleElement if isinstance(x, ComplexDoubleElement): return ComplexNumber(self, x.real(), x.imag()) elif isinstance(x, str): diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index f3242287052..37eff7a39fe 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -34,7 +34,6 @@ from sage.structure.element cimport FieldElement, RingElement, Element, ModuleEl from sage.structure.richcmp cimport rich_to_bool from sage.categories.map cimport Map -from .complex_double cimport ComplexDoubleElement from .real_mpfr cimport RealNumber import sage.misc.misc @@ -2675,26 +2674,6 @@ cdef class RRtoCC(Map): mpfr_set_ui(z.__im, 0, rnd) return z - -cdef class CCtoCDF(Map): - - cpdef Element _call_(self, x): - """ - EXAMPLES:: - - sage: from sage.rings.complex_number import CCtoCDF - sage: f = CCtoCDF(CC, CDF) # indirect doctest - sage: f(CC.0) - 1.0*I - sage: f(exp(pi*CC.0/4)) - 0.7071067811865476 + 0.7071067811865475*I - """ - z = ComplexDoubleElement.__new__(ComplexDoubleElement) - z._complex.real = mpfr_get_d((x).__re, MPFR_RNDN) - z._complex.imag = mpfr_get_d((x).__im, MPFR_RNDN) - return z - - cdef inline mp_exp_t min_exp_t(mp_exp_t a, mp_exp_t b): return a if a < b else b diff --git a/src/sage/rings/convert/mpfi.pyx b/src/sage/rings/convert/mpfi.pyx index bdf6d140712..e0106ef85aa 100644 --- a/src/sage/rings/convert/mpfi.pyx +++ b/src/sage/rings/convert/mpfi.pyx @@ -23,7 +23,6 @@ from sage.structure.element cimport Element, parent from ..integer cimport Integer from ..rational cimport Rational from ..real_mpfi cimport RealIntervalFieldElement, RealIntervalField_class -from ..complex_interval_field import ComplexIntervalField_class from ..real_mpfr cimport RealNumber from ..real_double cimport RealDoubleElement from ..complex_number cimport ComplexNumber @@ -188,6 +187,7 @@ cdef int mpfi_set_sage(mpfi_ptr re, mpfi_ptr im, x, field, int base) except -1: except AttributeError: pass else: + from ..complex_interval_field import ComplexIntervalField_class if not isinstance(field, ComplexIntervalField_class): field = field.complex_field() e = m(field) From 67c8cbb79521151d88e62ccafe95151e79f41bbb Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 28 Mar 2021 15:00:12 +0200 Subject: [PATCH 003/924] trac #30566: random spanning tree by weights --- src/sage/graphs/spanning_tree.pyx | 76 ++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 922ecc62ecb..e2b801e95e0 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -796,7 +796,7 @@ cpdef boruvka(G, wfunction=None, bint check=False, bint by_weight=True): return T -def random_spanning_tree(self, output_as_graph=False): +def random_spanning_tree(G, output_as_graph=False, by_weight=False, weight_function=None): r""" Return a random spanning tree of the graph. @@ -808,11 +808,27 @@ def random_spanning_tree(self, output_as_graph=False): edge `(i, j)` to the spanning tree, where `i` is the previous vertex in the random walk. + When ``by_weight`` is ``True`` or a weight function is given, the selection + of the neighbor is done proportionaly to the edge weights. + INPUT: + - ``G`` -- an undirected graph + - ``output_as_graph`` -- boolean (default: ``False``); whether to return a list of edges or a graph + - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges in + the graph are weighted, otherwise all edges have weight 1 + + - ``weight_function`` -- function (default: ``None``); a function that takes + as input an edge ``(u, v, l)`` and outputs its weight. If not ``None``, + ``by_weight`` is automatically set to ``True``. If ``None`` and + ``by_weight`` is ``True``, we use the edge label ``l`` , if ``l`` is not + ``None``, else ``1`` as a weight. The ``weight_function`` can be used to + transform the label into a weight (note that, if the weight returned is + not convertible to a float, an error is raised) + .. SEEALSO:: :meth:`~sage.graphs.generic_graph.GenericGraph.spanning_trees_count` @@ -846,6 +862,26 @@ def random_spanning_tree(self, output_as_graph=False): sage: T.set_pos(pos) sage: T.show(vertex_labels=False) + We can also use edge weights to change the probability of returning a + spanning tree:: + + sage: def foo(G, k): + ....: S = set() + ....: for _ in range(k): + ....: E = G.random_spanning_tree(by_weight=True) + ....: S.add(Graph(E).graph6_string()) + ....: return S + sage: K3 = graphs.CompleteGraph(3) + sage: for u, v in K3.edges(labels=False): + ....: K3.set_edge_label(u, v, randint(1, 2)) + sage: foo(K3, 100) == {'BW', 'Bg', 'Bo'} # random + True + sage: K4 = graphs.CompleteGraph(4) + sage: for u, v in K4.edges(labels=False): + ....: K4.set_edge_label(u, v, randint(1, 2)) + sage: print(len(foo(K4, 100))) # random + 16 + TESTS:: sage: G = Graph() @@ -861,21 +897,47 @@ def random_spanning_tree(self, output_as_graph=False): ValueError: works only for non-empty connected graphs """ from sage.misc.prandom import randint + from sage.misc.prandom import random from sage.graphs.graph import Graph - cdef int N = self.order() + cdef int N = G.order() - if not N or not self.is_connected(): + if not N or not G.is_connected(): raise ValueError('works only for non-empty connected graphs') - s = next(self.vertex_iterator()) + if G.order() == G.size() + 1: + # G is a tree + if output_as_graph: + return G.copy() + return list(G.edge_iterator(label=False)) + + if weight_function is not None: + by_weight = True + + if by_weight: + if weight_function is None: + def weight_function(e): + return 1 if e[2] is None else e[2] + + def next_neighbor(s): + p = random() * sum(weight_function(e) + for e in G.edge_iterator(s, sort_vertices=False)) + for e in G.edge_iterator(s, sort_vertices=False): + p -= weight_function(e) + if p <= 0: + break + return e[1] if e[0] == s else e[0] + + else: + def next_neighbor(s): + return G.neighbors(s)[randint(0, G.degree(s) - 1)] + + s = next(G.vertex_iterator()) cdef set found = set([s]) cdef int found_nr = 1 cdef list tree_edges = [] - cdef list neighbors while found_nr < N: - neighbors = self.neighbors(s) - new_s = neighbors[randint(0, len(neighbors) - 1)] + new_s = next_neighbor(s) if new_s not in found: found.add(new_s) found_nr += 1 From 26e16c7362a4a9833436bb36fc8b420da9ded798 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 14 Mar 2021 20:49:42 -0700 Subject: [PATCH 004/924] Add configure option --disable-sagelib --- build/make/Makefile.in | 1 - configure.ac | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 8ba89d78c74..f15eea4a139 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -232,7 +232,6 @@ base-toolchain: _clean-broken-gcc base # All targets except for the base packages all-sage: \ - sagelib \ $(INSTALLED_PACKAGE_INSTS) \ $(UNINSTALLED_PACKAGES_CLEANS) diff --git a/configure.ac b/configure.ac index e331c30435d..cc9303a7aac 100644 --- a/configure.ac +++ b/configure.ac @@ -428,6 +428,14 @@ AS_IF([test "x$enable_download_from_upstream_url" = "xyes"], [ ]) AC_SUBST([SAGE_SPKG_OPTIONS]) +AC_ARG_ENABLE([sagelib], + AS_HELP_STRING([--disable-sagelib], + [disable build of the Sage library and packages depending on it]), [ + for pkg in sagelib sage_docbuild; do + AS_VAR_SET([SAGE_ENABLE_$pkg], [$enableval]) + done + ]) + AC_ARG_ENABLE([notebook], AS_HELP_STRING([--disable-notebook], [disable build of the Jupyter notebook and related packages]), [ From 3dc9618377133f7983a374630cd3459568a3a10a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Jun 2020 14:34:45 -0700 Subject: [PATCH 005/924] pkgs/sage-conf_pypi: New --- build/bin/write-dockerfile.sh | 2 +- build/pkgs/sage_conf/spkg-src | 4 +- pkgs/sage-conf/.gitignore | 2 + pkgs/sage-conf/pyproject.toml | 3 + pkgs/sage-conf/sage_conf.py.in | 2 + pkgs/sage-conf_pypi/.gitignore | 5 ++ pkgs/sage-conf_pypi/MANIFEST.in | 16 ++++ pkgs/sage-conf_pypi/README.rst | 44 ++++++++++ pkgs/sage-conf_pypi/VERSION.txt | 1 + pkgs/sage-conf_pypi/bin/sage-env-config.in | 1 + pkgs/sage-conf_pypi/pyproject.toml | 3 + pkgs/sage-conf_pypi/sage_conf.py.in | 1 + .../sage_root/.homebrew-build-env | 1 + pkgs/sage-conf_pypi/sage_root/Makefile | 1 + pkgs/sage-conf_pypi/sage_root/README.md | 1 + pkgs/sage-conf_pypi/sage_root/VERSION.txt | 1 + pkgs/sage-conf_pypi/sage_root/bootstrap | 3 + pkgs/sage-conf_pypi/sage_root/build | 1 + pkgs/sage-conf_pypi/sage_root/config | 1 + pkgs/sage-conf_pypi/sage_root/configure | 1 + pkgs/sage-conf_pypi/sage_root/configure.ac | 1 + pkgs/sage-conf_pypi/sage_root/m4 | 1 + pkgs/sage-conf_pypi/sage_root/src/bin | 1 + .../sage_root/src/doc/bootstrap | 3 + pkgs/sage-conf_pypi/setup.cfg | 20 +++++ pkgs/sage-conf_pypi/setup.py | 82 +++++++++++++++++++ pkgs/sage-conf_pypi/tox.ini | 12 +++ 27 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 pkgs/sage-conf/pyproject.toml create mode 100644 pkgs/sage-conf_pypi/.gitignore create mode 100644 pkgs/sage-conf_pypi/MANIFEST.in create mode 100644 pkgs/sage-conf_pypi/README.rst create mode 120000 pkgs/sage-conf_pypi/VERSION.txt create mode 120000 pkgs/sage-conf_pypi/bin/sage-env-config.in create mode 100644 pkgs/sage-conf_pypi/pyproject.toml create mode 120000 pkgs/sage-conf_pypi/sage_conf.py.in create mode 120000 pkgs/sage-conf_pypi/sage_root/.homebrew-build-env create mode 120000 pkgs/sage-conf_pypi/sage_root/Makefile create mode 120000 pkgs/sage-conf_pypi/sage_root/README.md create mode 120000 pkgs/sage-conf_pypi/sage_root/VERSION.txt create mode 100755 pkgs/sage-conf_pypi/sage_root/bootstrap create mode 120000 pkgs/sage-conf_pypi/sage_root/build create mode 120000 pkgs/sage-conf_pypi/sage_root/config create mode 120000 pkgs/sage-conf_pypi/sage_root/configure create mode 120000 pkgs/sage-conf_pypi/sage_root/configure.ac create mode 120000 pkgs/sage-conf_pypi/sage_root/m4 create mode 120000 pkgs/sage-conf_pypi/sage_root/src/bin create mode 100755 pkgs/sage-conf_pypi/sage_root/src/doc/bootstrap create mode 100644 pkgs/sage-conf_pypi/setup.cfg create mode 100644 pkgs/sage-conf_pypi/setup.py create mode 100644 pkgs/sage-conf_pypi/tox.ini diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index b381f677a48..908b7a572bf 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -186,7 +186,7 @@ FROM with-system-packages as bootstrapped #:bootstrapping: RUN mkdir -p /sage WORKDIR /sage -ADD Makefile VERSION.txt README.md bootstrap configure.ac sage ./ +ADD Makefile VERSION.txt COPYING.txt condarc.yml README.md bootstrap configure.ac sage .homebrew-build-env tox.ini Pipfile.m4 ./ ADD src/doc/bootstrap src/doc/bootstrap ADD src/bin src/bin ADD m4 ./m4 diff --git a/build/pkgs/sage_conf/spkg-src b/build/pkgs/sage_conf/spkg-src index cad851bd9d5..af6e8b16342 100755 --- a/build/pkgs/sage_conf/spkg-src +++ b/build/pkgs/sage_conf/spkg-src @@ -15,7 +15,5 @@ fi # Exit on failure set -e -cd build/pkgs/sage_conf - -cd src +cd pkgs/sage-conf_pypi python3 -u setup.py --no-user-cfg sdist --dist-dir "$SAGE_DISTFILES" diff --git a/pkgs/sage-conf/.gitignore b/pkgs/sage-conf/.gitignore index c94082c9d9a..2975b16e54b 100644 --- a/pkgs/sage-conf/.gitignore +++ b/pkgs/sage-conf/.gitignore @@ -1,2 +1,4 @@ /sage_conf.py /setup.cfg +/build +/*.egg-info diff --git a/pkgs/sage-conf/pyproject.toml b/pkgs/sage-conf/pyproject.toml new file mode 100644 index 00000000000..9787c3bdf00 --- /dev/null +++ b/pkgs/sage-conf/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sage-conf/sage_conf.py.in b/pkgs/sage-conf/sage_conf.py.in index 0686462c96a..1b85d2e7c64 100644 --- a/pkgs/sage-conf/sage_conf.py.in +++ b/pkgs/sage-conf/sage_conf.py.in @@ -44,6 +44,8 @@ THREEJS_DIR = SAGE_LOCAL + "/share/threejs" OPENMP_CFLAGS = "@OPENMP_CFLAGS@" OPENMP_CXXFLAGS = "@OPENMP_CXXFLAGS@" +SAGE_SPKG_WHEELS = "@prefix@/var/lib/sage/wheels" + # Entry point 'sage-config'. It does not depend on any packages. def _main(): diff --git a/pkgs/sage-conf_pypi/.gitignore b/pkgs/sage-conf_pypi/.gitignore new file mode 100644 index 00000000000..3bb4a6af5d1 --- /dev/null +++ b/pkgs/sage-conf_pypi/.gitignore @@ -0,0 +1,5 @@ +/sage_conf.py +/build +/dist +/*.egg-info +/.tox diff --git a/pkgs/sage-conf_pypi/MANIFEST.in b/pkgs/sage-conf_pypi/MANIFEST.in new file mode 100644 index 00000000000..59c5e9d8290 --- /dev/null +++ b/pkgs/sage-conf_pypi/MANIFEST.in @@ -0,0 +1,16 @@ +graft sage_root +# These sources are not needed because individual distributions of these are made. +prune sage_root/build/pkgs/*/src* +# Except for this one because config.status writes there +graft sage_root/build/pkgs/sage_conf/src +prune sage_root/build/pkgs/sage_conf/src/build +# And this one because of declared dependencies on source files in Makefile.in +graft sage_root/build/pkgs/sage_docbuild/src +prune sage_root/build/pkgs/sage_docbuild/src/build +prune sage_root/build/pkgs/sage_docbuild/src/sage_docbuild.egg-info +# +global-exclude .tox +global-exclude *~* +global-exclude *.bak +global-exclude __pycache__ +global-exclude *.pyc diff --git a/pkgs/sage-conf_pypi/README.rst b/pkgs/sage-conf_pypi/README.rst new file mode 100644 index 00000000000..81cf41c321e --- /dev/null +++ b/pkgs/sage-conf_pypi/README.rst @@ -0,0 +1,44 @@ +sage_conf: Configuration module for the SageMath library (sdist version) +======================================================================== + +Description +----------- + +This package provides: + +- a single Python module, ``sage_conf``, providing configuration information + to the SageMath library at the time of its installation and at its runtime + +- a console script ``sage-config``, for querying the variables of ``sage_conf`` + from the shell + +- a sourcable shell script ``sage-env-config``, providing additional configuration + information in the form of environment variables + +This version of the package is suitable to be put as a source distribution on PyPI. + +On installation (or building a wheel), it invokes ``sage_bootstrap`` to establish +a build tree (``SAGE_ROOT``) and installation tree (``SAGE_LOCAL``) for +the SageMath distribution. By default, it uses a subdirectory of ``$HOME/.sage`` +that is specific to the version of the distribution and the version of Python in +use. If several virtual environments over the same version of Python install +``sage_conf``, they will share these trees. + +After installation of ``sage_conf``, a wheelhouse containing wheels of +various libraries is available; type ``ls $(sage-config +SAGE_SPKG_WHEELS)`` to list them and ``pip install $(sage-config +SAGE_SPKG_WHEELS)/*.whl`` to install them. After this, you can install the Sage library, for example, using ``pip install sagemath-standard``. + + +License +------- + +GNU General Public License (GPL) v3 or later + +Upstream Contact +---------------- + +https://www.sagemath.org + +This package is included in the source code of the Sage distribution, +in ``src/pkgs/sage_conf-pypi/``. diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt new file mode 120000 index 00000000000..43f4773d7de --- /dev/null +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -0,0 +1 @@ +../../src/VERSION.txt \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/bin/sage-env-config.in b/pkgs/sage-conf_pypi/bin/sage-env-config.in new file mode 120000 index 00000000000..4fd6ef7d1fd --- /dev/null +++ b/pkgs/sage-conf_pypi/bin/sage-env-config.in @@ -0,0 +1 @@ +../sage-conf/bin/sage-env-config.in \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/pyproject.toml b/pkgs/sage-conf_pypi/pyproject.toml new file mode 100644 index 00000000000..9787c3bdf00 --- /dev/null +++ b/pkgs/sage-conf_pypi/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sage-conf_pypi/sage_conf.py.in b/pkgs/sage-conf_pypi/sage_conf.py.in new file mode 120000 index 00000000000..ed20b4df7fd --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_conf.py.in @@ -0,0 +1 @@ +../sage-conf/sage_conf.py.in \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/.homebrew-build-env b/pkgs/sage-conf_pypi/sage_root/.homebrew-build-env new file mode 120000 index 00000000000..18573e75488 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/.homebrew-build-env @@ -0,0 +1 @@ +../../../.homebrew-build-env \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/Makefile b/pkgs/sage-conf_pypi/sage_root/Makefile new file mode 120000 index 00000000000..62a7b627cbc --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/Makefile @@ -0,0 +1 @@ +../../../Makefile \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/README.md b/pkgs/sage-conf_pypi/sage_root/README.md new file mode 120000 index 00000000000..8a33348c7d8 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/README.md @@ -0,0 +1 @@ +../../../README.md \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/VERSION.txt b/pkgs/sage-conf_pypi/sage_root/VERSION.txt new file mode 120000 index 00000000000..c4fcb84c1a7 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/VERSION.txt @@ -0,0 +1 @@ +../../../VERSION.txt \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/bootstrap b/pkgs/sage-conf_pypi/sage_root/bootstrap new file mode 100755 index 00000000000..38df9dbea8a --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/bootstrap @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +# This version of the bootstrap script does nothing. +exit 0 diff --git a/pkgs/sage-conf_pypi/sage_root/build b/pkgs/sage-conf_pypi/sage_root/build new file mode 120000 index 00000000000..44735d58664 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/build @@ -0,0 +1 @@ +../../../build \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/config b/pkgs/sage-conf_pypi/sage_root/config new file mode 120000 index 00000000000..101d543820c --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/config @@ -0,0 +1 @@ +../../../config \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/configure b/pkgs/sage-conf_pypi/sage_root/configure new file mode 120000 index 00000000000..5cc41f0cbb4 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/configure @@ -0,0 +1 @@ +../../../configure \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/configure.ac b/pkgs/sage-conf_pypi/sage_root/configure.ac new file mode 120000 index 00000000000..663513d0394 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/configure.ac @@ -0,0 +1 @@ +../../../configure.ac \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/m4 b/pkgs/sage-conf_pypi/sage_root/m4 new file mode 120000 index 00000000000..af1ea766194 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/m4 @@ -0,0 +1 @@ +../../../m4 \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/src/bin b/pkgs/sage-conf_pypi/sage_root/src/bin new file mode 120000 index 00000000000..459186de7e0 --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/src/bin @@ -0,0 +1 @@ +../../../../src/bin \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/sage_root/src/doc/bootstrap b/pkgs/sage-conf_pypi/sage_root/src/doc/bootstrap new file mode 100755 index 00000000000..2a2bb398b9a --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/src/doc/bootstrap @@ -0,0 +1,3 @@ +#! /usr/bin/env bash +# This version of the src/doc/bootstrap script does nothing. +exit 0 diff --git a/pkgs/sage-conf_pypi/setup.cfg b/pkgs/sage-conf_pypi/setup.cfg new file mode 100644 index 00000000000..859f59ddee3 --- /dev/null +++ b/pkgs/sage-conf_pypi/setup.cfg @@ -0,0 +1,20 @@ +[metadata] +name = sage_conf +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Configuration module for the SageMath library +long_description = file: README.rst +license = GNU General Public License (GPL) v3 or later +author = The Sage Developers +author_email = sage-support@googlegroups.com +url = https://www.sagemath.org + +[options] +py_modules = + sage_conf + +scripts = + bin/sage-env-config + +[options.entry_points] +console_scripts = + sage-config = sage_conf:_main diff --git a/pkgs/sage-conf_pypi/setup.py b/pkgs/sage-conf_pypi/setup.py new file mode 100644 index 00000000000..3592a26ddbd --- /dev/null +++ b/pkgs/sage-conf_pypi/setup.py @@ -0,0 +1,82 @@ +import os +import sys +import shutil + +from setuptools import setup +from distutils.command.build_scripts import build_scripts as distutils_build_scripts +from distutils.command.build_py import build_py as distutils_build_py +from distutils.errors import (DistutilsSetupError, DistutilsModuleError, + DistutilsOptionError) + +class build_py(distutils_build_py): + + def run(self): + DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), '.sage')) + HERE = os.path.dirname(__file__) + with open(os.path.join(HERE, 'VERSION.txt')) as f: + sage_version = f.read().strip() + # For convenience, set up the homebrew env automatically. This is a no-op if homebrew is not present. + SETENV = '(. ./.homebrew-build-env 2> /dev/null || :)' + # Until pynac is repackaged as a pip-installable package (#30534), SAGE_LOCAL still has to be specific to + # the Python version. Note that as of pynac-0.7.26.sage-2020-04-03, on Cygwin, pynac is linked through + # to libpython; whereas on all other platforms, it is not linked through, so we only key it to the SOABI. + soabi = sysconfig.get_config_var('SOABI') + if sys.platform == 'cygwin': + libdir_tag = sysconfig.get_config_var('LIBDIR').replace(' ', '-').replace('\\', '-').replace('/', '-') + ldversion = sysconfig.get_config_var('LDVERSION') + python_tag = f'{libdir_tag}-{ldversion}' + else: + python_tag = soabi + # TODO: These two should be user-configurable with options passed to "setup.py install" + SAGE_ROOT = os.path.join(DOT_SAGE, f'sage-{sage_version}-{python_tag}') + SAGE_LOCAL = os.path.join(SAGE_ROOT, 'local') + if os.path.exists(os.path.join(SAGE_ROOT, 'config.status')): + print(f'Reusing SAGE_ROOT={SAGE_ROOT}') + else: + # config.status and other configure output has to be writable. + # So (until the Sage distribution supports VPATH builds - #21469), we have to make a copy of sage_root. + try: + shutil.copytree('sage_root', SAGE_ROOT) # will fail if already exists + except Exception: + raise DistutilsSetupError(f"the directory SAGE_ROOT={SAGE_ROOT} already exists but it is not configured. Please remove it and try again.") + cmd = f"cd {SAGE_ROOT} && {SETENV} && ./configure --prefix={SAGE_LOCAL} --with-python={sys.executable} --enable-build-as-root --with-system-python3=force --disable-notebook --disable-sagelib" + print(f"Running {cmd}") + if os.system(cmd) != 0: + raise DistutilsSetupError("configure failed") + # Here we run "make build" -- which builds everything except for sagelib because we + # used configure --disable-sagelib + # Alternative: + # "make build-local" only builds the non-Python packages of the Sage distribution. + # It still makes an (empty) venv in SAGE_LOCAL, which is unused by default; + # but then a user could use "make build-venv" to build compatible wheels for all Python packages. + # TODO: A target to only build wheels of tricky packages + # (that use native libraries shared with other packages). + SETMAKE = 'if [ -z "$MAKE" ]; then export MAKE="make -j$(PATH=build/bin:$PATH build/bin/sage-build-num-threads | cut -d" " -f 2)"; fi' + cmd = f'cd {SAGE_ROOT} && {SETENV} && {SETMAKE} && $MAKE V=0 build' + if os.system(cmd) != 0: + raise DistutilsSetupError("make build-local failed") + + # Install configuration + shutil.copyfile(os.path.join(SAGE_ROOT, 'build', 'pkgs', 'sage_conf', 'src', 'sage_conf.py'), + os.path.join(HERE, 'sage_conf.py')) + if not self.distribution.py_modules: + self.py_modules = self.distribution.py_modules = [] + self.distribution.py_modules.append('sage_conf') + shutil.copyfile(os.path.join(SAGE_ROOT, 'src', 'bin', 'sage-env-config'), + os.path.join(HERE, 'bin', 'sage-env-config')) + distutils_build_py.run(self) + +class build_scripts(distutils_build_scripts): + + def run(self): + self.distribution.scripts.append(os.path.join('bin', 'sage-env-config')) + if not self.distribution.entry_points: + self.entry_points = self.distribution.entry_points = dict() + if 'console_scripts' not in self.distribution.entry_points: + self.distribution.entry_points['console_scripts'] = [] + self.distribution.entry_points['console_scripts'].append('sage-config=sage_conf:_main') + distutils_build_scripts.run(self) + +setup( + cmdclass=dict(build_py=build_py, build_scripts=build_scripts) +) diff --git a/pkgs/sage-conf_pypi/tox.ini b/pkgs/sage-conf_pypi/tox.ini new file mode 100644 index 00000000000..b530bd10554 --- /dev/null +++ b/pkgs/sage-conf_pypi/tox.ini @@ -0,0 +1,12 @@ +[tox] +envlist = python + +[testenv] +passenv = + MAKE + +setenv = + HOME={envdir} + +commands = + sage-config From ba67c92a5056241e0273feb9b701fb0c56b48683 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 00:29:09 -0700 Subject: [PATCH 006/924] Put pkgs/sage-conf in the root --- pkgs/sage-conf_pypi/sage_root/pkgs/sage-conf | 1 + 1 file changed, 1 insertion(+) create mode 120000 pkgs/sage-conf_pypi/sage_root/pkgs/sage-conf diff --git a/pkgs/sage-conf_pypi/sage_root/pkgs/sage-conf b/pkgs/sage-conf_pypi/sage_root/pkgs/sage-conf new file mode 120000 index 00000000000..a5beefae37f --- /dev/null +++ b/pkgs/sage-conf_pypi/sage_root/pkgs/sage-conf @@ -0,0 +1 @@ +../../../sage-conf \ No newline at end of file From e825a9f554fcc4eda8665fa9b9f708d6382b324d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 11:44:19 -0700 Subject: [PATCH 007/924] pkgs/sage-conf_pypi: Fixup MANIFEST.in, setup.py --- pkgs/sage-conf_pypi/MANIFEST.in | 1 + pkgs/sage-conf_pypi/setup.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkgs/sage-conf_pypi/MANIFEST.in b/pkgs/sage-conf_pypi/MANIFEST.in index 59c5e9d8290..bb46a994f5d 100644 --- a/pkgs/sage-conf_pypi/MANIFEST.in +++ b/pkgs/sage-conf_pypi/MANIFEST.in @@ -1,3 +1,4 @@ +include VERSION.txt graft sage_root # These sources are not needed because individual distributions of these are made. prune sage_root/build/pkgs/*/src* diff --git a/pkgs/sage-conf_pypi/setup.py b/pkgs/sage-conf_pypi/setup.py index 3592a26ddbd..631bf64149a 100644 --- a/pkgs/sage-conf_pypi/setup.py +++ b/pkgs/sage-conf_pypi/setup.py @@ -1,14 +1,16 @@ import os import sys import shutil +import sysconfig from setuptools import setup from distutils.command.build_scripts import build_scripts as distutils_build_scripts -from distutils.command.build_py import build_py as distutils_build_py +from setuptools.command.build_py import build_py as setuptools_build_py +from setuptools.command.egg_info import egg_info as setuptools_egg_info from distutils.errors import (DistutilsSetupError, DistutilsModuleError, DistutilsOptionError) -class build_py(distutils_build_py): +class build_py(setuptools_build_py): def run(self): DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), '.sage')) @@ -52,7 +54,7 @@ def run(self): # TODO: A target to only build wheels of tricky packages # (that use native libraries shared with other packages). SETMAKE = 'if [ -z "$MAKE" ]; then export MAKE="make -j$(PATH=build/bin:$PATH build/bin/sage-build-num-threads | cut -d" " -f 2)"; fi' - cmd = f'cd {SAGE_ROOT} && {SETENV} && {SETMAKE} && $MAKE V=0 build' + cmd = f'cd {SAGE_ROOT} && {SETENV} && {SETMAKE} && $MAKE V=0 build-local' if os.system(cmd) != 0: raise DistutilsSetupError("make build-local failed") From ad8bad166890a0df0794d7eadf1d383f3c7fd4a8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 12:01:36 -0700 Subject: [PATCH 008/924] Makefile (pypi-sdists): New --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index 567bffbffd0..6aaf4a0b4e1 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,13 @@ download: dist: build/make/Makefile ./sage --sdist +pypi-sdists: + ./sage --sh build/pkgs/sage_conf/spkg-src + ./sage --sh build/pkgs/sage_sws2rst/spkg-src + ./sage --sh build/pkgs/sage_docbuild/spkg-src + ./sage --sh build/pkgs/sagelib/spkg-src + @echo "Built sdists are in upstream/" + # ssl: build Sage, and also install pyOpenSSL. This is necessary for # running the secure notebook. This make target requires internet # access. Note that this requires that your system have OpenSSL From 8c0ccf319cf038e836c38f494d380f30eb5413ca Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Apr 2021 17:39:03 -0700 Subject: [PATCH 009/924] src/pkgs/sage_conf*/README.rst: Unify the files build/pkgs/sagelib/src/README.rst: Link to sage-conf on pypi Signed-off-by: Matthias Koeppe Fixup: Add missing symlinks src/pkgs/sage_conf-{pypi,relocatable}/README.rst --- pkgs/sage-conf/README.rst | 83 ++++++++++++++++++++++++++++--- pkgs/sage-conf_pypi/README.rst | 45 +---------------- pkgs/sagemath-standard/README.rst | 5 +- 3 files changed, 80 insertions(+), 53 deletions(-) mode change 100644 => 120000 pkgs/sage-conf_pypi/README.rst diff --git a/pkgs/sage-conf/README.rst b/pkgs/sage-conf/README.rst index 1c08375b03a..7b28f9f0de6 100644 --- a/pkgs/sage-conf/README.rst +++ b/pkgs/sage-conf/README.rst @@ -1,10 +1,10 @@ -sage_conf: Configuration module for the SageMath library -======================================================== +sage_conf: Configuration module for the SageMath library (distributable version) +================================================================================ Description ----------- -This package provides: +This distribution package provides: - a single Python module, ``sage_conf``, providing configuration information to the SageMath library at the time of its installation and at its runtime @@ -15,10 +15,77 @@ This package provides: - a sourcable shell script ``sage-env-config``, providing additional configuration information in the form of environment variables -This version of the package is generated by the Sage distribution's ``configure`` -script. Downstream packagers and advanced developers and users may want to provide -their own implementation of the package to support the intended deployment of -the SageMath library. +The ``sage_conf`` distribution package is polymorphic: It has several implementations. + + +sage_conf sdist on PyPI +----------------------- + +This implementation of the ``sage_conf`` distribution package comes from +https://trac.sagemath.org/ticket/29039, which adds the directory +``src/pkgs/sage_conf-pypi/``. + +On installation (or building a wheel), it invokes ``sage_bootstrap`` to establish +a build tree (``SAGE_ROOT``) and installation tree (``SAGE_LOCAL``) for +the SageMath distribution. By default, it uses a subdirectory of ``$HOME/.sage`` +that is specific to the version of the distribution and the version of Python in +use. If several virtual environments over the same version of Python install +``sage_conf``, they will share these trees. + +After installation of ``sage_conf``, a wheelhouse containing wheels of +various libraries is available; type ``ls $(sage-config +SAGE_SPKG_WHEELS)`` to list them and ``pip install $(sage-config +SAGE_SPKG_WHEELS)/*.whl`` to install them. After this, you can install the Sage +library, for example, using ``pip install sagemath-standard``. + + +sage_conf wheels +---------------- + +Prebuilt binary wheels of the ``sage_conf`` distribution package are available +at https://github.com/sagemath/sage-wheels/releases/ + +This implementation of ``sage_conf`` comes from https://trac.sagemath.org/ticket/31396, +which adds the directory ``src/pkgs/sage_conf-relocatable/``. + +On building a wheel, it invokes ``sage_bootstrap`` to establish a +build and installation tree (``SAGE_ROOT``, ``SAGE_LOCAL``) in a +subdirectory of the directory ``/var/tmp/``, whose name is specific to +the version of the distribution and the version of Python in use. + +The wheel distributes a copy of the prebuilt ``SAGE_ROOT`` and +``SAGE_LOCAL``. Importing ``sage_conf`` (or using the installed +``sage-config`` script), makes sure that a symlink from the +``/var/tmp`` location to the actual persistent installation location +is created. As the relocated libraries and programs contain the +hardcoded path ``SAGE_LOCAL`` in various ways (including as rpaths), +this symlink is necessary for the prebuilt libraries and programs to +work. + +``/var/tmp`` is a sticky directory on all Linux distributions +following the Filesystem Hierarchy Standard, as well as on macOS and +on Cygwin. On multi-user systems, only one user can use a given +version of the distribution; other installation schemes are recommended +for systems with multiple Sage users. + + +sage_conf in the SageMath distribution +-------------------------------------- + +The original version of the distribution package ``sage_conf`` is used +internally in the SageMath distribution. It is provided in the directory +``build/pkgs/sage_conf/src``. This version of the package is generated +by the Sage distribution's ``configure`` +script. + + +sage_conf in downstream distributions +------------------------------------- + +Downstream packagers and advanced developers and users may want to provide +their own implementation of the distribution package to support the intended +deployment of the SageMath library. + License ------- @@ -31,4 +98,4 @@ Upstream Contact https://www.sagemath.org This package is included in the source code of the Sage distribution, -in ``/sage_conf``. +in ``pkgs/sage-conf``. diff --git a/pkgs/sage-conf_pypi/README.rst b/pkgs/sage-conf_pypi/README.rst deleted file mode 100644 index 81cf41c321e..00000000000 --- a/pkgs/sage-conf_pypi/README.rst +++ /dev/null @@ -1,44 +0,0 @@ -sage_conf: Configuration module for the SageMath library (sdist version) -======================================================================== - -Description ------------ - -This package provides: - -- a single Python module, ``sage_conf``, providing configuration information - to the SageMath library at the time of its installation and at its runtime - -- a console script ``sage-config``, for querying the variables of ``sage_conf`` - from the shell - -- a sourcable shell script ``sage-env-config``, providing additional configuration - information in the form of environment variables - -This version of the package is suitable to be put as a source distribution on PyPI. - -On installation (or building a wheel), it invokes ``sage_bootstrap`` to establish -a build tree (``SAGE_ROOT``) and installation tree (``SAGE_LOCAL``) for -the SageMath distribution. By default, it uses a subdirectory of ``$HOME/.sage`` -that is specific to the version of the distribution and the version of Python in -use. If several virtual environments over the same version of Python install -``sage_conf``, they will share these trees. - -After installation of ``sage_conf``, a wheelhouse containing wheels of -various libraries is available; type ``ls $(sage-config -SAGE_SPKG_WHEELS)`` to list them and ``pip install $(sage-config -SAGE_SPKG_WHEELS)/*.whl`` to install them. After this, you can install the Sage library, for example, using ``pip install sagemath-standard``. - - -License -------- - -GNU General Public License (GPL) v3 or later - -Upstream Contact ----------------- - -https://www.sagemath.org - -This package is included in the source code of the Sage distribution, -in ``src/pkgs/sage_conf-pypi/``. diff --git a/pkgs/sage-conf_pypi/README.rst b/pkgs/sage-conf_pypi/README.rst new file mode 120000 index 00000000000..2ef14860f06 --- /dev/null +++ b/pkgs/sage-conf_pypi/README.rst @@ -0,0 +1 @@ +../sage_conf/README.rst \ No newline at end of file diff --git a/pkgs/sagemath-standard/README.rst b/pkgs/sagemath-standard/README.rst index 52e0533c8c2..5709c22249b 100644 --- a/pkgs/sagemath-standard/README.rst +++ b/pkgs/sagemath-standard/README.rst @@ -22,7 +22,10 @@ About this experimental pip-installable source distribution This pip-installable source distribution `sagemath-standard` is an experimental distribution of the Sage Library. Use at your own risk. -Building `sagemath-standard` has a large number of system packages as prerequisites. See https://doc.sagemath.org/html/en/installation/source.html#linux-recommended-installation +Building `sagemath-standard` has a large number of system packages as prerequisites. +See https://doc.sagemath.org/html/en/installation/source.html#linux-recommended-installation for partial lists for various systems. +The connection to the system environment is facilitated through the https://pypi.org/project/sage-conf/ distribution package. + A modularization effort is in progress with the goal of making it possible to install parts of the Sage Library with fewer prerequisites. https://trac.sagemath.org/ticket/29705 From 2181f10720e2c6531b33d84be0cc0aeb8e660030 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 12:59:56 -0700 Subject: [PATCH 010/924] pkgs/sage-conf_pypi: Fixup --- pkgs/sage-conf_pypi/MANIFEST.in | 1 + pkgs/sage-conf_pypi/README.rst | 2 +- pkgs/sage-conf_pypi/bin/sage-env-config.in | 2 +- pkgs/sage-conf_pypi/setup.cfg | 6 +----- pkgs/sage-conf_pypi/setup.py | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pkgs/sage-conf_pypi/MANIFEST.in b/pkgs/sage-conf_pypi/MANIFEST.in index bb46a994f5d..744d661c092 100644 --- a/pkgs/sage-conf_pypi/MANIFEST.in +++ b/pkgs/sage-conf_pypi/MANIFEST.in @@ -1,4 +1,5 @@ include VERSION.txt +graft bin graft sage_root # These sources are not needed because individual distributions of these are made. prune sage_root/build/pkgs/*/src* diff --git a/pkgs/sage-conf_pypi/README.rst b/pkgs/sage-conf_pypi/README.rst index 2ef14860f06..feda886cd36 120000 --- a/pkgs/sage-conf_pypi/README.rst +++ b/pkgs/sage-conf_pypi/README.rst @@ -1 +1 @@ -../sage_conf/README.rst \ No newline at end of file +../sage-conf/README.rst \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/bin/sage-env-config.in b/pkgs/sage-conf_pypi/bin/sage-env-config.in index 4fd6ef7d1fd..59e39e7e944 120000 --- a/pkgs/sage-conf_pypi/bin/sage-env-config.in +++ b/pkgs/sage-conf_pypi/bin/sage-env-config.in @@ -1 +1 @@ -../sage-conf/bin/sage-env-config.in \ No newline at end of file +../../sage-conf/bin/sage-env-config.in \ No newline at end of file diff --git a/pkgs/sage-conf_pypi/setup.cfg b/pkgs/sage-conf_pypi/setup.cfg index 859f59ddee3..a024eb955a0 100644 --- a/pkgs/sage-conf_pypi/setup.cfg +++ b/pkgs/sage-conf_pypi/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = sage_conf +name = sage-conf version = file: VERSION.txt description = Sage: Open Source Mathematics Software: Configuration module for the SageMath library long_description = file: README.rst @@ -14,7 +14,3 @@ py_modules = scripts = bin/sage-env-config - -[options.entry_points] -console_scripts = - sage-config = sage_conf:_main diff --git a/pkgs/sage-conf_pypi/setup.py b/pkgs/sage-conf_pypi/setup.py index 631bf64149a..2ed29f0d500 100644 --- a/pkgs/sage-conf_pypi/setup.py +++ b/pkgs/sage-conf_pypi/setup.py @@ -66,7 +66,7 @@ def run(self): self.distribution.py_modules.append('sage_conf') shutil.copyfile(os.path.join(SAGE_ROOT, 'src', 'bin', 'sage-env-config'), os.path.join(HERE, 'bin', 'sage-env-config')) - distutils_build_py.run(self) + setuptools_build_py.run(self) class build_scripts(distutils_build_scripts): From 44b1e8d88affb78b8c06f478f79d2414b4efa232 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 17:18:52 -0700 Subject: [PATCH 011/924] Makefile (pypi-sdists): Add dependency on sage_setup --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6aaf4a0b4e1..0b8db0d585d 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ download: dist: build/make/Makefile ./sage --sdist -pypi-sdists: +pypi-sdists: sage_setup ./sage --sh build/pkgs/sage_conf/spkg-src ./sage --sh build/pkgs/sage_sws2rst/spkg-src ./sage --sh build/pkgs/sage_docbuild/spkg-src From 1596cb58ab2eb42e62edabc5e94c6bf9e15a46ac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 28 Jun 2021 08:48:43 -0700 Subject: [PATCH 012/924] pkgs/sage-conf_pypi/setup.py: Use --enable-download-from-upstream-url --- pkgs/sage-conf_pypi/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/sage-conf_pypi/setup.py b/pkgs/sage-conf_pypi/setup.py index 2ed29f0d500..4a22b39b694 100644 --- a/pkgs/sage-conf_pypi/setup.py +++ b/pkgs/sage-conf_pypi/setup.py @@ -41,7 +41,7 @@ def run(self): shutil.copytree('sage_root', SAGE_ROOT) # will fail if already exists except Exception: raise DistutilsSetupError(f"the directory SAGE_ROOT={SAGE_ROOT} already exists but it is not configured. Please remove it and try again.") - cmd = f"cd {SAGE_ROOT} && {SETENV} && ./configure --prefix={SAGE_LOCAL} --with-python={sys.executable} --enable-build-as-root --with-system-python3=force --disable-notebook --disable-sagelib" + cmd = f"cd {SAGE_ROOT} && {SETENV} && ./configure --prefix={SAGE_LOCAL} --with-python={sys.executable} --enable-build-as-root --enable-download-from-upstream-url --with-system-python3=force --disable-notebook --disable-sagelib" print(f"Running {cmd}") if os.system(cmd) != 0: raise DistutilsSetupError("configure failed") From 4062f9ae87745b2af9b6c2f12aa33519010a1080 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 16 Jul 2021 21:14:18 +0200 Subject: [PATCH 013/924] refactor element construction in term monoids --- src/sage/rings/asymptotic/asymptotic_ring.py | 24 +- src/sage/rings/asymptotic/term_monoid.py | 592 +++++++++++++++++-- 2 files changed, 547 insertions(+), 69 deletions(-) diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 37913d563cf..da5ea3b606e 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -641,8 +641,10 @@ def __init__(self, parent, summands, simplify=True, convert=True): ValueError: Cannot include 1/2 with parent Exact Term Monoid x^QQ with coefficients in Rational Field in Asymptotic Ring over Integer Ring - > *previous* ValueError: 1/2 is not a coefficient in - Exact Term Monoid x^QQ with coefficients in Integer Ring. + > *previous* ValueError: Cannot create ExactTerm(1) + since given coefficient 1/2 is not valid in + Exact Term Monoid x^QQ with coefficients in Integer Ring. + >> *previous* TypeError: no conversion of this rational to integer Check :trac:`19921`:: @@ -1080,7 +1082,9 @@ def monomial_coefficient(self, monomial): ValueError: Cannot include n with parent Exact Term Monoid n^QQ with coefficients in Rational Field in Asymptotic Ring over Rational Field - > *previous* ValueError: n is not in Growth Group m^QQ. + > *previous* ValueError: Growth n is not valid in + Exact Term Monoid m^QQ with coefficients in Rational Field. + >> *previous* ValueError: n is not in Growth Group m^QQ. Only monomials are allowed:: @@ -3071,8 +3075,10 @@ def map_coefficients(self, f, new_coefficient_ring=None): sage: a.map_coefficients(lambda c: 1/c) Traceback (most recent call last): ... - ValueError: ... is not a coefficient in + ValueError: Cannot create ExactTerm(n^3) since + given coefficient 1/2 is not valid in Exact Term Monoid n^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer """ def mapping(term): T = term.parent().change_parameter( @@ -3925,7 +3931,7 @@ def _element_constructor_(self, data, simplify=True, convert=True): ... ValueError: Polynomial y is not in Asymptotic Ring over Integer Ring - > *previous* ValueError: Growth y is not in + > *previous* ValueError: Growth y is not valid in Exact Term Monoid x^ZZ with coefficients in Integer Ring. >> *previous* ValueError: y is not in Growth Group x^ZZ. @@ -3968,7 +3974,7 @@ def _element_constructor_(self, data, simplify=True, convert=True): ... ValueError: Polynomial a + c is not in Asymptotic Ring over Rational Field - > *previous* ValueError: Growth c is not in + > *previous* ValueError: Growth c is not valid in Exact Term Monoid a^ZZ * b^ZZ with coefficients in Rational Field. >> *previous* ValueError: c is not in Growth Group a^ZZ * b^ZZ. >...> *previous* ValueError: c is not in any of the factors @@ -3986,7 +3992,9 @@ def _element_constructor_(self, data, simplify=True, convert=True): ValueError: Cannot include m^3 with parent Exact Term Monoid m^ZZ with coefficients in Integer Ring in Asymptotic Ring over Rational Field - > *previous* ValueError: m^3 is not in Growth Group n^ZZ. + > *previous* ValueError: Growth m^3 is not valid in + Exact Term Monoid n^ZZ with coefficients in Rational Field. + >> *previous* ValueError: m^3 is not in Growth Group n^ZZ. :: @@ -4542,7 +4550,7 @@ def create_summand(self, type, data=None, **kwds): sage: R.create_summand('O', growth=42*x^2, coefficient=1) Traceback (most recent call last): ... - ValueError: Growth 42*x^2 is not in O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + ValueError: Growth 42*x^2 is not valid in O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. > *previous* ValueError: 42*x^2 is not in Growth Group x^ZZ. :: diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index d56e1d91ea6..f66ee0d440e 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -199,6 +199,7 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +from sage.misc.cachefunc import cached_method from sage.rings.big_oh import O from sage.structure.element import MultiplicativeGroupElement from sage.structure.factory import UniqueFactory @@ -371,6 +372,43 @@ def __init__(self, parent, growth): super(GenericTerm, self).__init__(parent=parent) + def construction(self): + r""" + Return a construction of this term. + + INPUT: + + Nothing. + + OUTPUT: + + A pair ``(cls, kwds)`` such that``cls(**kwds)`` equals this term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import OTermMonoid + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + + sage: OT = OTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) + sage: o = OT.an_element(); o + O(x) + sage: cls, kwds = o.construction(); cls, kwds + (, + {'growth': x, + 'parent': O-Term Monoid x^ZZ with implicit coefficients in Rational Field}) + sage: cls(**kwds) == o + True + + .. SEEALSO:: + + :meth:`TermWithCoefficient.construction`, + :meth:`GenericTermMonoid.from_construction` + """ + return (self.__class__, {'parent': self.parent(), + 'growth': self.growth}) + def _mul_(self, other): r""" Multiplication of this term by another. @@ -1712,6 +1750,8 @@ def _element_constructor_(self, data, coefficient=None): - ``coefficient`` -- (default: ``None``) an element of the coefficient ring. + - ``**kwds`` -- keyword arguments passed on to the term. + OUTPUT: An element of this term monoid. @@ -1803,22 +1843,22 @@ def _element_constructor_(self, data, coefficient=None): """ if isinstance(data, self.element_class) and data.parent() == self: return data - elif isinstance(data, TermWithCoefficient): - return self._create_element_(data.growth, data.coefficient) elif isinstance(data, GenericTerm): - return self._create_element_(data.growth, None) + return self.from_construction(data.construction(), **kwds) elif isinstance(data, int) and data == 0: raise ValueError('No input specified. Cannot continue ' 'creating an element of %s.' % (self,)) from .misc import combine_exceptions + coefficient = kwds.pop('coefficient', None) if coefficient is not None: - try: - data = self.growth_group(data) - except (ValueError, TypeError) as e: - raise combine_exceptions( - ValueError('Growth %s is not in %s.' % (data, self)), e) - return self._create_element_(data, coefficient) + growth = data + if 'growth' in kwds: + raise ValueError(f"Argument 'growth={kwds['growth']}' is ambiguous.") + return self.from_construction((None, + {'growth': growth, + 'coefficient': coefficient}), + **kwds) try: growth, coefficient = self._split_growth_and_coefficient_(data) @@ -1826,38 +1866,294 @@ def _element_constructor_(self, data, coefficient=None): raise combine_exceptions( ValueError('%s is not in %s.' % (data, self)), e) - return self._create_element_(growth, coefficient) + if 'growth' in kwds: + raise ValueError(f"Argument 'growth={kwds['growth']}' is ambiguous.") + if 'coefficient' in kwds: + raise ValueError(f"Argument 'coefficient={kwds['coefficient']}' is ambiguous.") + return self.from_construction((None, + {'growth': growth, + 'coefficient': coefficient}), + **kwds) - def _create_element_(self, growth, coefficient): + def _validate_growth_or_error_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which ensures that the keyword argument ``growth`` + of the term (in the element construction process) is valid. INPUT: - - ``growth`` -- a growth element. + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``growth`` in ``kwds_construction`` might be changed + or an error is raised. + + TESTS:: - - ``coefficient`` -- an element of the coefficient ring. + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T(G.gen()) # indirect doctest + O(x) + + :: + + sage: T._validate_growth_or_error_({'growth': G.gen()}) + sage: T._validate_growth_or_error_({'growth': None}) + Traceback (most recent call last): + ... + ValueError: Growth None is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* ValueError: None is not in Growth Group x^ZZ. + sage: T._validate_growth_or_error_({'growth': 7}) + Traceback (most recent call last): + ... + ValueError: Growth 7 is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* ValueError: 7 is not in Growth Group x^ZZ. + + :: + + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': 'x'} + sage: T._validate_growth_or_error_(kwds) + sage: kwds['growth'].parent() + Growth Group x^ZZ + """ + growth = kwds_construction.get('growth') + try: + growth = self.growth_group(growth) + except (ValueError, TypeError) as e: + growth = kwds_construction['growth'] + from .misc import combine_exceptions + raise combine_exceptions( + ValueError(f'Growth {growth} is not valid in {self}.'), e) + kwds_construction['growth'] = growth + + def _validate_coefficient_or_error_(self, kwds_construction): + r""" + Helper method which ensures that the keyword argument ``coefficient`` + of the term (in the element construction process) is valid. + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - A term. + Nothing, but ``coefficient`` in ``kwds_construction`` might be changed + or an error is raised. TESTS:: - sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T(G.gen()) # indirect doctest + O(x) + + :: + + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': None}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4/3}) + Traceback (most recent call last): + ... + ValueError: Cannot create OTerm(x) since + given coefficient 4/3 is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer + + :: + + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': G.gen(), 'coefficient': 4} + sage: T._validate_coefficient_or_error_(kwds) + sage: kwds['coefficient'].parent() + Rational Field + """ + coefficient = kwds_construction.get('coefficient', None) + if coefficient is None: + return + try: + coefficient = self.coefficient_ring(coefficient) + except (TypeError, ValueError) as e: + element_name = self.Element.__name__ + growth = kwds_construction['growth'] + from .misc import combine_exceptions + raise combine_exceptions( + ValueError(f'Cannot create {element_name}({growth}) ' + f'since given coefficient {coefficient} ' + f'is not valid in {self}.'), e) + if 'coefficient' in kwds_construction: + kwds_construction['coefficient'] = coefficient + + @cached_method + def _default_kwds_construction_(self): + r""" + Return the default keyword arguments for the construction of a term. + + INPUT: + + Nothing. + + OUTPUT: + + A dictionary. + + TESTS:: + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T._default_kwds_construction_() + {} + sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest + O(x) + """ + return {} - sage: G_ZZ = GrowthGroup('x^ZZ') - sage: T_ZZ = GenericTermMonoid(TermMonoid, G_ZZ, QQ) - sage: T_ZZ(G_ZZ.gen()) # indirect doctest - Generic Term with growth x + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = GenericTermMonoid(TermMonoid, G, QQ) + + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + Traceback (most recent call last): + ... + ValueError: Coefficient 3/2 is not 1, + but GenericTerm-Monoid x^ZZ with (implicit) coefficients + in Rational Field does not support coefficients. """ + coefficient = kwds_construction.pop('coefficient', None) if coefficient is not None and coefficient != self.coefficient_ring.one(): raise ValueError('Coefficient %s is not 1, but %s does not ' 'support coefficients.' % (coefficient, self)) - return self.element_class(self, growth) + + def from_construction(self, construction, **kwds_overrides): + r""" + Create a term from the construction of another term. + + INPUT: + + - ``construction`` -- a pair ``(cls, kwds_construction)`` + + - ``kwds_overrides`` -- a dictionary + + OUTPUT: + + A term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermMonoid('O', G, QQ) + sage: o = T.an_element() + + We use a construction directly as input:: + + sage: T.from_construction(o.construction()) + O(x) + + We can override the given data:: + + sage: T.from_construction(o.construction(), growth=x^2) + O(x^2) + + A minimalistic example:: + + sage: T.from_construction((None, {'growth': x})) + O(x) + + .. SEEALSO:: + + :meth:`GenericTerm.construction`, + :meth:`TermWithCoefficient.construction` + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: T = GenericTermMonoid(TermMonoid, G, QQ) + sage: T(G.gen()) # indirect doctest + Generic Term with growth x + + sage: T = TermMonoid('O', G, QQ) + sage: T(G.gen()) # indirect doctest + O(x) + sage: T(G.gen(), SR.var('y')) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Cannot create OTerm(x) since given coefficient y + is not valid in O-Term Monoid x^ZZ with implicit coefficients in + Rational Field. + > *previous* TypeError: unable to convert y to a rational + """ + kwds = {} + kwds.update(self._default_kwds_construction_()) + cls, kwds_construction = construction + kwds.update(kwds_construction) + kwds.update(kwds_overrides) + + self._validate_growth_or_error_(kwds) + self._validate_coefficient_or_error_(kwds) + + self._convert_construction_(kwds) + + try: + del kwds['parent'] + except KeyError: + pass + + return self.element_class(self, **kwds) def _create_element_in_extension_(self, growth, coefficient): r""" @@ -2671,20 +2967,23 @@ class OTermMonoid(GenericTermMonoid): # enable the category framework for elements Element = OTerm - def _create_element_(self, growth, coefficient): + def _convert_construction_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). - INPUT: + This is used e.g. for converting one type of term into another - - ``growth`` -- a growth element. + INPUT: - - ``coefficient`` -- an element of the coefficient ring (will be - ignored since we create an O-Term). + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - An O-term. + Nothing, but ``kwds_construction`` might be changed. TESTS:: @@ -2692,27 +2991,21 @@ def _create_element_(self, growth, coefficient): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() sage: T = TermMonoid('O', G, QQ) - sage: T(G.gen()) # indirect doctest - O(x) - sage: T(G.gen(), SR.var('y')) # indirect doctest - Traceback (most recent call last): - ... - ValueError: Cannot create O(x) since given coefficient y - is not valid in O-Term Monoid x^ZZ with implicit coefficients in - Rational Field. - > *previous* TypeError: unable to convert y to a rational + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'growth': x} """ - if coefficient is not None: - try: - self.coefficient_ring(coefficient) - except (TypeError, ValueError) as e: - from .misc import combine_exceptions - raise combine_exceptions( - ValueError('Cannot create O(%s) since given coefficient %s ' - 'is not valid in %s.' % - (growth, coefficient, self)), e) - return self.element_class(self, growth) + try: + del kwds_construction['coefficient'] + except KeyError: + pass def _coerce_map_from_(self, S): r""" @@ -2842,17 +3135,19 @@ def __init__(self, parent, growth, coefficient): The coefficients have to be from the given coefficient ring:: - sage: t = CT_ZZ(x, 1/2) + sage: CT_ZZ(x, 1/2) Traceback (most recent call last): ... - ValueError: 1/2 is not a coefficient in + ValueError: Cannot create TermWithCoefficient(x) + since given coefficient 1/2 is not valid in TermWithCoefficient-Monoid x^ZZ with coefficients in Integer Ring. - sage: t = CT_QQ(x, 1/2); t + > *previous* TypeError: no conversion of this rational to integer + sage: CT_QQ(x, 1/2) Term with coefficient 1/2 and growth x For technical reasons, the coefficient 0 is not allowed:: - sage: t = CT_ZZ(x^42, 0) + sage: CT_ZZ(x^42, 0) Traceback (most recent call last): ... ZeroCoefficientError: Zero coefficient 0 is not allowed in @@ -2879,6 +3174,45 @@ def __init__(self, parent, growth, coefficient): self.coefficient = coefficient super(TermWithCoefficient, self).__init__(parent=parent, growth=growth) + def construction(self): + r""" + Return a construction of this term. + + INPUT: + + Nothing. + + OUTPUT: + + A pair ``(cls, kwds)`` such that``cls(**kwds)`` equals this term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import ExactTermMonoid + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + + sage: OT = ExactTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) + sage: e = OT.an_element(); e + 1/2*x + sage: cls, kwds = e.construction(); cls, kwds + (, + {'coefficient': 1/2, + 'growth': x, + 'parent': Exact Term Monoid x^ZZ with coefficients in Rational Field}) + sage: cls(**kwds) == e + True + + .. SEEALSO:: + + :meth:`GenericTerm.construction`, + :meth:`GenericTermMonoid.from_construction` + """ + return (self.__class__, {'parent': self.parent(), + 'growth': self.growth, + 'coefficient': self.coefficient}) + def _repr_(self): r""" A representation string for this term with coefficient. @@ -3170,31 +3504,130 @@ def _repr_(self): return 'TermWithCoefficient-Monoid %s with coefficients in %s' % \ (self.growth_group._repr_short_(), self.coefficient_ring) - def _create_element_(self, growth, coefficient): + def _validate_coefficient_or_error_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which ensures that the keyword argument ``coefficient`` + of the term (in the element construction process) is valid. INPUT: - - ``growth`` -- a growth element. - - - ``coefficient`` -- an element of the coefficient ring. + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - A term. + Nothing, but ``coefficient`` in ``kwds_construction`` might be changed + or an error is raised. TESTS:: sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: G_ZZ = GrowthGroup('x^ZZ') - sage: T_ZZ = TermMonoid('exact', G_ZZ, QQ) - sage: T_ZZ(G_ZZ.gen(), 4/3) # indirect doctest + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('exact', G, ZZ) + sage: T(G.gen(), 4) # indirect doctest + 4*x + + :: + + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': None}) + Traceback (most recent call last): + ... + ValueError: Cannot create ExactTerm(x) since no coefficient is given. + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4/3}) + Traceback (most recent call last): + ... + ValueError: Cannot create ExactTerm(x) since + given coefficient 4/3 is not valid in + Exact Term Monoid x^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer + + :: + + sage: T = TermMonoid('exact', G, QQ) + sage: T(G.gen(), 4/3) # indirect doctest 4/3*x """ - return self.element_class(self, growth, coefficient) + coefficient = kwds_construction.get('coefficient', None) + if coefficient is None: + element_name = self.Element.__name__ + growth = kwds_construction['growth'] + raise ValueError(f'Cannot create {element_name}({growth}) ' + f'since no coefficient is given.') + super()._validate_coefficient_or_error_(kwds_construction) + + @cached_method + def _default_kwds_construction_(self): + r""" + Return the default keyword arguments for the construction of a term. + + INPUT: + + Nothing. + + OUTPUT: + + A dictionary. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('exact', G, ZZ) + sage: T._default_kwds_construction_() + {'coefficient': 1} + sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest + x + """ + return {'coefficient': self.coefficient_ring.one()} + + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermWithCoefficientMonoid(TermMonoid, G, QQ) + + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'coefficient': 1, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'coefficient': None, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'coefficient': 3/2, 'growth': x} + """ + pass def _an_element_(self): r""" @@ -3975,6 +4408,43 @@ class ExactTermMonoid(TermWithCoefficientMonoid): # enable the category framework for elements Element = ExactTerm + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'coefficient': 1, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'coefficient': None, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'coefficient': 3/2, 'growth': x} + """ + pass + def _repr_(self): r""" A representation string for this exact term monoid. From c805fc19b60e9ac4ff028f63d4c687cb4d50bca2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Fri, 16 Jul 2021 22:02:51 +0200 Subject: [PATCH 014/924] Trac #32215: deprecate positional "coefficient" argument --- src/sage/rings/asymptotic/asymptotic_ring.py | 6 +- src/sage/rings/asymptotic/term_monoid.py | 124 +++++++++++-------- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index da5ea3b606e..c28fb2200b7 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -575,7 +575,7 @@ def __init__(self, parent, summands, simplify=True, convert=True): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: OT = TermMonoid('O', G, ZZ); ET = TermMonoid('exact', G, ZZ) sage: R = AsymptoticRing(G, ZZ) - sage: lst = [ET(x, 1), ET(x^2, 2), OT(x^3), ET(x^4, 4)] + sage: lst = [ET(x, coefficient=1), ET(x^2, coefficient=2), OT(x^3), ET(x^4, coefficient=4)] sage: expr = R(lst, simplify=False); expr # indirect doctest 4*x^4 + O(x^3) + 2*x^2 + x sage: print(expr.summands.repr_full()) @@ -952,7 +952,7 @@ def _simplify_(self): sage: G = GrowthGroup('x^ZZ') sage: OT = TermMonoid('O', G, ZZ); ET = TermMonoid('exact', G, ZZ) sage: R = AsymptoticRing(G, ZZ) - sage: lst = [ET(x, 1), ET(x^2, 2), OT(x^3), ET(x^4, 4)] + sage: lst = [ET(x, coefficient=1), ET(x^2, coefficient=2), OT(x^3), ET(x^4, coefficient=4)] sage: expr = R(lst, simplify=False); expr # indirect doctest 4*x^4 + O(x^3) + 2*x^2 + x sage: expr._simplify_(); expr @@ -3087,7 +3087,7 @@ def mapping(term): c = f(term.coefficient) if c.is_zero(): return None - return T(term.growth, c) + return T(term.growth, coefficient=c) else: return T(term.growth) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index f66ee0d440e..488323a7b8d 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -62,7 +62,7 @@ sage: OT = OTermMonoid(TermMonoid, growth_group=G, coefficient_ring=QQ) sage: ET = ExactTermMonoid(TermMonoid, growth_group=G, coefficient_ring=QQ) sage: ot1 = OT(x); ot2 = OT(x^2) - sage: et1 = ET(x^2, 2) + sage: et1 = ET(x^2, coefficient=2) - Because of the definition of `O`-terms (see :wikipedia:`Big_O_notation`), :class:`OTerm` are able to absorb all @@ -100,7 +100,7 @@ :class:`ExactTerm` if the growth coincides with the growth of this element:: - sage: et1.can_absorb(ET(x^2, 5)) + sage: et1.can_absorb(ET(x^2, coefficient=5)) True sage: any(et1.can_absorb(t) for t in [ot1, ot2]) False @@ -108,16 +108,16 @@ As mentioned above, absorption directly corresponds to addition in this case:: - sage: et1.absorb(ET(x^2, 5)) + sage: et1.absorb(ET(x^2, coefficient=5)) 7*x^2 When adding two exact terms, they might cancel out. For technical reasons, ``None`` is returned in this case:: - sage: ET(x^2, 5).can_absorb(ET(x^2, -5)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^2, coefficient=-5)) True - sage: ET(x^2, 5).absorb(ET(x^2, -5)) is None + sage: ET(x^2, coefficient=5).absorb(ET(x^2, coefficient=-5)) is None True - The abstract base terms :class:`GenericTerm` and @@ -696,8 +696,8 @@ def absorb(self, other, check=True): sage: OT = TermMonoid('O', G_QQ, coefficient_ring=ZZ) sage: ET = TermMonoid('exact', G_QQ, coefficient_ring=QQ) sage: ot1 = OT(x); ot2 = OT(x^2) - sage: et1 = ET(x, 100); et2 = ET(x^2, 2) - sage: et3 = ET(x^2, 1); et4 = ET(x^2, -2) + sage: et1 = ET(x, coefficient=100); et2 = ET(x^2, coefficient=2) + sage: et3 = ET(x^2, coefficient=1); et4 = ET(x^2, coefficient=-2) `O`-Terms are able to absorb other `O`-terms and exact terms with weaker or equal growth. :: @@ -940,7 +940,7 @@ def _lt_(self, other): (Generic Term with growth x, Generic Term with growth x^2) sage: o1 = OT(x^-1); o2 = OT(x^3); o1, o2 (O(x^(-1)), O(x^3)) - sage: t1 = ET_ZZ(x^2, 5); t2 = ET_QQ(x^3, 2/7); t1, t2 + sage: t1 = ET_ZZ(x^2, coefficient=5); t2 = ET_QQ(x^3, coefficient=2/7); t1, t2 (5*x^2, 2/7*x^3) In order for the comparison to work, the terms have to come from @@ -968,9 +968,9 @@ def _lt_(self, other): sage: t1 <= t2 True - sage: ET_ZZ(x, -5) <= ET_ZZ(x, 42) + sage: ET_ZZ(x, coefficient=-5) <= ET_ZZ(x, coefficient=42) False - sage: ET_ZZ(x, 5) <= ET_ZZ(x, 5) + sage: ET_ZZ(x, coefficient=5) <= ET_ZZ(x, coefficient=5) True :: @@ -980,7 +980,7 @@ def _lt_(self, other): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, QQ) - sage: t1 = ET(x, 5); t2 = ET(x^2, 3); t3 = ET(x^2, 42) + sage: t1 = ET(x, coefficient=5); t2 = ET(x^2, coefficient=3); t3 = ET(x^2, coefficient=42) sage: t1 <= t2 # indirect doctest True sage: t2 <= t1 # indirect doctest @@ -994,7 +994,7 @@ def _lt_(self, other): TESTS:: - sage: ET(x, -2) <= ET(x, 1) + sage: ET(x, coefficient=-2) <= ET(x, coefficient=1) False :: @@ -1044,7 +1044,7 @@ def _eq_(self, other): (Generic Term with growth x, x, O(x)) sage: e == e^2 # indirect doctest False - sage: e == ET(x,1) # indirect doctest + sage: e == ET(x, coefficient=1) # indirect doctest True sage: o == OT(x^2) # indirect doctest False @@ -1738,7 +1738,7 @@ def _coerce_map_from_(self, S): self.coefficient_ring.has_coerce_map_from(S.coefficient_ring): return True - def _element_constructor_(self, data, coefficient=None): + def _element_constructor_(self, data, *args, **kwds): r""" Convert the given object to this term monoid. @@ -1810,7 +1810,7 @@ def _element_constructor_(self, data, coefficient=None): sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) - sage: t1 = T(x^2, 5); t1 # indirect doctest + sage: t1 = T(x^2, coefficient=5); t1 # indirect doctest Term with coefficient 5 and growth x^2 TESTS:: @@ -1841,6 +1841,22 @@ def _element_constructor_(self, data, coefficient=None): Term with coefficient 1 and growth log(x) """ + if len(args) > 1: + raise TypeError( + f'GenericTermMonoid._element_constructor_ ' + f'takes one positional arguments, ' + f'another positional argument is deprecated, ' + f'but {len(args)+1} were given') + elif len(args) == 1: + from sage.misc.superseded import deprecation + deprecation( + 32215, + "Giving 'coefficient' as positional argument is deprecated; " + "specify it as keyword argument 'coefficient=...'.") + if 'coefficient' in kwds: + raise ValueError(f"Argument 'coefficient={kwds['coefficient']}' is ambiguous.") + kwds['coefficient'] = args[0] + if isinstance(data, self.element_class) and data.parent() == self: return data elif isinstance(data, GenericTerm): @@ -2190,7 +2206,7 @@ def _create_element_in_extension_(self, growth, coefficient): if coefficient is not None else self.coefficient_ring, category=self.category()) - return parent(growth, coefficient) + return parent(growth, coefficient=coefficient) def _split_growth_and_coefficient_(self, data): r""" @@ -3110,9 +3126,9 @@ class TermWithCoefficient(GenericTerm): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) - sage: CT_ZZ(x^2, 5) + sage: CT_ZZ(x^2, coefficient=5) Term with coefficient 5 and growth x^2 - sage: CT_QQ(x^3, 3/8) + sage: CT_QQ(x^3, coefficient=3/8) Term with coefficient 3/8 and growth x^3 """ @@ -3142,7 +3158,7 @@ def __init__(self, parent, growth, coefficient): since given coefficient 1/2 is not valid in TermWithCoefficient-Monoid x^ZZ with coefficients in Integer Ring. > *previous* TypeError: no conversion of this rational to integer - sage: CT_QQ(x, 1/2) + sage: CT_QQ(x, coefficient=1/2) Term with coefficient 1/2 and growth x For technical reasons, the coefficient 0 is not allowed:: @@ -3158,7 +3174,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring - sage: CT_ZZ(x^42, 42) + sage: CT_ZZ(x^42, coefficient=42) Term with coefficient 42 and growth x^42 """ try: @@ -3234,7 +3250,7 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) - sage: T(x^2, 5)._repr_() + sage: T(x^2, coefficient=5)._repr_() 'Term with coefficient 5 and growth x^2' """ return 'Term with coefficient %s and growth %s' % \ @@ -3273,18 +3289,18 @@ def _mul_(self, other): exact terms (i.e. :class:`ExactTerm`). First, an example for abstract terms:: - sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) + sage: t1 = CT(x^2, coefficient=2); t2 = CT(x^3, coefficient=3) sage: t1 * t2 Term with coefficient 6 and growth x^5 And now, an example for exact terms:: - sage: t1 = ET(x^2, 2); t2 = ET(x^3, 3) + sage: t1 = ET(x^2, coefficient=2); t2 = ET(x^3, coefficient=3) sage: t1 * t2 6*x^5 """ return self.parent()(self.growth * other.growth, - self.coefficient * other.coefficient) + coefficient=self.coefficient * other.coefficient) def _calculate_pow_(self, exponent): r""" @@ -3423,11 +3439,11 @@ def _eq_(self, other): sage: T = TermWithCoefficientMonoid(TermMonoid, GrowthGroup('x^ZZ'), ZZ) sage: t = T.an_element(); t Term with coefficient 1 and growth x - sage: t == T(x, 1) + sage: t == T(x, coefficient=1) True - sage: t == T(x, 2) + sage: t == T(x, coefficient=2) False - sage: t == T(x^2, 1) + sage: t == T(x^2, coefficient=1) False """ return super(TermWithCoefficient, self)._eq_(other) and \ @@ -3528,7 +3544,7 @@ def _validate_coefficient_or_error_(self, kwds_construction): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('exact', G, ZZ) - sage: T(G.gen(), 4) # indirect doctest + sage: T(G.gen(), coefficient=4) # indirect doctest 4*x :: @@ -3552,7 +3568,7 @@ def _validate_coefficient_or_error_(self, kwds_construction): :: sage: T = TermMonoid('exact', G, QQ) - sage: T(G.gen(), 4/3) # indirect doctest + sage: T(G.gen(), coefficient=4/3) # indirect doctest 4/3*x """ coefficient = kwds_construction.get('coefficient', None) @@ -3655,7 +3671,7 @@ def _an_element_(self): 1/2*x """ return self(self.growth_group.an_element(), - self.coefficient_ring.an_element()) + coefficient=self.coefficient_ring.an_element()) def some_elements(self): r""" @@ -3684,9 +3700,9 @@ def some_elements(self): z^(-2), -z^2, 2*z^(-1/2), -2*z^(1/2)) """ from sage.misc.mrange import cantor_product - return iter(self(g, c) for g, c in cantor_product( + return (self(g, coefficient=c) for g, c in cantor_product( self.growth_group.some_elements(), - iter(c for c in self.coefficient_ring.some_elements() if c != 0))) + (c for c in self.coefficient_ring.some_elements() if c != 0))) class ExactTerm(TermWithCoefficient): @@ -3715,24 +3731,24 @@ class ExactTerm(TermWithCoefficient): Asymptotic exact terms may be multiplied (with the usual rules applying):: - sage: ET(x^2, 3) * ET(x, -1) + sage: ET(x^2, coefficient=3) * ET(x, coefficient=-1) -3*x^3 - sage: ET(x^0, 4) * ET(x^5, 2) + sage: ET(x^0, coefficient=4) * ET(x^5, coefficient=2) 8*x^5 They may also be multiplied with `O`-terms:: sage: OT = TermMonoid('O', G, QQ) - sage: ET(x^2, 42) * OT(x) + sage: ET(x^2, coefficient=42) * OT(x) O(x^3) Absorption for asymptotic exact terms relates to addition:: - sage: ET(x^2, 5).can_absorb(ET(x^5, 12)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^5, coefficient=12)) False - sage: ET(x^2, 5).can_absorb(ET(x^2, 1)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^2, coefficient=1)) True - sage: ET(x^2, 5).absorb(ET(x^2, 1)) + sage: ET(x^2, coefficient=5).absorb(ET(x^2, coefficient=1)) 6*x^2 Note that, as for technical reasons, `0` is not allowed as a @@ -3740,9 +3756,9 @@ class ExactTerm(TermWithCoefficient): is returned if two asymptotic exact terms cancel out each other during absorption:: - sage: ET(x^2, 42).can_absorb(ET(x^2, -42)) + sage: ET(x^2, coefficient=42).can_absorb(ET(x^2, coefficient=-42)) True - sage: ET(x^2, 42).absorb(ET(x^2, -42)) is None + sage: ET(x^2, coefficient=42).absorb(ET(x^2, coefficient=-42)) is None True Exact terms can also be created by converting monomials with @@ -3775,16 +3791,16 @@ def _repr_(self, latex=False): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, ZZ) - sage: ET(x^2, 2) + sage: ET(x^2, coefficient=2) 2*x^2 TESTS:: - sage: ET(x^2, 1) + sage: ET(x^2, coefficient=1) x^2 - sage: ET(x^2, -1) + sage: ET(x^2, coefficient=-1) -x^2 - sage: ET(x^0, 42) + sage: ET(x^0, coefficient=42) 42 Check that :trac:`19576` is fixed:: @@ -3841,16 +3857,16 @@ def _latex_(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, ZZ) - sage: latex(ET(x^2, 2)) + sage: latex(ET(x^2, coefficient=2)) 2 x^{2} :: - sage: latex(ET(x^2, 1)) + sage: latex(ET(x^2, coefficient=1)) x^{2} - sage: latex(ET(x^2, -1)) + sage: latex(ET(x^2, coefficient=-1)) -x^{2} - sage: latex(ET(x^0, 42)) + sage: latex(ET(x^0, coefficient=42)) 42 :: @@ -3886,9 +3902,9 @@ def __invert__(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermMonoid('exact', G, ZZ) - sage: ~T(x, 2) # indirect doctest + sage: ~T(x, coefficient=2) # indirect doctest 1/2*x^(-1) - sage: (~T(x, 2)).parent() + sage: (~T(x, coefficient=2)).parent() Exact Term Monoid x^ZZ with coefficients in Rational Field """ try: @@ -3955,7 +3971,7 @@ def can_absorb(self, other): sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), ZZ) - sage: t1 = ET(x^21, 1); t2 = ET(x^21, 2); t3 = ET(x^42, 1) + sage: t1 = ET(x^21, coefficient=1); t2 = ET(x^21, coefficient=2); t3 = ET(x^42, coefficient=1) sage: t1.can_absorb(t2) True sage: t2.can_absorb(t1) @@ -3997,7 +4013,7 @@ def _absorb_(self, other): Asymptotic exact terms can absorb other asymptotic exact terms with the same growth:: - sage: et1 = ET(x^2, 2); et2 = ET(x^2, -2) + sage: et1 = ET(x^2, coefficient=2); et2 = ET(x^2, coefficient=-2) sage: et1.absorb(et1) 4*x^2 sage: et1.absorb(et2) is None @@ -4005,7 +4021,7 @@ def _absorb_(self, other): If the growth differs, an ``ArithmeticException`` is raised:: - sage: ET(x^5, 1).absorb(et1) + sage: ET(x^5, coefficient=1).absorb(et1) Traceback (most recent call last): ... ArithmeticError: x^5 cannot absorb 2*x^2 @@ -4014,7 +4030,7 @@ def _absorb_(self, other): if coeff_new.is_zero(): return None else: - return self.parent()(self.growth, coeff_new) + return self.parent()(self.growth, coefficient=coeff_new) def log_term(self, base=None, locals=None): r""" From f76b7e7ef42e8cf09301cc38277a11024e210f1d Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sat, 17 Jul 2021 12:00:10 +0200 Subject: [PATCH 015/924] Trac #32215: use defaults of super class --- src/sage/rings/asymptotic/term_monoid.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 488323a7b8d..7ddc5c294ff 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -3604,7 +3604,10 @@ def _default_kwds_construction_(self): sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest x """ - return {'coefficient': self.coefficient_ring.one()} + defaults = {} + defaults.update(GenericTermMonoid._default_kwds_construction_()) + defaults.update({'coefficient': self.coefficient_ring.one()}) + return defaults def _convert_construction_(self, kwds_construction): r""" From bedafdf2339f4c999811d719c9dd4b9fbb23cc4c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sat, 17 Jul 2021 14:08:58 +0200 Subject: [PATCH 016/924] fix doctest due to changes in #32215 (keyword arg coefficient) --- src/sage/rings/asymptotic/term_monoid.py | 39 +++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 2a32c8e797f..e28ae0557f4 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -4562,10 +4562,13 @@ def __init__(self, parent, growth, coefficient, valid_from): BTerm Monoid x^ZZ with coefficients in Rational Field. sage: BT_ZZ = BTermMonoid(TermMonoid, G, ZZ) - sage: BT_ZZ(x, 1/2, valid_from={'x': 20}) + sage: BT_ZZ(x, coefficient=1/2, valid_from={'x': 20}) Traceback (most recent call last): ... - ValueError: 1/2 is not a coefficient in Integer Ring. + ValueError: Cannot create BTerm(x) + since given coefficient 1/2 is not valid in + BTerm Monoid x^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) @@ -4624,12 +4627,12 @@ def _repr_(self): sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) - sage: BT_QQ(x^3, 3, valid_from={'x': 20}) + sage: BT_QQ(x^3, coefficient=3, valid_from={'x': 20}) BTerm with coefficient 3, growth x^3 and valid for x >= 20 sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) - sage: BT_ZZ(x^2, 4, valid_from={'x': 10, 'y': 15}) + sage: BT_ZZ(x^2, coefficient=4, valid_from={'x': 10, 'y': 15}) BTerm with coefficient 4, growth x^2 and valid for x >= 10 and y >= 15 """ valid_from_string = ' and '.join(f'{variable} >= {value}' @@ -4663,9 +4666,9 @@ def can_absorb(self, other): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: BT = TermMonoid('B', GrowthGroup('x^ZZ'), QQ) - sage: t1 = BT(x^3, 3, valid_from={'x': 20}) - sage: t2 = BT(x^2, 1, valid_from={'x': 10}) - sage: t3 = BT(x^3, 10, valid_from={'x': 10}) + sage: t1 = BT(x^3, coefficient=3, valid_from={'x': 20}) + sage: t2 = BT(x^2, coefficient=1, valid_from={'x': 10}) + sage: t3 = BT(x^3, coefficient=10, valid_from={'x': 10}) sage: t1.can_absorb(t2) True sage: t2.can_absorb(t1) @@ -4675,7 +4678,7 @@ def can_absorb(self, other): sage: t3.can_absorb(t1) True sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) - sage: t4 = ET(x^3, 5) + sage: t4 = ET(x^3, coefficient=5) sage: t1.can_absorb(t4) True @@ -4687,8 +4690,9 @@ def can_absorb(self, other): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = BTermMonoid(TermMonoid, G, QQ) - sage: t1 = BT(x, 3, valid_from={'x': 20}); t2 = BT(x^3, 5, valid_from={'x': 5}) - sage: t3 = BT(x^3, 10, valid_from={'x': 10}) + sage: t1 = BT(x, coefficient=3, valid_from={'x': 20}) + sage: t2 = BT(x^3, coefficient=5, valid_from={'x': 5}) + sage: t3 = BT(x^3, coefficient=10, valid_from={'x': 10}) sage: t2.absorb(t1) BTerm with coefficient 2003/400, growth x^3 and valid for x >= 20 sage: t2.absorb(t3) @@ -4717,9 +4721,10 @@ def _absorb_(self, other): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = BTermMonoid(TermMonoid, G, QQ) - sage: t1 = BT(x^3, 4, valid_from={'x': 10}); t2 = BT(x, 5, valid_from={'x': 20}) - sage: t1 + sage: t1 = BT(x^3, coefficient=4, valid_from={'x': 10}); t1 BTerm with coefficient 4, growth x^3 and valid for x >= 10 + sage: t2 = BT(x, coefficient=5, valid_from={'x': 20}); t2 + BTerm with coefficient 5, growth x and valid for x >= 20 sage: t1.can_absorb(t2) True sage: t1.absorb(t2) @@ -4730,7 +4735,7 @@ def _absorb_(self, other): ArithmeticError: BTerm with coefficient 5, growth x and valid for x >= 20 cannot absorb BTerm with coefficient 4, growth x^3 and valid for x >= 10 sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) - sage: t4 = ET(x^3, 5) + sage: t4 = ET(x^3, coefficient=5) sage: t1.absorb(t4) Traceback (most recent call last): ... @@ -4752,7 +4757,7 @@ def _absorb_(self, other): valid_from_new[variable_name] = (other.valid_from[variable_name]) q = self.growth / other.growth coeff_new = self.coefficient + (other.coefficient / q._find_minimum_(valid_from_new)) - return self.parent()(self.growth, coeff_new, valid_from=valid_from_new) + return self.parent()(self.growth, coefficient=coeff_new, valid_from=valid_from_new) class BTermMonoid(TermWithCoefficientMonoid): @@ -4834,13 +4839,13 @@ def _create_element_(self, growth, coefficient, valid_from): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = BTermMonoid(TermMonoid, G, QQ) - sage: BT(x^3, 4, valid_from={'x': 10}) + sage: BT(x^3, coefficient=4, valid_from={'x': 10}) BTerm with coefficient 4, growth x^3 and valid for x >= 10 - sage: BT(x^3, 4, valid_from=10) + sage: BT(x^3, coefficient=4, valid_from=10) Traceback (most recent call last): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute 'keys' - sage: BT(x^3, 4, 10) + sage: BT(x^3, coefficient=4, 10) Traceback (most recent call last): ... TypeError: _element_constructor_() takes from 2 to 3 positional arguments but 4 were given From 50875ee6da2aadf3219d457632f4542b9da37dd6 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:12:24 +0200 Subject: [PATCH 017/924] Trac #32215: unify docstrings of .construction --- src/sage/rings/asymptotic/term_monoid.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index e28ae0557f4..bcc9869893a 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -388,18 +388,17 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import OTermMonoid sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') - sage: OT = OTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) - sage: o = OT.an_element(); o + sage: T = TermMonoid('O', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a O(x) - sage: cls, kwds = o.construction(); cls, kwds + sage: cls, kwds = a.construction(); cls, kwds (, {'growth': x, 'parent': O-Term Monoid x^ZZ with implicit coefficients in Rational Field}) - sage: cls(**kwds) == o + sage: cls(**kwds) == a True .. SEEALSO:: @@ -3206,19 +3205,18 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import ExactTermMonoid sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') - sage: OT = ExactTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) - sage: e = OT.an_element(); e + sage: T = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a 1/2*x - sage: cls, kwds = e.construction(); cls, kwds + sage: cls, kwds = a.construction(); cls, kwds (, {'coefficient': 1/2, 'growth': x, 'parent': Exact Term Monoid x^ZZ with coefficients in Rational Field}) - sage: cls(**kwds) == e + sage: cls(**kwds) == a True .. SEEALSO:: @@ -3226,9 +3224,9 @@ def construction(self): :meth:`GenericTerm.construction`, :meth:`GenericTermMonoid.from_construction` """ - return (self.__class__, {'parent': self.parent(), - 'growth': self.growth, - 'coefficient': self.coefficient}) + cls, kwds = super().construction() + kwds.update({'coefficient': self.coefficient}) + return cls, kwds def _repr_(self): r""" From 06ff17abfc002ab7f202d2ecc577ee790bf53d30 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:13:41 +0200 Subject: [PATCH 018/924] Trac #32215: remove caching from _default_kwds_construction_ --- src/sage/rings/asymptotic/term_monoid.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index bcc9869893a..2af70a22dd3 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2018,7 +2018,6 @@ def _validate_coefficient_or_error_(self, kwds_construction): if 'coefficient' in kwds_construction: kwds_construction['coefficient'] = coefficient - @cached_method def _default_kwds_construction_(self): r""" Return the default keyword arguments for the construction of a term. @@ -3578,7 +3577,6 @@ def _validate_coefficient_or_error_(self, kwds_construction): f'since no coefficient is given.') super()._validate_coefficient_or_error_(kwds_construction) - @cached_method def _default_kwds_construction_(self): r""" Return the default keyword arguments for the construction of a term. @@ -3604,7 +3602,7 @@ def _default_kwds_construction_(self): x """ defaults = {} - defaults.update(GenericTermMonoid._default_kwds_construction_()) + defaults.update(super()._default_kwds_construction_()) defaults.update({'coefficient': self.coefficient_ring.one()}) return defaults From 4679fceb3d74e844bdd307430bd973a89306b804 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:16:46 +0200 Subject: [PATCH 019/924] Trac #32229: fix conversion --- src/sage/rings/asymptotic/term_monoid.py | 142 ++++++++++++++++++++--- 1 file changed, 123 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 2af70a22dd3..c74e5556789 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -4519,7 +4519,7 @@ class BTerm(TermWithCoefficient): sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) - sage: BT_QQ(x, 3, valid_from={'x': 20}) + sage: BT_QQ(x, coefficient=3, valid_from={'x': 20}) doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. @@ -4603,6 +4603,46 @@ def __init__(self, parent, growth, coefficient, valid_from): super().__init__(parent=parent, growth=growth, coefficient=coefficient) + def construction(self): + r""" + Return a construction of this term. + + INPUT: + + Nothing. + + OUTPUT: + + A pair ``(cls, kwds)`` such that``cls(**kwds)`` equals this term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + + sage: T = TermMonoid('B', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a + BTerm with coefficient 1/2, growth x and valid for x >= 42 + sage: cls, kwds = a.construction(); cls, kwds + (, + {'coefficient': 1/2, + 'growth': x, + 'parent': BTerm Monoid x^ZZ with coefficients in Rational Field, + 'valid_from': {'x': 42}}) + sage: cls(**kwds) == a + True + + .. SEEALSO:: + + :meth:`GenericTerm.construction`, + :meth:`TermWithCoefficient.construction`, + :meth:`GenericTermMonoid.from_construction` + """ + cls, kwds = super().construction() + kwds.update({'valid_from': self.valid_from}) + return cls, kwds + def _repr_(self): r""" A representation string for this B term. @@ -4733,11 +4773,7 @@ def _absorb_(self, other): sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) sage: t4 = ET(x^3, coefficient=5) sage: t1.absorb(t4) - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for : 'BTerm Monoid - x^ZZ with coefficients in Rational Field' and 'Exact Term Monoid x^ZZ - with coefficients in Rational Field' + BTerm with coefficient 9, growth x^3 and valid for x >= 10 """ if self.growth < other.growth: raise ArithmeticError(f'{self} cannot absorb {other}') @@ -4810,31 +4846,98 @@ def _repr_(self): return (f'BTerm Monoid {self.growth_group._repr_short_()} with ' f'coefficients in {self.coefficient_ring}') - def _create_element_(self, growth, coefficient, valid_from): + def _default_kwds_construction_(self): r""" - Helper method which creates an element by using the ``element_class``. + Return the default keyword arguments for the construction of a term. INPUT: - - ``growth_group`` -- a growth group + Nothing. + + OUTPUT: - - ``coefficient`` -- an element of the coefficient ring + A dictionary. - - ``valid_from`` -- a dictionary mapping variable names to lower - bounds for the corresponding variable + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('B', G, ZZ) + sage: T._default_kwds_construction_() + {'coefficient': 1, 'valid_from': {'x': 0}} + sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest + BTerm with coefficient 1, growth x and valid for x >= 0 + sage: T.from_construction( + ....: (None, {'growth': G.gen(), 'coefficient': 2})) # indirect doctest + BTerm with coefficient 2, growth x and valid for x >= 0 + sage: T.from_construction( + ....: (None, {'growth': G.gen(), 'valid_from': {'x': 5}})) # indirect doctest + BTerm with coefficient 1, growth x and valid for x >= 5 + """ + defaults = {} + defaults.update(super()._default_kwds_construction_()) + defaults.update( + {'valid_from': {v: 0 for v in self.growth_group.variable_names()}}) + return defaults + + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - A BTerm + Nothing, but ``kwds_construction`` might be changed. TESTS:: - sage: from sage.rings.asymptotic.growth_group import MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid - sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermMonoid('B', G, ZZ) - sage: G = MonomialGrowthGroup(ZZ, 'x') - sage: BT = BTermMonoid(TermMonoid, G, QQ) + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'coefficient': 1, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'coefficient': None, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'coefficient': 3/2, 'growth': x} + + :: + + sage: T = TermMonoid('B', G, ZZ) + sage: T(TermMonoid('exact', G, QQ)(x, coefficient=42)) + BTerm with coefficient 42, growth x and valid for x >= 0 + sage: T(TermMonoid('O', G, QQ)(x)) + BTerm with coefficient 1, growth x and valid for x >= 0 + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42)) + BTerm with coefficient 42, growth x and valid for x >= 0 + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42, valid_from={'x': 7})) + BTerm with coefficient 42, growth x and valid for x >= 7 + + :: + + sage: T(TermMonoid('exact', G, QQ)(x, coefficient=-42)) + BTerm with coefficient 42, growth x and valid for x >= 0 + + :: + + sage: BT = TermMonoid('B', G, QQ) sage: BT(x^3, coefficient=4, valid_from={'x': 10}) BTerm with coefficient 4, growth x^3 and valid for x >= 10 sage: BT(x^3, coefficient=4, valid_from=10) @@ -4846,7 +4949,8 @@ def _create_element_(self, growth, coefficient, valid_from): ... TypeError: _element_constructor_() takes from 2 to 3 positional arguments but 4 were given """ - return self.element_class(self, growth, coefficient, valid_from) + # TODO handle negative coefficients of exact terms etc. + pass def _coerce_map_from_(self, S): r""" From 7736ac2f233770518fc85678cf60b13730b43bed Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:17:19 +0200 Subject: [PATCH 020/924] Trac #32229: extensively test conversion --- src/sage/rings/asymptotic/term_monoid.py | 48 +++++++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index c74e5556789..3bb3e2f755a 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2090,6 +2090,12 @@ def _convert_construction_(self, kwds_construction): raise ValueError('Coefficient %s is not 1, but %s does not ' 'support coefficients.' % (coefficient, self)) + if 'parent' in kwds_construction and isinstance(kwds_construction['parent'], BTermMonoid): + try: + del kwds_construction['valid_from'] + except KeyError: + pass + def from_construction(self, construction, **kwds_overrides): r""" Create a term from the construction of another term. @@ -3016,12 +3022,30 @@ def _convert_construction_(self, kwds_construction): {'growth': x} sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds {'growth': x} + + :: + + sage: T = TermMonoid('O', G, ZZ) + sage: T(TermMonoid('exact', G, QQ)(x, coefficient=42)) + O(x) + sage: T(TermMonoid('O', G, QQ)(x)) + O(x) + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42)) + O(x) + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42, valid_from={'x': 7})) + O(x) """ try: del kwds_construction['coefficient'] except KeyError: pass + if 'parent' in kwds_construction and isinstance(kwds_construction['parent'], BTermMonoid): + try: + del kwds_construction['valid_from'] + except KeyError: + pass + def _coerce_map_from_(self, S): r""" Return whether ``S`` coerces into this term monoid. @@ -3643,7 +3667,11 @@ def _convert_construction_(self, kwds_construction): sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds {'coefficient': 3/2, 'growth': x} """ - pass + if 'parent' in kwds_construction and isinstance(kwds_construction['parent'], BTermMonoid): + try: + del kwds_construction['valid_from'] + except KeyError: + pass def _an_element_(self): r""" @@ -4458,8 +4486,24 @@ def _convert_construction_(self, kwds_construction): {'coefficient': None, 'growth': x} sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds {'coefficient': 3/2, 'growth': x} + + :: + + sage: T = TermMonoid('exact', G, ZZ) + sage: T(TermMonoid('exact', G, QQ)(x, coefficient=42)) + 42*x + sage: T(TermMonoid('O', G, QQ)(x)) + x + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42)) + 42*x + sage: T(TermMonoid('B', G, QQ)(x, coefficient=42, valid_from={'x': 7})) + 42*x """ - pass + if 'parent' in kwds_construction and isinstance(kwds_construction['parent'], BTermMonoid): + try: + del kwds_construction['valid_from'] + except KeyError: + pass def _repr_(self): r""" From 7a8c48440f5008f95e55865fbf214c59ac95d1e3 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:17:39 +0200 Subject: [PATCH 021/924] Trac #32229: add an_element etc. for BTerms --- src/sage/rings/asymptotic/term_monoid.py | 72 ++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 3bb3e2f755a..f3a52fbe2fe 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -5052,6 +5052,78 @@ def _coerce_map_from_(self, S): else: return super()._coerce_map_from_(S) + def _an_element_(self): + r""" + Return an element of this B-term monoid. + + INPUT: + + Nothing. + + OUTPUT: + + An element of this term monoid. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: G = GrowthGroup('x^ZZ') + sage: TermMonoid('B', G, ZZ).an_element() # indirect doctest + BTerm with coefficient 1, growth x and valid for x >= 42 + """ + from sage.rings.semirings.non_negative_integer_semiring import NN + return self(self.growth_group.an_element(), + coefficient=self.coefficient_ring.an_element(), + valid_from={v: NN.an_element() + for v in self.growth_group.variable_names()}) + + def some_elements(self): + r""" + Return some elements of this B-term monoid. + + See :class:`TestSuite` for a typical use case. + + INPUT: + + Nothing. + + OUTPUT: + + An iterator. + + EXAMPLES:: + + sage: from itertools import islice + sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory + sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('z^QQ') + sage: T = TermMonoid('B', G, ZZ) + sage: tuple(islice(T.some_elements(), int(10))) + (BTerm with coefficient 1, growth z^(1/2) and valid for z >= 0, + BTerm with coefficient 1, growth z^(-1/2) and valid for z >= 1, + BTerm with coefficient 1, growth z^(1/2) and valid for z >= 3, + BTerm with coefficient 1, growth z^2 and valid for z >= 42, + BTerm with coefficient 1, growth z^(-1/2) and valid for z >= 0, + BTerm with coefficient 2, growth z^(1/2) and valid for z >= 1, + BTerm with coefficient 1, growth z^(-2) and valid for z >= 3, + BTerm with coefficient 1, growth z^2 and valid for z >= 42, + BTerm with coefficient 2, growth z^(-1/2) and valid for z >= 0, + BTerm with coefficient 2, growth z^(1/2) and valid for z >= 1) + """ + from itertools import cycle + from sage.misc.mrange import cantor_product + from sage.rings.semirings.non_negative_integer_semiring import NN + return (self(g, + coefficient=c, + valid_from={v: f for v in self.growth_group.variable_names()}) + for (g, c), f in zip(cantor_product( + self.growth_group.some_elements(), + (c for c in self.coefficient_ring.some_elements() if c != 0)), + cycle(NN.some_elements()))) + class TermMonoidFactory(UniqueRepresentation, UniqueFactory): r""" From c98180548fde5d14ff8999c89bd39b60b5640436 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:12:24 +0200 Subject: [PATCH 022/924] Trac #32215: unify docstrings of .construction --- src/sage/rings/asymptotic/term_monoid.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 7ddc5c294ff..81e85540ae2 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -387,18 +387,17 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import OTermMonoid sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') - sage: OT = OTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) - sage: o = OT.an_element(); o + sage: T = TermMonoid('O', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a O(x) - sage: cls, kwds = o.construction(); cls, kwds + sage: cls, kwds = a.construction(); cls, kwds (, {'growth': x, 'parent': O-Term Monoid x^ZZ with implicit coefficients in Rational Field}) - sage: cls(**kwds) == o + sage: cls(**kwds) == a True .. SEEALSO:: @@ -3205,19 +3204,18 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import ExactTermMonoid sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') - sage: OT = ExactTermMonoid(TermMonoid, GrowthGroup('x^ZZ'), QQ) - sage: e = OT.an_element(); e + sage: T = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a 1/2*x - sage: cls, kwds = e.construction(); cls, kwds + sage: cls, kwds = a.construction(); cls, kwds (, {'coefficient': 1/2, 'growth': x, 'parent': Exact Term Monoid x^ZZ with coefficients in Rational Field}) - sage: cls(**kwds) == e + sage: cls(**kwds) == a True .. SEEALSO:: @@ -3225,9 +3223,9 @@ def construction(self): :meth:`GenericTerm.construction`, :meth:`GenericTermMonoid.from_construction` """ - return (self.__class__, {'parent': self.parent(), - 'growth': self.growth, - 'coefficient': self.coefficient}) + cls, kwds = super().construction() + kwds.update({'coefficient': self.coefficient}) + return cls, kwds def _repr_(self): r""" From c4792672602326a9cc615fb0060d5e09a20d69dd Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 13:13:41 +0200 Subject: [PATCH 023/924] Trac #32215: remove caching from _default_kwds_construction_ --- src/sage/rings/asymptotic/term_monoid.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 81e85540ae2..4c4ba8f62a9 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2017,7 +2017,6 @@ def _validate_coefficient_or_error_(self, kwds_construction): if 'coefficient' in kwds_construction: kwds_construction['coefficient'] = coefficient - @cached_method def _default_kwds_construction_(self): r""" Return the default keyword arguments for the construction of a term. @@ -3577,7 +3576,6 @@ def _validate_coefficient_or_error_(self, kwds_construction): f'since no coefficient is given.') super()._validate_coefficient_or_error_(kwds_construction) - @cached_method def _default_kwds_construction_(self): r""" Return the default keyword arguments for the construction of a term. @@ -3603,7 +3601,7 @@ def _default_kwds_construction_(self): x """ defaults = {} - defaults.update(GenericTermMonoid._default_kwds_construction_()) + defaults.update(super()._default_kwds_construction_()) defaults.update({'coefficient': self.coefficient_ring.one()}) return defaults From 3498449102a89f07c2bfa0d757e2cb2fce383249 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 14:04:23 +0200 Subject: [PATCH 024/924] Trac #31933 review: unify spelling B-term --- src/sage/rings/asymptotic/term_monoid.py | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 10e9426ccc3..74acd60507b 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -3980,7 +3980,7 @@ class BTerm(TermWithCoefficient): r""" This class implements the base structure for parents of BTerms. - Examples of BTerms with explanation: + Examples of B-terms with explanation: - ``BT_ZZ(x^2, 5, valid_from={'x': 3})``. This is a term whose absolute value is bounded by ``5|x|^2 for |x| >= 3``. @@ -4014,11 +4014,11 @@ class BTerm(TermWithCoefficient): FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://trac.sagemath.org/31922 for details. - BTerm with coefficient 3, growth x and valid for x >= 20 + B-Term with coefficient 3, growth x and valid for x >= 20 .. WARNING:: - As this code is experimental, warnings are thrown when a BTerm + As this code is experimental, warnings are thrown when a B-term is created for the first time in a session (see :class:`sage.misc.superseded.experimental`). """ @@ -4039,13 +4039,13 @@ def __init__(self, parent, growth, coefficient, valid_from): sage: BT_QQ(x^3, 3, valid_from={'m': 20}) Traceback (most recent call last): ... - ValueError: BTerm has valid_from variables defined which do + ValueError: B-Term has valid_from variables defined which do not occur in the term. sage: BT_QQ(x^3, 0, valid_from={'x': 20}) Traceback (most recent call last): ... ZeroCoefficientError: Zero coefficient 0 is not allowed in - BTerm Monoid x^ZZ with coefficients in Rational Field. + B-Term Monoid x^ZZ with coefficients in Rational Field. sage: BT_ZZ = BTermMonoid(TermMonoid, G, ZZ) sage: BT_ZZ(x, 1/2, valid_from={'x': 20}) @@ -4056,16 +4056,16 @@ def __init__(self, parent, growth, coefficient, valid_from): sage: x, y = B('x'), B('y') sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) sage: BT_ZZ(x^3, 42, valid_from={'x': 10}) - BTerm with coefficient 42, growth x^3 and valid for x >= 10 + B-Term with coefficient 42, growth x^3 and valid for x >= 10 sage: BT_ZZ(x^3, 42, valid_from={'x': 10, 'y': 20}) - BTerm with coefficient 42, growth x^3 and valid for x >= 10 and y >= 20 + B-Term with coefficient 42, growth x^3 and valid for x >= 10 and y >= 20 sage: BT_ZZ(x^3*y^2, 42, valid_from={'x': 10}) Traceback (most recent call last): - ValueError: BTerm has not defined all variables which occur in the term in valid_from. + ValueError: B-Term has not defined all variables which occur in the term in valid_from. sage: BT_ZZ(x^3, 42, valid_from={'x': 10, 'z': 20}) Traceback (most recent call last): ... - ValueError: BTerm has valid_from variables defined which do not occur in the term. + ValueError: B-Term has valid_from variables defined which do not occur in the term. """ try: coefficient = parent.coefficient_ring(coefficient) @@ -4082,11 +4082,11 @@ def __init__(self, parent, growth, coefficient, valid_from): for variable_name in self.valid_from.keys(): if variable_name not in parent.growth_group.variable_names(): - raise ValueError('BTerm has valid_from variables defined which do not occur in the term.') + raise ValueError('B-Term has valid_from variables defined which do not occur in the term.') for variable_name in growth.variable_names(): if variable_name not in self.valid_from: - raise ValueError('BTerm has not defined all variables which occur in the term in valid_from.') + raise ValueError('B-Term has not defined all variables which occur in the term in valid_from.') super().__init__(parent=parent, growth=growth, coefficient=coefficient) @@ -4111,22 +4111,22 @@ def _repr_(self): sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) sage: BT_QQ(x^3, 3, valid_from={'x': 20}) - BTerm with coefficient 3, growth x^3 and valid for x >= 20 + B-Term with coefficient 3, growth x^3 and valid for x >= 20 sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) sage: BT_ZZ(x^2, 4, valid_from={'x': 10, 'y': 15}) - BTerm with coefficient 4, growth x^2 and valid for x >= 10 and y >= 15 + B-Term with coefficient 4, growth x^2 and valid for x >= 10 and y >= 15 """ valid_from_string = ' and '.join(f'{variable} >= {value}' for variable, value in self.valid_from.items()) - return (f'BTerm with coefficient {self.coefficient}, growth {self.growth} ' + return (f'B-Term with coefficient {self.coefficient}, growth {self.growth} ' f'and valid for {valid_from_string}') def can_absorb(self, other): r""" - Check whether this ``BTerm`` can absorb ``other``. + Check whether this B-term can absorb ``other``. INPUT: @@ -4176,11 +4176,11 @@ def can_absorb(self, other): sage: t1 = BT(x, 3, valid_from={'x': 20}); t2 = BT(x^3, 5, valid_from={'x': 5}) sage: t3 = BT(x^3, 10, valid_from={'x': 10}) sage: t2.absorb(t1) - BTerm with coefficient 2003/400, growth x^3 and valid for x >= 20 + B-Term with coefficient 2003/400, growth x^3 and valid for x >= 20 sage: t2.absorb(t3) - BTerm with coefficient 15, growth x^3 and valid for x >= 10 + B-Term with coefficient 15, growth x^3 and valid for x >= 10 sage: t3.absorb(t2) - BTerm with coefficient 15, growth x^3 and valid for x >= 10 + B-Term with coefficient 15, growth x^3 and valid for x >= 10 """ if not isinstance(other, (BTerm, ExactTerm)): return False @@ -4189,7 +4189,7 @@ def can_absorb(self, other): def _absorb_(self, other): r""" - Absorb another BTerm. + Absorb another B-term. INPUT: @@ -4205,22 +4205,22 @@ def _absorb_(self, other): sage: BT = BTermMonoid(TermMonoid, G, QQ) sage: t1 = BT(x^3, 4, valid_from={'x': 10}); t2 = BT(x, 5, valid_from={'x': 20}) sage: t1 - BTerm with coefficient 4, growth x^3 and valid for x >= 10 + B-Term with coefficient 4, growth x^3 and valid for x >= 10 sage: t1.can_absorb(t2) True sage: t1.absorb(t2) - BTerm with coefficient 321/80, growth x^3 and valid for x >= 20 + B-Term with coefficient 321/80, growth x^3 and valid for x >= 20 sage: t2.absorb(t1) Traceback (most recent call last): ... - ArithmeticError: BTerm with coefficient 5, growth x and valid for x >= 20 - cannot absorb BTerm with coefficient 4, growth x^3 and valid for x >= 10 + ArithmeticError: B-Term with coefficient 5, growth x and valid for x >= 20 + cannot absorb B-Term with coefficient 4, growth x^3 and valid for x >= 10 sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) sage: t4 = ET(x^3, 5) sage: t1.absorb(t4) Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for : 'BTerm Monoid + TypeError: unsupported operand parent(s) for : 'B-Term Monoid x^ZZ with coefficients in Rational Field' and 'Exact Term Monoid x^ZZ with coefficients in Rational Field' """ @@ -4266,7 +4266,7 @@ class BTermMonoid(TermWithCoefficientMonoid): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = BTermMonoid(TermMonoid, G, QQ) sage: BT - BTerm Monoid x^ZZ with coefficients in Rational Field + B-Term Monoid x^ZZ with coefficients in Rational Field """ # enable the category framework for elements @@ -4290,9 +4290,9 @@ def _repr_(self): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: TermMonoid('B', G, QQ)._repr_() - 'BTerm Monoid x^ZZ with coefficients in Rational Field' + 'B-Term Monoid x^ZZ with coefficients in Rational Field' """ - return (f'BTerm Monoid {self.growth_group._repr_short_()} with ' + return (f'B-Term Monoid {self.growth_group._repr_short_()} with ' f'coefficients in {self.coefficient_ring}') def _create_element_(self, growth, coefficient, valid_from): @@ -4310,7 +4310,7 @@ def _create_element_(self, growth, coefficient, valid_from): OUTPUT: - A BTerm + A B-term TESTS:: @@ -4321,7 +4321,7 @@ def _create_element_(self, growth, coefficient, valid_from): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = BTermMonoid(TermMonoid, G, QQ) sage: BT(x^3, 4, valid_from={'x': 10}) - BTerm with coefficient 4, growth x^3 and valid for x >= 10 + B-Term with coefficient 4, growth x^3 and valid for x >= 10 sage: BT(x^3, 4, valid_from=10) Traceback (most recent call last): ... From 1933586595d8bd7730a41e5e13c2dae8d3d53710 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 15:09:02 +0200 Subject: [PATCH 025/924] Trac #31933 review: simplfy doctests by using factory --- src/sage/rings/asymptotic/term_monoid.py | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 74acd60507b..85bbe1e949b 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -4004,11 +4004,10 @@ class BTerm(TermWithCoefficient): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x'); - sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) + sage: BT_QQ = TermMonoid('B', G, QQ) sage: BT_QQ(x, 3, valid_from={'x': 20}) doctest:...: FutureWarning: This class/method/function is marked as experimental. It, @@ -4031,11 +4030,10 @@ def __init__(self, parent, growth, coefficient, valid_from): sage: from sage.rings.asymptotic.growth_group import MonomialGrowthGroup sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x'); - sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) + sage: BT_QQ = TermMonoid('B', G, QQ) sage: BT_QQ(x^3, 3, valid_from={'m': 20}) Traceback (most recent call last): ... @@ -4047,14 +4045,14 @@ def __init__(self, parent, growth, coefficient, valid_from): ZeroCoefficientError: Zero coefficient 0 is not allowed in B-Term Monoid x^ZZ with coefficients in Rational Field. - sage: BT_ZZ = BTermMonoid(TermMonoid, G, ZZ) + sage: BT_ZZ = TermMonoid('B', G, ZZ) sage: BT_ZZ(x, 1/2, valid_from={'x': 20}) Traceback (most recent call last): ... ValueError: 1/2 is not a coefficient in Integer Ring. sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') - sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) + sage: BT_ZZ = TermMonoid('B', B, ZZ) sage: BT_ZZ(x^3, 42, valid_from={'x': 10}) B-Term with coefficient 42, growth x^3 and valid for x >= 10 sage: BT_ZZ(x^3, 42, valid_from={'x': 10, 'y': 20}) @@ -4105,16 +4103,15 @@ def _repr_(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup, MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x'); - sage: BT_QQ = BTermMonoid(TermMonoid, G, QQ) + sage: BT_QQ = TermMonoid('B', G, QQ) sage: BT_QQ(x^3, 3, valid_from={'x': 20}) B-Term with coefficient 3, growth x^3 and valid for x >= 20 sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') - sage: BT_ZZ = BTermMonoid(TermMonoid, B, ZZ) + sage: BT_ZZ = TermMonoid('B', B, ZZ) sage: BT_ZZ(x^2, 4, valid_from={'x': 10, 'y': 15}) B-Term with coefficient 4, growth x^2 and valid for x >= 10 and y >= 15 """ @@ -4168,11 +4165,10 @@ def can_absorb(self, other): TESTS:: sage: from sage.rings.asymptotic.growth_group import MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x') - sage: BT = BTermMonoid(TermMonoid, G, QQ) + sage: BT = TermMonoid('B', G, QQ) sage: t1 = BT(x, 3, valid_from={'x': 20}); t2 = BT(x^3, 5, valid_from={'x': 5}) sage: t3 = BT(x^3, 10, valid_from={'x': 10}) sage: t2.absorb(t1) @@ -4198,11 +4194,10 @@ def _absorb_(self, other): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup, MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x') - sage: BT = BTermMonoid(TermMonoid, G, QQ) + sage: BT = TermMonoid('B', G, QQ) sage: t1 = BT(x^3, 4, valid_from={'x': 10}); t2 = BT(x, 5, valid_from={'x': 20}) sage: t1 B-Term with coefficient 4, growth x^3 and valid for x >= 10 @@ -4264,9 +4259,11 @@ class BTermMonoid(TermWithCoefficientMonoid): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x') - sage: BT = BTermMonoid(TermMonoid, G, QQ) + sage: BT = TermMonoid('B', G, QQ) sage: BT B-Term Monoid x^ZZ with coefficients in Rational Field + sage: BT is BTermMonoid(TermMonoid, G, QQ) + True """ # enable the category framework for elements @@ -4315,11 +4312,10 @@ def _create_element_(self, growth, coefficient, valid_from): TESTS:: sage: from sage.rings.asymptotic.growth_group import MonomialGrowthGroup - sage: from sage.rings.asymptotic.term_monoid import BTermMonoid sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = MonomialGrowthGroup(ZZ, 'x') - sage: BT = BTermMonoid(TermMonoid, G, QQ) + sage: BT = TermMonoid('B', G, QQ) sage: BT(x^3, 4, valid_from={'x': 10}) B-Term with coefficient 4, growth x^3 and valid for x >= 10 sage: BT(x^3, 4, valid_from=10) From 77717e4d71091ad09810b0fdc08133b84c17b046 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 15:09:58 +0200 Subject: [PATCH 026/924] Trac #31933 review: minor changes to docstrings etc --- src/sage/rings/asymptotic/growth_group.py | 25 +++++++++++------------ src/sage/rings/asymptotic/term_monoid.py | 18 +++++++++------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index eab23e3216f..49f0f677d22 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -1613,7 +1613,7 @@ def _singularity_analysis_(self, var, zeta, precision): def _find_minimum_(self, valid_from): r""" - Find the minimum of this growth element over the range implied by valid_from. + Find the minimum of this growth element over the range implied by ``valid_from``. INPUT: @@ -1624,7 +1624,7 @@ def _find_minimum_(self, valid_from): OUTPUT: - The minimum + A number TESTS:: @@ -1634,10 +1634,9 @@ def _find_minimum_(self, valid_from): sage: G(raw_element=42)._find_minimum_(valid_from={'m': 10}) Traceback (most recent call last): ... - NotImplementedError: find minimum for GenericGrowthElement(42) not implemented. + NotImplementedError: find minimum for GenericGrowthElement(42) not implemented """ - raise NotImplementedError(f'find minimum for {self} ' - 'not implemented.') + raise NotImplementedError(f'find minimum for {self} not implemented') class GenericGrowthGroup(UniqueRepresentation, Parent, WithLocals): @@ -2810,14 +2809,14 @@ def __ne__(self, other): class DecreasingGrowthElementError(ValueError): r""" A special :python:`ValueError` - which is raised when a GrowthElement is less than one. + which is raised when a growth element is less than one. INPUT: - ``element`` -- a :class:`GenericGrowthElement` - The remaining argument passed on to - :python:`ValueError` + The remaining arguments are passed on to + :python:`ValueError`. """ def __init__(self, element, *args, **kwds): r""" @@ -3367,7 +3366,7 @@ def _singularity_analysis_(self, var, zeta, precision): def _find_minimum_(self, valid_from): r""" - Find the minimum of this growth element over the range implied by valid_from. + Find the minimum of this growth element over the range implied by ``valid_from``. INPUT: @@ -3378,7 +3377,7 @@ def _find_minimum_(self, valid_from): OUTPUT: - The minimum + A number TESTS:: @@ -3400,16 +3399,16 @@ def _find_minimum_(self, valid_from): sage: l1._find_minimum_(valid_from={'x': 5}) Traceback (most recent call last): ... - NotImplementedError: log(x)^2 is not implemented yet + NotImplementedError: log(x)^2 is not implemented sage: I = GrowthGroup('log(log(x))^ZZ') sage: l2 = I(raw_element=5) sage: l2._find_minimum_(valid_from={'x': 5}) Traceback (most recent call last): ... - NotImplementedError: log(log(x))^5 is not implemented yet + NotImplementedError: log(log(x))^5 is not implemented """ if not self.parent().gens_monomial(): - raise NotImplementedError(f'{self} is not implemented yet') + raise NotImplementedError(f'{self} is not implemented') if self.is_lt_one(): raise DecreasingGrowthElementError(self, f'the growth of {self} is less than one') elif self.is_one(): diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 85bbe1e949b..b89cd11bac1 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -3235,8 +3235,7 @@ def some_elements(self): class ExactTerm(TermWithCoefficient): r""" Class for asymptotic exact terms. These terms primarily consist of - an asymptotic growth element as well as a coefficient specifying - the growth of the asymptotic term. + an asymptotic growth element as well as a coefficient. INPUT: @@ -3978,7 +3977,7 @@ def _repr_(self): class BTerm(TermWithCoefficient): r""" - This class implements the base structure for parents of BTerms. + Class for asymptotic B-terms. Examples of B-terms with explanation: @@ -4090,7 +4089,7 @@ def __init__(self, parent, growth, coefficient, valid_from): def _repr_(self): r""" - A representation string for this B term. + A representation string for this B-term. INPUT: @@ -4169,7 +4168,8 @@ def can_absorb(self, other): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = TermMonoid('B', G, QQ) - sage: t1 = BT(x, 3, valid_from={'x': 20}); t2 = BT(x^3, 5, valid_from={'x': 5}) + sage: t1 = BT(x, 3, valid_from={'x': 20}) + sage: t2 = BT(x^3, 5, valid_from={'x': 5}) sage: t3 = BT(x^3, 10, valid_from={'x': 10}) sage: t2.absorb(t1) B-Term with coefficient 2003/400, growth x^3 and valid for x >= 20 @@ -4359,8 +4359,8 @@ def _coerce_map_from_(self, S): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid - sage: G_ZZ = GrowthGroup('x^ZZ'); x_ZZ = G_ZZ.gen() - sage: G_QQ = GrowthGroup('x^QQ'); x_QQ = G_QQ.gen() + sage: G_ZZ = GrowthGroup('x^ZZ') + sage: G_QQ = GrowthGroup('x^QQ') sage: BT_ZZ = TermMonoid('B', G_ZZ, QQ) sage: BT_QQ = TermMonoid('B', G_QQ, QQ) sage: ET = TermMonoid('exact', G_ZZ, ZZ) @@ -4377,6 +4377,10 @@ def _coerce_map_from_(self, S): True sage: ET.has_coerce_map_from(BT_ZZ) # indirect doctest False + sage: BT_ZZ.has_coerce_map_from(BT_QQ) # indirect doctest + False + sage: BT_ZZ.has_coerce_map_from(ET) # indirect doctest + True """ if isinstance(S, (ExactTermMonoid,)): if self.growth_group.has_coerce_map_from(S.growth_group) and \ From 3c5c0f0d88eec1853137f15466f254c378b32b5d Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 18 Jul 2021 16:09:25 +0200 Subject: [PATCH 027/924] fix coercion related to absorbtion --- src/sage/rings/asymptotic/term_monoid.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 4c4ba8f62a9..1c1b9a126a1 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -692,7 +692,7 @@ def absorb(self, other, check=True): sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G_QQ = GrowthGroup('x^QQ'); x = G_QQ.gen() - sage: OT = TermMonoid('O', G_QQ, coefficient_ring=ZZ) + sage: OT = TermMonoid('O', G_QQ, coefficient_ring=QQ) sage: ET = TermMonoid('exact', G_QQ, coefficient_ring=QQ) sage: ot1 = OT(x); ot2 = OT(x^2) sage: et1 = ET(x, coefficient=100); et2 = ET(x^2, coefficient=2) @@ -2604,7 +2604,7 @@ def can_absorb(self, other): sage: t2.can_absorb(t1) True """ - return other <= self + return self.growth >= other.growth def _absorb_(self, other): r""" @@ -3050,8 +3050,8 @@ def _coerce_map_from_(self, S): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') - sage: G_ZZ = GrowthGroup('x^ZZ'); x_ZZ = G_ZZ.gen() - sage: G_QQ = GrowthGroup('x^QQ'); x_QQ = G_QQ.gen() + sage: G_ZZ = GrowthGroup('x^ZZ') + sage: G_QQ = GrowthGroup('x^QQ') sage: OT_ZZ = TermMonoid('O', G_ZZ, QQ) sage: OT_QQ = TermMonoid('O', G_QQ, QQ) sage: ET = TermMonoid('exact', G_ZZ, ZZ) @@ -3068,9 +3068,14 @@ def _coerce_map_from_(self, S): True sage: ET.has_coerce_map_from(OT_ZZ) # indirect doctest False + sage: OT_ZZ.has_coerce_map_from(OT_QQ) # indirect doctest + False + sage: OT_ZZ.has_coerce_map_from(ET) # indirect doctest + True """ if isinstance(S, (ExactTermMonoid,)): - if self.growth_group.has_coerce_map_from(S.growth_group): + if self.growth_group.has_coerce_map_from(S.growth_group) and \ + self.coefficient_ring.has_coerce_map_from(S.coefficient_ring): return True else: return super(OTermMonoid, self)._coerce_map_from_(S) From f39d0118cbfe05d86544f8e41fe199db21adc8ed Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 20 Jul 2021 09:54:20 +0800 Subject: [PATCH 028/924] should pass a tuple to QuaternionAlgebra.ideal() with check=False Quaternion lattice bases are internally stored as a tuple, but we were passing a list here. This could lead to bugs such as I.scale(1) != I because lists never equal tuples. --- src/sage/algebras/quatalg/quaternion_algebra.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index f6b4271edfd..f823179bdb9 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1890,11 +1890,11 @@ def scale(self, alpha, left=False): sage: B = BrandtModule(5,37); I = B.right_ideals()[0]; i,j,k = B.quaternion_algebra().gens(); I Fractional ideal (2 + 2*j + 106*k, i + 2*j + 105*k, 4*j + 64*k, 148*k) sage: I.scale(i) - Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j] + Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) sage: I.scale(i, left=True) - Fractional ideal [2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j] + Fractional ideal (2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j) sage: I.scale(i, left=False) - Fractional ideal [2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j] + Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) sage: i * I.gens()[0] 2*i - 212*j + 2*k sage: I.gens()[0] * i @@ -1906,7 +1906,7 @@ def scale(self, alpha, left=False): gens = [alpha * b for b in self.basis()] else: gens = [b * alpha for b in self.basis()] - return Q.ideal(gens, left_order=self.__left_order, + return Q.ideal(tuple(gens), left_order=self.__left_order, right_order=self.__right_order, check=False) def quaternion_algebra(self): From 3756c49efa7bac6d1f494d7ca499f74aa5012788 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 20 Jul 2021 23:32:04 +0800 Subject: [PATCH 029/924] always cast given basis to tuple to prevent bugs like trac #32245 --- src/sage/algebras/quatalg/quaternion_algebra.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index f823179bdb9..e49851a75ec 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1099,9 +1099,9 @@ def quaternion_order(self, basis, check=True): We test out ``check=False``:: sage: Q.quaternion_order([1,i,j,k], check=False) - Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis [1, i, j, k] + Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (1, i, j, k) sage: Q.quaternion_order([i,j,k], check=False) - Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis [i, j, k] + Order of Quaternion Algebra (-11, -1) with base ring Rational Field with basis (i, j, k) """ return QuaternionOrder(self, basis, check=check) @@ -1411,7 +1411,7 @@ def __init__(self, A, basis, check=True): if any(a not in O for x in X for a in x): raise ValueError("given lattice must be a ring") - self.__basis = basis + self.__basis = tuple(basis) self.__quaternion_algebra = A Parent.__init__(self, base=ZZ, facade=(A,), category=Algebras(ZZ)) @@ -1867,7 +1867,7 @@ def __init__(self, basis, left_order=None, right_order=None, check=True): Q = basis[0].parent() basis = tuple([Q(v) for v in (QQ**4).span([Q(v).coefficient_tuple() for v in basis], ZZ).basis()]) - self.__basis = basis + self.__basis = tuple(basis) def scale(self, alpha, left=False): r""" @@ -1906,7 +1906,7 @@ def scale(self, alpha, left=False): gens = [alpha * b for b in self.basis()] else: gens = [b * alpha for b in self.basis()] - return Q.ideal(tuple(gens), left_order=self.__left_order, + return Q.ideal(gens, left_order=self.__left_order, right_order=self.__right_order, check=False) def quaternion_algebra(self): From 68aa67ed787624e7fe60b6870383a1816acbd5f4 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 21 Jul 2021 10:14:35 +0800 Subject: [PATCH 030/924] add doctest for trac #32245 --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index e49851a75ec..54b3d676a27 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1895,6 +1895,8 @@ def scale(self, alpha, left=False): Fractional ideal (2*i - 212*j + 2*k, -2 - 210*j + 2*k, -128*j + 4*k, -296*j) sage: I.scale(i, left=False) Fractional ideal (2*i + 212*j - 2*k, -2 + 210*j - 2*k, 128*j - 4*k, 296*j) + sage: I.scale(1) == I # trac #32245 + True sage: i * I.gens()[0] 2*i - 212*j + 2*k sage: I.gens()[0] * i From 64c6b308b886982a546b15dafa3f44c9220ebb97 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 21 Jul 2021 09:18:38 +0200 Subject: [PATCH 031/924] Trac 32245: ensure basis matrix is in Hermite normal form --- .../algebras/quatalg/quaternion_algebra.py | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 54b3d676a27..9ce75d98530 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1115,8 +1115,7 @@ def ideal(self, gens, left_order=None, right_order=None, check=True, **kwds): - ``gens`` -- a list of elements of this quaternion order - - ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must - 4-tuple that forms a Hermite basis for an ideal + - ``check`` -- bool (default: ``True``) - ``left_order`` -- a quaternion order or ``None`` @@ -1679,8 +1678,7 @@ def left_ideal(self, gens, check=True): - ``gens`` -- a list of elements of this quaternion order - - ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must - 4-tuple that forms a Hermite basis for an ideal + - ``check`` -- bool (default: ``True``) EXAMPLES:: @@ -1701,8 +1699,7 @@ def right_ideal(self, gens, check=True): - ``gens`` -- a list of elements of this quaternion order - - ``check`` -- bool (default: ``True``); if ``False``, then ``gens`` must - 4-tuple that forms a Hermite basis for an ideal + - ``check`` -- bool (default: ``True``) EXAMPLES:: @@ -1837,7 +1834,7 @@ class QuaternionFractionalIdeal_rational(QuaternionFractionalIdeal): quaternion algebra whose `\\ZZ`-span is an ideal - ``check`` -- bool (default: ``True``); if ``False``, do no type - checking, and the input basis *must* be in Hermite form. + checking. """ def __init__(self, basis, left_order=None, right_order=None, check=True): """ @@ -2114,9 +2111,7 @@ def ring(self): def basis(self): """ - Return basis for this fractional ideal. - - The basis is in Hermite form. + Return a basis for this fractional ideal. OUTPUT: tuple @@ -2163,7 +2158,7 @@ def __eq__(self, right): """ if not isinstance(right, QuaternionFractionalIdeal_rational): return False - return self.__basis == right.__basis + return self.basis_matrix() == right.basis_matrix() def __ne__(self, other): """ @@ -2198,6 +2193,7 @@ def __hash__(self): """ return hash(self.__basis) + @cached_method def basis_matrix(self): r""" Return basis matrix `M` in Hermite normal form for self as a @@ -2213,18 +2209,14 @@ def basis_matrix(self): EXAMPLES:: sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis_matrix() - [ 1/2 1/2 0 0] - [ 0 0 1/2 -1/2] - [ 0 1 0 0] - [ 0 0 0 -1] + [1/2 1/2 0 0] + [ 0 1 0 0] + [ 0 0 1/2 1/2] + [ 0 0 0 1] """ - try: - return self.__hermite_basis_matrix - except AttributeError: - pass B = quaternion_algebra_cython.rational_matrix_from_rational_quaternions(self.__basis) - self.__hermite_basis_matrix = B - return B + C, d = B._clear_denom() + return C.hermite_form() / d def theta_series_vector(self, B): r""" @@ -2483,10 +2475,10 @@ def free_module(self): [ 0 0 0 20/7] sage: QuaternionAlgebra(-11,-1).maximal_order().unit_ideal().basis_matrix() - [ 1/2 1/2 0 0] - [ 0 0 1/2 -1/2] - [ 0 1 0 0] - [ 0 0 0 -1] + [1/2 1/2 0 0] + [ 0 1 0 0] + [ 0 0 1/2 1/2] + [ 0 0 0 1] The free module method is also useful since it allows for checking if one ideal is contained in another, computing quotients `I/J`, etc.:: From aa8333d3d4f524594ae527c98271d2ac12405623 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Wed, 21 Jul 2021 10:53:09 +0200 Subject: [PATCH 032/924] Trac 32245: compare ambient quaternion algebra in __eq__(), add doctest --- .../algebras/quatalg/quaternion_algebra.py | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 9ce75d98530..59bbf74698e 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2138,11 +2138,9 @@ def __eq__(self, right): """ Compare this fractional quaternion ideal to ``right``. - If ``right`` is not a fractional quaternion ideal, return ``False``. - - If the fractional ideals are in different ambient - quaternion algebras, then the quaternion algebras themselves - are compared. + If ``right`` is not a fractional quaternion ideal or if the + fractional ideals are in different ambient quaternion + algebras, return ``False``. INPUT: @@ -2150,15 +2148,26 @@ def __eq__(self, right): EXAMPLES:: - sage: I = QuaternionAlgebra(-11,-1).maximal_order().unit_ideal() + sage: I = QuaternionAlgebra(-11, -1).maximal_order().unit_ideal() sage: I == I # indirect doctest True sage: I == 5 False + + sage: J = QuaternionAlgebra(-7, -1).maximal_order().unit_ideal() + sage: J == I + False + + Ideals can be equal even if they are defined by different + bases (see :trac:`32245`):: + + sage: I == I.scale(-1) + True """ if not isinstance(right, QuaternionFractionalIdeal_rational): return False - return self.basis_matrix() == right.basis_matrix() + return (self.quaternion_algebra() == right.quaternion_algebra() + and self.basis_matrix() == right.basis_matrix()) def __ne__(self, other): """ From d1c2234d919819c72ee41c3bea36e6eeb26f1948 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 21 Jul 2021 16:52:27 +0800 Subject: [PATCH 033/924] trac #32245: Quaternion orders should be equal if they are equal as lattices --- src/sage/algebras/quatalg/quaternion_algebra.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 59bbf74698e..4ee680cedf8 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -1463,9 +1463,6 @@ def __eq__(self, R): """ Compare orders self and other. - Two orders are equal if they - have the same basis and are in the same quaternion algebra. - EXAMPLES:: sage: R = QuaternionAlgebra(-11,-1).maximal_order() @@ -1475,11 +1472,14 @@ def __eq__(self, R): False sage: R == 5 False + sage: Q. = QuaternionAlgebra(-1,-19) + sage: Q.quaternion_order([1,-i,k,j+i*7]) == Q.quaternion_order([1,i,j,k]) # trac #32245 + True """ if not isinstance(R, QuaternionOrder): return False return (self.__quaternion_algebra == R.__quaternion_algebra and - self.__basis == R.__basis) + self.unit_ideal() == R.unit_ideal()) def __ne__(self, other): """ @@ -1712,6 +1712,7 @@ def right_ideal(self, gens, check=True): else: raise NotImplementedError("ideal only implemented for quaternion algebras over QQ") + @cached_method def unit_ideal(self): """ Return the unit ideal in this quaternion order. From bc7b2bc56ad6541bd8a8e5adf55e698915fad6dc Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Thu, 22 Jul 2021 12:56:42 +0200 Subject: [PATCH 034/924] removed superfluous import --- src/sage/rings/asymptotic/term_monoid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 1c1b9a126a1..1e6baca4f68 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -199,7 +199,6 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** -from sage.misc.cachefunc import cached_method from sage.rings.big_oh import O from sage.structure.element import MultiplicativeGroupElement from sage.structure.factory import UniqueFactory From d7cefd91f1218a4253920b32f21ac06dd0c828e2 Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Thu, 22 Jul 2021 12:58:29 +0200 Subject: [PATCH 035/924] minor language improvements for docstrings and deprecation warning --- src/sage/rings/asymptotic/term_monoid.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 1e6baca4f68..ab1208e7ea8 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1842,14 +1842,14 @@ def _element_constructor_(self, data, *args, **kwds): if len(args) > 1: raise TypeError( f'GenericTermMonoid._element_constructor_ ' - f'takes one positional arguments, ' + f'takes one positional argument, ' f'another positional argument is deprecated, ' f'but {len(args)+1} were given') elif len(args) == 1: from sage.misc.superseded import deprecation deprecation( 32215, - "Giving 'coefficient' as positional argument is deprecated; " + "Passing 'coefficient' as a positional argument is deprecated; " "specify it as keyword argument 'coefficient=...'.") if 'coefficient' in kwds: raise ValueError(f"Argument 'coefficient={kwds['coefficient']}' is ambiguous.") @@ -1903,8 +1903,8 @@ def _validate_growth_or_error_(self, kwds_construction): OUTPUT: - Nothing, but ``growth`` in ``kwds_construction`` might be changed - or an error is raised. + Nothing, but ``growth`` in ``kwds_construction`` might be changed. + If ``growth`` is not valid, an error is raised. TESTS:: @@ -1964,8 +1964,8 @@ def _validate_coefficient_or_error_(self, kwds_construction): OUTPUT: - Nothing, but ``coefficient`` in ``kwds_construction`` might be changed - or an error is raised. + Nothing, but ``coefficient`` in ``kwds_construction`` might be changed. + If the coefficient is not valid, an error is raised. TESTS:: From 1975a10ac0f58240e195e66cf08c6105ad125a7e Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Thu, 22 Jul 2021 13:00:49 +0200 Subject: [PATCH 036/924] changed doctests of new methods to use DefaultTermMonoidFactory --- src/sage/rings/asymptotic/term_monoid.py | 39 ++++++++---------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index ab1208e7ea8..3fb03afc4fd 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -386,8 +386,7 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: T = TermMonoid('O', GrowthGroup('x^ZZ'), QQ) sage: a = T.an_element(); a @@ -1908,8 +1907,7 @@ def _validate_growth_or_error_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('O', G, ZZ) @@ -1969,8 +1967,7 @@ def _validate_coefficient_or_error_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('O', G, ZZ) @@ -2030,8 +2027,7 @@ def _default_kwds_construction_(self): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('O', G, ZZ) @@ -2062,8 +2058,7 @@ def _convert_construction_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') @@ -2105,8 +2100,7 @@ def from_construction(self, construction, **kwds_overrides): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ') sage: x = G.gen() sage: T = TermMonoid('O', G, QQ) @@ -3000,8 +2994,7 @@ def _convert_construction_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: x = G.gen() @@ -3207,8 +3200,7 @@ def construction(self): EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: T = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) sage: a = T.an_element(); a @@ -3510,8 +3502,7 @@ def _repr_(self): sage: from sage.rings.asymptotic.growth_group import (GenericGrowthGroup, GrowthGroup) sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: TermWithCoefficientMonoid(TermMonoid, GenericGrowthGroup(ZZ), QQ)._repr_() 'TermWithCoefficient-Monoid Generic(ZZ) with coefficients in Rational Field' @@ -3540,8 +3531,7 @@ def _validate_coefficient_or_error_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('exact', G, ZZ) @@ -3594,8 +3584,7 @@ def _default_kwds_construction_(self): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: T = TermMonoid('exact', G, ZZ) @@ -3629,8 +3618,7 @@ def _convert_construction_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') @@ -4447,8 +4435,7 @@ def _convert_construction_(self, kwds_construction): TESTS:: - sage: from sage.rings.asymptotic.term_monoid import TermMonoidFactory - sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') sage: x = G.gen() From 5c5079df6021937e33f0a31f8768169e8128c21d Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Tue, 27 Jul 2021 18:30:57 +0200 Subject: [PATCH 037/924] Trac #32278. Added initial support for B-terms. Doctests are failing for now --- src/sage/rings/asymptotic/asymptotic_ring.py | 65 ++++++++++++++++++++ src/sage/rings/asymptotic/growth_group.py | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 37913d563cf..46151034a1c 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -3359,6 +3359,48 @@ def limit(self): else: raise ValueError("Cannot determine limit of {}".format(self)) + def B(self, valid_from): + r""" + Convert all terms in this asymptotic expansion to `B`-terms. + + INPUT: + + - ``valid_from``-- dictionary mapping variable names to lower bounds + for the corresponding variable. The bound implied by this term is valid when + all variables are at least their corresponding lower bound + + OUTPUT: + + An asymptotic expansion + + EXAMPLES:: + + sage: AR. = AsymptoticRing(growth_group='x^ZZ', coefficient_ring=ZZ) + sage: AR.B(2*x^2, {x: 10}) + B(2*x^2, {x: 10}) + sage: expr = 42*x^42 + x^10 + AR.B(x^2, 20); expr + 42*x^42 + x^10 + B(x^2, x >= 20) + """ + # examples which do not work yet + # sage: type(B(x)) + # + # sage: 2*z^3 + AR.B(5*z^2, {z: 20}) + # sage: (2*x).B({z: 20}) + # TESTS:: + # tests do not work yet + # sage: AR(0).B() + # Traceback (most recent call last): + # ... + # NotImplementedOZero: got B(0) + # The error term B(0) means B for sufficiently large x. + # """ + if not self.summands: + from .misc import NotImplementedOZero + raise NotImplementedOZero(self.parent(), exact_part=self.parent().zero()) + return sum(self.parent().create_summand('B', growth=element, valid_from=valid_from) + for element in self.summands.elements()) + + class AsymptoticRing(Algebra, UniqueRepresentation, WithLocals): r""" A ring consisting of :class:`asymptotic expansions `. @@ -4628,6 +4670,29 @@ def construction(self): cls=self._underlying_class()), self.coefficient_ring) + @staticmethod + def B(self, valid_from): + r"""" + Create a B-term. + + INPUT: + + - ``valid_from``-- dictionary mapping variable names to lower bounds + for the corresponding variable. The bound implied by this term is valid when + all variables are at least their corresponding lower bound + + OUTPUT: + + A B-term + + EXAMPLES:: + + sage: A. = AsymptoticRing(growth_group='x^ZZ * QQ^y', coefficient_ring=QQ) + sage: A.B(2*x^3, {x: 5}) + + """ + return self.B(valid_from) + from sage.categories.pushout import ConstructionFunctor diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index 9428f3fb21a..89ffd6f0d67 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -1617,7 +1617,7 @@ def _find_minimum_(self, valid_from): INPUT: - - ``valid_from`` --dictionary mapping variable names to lower bounds + - ``valid_from`` -- dictionary mapping variable names to lower bounds for the corresponding variable. The bound implied by this term is valid when all variables are at least their corresponding lower bound OUTPUT: From d677a53ea96a20dc332c00d1469b3ebf39521892 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sun, 1 Aug 2021 12:43:53 +0800 Subject: [PATCH 038/924] warn when Ideal() creates an ideal in a field --- src/sage/rings/ideal.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 53076ac62ed..57757e36703 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -35,6 +35,8 @@ import sage.rings.infinity from sage.structure.sequence import Sequence +import warnings + def Ideal(*args, **kwds): r""" Create the ideal in ring with given generators. @@ -183,6 +185,10 @@ def Ideal(*args, **kwds): gens = args gens = Sequence(gens) R = gens.universe() + if isinstance(R, sage.rings.ring.Field) and not hasattr(R, 'fractional_ideal'): + warnings.warn(f'Constructing an ideal in {R}, which is a field.' + ' Did you intend to take numerators first?' + ' This warning can be muted by passing the base ring to Ideal() explicitly.') else: R = first gens = args[1:] From 159ec4b5b77cc7b3b5a008a08d01a691ed506b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Aug 2021 10:20:45 +0200 Subject: [PATCH 039/924] first step towards using argparse for doc command line arguments --- src/sage_docbuild/__init__.py | 209 ++++++++++++++++------------------ 1 file changed, 97 insertions(+), 112 deletions(-) diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index b63e53539ed..85fede1c385 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -38,7 +38,7 @@ # **************************************************************************** import logging -import optparse +import argparse import os import pickle import re @@ -49,8 +49,6 @@ import types import warnings -logger = logging.getLogger(__name__) - import sphinx.util.console import sphinx.ext.intersphinx @@ -59,10 +57,15 @@ from sage.misc.misc import sage_makedirs from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC, DOT_SAGE -from .build_options import (LANGUAGES, SPHINXOPTS, PAPER, OMIT, - PAPEROPTS, ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, +from .build_options import (LANGUAGES, SPHINXOPTS, OMIT, + ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, INCREMENTAL_BUILD, ABORT_ON_ERROR) +from .utils import build_many as _build_many + +logger = logging.getLogger(__name__) + + ########################################## # Parallel Building Ref Manual # ########################################## @@ -77,6 +80,7 @@ def build_ref_doc(args): kwds['use_multidoc_inventory'] = False getattr(ReferenceSubBuilder(doc, lang), format)(*args, **kwds) + ########################################## # Builders # ########################################## @@ -121,7 +125,7 @@ def f(self, *args, **kwds): else: options += ' -D multidoc_first_pass=1' - build_command = '-b %s -d %s %s %s %s'%(type, self._doctrees_dir(), + build_command = '-b %s -d %s %s %s %s' % (type, self._doctrees_dir(), options, self.dir, output_dir) logger.debug(build_command) @@ -139,7 +143,7 @@ def f(self, *args, **kwds): # regular Exception. Otherwise multiprocessing.Pool.get hangs, see # #25161 if ABORT_ON_ERROR: - raise Exception("Non-exception during docbuild: %s"%(e,), e) + raise Exception("Non-exception during docbuild: %s" % (e,), e) if "/latex" in output_dir: logger.warning("LaTeX file written to {}".format(output_dir)) @@ -218,9 +222,9 @@ def _output_formats(self): ['changes', 'html', 'htmlhelp', 'inventory', 'json', 'latex', 'linkcheck', 'pickle', 'web'] """ - #Go through all the attributes of self and check to - #see which ones have an 'is_output_format' attribute. These - #are the ones created with builder_helper. + # Go through all the attributes of self and check to + # see which ones have an 'is_output_format' attribute. These + # are the ones created with builder_helper. output_formats = [] for attr in dir(self): if hasattr(getattr(self, attr), 'is_output_format'): @@ -262,8 +266,8 @@ def pdf(self): error_message = "failed to run $MAKE %s in %s" command = 'all-pdf' - if subprocess.call(make_target%(tex_dir, command, pdf_dir), shell=True): - raise RuntimeError(error_message%(command, tex_dir)) + if subprocess.call(make_target % (tex_dir, command, pdf_dir), shell=True): + raise RuntimeError(error_message % (command, tex_dir)) logger.warning("Build finished. The built documents can be found in %s", pdf_dir) def clean(self, *args): @@ -284,8 +288,6 @@ def clean(self, *args): inventory = builder_helper('inventory') -from .utils import build_many as _build_many - def build_many(target, args, processes=None): """ Thin wrapper around `sage_docbuild.utils.build_many` which uses the @@ -295,10 +297,11 @@ def build_many(target, args, processes=None): processes = NUM_THREADS try: _build_many(target, args, processes=processes) - except BaseException as exc: + except BaseException: if ABORT_ON_ERROR: raise + ########################################## # Parallel Building Ref Manual # ########################################## @@ -358,7 +361,7 @@ def _wrapper(self, name, *args, **kwds): build_other_doc(target) else: build_many(build_other_doc, L) - logger.warning("Elapsed time: %.1f seconds."%(time.time()-start)) + logger.warning("Elapsed time: %.1f seconds." % (time.time() - start)) logger.warning("Done building the documentation!") def get_all_documents(self): @@ -380,7 +383,7 @@ def get_all_documents(self): for lang in LANGUAGES: for document in os.listdir(os.path.join(SAGE_DOC_SRC, lang)): if (document not in OMIT - and os.path.isdir(os.path.join(SAGE_DOC_SRC, lang, document))): + and os.path.isdir(os.path.join(SAGE_DOC_SRC, lang, document))): documents.append(os.path.join(lang, document)) # Ensure that the reference guide is compiled first so that links from @@ -445,7 +448,7 @@ def create_html_redirects(self): # Walk through all of the files in the sage_directory for dirpath, dirnames, filenames in os.walk(sage_directory): # a string like reference/algebras/sage/algebras - short_path = dirpath[len(path)+1:] + short_path = dirpath[len(path) + 1:] # a string like sage/algebras shorter_path = os.path.join(*short_path.split(os.sep)[2:]) @@ -467,14 +470,13 @@ def create_html_redirects(self): levels_up = len(shorter_path.split(os.sep)) # the relative url that we will redirect to - redirect_url = "/".join(['..']*levels_up + [document_name, shorter_path, filename]) + redirect_url = "/".join(['..'] * levels_up + [document_name, shorter_path, filename]) # write the html file which performs the redirect with open(redirect_filename, 'w') as f: print(redirect_filename) f.write(html_template % redirect_url) - def clean(self): """ When we clean the output for the website index, we need to @@ -575,7 +577,6 @@ def _wrapper(self, format, *args, **kwds): Builds reference manuals: build the top-level document and its components. """ - refdir = self._refdir() logger.info('Building bibliography') self._build_bibliography(format, *args, **kwds) logger.info('Bibliography finished, building dependent manuals') @@ -616,7 +617,7 @@ def get_all_documents(self, refdir): n = len(os.listdir(directory)) documents.append((-n, os.path.join(self.name, doc))) - return [ doc[1] for doc in sorted(documents) ] + return [doc[1] for doc in sorted(documents)] class ReferenceTopBuilder(DocBuilder): @@ -706,7 +707,7 @@ def _wrapper(self, format, *args, **kwds): rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)', r'\1.', rst) rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_', - r'\1', rst) + r'\1', rst) rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`', r'\1 ', rst) # Body: add paragraph

markup. @@ -728,7 +729,7 @@ def _wrapper(self, format, *args, **kwds): # now write the file. with open(os.path.join(output_dir, 'index.html'), 'w') as new_index: new_index.write(html[:html_end_preamble]) - new_index.write('

Sage Reference Manual (PDF version)'+ '

') + new_index.write('

Sage Reference Manual (PDF version)' + '

') new_index.write(rst_body) new_index.write('