From 965ed405e414da11531bd0da90fc2a75a2068f22 Mon Sep 17 00:00:00 2001 From: Franco Saliola Date: Thu, 25 Jul 2019 12:35:06 -0400 Subject: [PATCH 001/706] add new options for representing elements of intersection poset --- .../hyperplane_arrangement/arrangement.py | 114 +++++++++++++++--- 1 file changed, 98 insertions(+), 16 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index e88e3e47d0c..b62f72d76d9 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -683,50 +683,132 @@ def cone(self, variable='t'): return H(*hyperplanes) @cached_method - def intersection_poset(self): + def intersection_poset(self, element_class="int"): r""" Return the intersection poset of the hyperplane arrangement. + INPUT: + + - ``element_class`` -- either "subspace", "subset", "int" (default) + specifying how an intersection should be represented. + OUTPUT: - The poset of non-empty intersections of hyperplanes. + The poset of non-empty intersections of hyperplanes, with intersections + represented by integers, subsets of integers or subspaces (see the + examples for more details). - EXAMPLES:: + EXAMPLES: - sage: a = hyperplane_arrangements.coordinate(2) - sage: a.intersection_poset() + By default, the elements of the poset are the integers from `0` through + the cardinality of the poset *minus one*. The element labelled `0` + always corresponds to the ambient vector space, and the hyperplanes + themselves are labelled `1`, `2`, `\dots`, `n`, where `n` is the number + of hyperplanes of the arrangement. :: + + sage: A = hyperplane_arrangements.coordinate(2) + sage: L = A.intersection_poset(); L Finite poset containing 4 elements + sage: sorted(L) + [0, 1, 2, 3] + sage: L.level_sets() + [[0], [1, 2], [3]] + + :: sage: A = hyperplane_arrangements.semiorder(3) - sage: A.intersection_poset() + sage: L = A.intersection_poset(); L Finite poset containing 19 elements + sage: sorted(L) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + sage: map(sorted, L.level_sets()) + [[0], [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]] + + By passing the argument ``element_class="subset"``, each element of the + intesection poset is labelled by the set of indices of the hyperplanes + whose intersection is said element. The index of a hyperplane is its + index in ``self.hyperplanes()``. :: + + sage: A = hyperplane_arrangements.semiorder(3) + sage: L = A.intersection_poset(element_class='subset') + sage: [sorted(level, key=sorted) for level in L.level_sets()] + [[{}], + [{0}, {1}, {2}, {3}, {4}, {5}], + [{0, 2}, {0, 3}, {0, 4}, {0, 5}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 4}, {2, 5}, {3, 4}, {3, 5}]] + + :: + + sage: H. = HyperplaneArrangements(QQ) + sage: A = H((y , (y-1) , (y+1) , (x - y) , (x + y))) + sage: L = A.intersection_poset(element_class='subset') + sage: sorted(L, key=sorted) + [{}, {0}, {0, 3}, {0, 4}, {1}, {1, 3, 4}, {2}, {2, 3}, {2, 4}, {3}, {4}] + + One can instead use affine subspaces as elements, + which is what is used to compute the poset in the first place:: + + sage: A = hyperplane_arrangements.coordinate(2) + sage: L = A.intersection_poset(element_class='subspace'); L + Finite poset containing 4 elements + sage: sorted(L) + [Affine space p + W where: + p = (0, 0) + W = Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [], Affine space p + W where: + p = (0, 0) + W = Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0], Affine space p + W where: + p = (0, 0) + W = Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [0 1], Affine space p + W where: + p = (0, 0) + W = Vector space of dimension 2 over Rational Field] """ K = self.base_ring() from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace + whole_space = AffineSubspace(0, VectorSpace(K, self.dimension())) L = [[whole_space]] + + hyperplanes = [H._affine_subspace() for H in self.hyperplanes()] + L.append(hyperplanes) + active = True - codim = 0 + codim = 1 while active: active = False new_level = [] for T in L[codim]: - for H in self: - I = H._affine_subspace().intersection(T) + for H in hyperplanes: + I = H.intersection(T) if I is not None and I != T and I not in new_level: new_level.append(I) active = True if active: L.append(new_level) codim += 1 - from sage.misc.flatten import flatten - L = flatten(L) - t = {} - for i in range(len(L)): - t[i] = L[i] - cmp_fn = lambda p, q: t[q] < t[p] + + L = [U for level in L for U in level] + + if element_class == "int": + elements = dict(enumerate(L)) + cmp_fn = lambda p, q: elements[q] < elements[p] + + elif element_class == "subset": + from sage.sets.set import Set + hyperplanes_dict = dict(enumerate(hyperplanes)) + elements = [Set([i for i in hyperplanes_dict if U <= hyperplanes_dict[i]]) for U in L] + cmp_fn = lambda U, V: U.issubset(V) + + else: # element_class == "subspace" + elements = L + cmp_fn = lambda U, V: U < V + from sage.combinat.posets.posets import Poset - return Poset((t, cmp_fn)) + return Poset((elements, cmp_fn)) def _slow_characteristic_polynomial(self): """ From 19734212bcdff08dcd0afc4840d124ac48a521cf Mon Sep 17 00:00:00 2001 From: Franco Saliola Date: Thu, 25 Jul 2019 18:29:51 -0400 Subject: [PATCH 002/706] replace `map` by a list comprehension in doctest (python3 compatibility) --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index b62f72d76d9..d4dcae1d0fb 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -721,7 +721,7 @@ def intersection_poset(self, element_class="int"): Finite poset containing 19 elements sage: sorted(L) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] - sage: map(sorted, L.level_sets()) + sage: [sorted(level_set) for level_set in L.level_sets()] [[0], [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]] By passing the argument ``element_class="subset"``, each element of the From 4c672a0d25b00b0f33c873a71164d5b6e6ac5916 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 24 Oct 2019 13:43:45 +0200 Subject: [PATCH 003/706] make sagemath SR convert to FriCAS EXPR INT --- src/sage/symbolic/expression_conversions.py | 27 +++++++++++++++++++++ src/sage/symbolic/integration/external.py | 17 +++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index ad2e20ab23f..c491b446f88 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -898,6 +898,33 @@ def __init__(self): import sage.interfaces.fricas super(FriCASConverter, self).__init__(sage.interfaces.fricas.fricas) + def pyobject(self, ex, obj): + """ + EXAMPLES:: + + sage: 2._fricas_().domainOf() # optional - fricas + PositiveInteger() + + sage: (-1/2)._fricas_().domainOf() # optional - fricas + Fraction(Integer()) + + sage: SR(2)._fricas_().domainOf() # optional - fricas + Expression(Integer()) + + sage: (sqrt(2))._fricas_().domainOf() # optional - fricas + Expression(Integer()) + """ + return "((%s)::EXPR INT)" % ex + + def symbol(self, ex): + """ + EXAMPLES:: + + sage: x._fricas_().domainOf() # optional - fricas + Expression(Integer()) + """ + return "(%s::EXPR INT)" % repr(ex) + def derivative(self, ex, operator): """ Convert the derivative of ``self`` in FriCAS. diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 057b113f36c..c116536c044 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -371,16 +371,29 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): Check that in case of failure one gets unevaluated integral:: - sage: integral(cos(ln(cos(x))), x, 0, pi/8, algorithm='fricas') # optional - fricas + sage: integral(cos(ln(cos(x))), x, 0, pi/8, algorithm='fricas') # optional - fricas integrate(cos(log(cos(x))), x, 0, 1/8*pi) - sage: integral(cos(ln(cos(x))), x, algorithm='fricas') # optional - fricas + + sage: integral(cos(ln(cos(x))), x, algorithm='fricas') # optional - fricas integral(cos(log(cos(x))), x) + + Check that :trac:`28641` is fixed:: + + sage: integrate(sqrt(2)*x^2 + 2*x, x, algorithm="fricas") # optional - fricas + 1/3*sqrt(2)*x^3 + x^2 + + sage: integrate(sqrt(2), x, algorithm="fricas") # optional - fricas + sqrt(2)*x + + sage: integrate(1, x, algorithm="fricas") # optional - fricas + x """ if not isinstance(expression, Expression): expression = SR(expression) from sage.interfaces.fricas import fricas ex = fricas(expression) + v = fricas("%s::Symbol" % v) if a is None: result = ex.integrate(v) From 86017009cda3702f6f592377a2ff32de44193664 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 25 Oct 2019 12:21:13 +0200 Subject: [PATCH 004/706] fix doctests --- src/sage/functions/exp_integral.py | 6 +++--- src/sage/functions/generalized.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index f3f1a6513bc..ca6477589ad 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -437,7 +437,7 @@ def __init__(self): sage: log_integral(x)._sympy_() li(x) sage: log_integral(x)._fricas_init_() - 'li(x)' + 'li((x::EXPR INT))' """ BuiltinFunction.__init__(self, "log_integral", nargs=1, latex_name=r'log_integral', @@ -795,7 +795,7 @@ def __init__(self): sage: sin_integral(x)._sympy_() Si(x) sage: sin_integral(x)._fricas_init_() - 'Si(x)' + 'Si((x::EXPR INT))' sage: sin_integral(x)._giac_() Si(x) """ @@ -971,7 +971,7 @@ def __init__(self): sage: cos_integral(x)._sympy_() Ci(x) sage: cos_integral(x)._fricas_init_() - 'Ci(x)' + 'Ci((x::EXPR INT))' sage: cos_integral(x)._giac_() Ci(x) """ diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index e11d807e959..c19770b95f4 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -396,7 +396,7 @@ class FunctionSignum(BuiltinFunction): sage: sgn(x)._sympy_() sign(x) sage: sgn(x)._fricas_init_() - 'sign(x)' + 'sign((x::EXPR INT))' sage: sgn(x)._giac_() sign(x) From 8948d3883678895a95fcc67f72801375e3f23832 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 16 Nov 2019 15:34:51 +0100 Subject: [PATCH 005/706] try to fix conversion of complex expressions to fricas --- src/sage/interfaces/fricas.py | 2 +- src/sage/symbolic/expression_conversions.py | 30 +++++++++++++++++++-- src/sage/symbolic/integration/external.py | 16 +++++------ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 407617f53f3..a733a0a9d63 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1524,7 +1524,7 @@ def _sage_expression(fricas_InputForm): from sage.functions.hyperbolic import tanh, sinh, cosh, coth, sech, csch from sage.misc.functional import symbolic_sum, symbolic_prod from sage.rings.infinity import infinity - register_symbol(I, {'fricas': '%i'}) + register_symbol(I, {'fricas': '(%i::EXPR Complex INT)'}) register_symbol(e, {'fricas': '%e'}) register_symbol(pi, {'fricas': 'pi'}) # fricas uses both pi and %pi register_symbol(lambda: infinity, {'fricas': 'infinity'}) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index c491b446f88..0acff56aa19 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -913,8 +913,34 @@ def pyobject(self, ex, obj): sage: (sqrt(2))._fricas_().domainOf() # optional - fricas Expression(Integer()) + + sage: pi._fricas_().domainOf() # optional - fricas + Pi() + + sage: asin(pi)._fricas_() # optional - fricas + asin(%pi) + + sage: ex = (I+sqrt(2)+2) + sage: ex._fricas_().domainOf() # optional - fricas + Expression(Complex(Integer())) + + sage: ex._fricas_()^2 # optional - fricas + +-+ + (4 + 2 %i)\|2 + 5 + 4 %i + + sage: (ex^2)._fricas_() # optional - fricas + +-+ + (4 + 2 %i)\|2 + 5 + 4 %i + """ - return "((%s)::EXPR INT)" % ex + try: + result = getattr(obj, self.name_init)() + if (isinstance(obj, NumberFieldElement_quadratic) and + obj.parent() == GaussianField): + return "((%s)::EXPR COMPLEX INT)" % result + except AttributeError: + result = repr(obj) + return "((%s)::EXPR INT)" % result def symbol(self, ex): """ @@ -966,7 +992,7 @@ def derivative(self, ex, operator): f = operator.function()(*args) params = operator.parameter_set() params_set = set(params) - vars = "[" + ",".join(args[i]._fricas_init_() for i in params_set) + "]" + vars = "[" + ",".join("%s::Symbol"%repr(args[i]) for i in params_set) + "]" mult = "[" + ",".join(str(params.count(i)) for i in params_set) + "]" outstr = "D(%s, %s, %s)"%(f._fricas_init_(), vars, mult) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index c116536c044..3392ab46acb 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -392,25 +392,25 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): expression = SR(expression) from sage.interfaces.fricas import fricas - ex = fricas(expression) - v = fricas("%s::Symbol" % v) + e_fricas = fricas(expression) + v_fricas = fricas("%s::Symbol" % v) if a is None: - result = ex.integrate(v) + result = e_fricas.integrate(v_fricas) else: - seg = fricas.equation(v, fricas.segment(a, b)) + seg = fricas.equation(v_fricas, fricas.segment(a, b)) if noPole: - result = ex.integrate(seg, '"noPole"') + result = e_fricas.integrate(seg, '"noPole"') else: - result = ex.integrate(seg) + result = e_fricas.integrate(seg) result = result.sage() if result == "failed": - return expression.integrate(v, a, b, hold=True) + result = expression.integrate(v, a, b, hold=True) - if result == "potentialPole": + elif result == "potentialPole": raise ValueError("The integrand has a potential pole" " in the integration interval") From b95f2a46b2a7eecdc3c42d042fae1031b3ade548 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 20 Nov 2019 09:38:48 +0100 Subject: [PATCH 006/706] make an exception for symbols --- src/sage/interfaces/fricas.py | 2 +- src/sage/symbolic/expression_conversions.py | 22 ++++++++++++++++++--- src/sage/symbolic/integration/external.py | 4 ++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 98a0fe9d4b5..ab82ec31e38 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1663,7 +1663,7 @@ def _sage_(self): We can also convert FriCAS's polynomials to Sage polynomials:: - sage: a = fricas(x^2 + 1); a.typeOf() # optional - fricas + sage: a = fricas("x^2 + 1"); a.typeOf() # optional - fricas Polynomial(Integer) sage: a.sage() # optional - fricas x^2 + 1 diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 0acff56aa19..6c9d2d0bbb0 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -943,13 +943,29 @@ def pyobject(self, ex, obj): return "((%s)::EXPR INT)" % result def symbol(self, ex): - """ + """Convert the argument, which is a symbol, to FriCAS. + + In this case, we do not return an `Expression Integer`, + because FriCAS frequently requires elements of domain + `Symbol` or `Variable` as arguments, for example to + `integrate`. Moreover, FriCAS is able to do the conversion + itself, whenever the argument should be interpreted as a + symbolic expression. + EXAMPLES:: sage: x._fricas_().domainOf() # optional - fricas + Variable(x) + + sage: (x^2)._fricas_().domainOf() # optional - fricas Expression(Integer()) + + sage: (2*x)._fricas_().integrate(x) # optional - fricas + 2 + x + """ - return "(%s::EXPR INT)" % repr(ex) + return repr(ex) def derivative(self, ex, operator): """ @@ -992,7 +1008,7 @@ def derivative(self, ex, operator): f = operator.function()(*args) params = operator.parameter_set() params_set = set(params) - vars = "[" + ",".join("%s::Symbol"%repr(args[i]) for i in params_set) + "]" + vars = "[" + ",".join(repr(args[i]) for i in params_set) + "]" mult = "[" + ",".join(str(params.count(i)) for i in params_set) + "]" outstr = "D(%s, %s, %s)"%(f._fricas_init_(), vars, mult) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 3392ab46acb..9ec6216e3ab 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -358,7 +358,7 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): sage: fricas_integrator(cos(x), x) # optional - fricas sin(x) sage: fricas_integrator(1/(x^2-2), x, 0, 1) # optional - fricas - 1/4*sqrt(2)*(log(3*sqrt(2) - 4) - log(sqrt(2))) + -1/8*sqrt(2)*(log(2) - log(-24*sqrt(2) + 34)) sage: fricas_integrator(1/(x^2+6), x, -oo, oo) # optional - fricas 1/6*sqrt(6)*pi @@ -393,7 +393,7 @@ def fricas_integrator(expression, v, a=None, b=None, noPole=True): from sage.interfaces.fricas import fricas e_fricas = fricas(expression) - v_fricas = fricas("%s::Symbol" % v) + v_fricas = fricas(v) if a is None: result = e_fricas.integrate(v_fricas) From 09bee2611190ef2d5071427ac69b6a8927e4fece Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 21 Nov 2019 10:56:39 +0100 Subject: [PATCH 007/706] fix doctests --- src/sage/functions/exp_integral.py | 6 +++--- src/sage/functions/generalized.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index ca6477589ad..f3f1a6513bc 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -437,7 +437,7 @@ def __init__(self): sage: log_integral(x)._sympy_() li(x) sage: log_integral(x)._fricas_init_() - 'li((x::EXPR INT))' + 'li(x)' """ BuiltinFunction.__init__(self, "log_integral", nargs=1, latex_name=r'log_integral', @@ -795,7 +795,7 @@ def __init__(self): sage: sin_integral(x)._sympy_() Si(x) sage: sin_integral(x)._fricas_init_() - 'Si((x::EXPR INT))' + 'Si(x)' sage: sin_integral(x)._giac_() Si(x) """ @@ -971,7 +971,7 @@ def __init__(self): sage: cos_integral(x)._sympy_() Ci(x) sage: cos_integral(x)._fricas_init_() - 'Ci((x::EXPR INT))' + 'Ci(x)' sage: cos_integral(x)._giac_() Ci(x) """ diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index c19770b95f4..e11d807e959 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -396,7 +396,7 @@ class FunctionSignum(BuiltinFunction): sage: sgn(x)._sympy_() sign(x) sage: sgn(x)._fricas_init_() - 'sign((x::EXPR INT))' + 'sign(x)' sage: sgn(x)._giac_() sign(x) From 8c675b40b2ea50d7b615132e6e6f805eb9191c04 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 31 Oct 2020 19:17:24 +0100 Subject: [PATCH 008/706] upgrade to pari 2.13.0 --- build/pkgs/pari/checksums.ini | 6 +++--- build/pkgs/pari/package-version.txt | 2 +- build/pkgs/pari/patches/prot_none_cygwin.patch | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index f10b432dbb7..177cc0ea4e0 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,5 +1,5 @@ tarball=pari-VERSION.tar.gz -sha1=2b9ff51feb388664b834dc346a44867546c78618 -md5=fb2968d7805424518fe44a59a2024afd -cksum=1247903778 +sha1=357ba353a3ae66d9d9198a4a8976d9acb193172e +md5=401298e079ce11bc63ae25e2ac5784a0 +cksum=1349754076 upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index a07f650f820..fb2c0766b7c 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.11.4.p0 +2.13.0 diff --git a/build/pkgs/pari/patches/prot_none_cygwin.patch b/build/pkgs/pari/patches/prot_none_cygwin.patch index 5cb7438b145..08006be0aff 100644 --- a/build/pkgs/pari/patches/prot_none_cygwin.patch +++ b/build/pkgs/pari/patches/prot_none_cygwin.patch @@ -3,10 +3,10 @@ Fix pari_mainstack_mreset() on Cygwin Rejected upstream because Cygwin is considered a dead platform diff --git a/src/language/init.c b/src/language/init.c -index 2d13684..6ea2888 100644 +index 080e83b86..b7855c665 100644 --- a/src/language/init.c +++ b/src/language/init.c -@@ -653,8 +653,8 @@ pari_mainstack_mfree(void *s, size_t size) +@@ -877,8 +877,8 @@ pari_mainstack_mfree(void *s, size_t size) /* Completely discard the memory mapped between the addresses "from" * and "to" (which must be page-aligned). * @@ -17,7 +17,7 @@ index 2d13684..6ea2888 100644 * still keep the mapping such that we can change the flags to * PROT_READ|PROT_WRITE later. * -@@ -662,7 +662,12 @@ pari_mainstack_mfree(void *s, size_t size) +@@ -886,7 +886,12 @@ pari_mainstack_mfree(void *s, size_t size) * calling mprotect(..., PROT_NONE) because the latter will keep the * memory committed (this is in particular relevant on Linux with * vm.overcommit = 2). This remains true even when calling @@ -31,7 +31,7 @@ index 2d13684..6ea2888 100644 static void pari_mainstack_mreset(pari_sp from, pari_sp to) { -@@ -671,9 +676,13 @@ pari_mainstack_mreset(pari_sp from, pari_sp to) +@@ -895,9 +900,13 @@ pari_mainstack_mreset(pari_sp from, pari_sp to) if (!s) return; addr = (void*)from; From 2833d068dd5d3eb768e2eddb84b3d19960f30188 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:09:59 +0100 Subject: [PATCH 009/706] upgrade cypari2 to 2.1.2b1 --- build/pkgs/cypari/checksums.ini | 6 +++--- build/pkgs/cypari/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index 7539ab6a614..225fe05406b 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,4 +1,4 @@ tarball=cypari2-VERSION.tar.gz -sha1=d3fc80e83bafa3c7e6eb5a779de9395d4242dddc -md5=9f431126112828c923f82e0393cbf317 -cksum=2176941985 +sha1=d5029d83665eb09142dd880e951d810ffa8d8c23 +md5=5db3ba12aee6716d74beaceabe7e979a +cksum=3866847803 diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index 3e3c2f1e5ed..1585c25cbf5 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -2.1.1 +2.1.2b1 From 886e9d2c58077103bd97959ca76e95a5b447d1d7 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:11:03 +0100 Subject: [PATCH 010/706] adapt libs/pari to 2.13.0 --- src/sage/libs/pari/convert_sage.pyx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/sage/libs/pari/convert_sage.pyx b/src/sage/libs/pari/convert_sage.pyx index 33aa4d2d46d..6261d1dddcc 100644 --- a/src/sage/libs/pari/convert_sage.pyx +++ b/src/sage/libs/pari/convert_sage.pyx @@ -241,7 +241,9 @@ cpdef gen_to_sage(Gen z, locals=None): elif t == t_FRAC: return Rational(z) elif t == t_REAL: - prec = prec_words_to_bits(z.precision()) + prec = z.bitprecision() + if prec.type() == 't_INFINITY': + prec = 53 return RealField(prec)(z) elif t == t_COMPLEX: real = z.real() @@ -251,14 +253,19 @@ cpdef gen_to_sage(Gen z, locals=None): if tx in [t_INTMOD, t_PADIC] or ty in [t_INTMOD, t_PADIC]: raise NotImplementedError("No conversion to python available for t_COMPLEX with t_INTMOD or t_PADIC components") if tx == t_REAL or ty == t_REAL: - xprec = real.precision() # will be 0 if exact - yprec = imag.precision() # will be 0 if exact - if xprec == 0: - prec = prec_words_to_bits(yprec) - elif yprec == 0: - prec = prec_words_to_bits(xprec) + xprec = real.bitprecision() # will be 0 if exact + yprec = imag.bitprecision() # will be 0 if exact + if xprec == 0 or yprec == 0: + raise RuntimeError + if xprec.type() == 't_INFINITY': + if yprec.type() == 't_INFINITY': + prec = 53 + else: + prec = yprec + elif yprec.type() == 't_INFINITY': + prec = xprec else: - prec = max(prec_words_to_bits(xprec), prec_words_to_bits(yprec)) + prec = max(xprec, yprec) R = RealField(prec) C = ComplexField(prec) From d93c350acf22b492b9835dcb74fe7eab190b8004 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:11:16 +0100 Subject: [PATCH 011/706] doctests in libs/pari --- src/sage/libs/pari/convert_sage.pyx | 14 ++++++++++++++ src/sage/libs/pari/tests.py | 16 +++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/pari/convert_sage.pyx b/src/sage/libs/pari/convert_sage.pyx index 6261d1dddcc..b506227cffe 100644 --- a/src/sage/libs/pari/convert_sage.pyx +++ b/src/sage/libs/pari/convert_sage.pyx @@ -144,6 +144,20 @@ cpdef gen_to_sage(Gen z, locals=None): sage: a.parent() Complex Field with 64 bits of precision + sage: z = pari('1 + 1.0*I'); z + 1 + 1.00000000000000*I + sage: a = gen_to_sage(z); a + 1.00000000000000000 + 1.00000000000000000*I + sage: a.parent() + Complex Field with 64 bits of precision + + sage: z = pari('1.0 + 1*I'); z + 1.00000000000000 + I + sage: a = gen_to_sage(z); a + 1.00000000000000000 + 1.00000000000000000*I + sage: a.parent() + Complex Field with 64 bits of precision + Converting polynomials:: sage: f = pari('(2/3)*x^3 + x - 5/7 + y') diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index 415a707fe8b..2dface82e32 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -135,7 +135,7 @@ sage: K. = NumberField(polygen(QQ)^3 - 2) sage: pari(K) - [y^3 - 2, [1, 1], -108, 1, [[1, 1.25992104989487, 1.58740105196820; 1, -0.629960524947437 + 1.09112363597172*I, -0.793700525984100 - 1.37472963699860*I], [1, 1.25992104989487, 1.58740105196820; 1, 0.461163111024285, -2.16843016298270; 1, -1.72108416091916, 0.581029111014503], [1, 1, 2; 1, 0, -2; 1, -2, 1], [3, 0, 0; 0, 0, 6; 0, 6, 0], [6, 0, 0; 0, 6, 0; 0, 0, 3], [2, 0, 0; 0, 0, 1; 0, 1, 0], [2, [0, 0, 2; 1, 0, 0; 0, 1, 0]], []], [1.25992104989487, -0.629960524947437 + 1.09112363597172*I], [1, y, y^2], [1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0, 0, 0, 2, 0, 2, 0; 0, 1, 0, 1, 0, 0, 0, 0, 2; 0, 0, 1, 0, 1, 0, 1, 0, 0]] + [y^3 - 2, [1, 1], -108, 1, [[1, 1.25992104989487, 1.58740105196820; 1, -0.629960524947437 + 1.09112363597172*I, -0.793700525984100 - 1.37472963699860*I], [1, 1.25992104989487, 1.58740105196820; 1, 0.461163111024285, -2.16843016298270; 1, -1.72108416091916, 0.581029111014503], [16, 20, 25; 16, 7, -35; 16, -28, 9], [3, 0, 0; 0, 0, 6; 0, 6, 0], [6, 0, 0; 0, 6, 0; 0, 0, 3], [2, 0, 0; 0, 0, 1; 0, 1, 0], [2, [0, 0, 2; 1, 0, 0; 0, 1, 0]], [2, 3]], [1.25992104989487, -0.629960524947437 + 1.09112363597172*I], [1, y, y^2], [1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0, 0, 0, 2, 0, 2, 0; 0, 1, 0, 1, 0, 0, 0, 0, 2; 0, 0, 1, 0, 1, 0, 1, 0, 0]] sage: E = EllipticCurve('37a1') sage: pari(E) @@ -376,6 +376,9 @@ ["ab", "bc"] sage: pari([65,66,123]).Strchr() + doctest:warning + ... + DeprecationWarning: the PARI/GP function Strchr is obsolete (2018-10-01) "AB{" sage: pari('"Sage"').Vecsmall() Vecsmall([83, 97, 103, 101]) @@ -448,7 +451,7 @@ sage: pari('x').component(0) Traceback (most recent call last): ... - PariError: non-existent component: index < 1 + PariError: nonexistent component: index < 1 sage: pari('x+1').conj() x + 1 @@ -764,6 +767,9 @@ 1.12539407613913 + 2.08313822670661*I sage: C. = ComplexField() sage: pari(2+i).besseln(3) + doctest:warning + ... + DeprecationWarning: the PARI/GP function besseln is obsolete (2018-12-10) -0.280775566958244 - 0.486708533223726*I sage: pari(1.5).cos() @@ -818,7 +824,7 @@ sage: pari(-1).gamma() Traceback (most recent call last): ... - PariError: domain error in gamma: argument = non-positive integer + PariError: domain error in gamma: argument = nonpositive integer sage: pari(2).gammah() 1.32934038817914 @@ -1629,7 +1635,7 @@ sage: x = QQ['x'].0; nf = pari(x^2 + 2).nfinit() sage: nf.nfgaloisconj() - [x, -x]~ + [-x, x]~ sage: nf = pari(x^3 + 2).nfinit() sage: nf.nfgaloisconj() [x]~ @@ -1672,7 +1678,7 @@ [[1, [7605, 4]~, [5610, 5]~, [7913, -6]~; 0, 1, 0, -1; 0, 0, 1, 0; 0, 0, 0, 1], [[19320, 13720; 0, 56], [2, 1; 0, 1], 1, 1]] sage: pari('x^3 - 17').nfinit() - [x^3 - 17, [1, 1], -867, 3, [[1, 1.68006914259990, 2.57128159065824; 1, -0.340034571299952 - 2.65083754153991*I, -1.28564079532912 + 2.22679517779329*I], [1, 1.68006914259990, 2.57128159065824; 1, -2.99087211283986, 0.941154382464174; 1, 2.31080297023995, -3.51243597312241], [1, 2, 3; 1, -3, 1; 1, 2, -4], [3, 1, 0; 1, -11, 17; 0, 17, 0], [51, 0, 16; 0, 17, 3; 0, 0, 1], [17, 0, -1; 0, 0, 3; -1, 3, 2], [51, [-17, 6, -1; 0, -18, 3; 1, 0, -16]], [3, 17]], [2.57128159065824, -1.28564079532912 + 2.22679517779329*I], [3, x^2 - x + 1, 3*x], [1, 0, -1; 0, 0, 3; 0, 1, 1], [1, 0, 0, 0, -4, 6, 0, 6, -1; 0, 1, 0, 1, 1, -1, 0, -1, 3; 0, 0, 1, 0, 2, 0, 1, 0, 1]] + [x^3 - 17, [1, 1], -867, 3, [[1, 1.68006914259990, 2.57128159065824; 1, -0.340034571299952 - 2.65083754153991*I, -1.28564079532912 + 2.22679517779329*I], [1, 1.68006914259990, 2.57128159065824; 1, -2.99087211283986, 0.941154382464174; 1, 2.31080297023995, -3.51243597312241], [16, 27, 41; 16, -48, 15; 16, 37, -56], [3, 1, 0; 1, -11, 17; 0, 17, 0], [51, 0, 16; 0, 17, 3; 0, 0, 1], [17, 0, -1; 0, 0, 3; -1, 3, 2], [51, [-17, 6, -1; 0, -18, 3; 1, 0, -16]], [3, 17]], [2.57128159065824, -1.28564079532912 + 2.22679517779329*I], [3, x^2 - x + 1, 3*x], [1, 0, -1; 0, 0, 3; 0, 1, 1], [1, 0, 0, 0, -4, 6, 0, 6, -1; 0, 1, 0, 1, 1, -1, 0, -1, 3; 0, 0, 1, 0, 2, 0, 1, 0, 1]] sage: pari('x^2 + 10^100 + 1').nfinit() [...] sage: pari('1.0').nfinit() From b7fed64c55b4c776abfd81814a001fa42d317d81 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:14:10 +0100 Subject: [PATCH 012/706] fix some doctests and discard .pari_nf() in number_field.py --- src/sage/rings/number_field/number_field.py | 37 +++++++++++---------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 9bb756cc82c..6de69f05e87 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -3422,7 +3422,7 @@ def fractional_ideal(self, *gens, **kwds): sage: L. = K.extension(x^2 - 3, x^2 + 1) sage: M. = L.extension(x^2 + 1) sage: L.ideal(K.ideal(2, a)) - Fractional ideal (a) + Fractional ideal (-a) sage: M.ideal(K.ideal(2, a)) == M.ideal(a*(b - c)/2) True @@ -4134,13 +4134,13 @@ def pari_nf(self, important=True): a very long time without the ``maximize_at_primes`` option:: sage: K. = NumberField(x^2 - p*q, maximize_at_primes=[p]) - sage: K.pari_nf() + sage: K.pari_nf() # not tested # TODO: pari-2.13.0 [y^2 - 100000000000000000000...] Since the discriminant is square-free, this also works:: sage: K. = NumberField(x^2 - p*q, assume_disc_small=True) - sage: K.pari_nf() + sage: K.pari_nf() # not tested # TODO: pari-2.13.0 [y^2 - 100000000000000000000...] """ try: @@ -4590,7 +4590,7 @@ def _S_class_group_and_units(self, S, proof=True): 1/13*a^2 + 7/13*a - 332/13, -1/13*a^2 + 6/13*a + 345/13, -1, - 2/13*a^2 + 1/13*a - 755/13] + -2/13*a^2 - 1/13*a + 755/13] sage: units[5] in (1/13*a^2 - 19/13*a - 7/13, 1/13*a^2 + 20/13*a - 7/13) True sage: len(units) == 6 @@ -4632,8 +4632,8 @@ def _S_class_group_quotient_matrix(self, S): sage: K. = QuadraticField(-105) sage: K._S_class_group_quotient_matrix((K.ideal(11, a + 4),)) [0 0] - [1 0] [0 1] + [1 0] """ from sage.matrix.constructor import matrix S_clgp_gens = self._S_class_group_and_units(S)[1] @@ -4754,7 +4754,7 @@ def selmer_group(self, S, m, proof=True, orders=False): 1/13*a^2 + 7/13*a - 332/13, -1/13*a^2 + 6/13*a + 345/13, -1, - 2/13*a^2 + 1/13*a - 755/13] + -2/13*a^2 - 1/13*a + 755/13] sage: gens[5] in (1/13*a^2 - 19/13*a - 7/13, 1/13*a^2 + 20/13*a - 7/13) True sage: gens[6] in (-1/13*a^2 + 45/13*a - 97/13, 1/13*a^2 - 45/13*a + 97/13) @@ -6659,7 +6659,7 @@ def units(self, proof=None): sage: A = x^4 - 10*x^3 + 20*5*x^2 - 15*5^2*x + 11*5^3 sage: K = NumberField(A, 'a') sage: K.units() - (8/275*a^3 - 12/55*a^2 + 15/11*a - 3,) + (-1/275*a^3 - 4/55*a^2 + 5/11*a - 3,) For big number fields, provably computing the unit group can take a very long time. In this case, one can ask for the @@ -6670,14 +6670,14 @@ def units(self, proof=None): sage: K.units(proof=True) # takes forever, not tested ... sage: K.units(proof=False) # result not independently verified - (a^9 + a - 1, - a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, - a^16 - a^15 + a^14 - a^12 + a^11 - a^10 - a^8 + a^7 - 2*a^6 + a^4 - 3*a^3 + 2*a^2 - 2*a + 1, + (-a^9 - a + 1, + -a^16 + a^15 - a^14 + a^12 - a^11 + a^10 + a^8 - a^7 + 2*a^6 - a^4 + 3*a^3 - 2*a^2 + 2*a - 1, 2*a^16 - a^14 - a^13 + 3*a^12 - 2*a^10 + a^9 + 3*a^8 - 3*a^6 + 3*a^5 + 3*a^4 - 2*a^3 - 2*a^2 + 3*a + 4, - 2*a^16 - 3*a^15 + 3*a^14 - 3*a^13 + 3*a^12 - a^11 + a^9 - 3*a^8 + 4*a^7 - 5*a^6 + 6*a^5 - 4*a^4 + 3*a^3 - 2*a^2 - 2*a + 4, - a^16 - a^15 - 3*a^14 - 4*a^13 - 4*a^12 - 3*a^11 - a^10 + 2*a^9 + 4*a^8 + 5*a^7 + 4*a^6 + 2*a^5 - 2*a^4 - 6*a^3 - 9*a^2 - 9*a - 7, a^15 + a^14 + 2*a^11 + a^10 - a^9 + a^8 + 2*a^7 - a^5 + 2*a^3 - a^2 - 3*a + 1, - 5*a^16 - 6*a^14 + a^13 + 7*a^12 - 2*a^11 - 7*a^10 + 4*a^9 + 7*a^8 - 6*a^7 - 7*a^6 + 8*a^5 + 6*a^4 - 11*a^3 - 5*a^2 + 13*a + 4) + -a^16 - a^15 - a^14 - a^13 - a^12 - a^11 - a^10 - a^9 - a^8 - a^7 - a^6 - a^5 - a^4 - a^3 - a^2 + 2, + -2*a^16 + 3*a^15 - 3*a^14 + 3*a^13 - 3*a^12 + a^11 - a^9 + 3*a^8 - 4*a^7 + 5*a^6 - 6*a^5 + 4*a^4 - 3*a^3 + 2*a^2 + 2*a - 4, + a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, + -a^14 - a^13 + a^12 + 2*a^10 + a^8 - 2*a^7 - 2*a^6 + 2*a^3 - a^2 + 2*a - 2) TESTS: @@ -6686,7 +6686,7 @@ def units(self, proof=None): sage: K. = NumberField(1/2*x^2 - 1/6) sage: K.units() - (3*a - 2,) + (-3*a + 2,) """ proof = proof_flag(proof) @@ -6765,7 +6765,7 @@ def unit_group(self, proof=None): sage: U.gens() (u0, u1, u2, u3, u4, u5, u6, u7, u8) sage: U.gens_values() # result not independently verified - [-1, a^9 + a - 1, a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, a^16 - a^15 + a^14 - a^12 + a^11 - a^10 - a^8 + a^7 - 2*a^6 + a^4 - 3*a^3 + 2*a^2 - 2*a + 1, 2*a^16 - a^14 - a^13 + 3*a^12 - 2*a^10 + a^9 + 3*a^8 - 3*a^6 + 3*a^5 + 3*a^4 - 2*a^3 - 2*a^2 + 3*a + 4, 2*a^16 - 3*a^15 + 3*a^14 - 3*a^13 + 3*a^12 - a^11 + a^9 - 3*a^8 + 4*a^7 - 5*a^6 + 6*a^5 - 4*a^4 + 3*a^3 - 2*a^2 - 2*a + 4, a^16 - a^15 - 3*a^14 - 4*a^13 - 4*a^12 - 3*a^11 - a^10 + 2*a^9 + 4*a^8 + 5*a^7 + 4*a^6 + 2*a^5 - 2*a^4 - 6*a^3 - 9*a^2 - 9*a - 7, a^15 + a^14 + 2*a^11 + a^10 - a^9 + a^8 + 2*a^7 - a^5 + 2*a^3 - a^2 - 3*a + 1, 5*a^16 - 6*a^14 + a^13 + 7*a^12 - 2*a^11 - 7*a^10 + 4*a^9 + 7*a^8 - 6*a^7 - 7*a^6 + 8*a^5 + 6*a^4 - 11*a^3 - 5*a^2 + 13*a + 4] + [-1, -a^9 - a + 1, -a^16 + a^15 - a^14 + a^12 - a^11 + a^10 + a^8 - a^7 + 2*a^6 - a^4 + 3*a^3 - 2*a^2 + 2*a - 1, 2*a^16 - a^14 - a^13 + 3*a^12 - 2*a^10 + a^9 + 3*a^8 - 3*a^6 + 3*a^5 + 3*a^4 - 2*a^3 - 2*a^2 + 3*a + 4, a^15 + a^14 + 2*a^11 + a^10 - a^9 + a^8 + 2*a^7 - a^5 + 2*a^3 - a^2 - 3*a + 1, -a^16 - a^15 - a^14 - a^13 - a^12 - a^11 - a^10 - a^9 - a^8 - a^7 - a^6 - a^5 - a^4 - a^3 - a^2 + 2, -2*a^16 + 3*a^15 - 3*a^14 + 3*a^13 - 3*a^12 + a^11 - a^9 + 3*a^8 - 4*a^7 + 5*a^6 - 6*a^5 + 4*a^4 - 3*a^3 + 2*a^2 + 2*a - 4, a^15 - a^12 + a^10 - a^9 - 2*a^8 + 3*a^7 + a^6 - 3*a^5 + a^4 + 4*a^3 - 3*a^2 - 2*a + 2, -a^14 - a^13 + a^12 + 2*a^10 + a^8 - 2*a^7 - 2*a^6 + 2*a^3 - a^2 + 2*a - 2] """ proof = proof_flag(proof) @@ -10792,9 +10792,9 @@ def _coerce_from_gap(self, x): EXAMPLES:: sage: k5. = CyclotomicField(5) - sage: gap('E(5)^7 + 3') + sage: w = libgap.eval('E(5)^7 + 3') + sage: w -3*E(5)-2*E(5)^2-3*E(5)^3-3*E(5)^4 - sage: w = gap('E(5)^7 + 3') sage: z^7 + 3 z^2 + 3 sage: k5(w) # indirect doctest @@ -10805,7 +10805,7 @@ def _coerce_from_gap(self, x): sage: F = CyclotomicField(8) sage: z = F.gen() - sage: a = gap(z+1/z); a + sage: a = libgap(z+1/z); a E(8)-E(8)^3 sage: F(a) -zeta8^3 + zeta8 @@ -10819,6 +10819,7 @@ def _coerce_from_gap(self, x): It also works with the old pexpect interface to GAP:: + sage: a = gap(z + 1/z) sage: b = gap(Matrix(F,[[z^2,1],[0,a+1]])); b [ [ E(4), 1 ], [ 0, 1+E(8)-E(8)^3 ] ] sage: b[1,2] From b4afd2acdea931f29c4e0ab3a0db1aa76ab8bb50 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:14:42 +0100 Subject: [PATCH 013/706] doctests in unit_group.py --- src/sage/rings/number_field/unit_group.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index 5c94f7407dd..a9dd4403c64 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -15,12 +15,12 @@ sage: UK.gens_values() # random [-1/12*a^3 + 1/6*a, 1/24*a^3 + 1/4*a^2 - 1/12*a - 1] sage: UK.gen(0).value() - -1/12*a^3 + 1/6*a + 1/12*a^3 - 1/6*a sage: UK.gen(0) u0 sage: UK.gen(0) + K.one() # coerce abstract generator into number field - -1/12*a^3 + 1/6*a + 1 + 1/12*a^3 - 1/6*a + 1 sage: [u.multiplicative_order() for u in UK.gens()] [4, +Infinity] @@ -37,18 +37,18 @@ sage: UK(-1) u0^2 sage: [UK(u) for u in (x^4-1).roots(K, multiplicities=False)] - [1, u0^2, u0^3, u0] + [1, u0^2, u0, u0^3] sage: UK.fundamental_units() # random [1/24*a^3 + 1/4*a^2 - 1/12*a - 1] sage: torsion_gen = UK.torsion_generator(); torsion_gen u0 sage: torsion_gen.value() - -1/12*a^3 + 1/6*a + 1/12*a^3 - 1/6*a sage: UK.zeta_order() 4 sage: UK.roots_of_unity() - [-1/12*a^3 + 1/6*a, -1, 1/12*a^3 - 1/6*a, 1] + [1/12*a^3 - 1/6*a, -1, -1/12*a^3 + 1/6*a, 1] Exp and log functions provide maps between units as field elements and exponent vectors with respect to the generators:: @@ -82,7 +82,7 @@ sage: SUK.rank() 4 sage: SUK.gens_values() - [-1, a^2 + 1, a^5 + a^4 - a^2 - a - 1, a + 1, -a + 1] + [-1, a^2 + 1, -a^5 - a^4 + a^2 + a + 1, a + 1, a - 1] sage: u = 9*prod(SUK.gens_values()); u -18*a^5 - 18*a^4 - 18*a^3 - 9*a^2 + 9*a + 27 sage: SUK.log(u) From 7679cfa77b6a393bc7898865d43c3fb42a71cc35 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 1 Nov 2020 18:21:16 +0100 Subject: [PATCH 014/706] fix cygwin patch --- build/pkgs/pari/patches/prot_none_cygwin.patch | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pari/patches/prot_none_cygwin.patch b/build/pkgs/pari/patches/prot_none_cygwin.patch index 08006be0aff..16340dd492b 100644 --- a/build/pkgs/pari/patches/prot_none_cygwin.patch +++ b/build/pkgs/pari/patches/prot_none_cygwin.patch @@ -3,10 +3,10 @@ Fix pari_mainstack_mreset() on Cygwin Rejected upstream because Cygwin is considered a dead platform diff --git a/src/language/init.c b/src/language/init.c -index 080e83b86..b7855c665 100644 +index 8473a3b..d9993cc 100644 --- a/src/language/init.c +++ b/src/language/init.c -@@ -877,8 +877,8 @@ pari_mainstack_mfree(void *s, size_t size) +@@ -884,8 +884,8 @@ pari_mainstack_mfree(void *s, size_t size) /* Completely discard the memory mapped between the addresses "from" * and "to" (which must be page-aligned). * @@ -17,7 +17,7 @@ index 080e83b86..b7855c665 100644 * still keep the mapping such that we can change the flags to * PROT_READ|PROT_WRITE later. * -@@ -886,7 +886,12 @@ pari_mainstack_mfree(void *s, size_t size) +@@ -893,7 +893,12 @@ pari_mainstack_mfree(void *s, size_t size) * calling mprotect(..., PROT_NONE) because the latter will keep the * memory committed (this is in particular relevant on Linux with * vm.overcommit = 2). This remains true even when calling @@ -31,15 +31,17 @@ index 080e83b86..b7855c665 100644 static void pari_mainstack_mreset(pari_sp from, pari_sp to) { -@@ -895,9 +900,13 @@ pari_mainstack_mreset(pari_sp from, pari_sp to) +@@ -902,11 +907,15 @@ pari_mainstack_mreset(pari_sp from, pari_sp to) if (!s) return; addr = (void*)from; +#ifdef __CYGWIN__ + if (mprotect(addr, s, PROT_NONE)) pari_err(e_MEM); +#else + BLOCK_SIGINT_START; res = mmap(addr, s, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); + BLOCK_SIGINT_END; if (res != addr) pari_err(e_MEM); +#endif } From e04727dcfc5bcc30601554480e247aee7fe7531f Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 2 Nov 2020 12:15:07 +0100 Subject: [PATCH 015/706] fix argument order in rnfisnorm call --- src/sage/rings/number_field/number_field_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 811edde5f00..340a8b26a01 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -1801,7 +1801,7 @@ cdef class NumberFieldElement(FieldElement): raise ValueError("L (=%s) must be a relative number field with base field K (=%s) in rnfisnorm" % (L, K)) rnf_data = K.pari_rnfnorm_data(L, proof=proof) - x, q = self.__pari__().rnfisnorm(rnf_data) + x, q = pari.rnfisnorm(rnf_data, self) return L(x, check=False), K(q, check=False) def _mpfr_(self, R): From 92ad1bbc57038f2b4f31b54b4af3aec05f95d21e Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 2 Nov 2020 13:35:44 +0100 Subject: [PATCH 016/706] fix call to zetamultall --- src/sage/modular/multiple_zeta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py index 540e29d0e50..9083303db35 100644 --- a/src/sage/modular/multiple_zeta.py +++ b/src/sage/modular/multiple_zeta.py @@ -455,7 +455,7 @@ def reset(self, max_weight=8, prec=1024): """ self.prec = int(prec) self.max_weight = int(max_weight) - self._data = pari.zetamultall(self.max_weight, self.prec) + self._data = pari.zetamultall(self.max_weight, precision=self.prec) def update(self, max_weight, prec): """ From 1ce6c2271115018f2eeeb1d487a2cb131bc46689 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 2 Nov 2020 13:55:56 +0100 Subject: [PATCH 017/706] fix invalid t_INT + t_INFINITY operation in multi_polynomial_ring.py --- src/sage/rings/polynomial/multi_polynomial_ring.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 20f1574e900..bf4b831a750 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -526,6 +526,8 @@ def __call__(self, x, check=True): # univariate polynomials. Below, v is the variable # with highest priority, and the x[i] are expressions # in the remaining variables. + if x == 0: + return self.zero() v = self.gens_dict_recursive()[str(x.variable())] return sum(self(x[i]) * v ** i for i in range(x.poldegree() + 1)) From 264775b12d89cade4180370aae3e3cbf0da5f875 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 3 Nov 2020 09:08:03 +0100 Subject: [PATCH 018/706] Fix Denis Simon GP script (following A. Rojas) --- src/sage/ext_data/pari/simon/ell.gp | 14 +++++++------- src/sage/ext_data/pari/simon/ellQ.gp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/ext_data/pari/simon/ell.gp b/src/sage/ext_data/pari/simon/ell.gp index 74f07866468..21cff9cbb36 100644 --- a/src/sage/ext_data/pari/simon/ell.gp +++ b/src/sage/ext_data/pari/simon/ell.gp @@ -1038,7 +1038,7 @@ if( DEBUGLEVEL_ell >= 1, print(" trivial points on E(K) = "); KS2gen = KS2gen[1]; for( i = 1, #KS2gen, KS2gen[i] = nfbasistoalg(bnf, KS2gen[i])); - KS2gen = concat(Mod(lift(bnf.tufu),bnf.pol),KS2gen); + KS2gen = concat(Mod(lift(concat(bnf.tu[2], bnf.fu)),bnf.pol),KS2gen); if( DEBUGLEVEL_ell >= 2, print(" #K(b,2)gen = ",#KS2gen); print(" K(b,2)gen = ",KS2gen)); @@ -1072,7 +1072,7 @@ if( DEBUGLEVEL_ell >= 1, KS2gen = KS2gen[1]; for( i = 1, #KS2gen, KS2gen[i] = nfbasistoalg(bnf, KS2gen[i])); - KS2gen = concat(Mod(lift(bnf.tufu),bnf.pol),KS2gen); + KS2gen = concat(Mod(lift(concat(bnf.tu[2], bnf.fu)),bnf.pol),KS2gen); if( DEBUGLEVEL_ell >= 2, print(" #K(a^2-4b,2)gen = ",#KS2gen); print(" K(a^2-4b,2)gen = ",KS2gen)); @@ -1244,11 +1244,11 @@ if( DEBUGLEVEL_ell >= 4, print(" bbbnf.clgp = ",bbbnf.clgp)); SL1 = idealmul(bbbnf,SL0,rnfeltup(rrrnf,bleg)); SL = idealfactor(bbbnf,SL1)[,1]~; sunL = bnfsunit(bbbnf,SL); - fondsunL = concat(bbbnf.futu,vector(#sunL[1],i,nfbasistoalg(bbbnf,sunL[1][i]))); + fondsunL = concat(concat(bbbnf.fu, bbbnf.tu[2]),vector(#sunL[1],i,nfbasistoalg(bbbnf,sunL[1][i]))); normfondsunL = vector(#fondsunL, i, norm(rnfeltabstorel(rrrnf,fondsunL[i]))); SK = idealfactor(bnf,idealnorm(bbbnf,SL1))[,1]~; sunK = bnfsunit(bnf,SK); - fondsunK = concat(bnf.futu,vector(#sunK[1],i,nfbasistoalg(bnf,sunK[1][i]))); + fondsunK = concat(concat(bnf.fu, bnf.tu[2]),vector(#sunK[1],i,nfbasistoalg(bnf,sunK[1][i]))); vecbleg = bnfissunit(bnf,sunK,bleg); matnorm = matrix(#fondsunK,#normfondsunL,i,j,0); for( i = 1, #normfondsunL, @@ -1345,7 +1345,7 @@ if( DEBUGLEVEL_ell >= 4, print("on factorise bb = ",bb)); sun = bnfsunit(bnf,idealfactor(bnf,bb)[,1]~); fact = lift(bnfissunit(bnf,sun,bb)); if( DEBUGLEVEL_ell >= 4, print("fact = ",fact)); - suni = concat(bnf.futu,vector(#sun[1],i,nfbasistoalg(bnf,sun[1][i]))); + suni = concat(concat(bnf.fu, bnf.tu[2]),vector(#sun[1],i,nfbasistoalg(bnf,sun[1][i]))); for( i = 1, #suni, if( (f = fact[i]>>1), test =0; @@ -1554,7 +1554,7 @@ if( DEBUGLEVEL_ell >= 3, print(" KS2gen = ",KS2gen[1])); LS2gen = LS2gen[1]; LS2 = vector(#LS2gen,i,lift(nfbasistoalg(Lrnf,LS2gen[i]))); - LS2 = concat(lift(Lrnf.futu),LS2); + LS2 = concat(lift(concat(Lrnf.fu, Lrnf.tu[2])),LS2); LS2 = subst(LS2,'x,ttheta); LS2 = LS2*Mod(1,polrel); @@ -1992,7 +1992,7 @@ if( DEBUGLEVEL_ell >= 2, print(" Algorithm of complete 2-descent")); KS2gen = KS2gen[1]; for( i = 1, #KS2gen, KS2gen[i] = nfbasistoalg(bnf, KS2gen[i])); - KS2gen = concat(Mod(lift(bnf.tufu),bnf.pol),KS2gen); + KS2gen = concat(Mod(lift(concat(bnf.tu[2], bnf.fu)),bnf.pol),KS2gen); if( DEBUGLEVEL_ell >= 2, print(" #K(S,2)gen = ",#KS2gen); print(" K(S,2)gen = ",KS2gen) diff --git a/src/sage/ext_data/pari/simon/ellQ.gp b/src/sage/ext_data/pari/simon/ellQ.gp index aede9fc9419..27cc1243729 100644 --- a/src/sage/ext_data/pari/simon/ellQ.gp +++ b/src/sage/ext_data/pari/simon/ellQ.gp @@ -1162,7 +1162,7 @@ if( DEBUGLEVEL_ell >= 4, print(" kerval = ",kerval)); LS2gen[j]^kerval[j,i])); \\ Add the units - LS2gen = concat(Mod(bnf[8][5],bnf.pol),LS2gen); \\ LS2gen = concat(bnf.fu,LS2gen); + LS2gen = concat(bnf.fu,LS2gen); \\ LS2gen = concat(bnf.fu,LS2gen); \\ Add also the torsion unit if its order is divisible by p. if( bnf[8][4][1]%p == 0, \\ if( bnf.tu[1]%p == 0, LS2gen = concat( [Mod(bnf[8][4][2],bnf.pol)], LS2gen)); \\ LS2gen = concat( [Mod(bnf.tu[2],bnf.pol)], LS2gen)); From 165c470d30641da4d37e7042886c8bf70e1d40ae Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Nov 2020 09:33:56 +0100 Subject: [PATCH 019/706] fix units in number fields --- src/sage/rings/number_field/unit_group.py | 50 +++++++++++------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index a9dd4403c64..191c083dd21 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -323,34 +323,31 @@ def __init__(self, number_field, proof=True, S=None): self.__S = S self.__pS = pS = [P.pari_prime() for P in S] - # compute the fundamental units via pari: - fu = [K(u, check=False) for u in pK.bnfunit()] - self.__nfu = len(fu) - - # compute the additional S-unit generators: + # compute units + # NOTE: old pari syntax for S-units (< 2.13.0): pK.bnfsunit(pS) + # NOTE: pari >= 2.13.0: the first component of the result of bnfunits + # are *all* units starting with S-units, followed by fundamental units + # followed by the torsion unit. if S: - self.__S_unit_data = pK.bnfsunit(pS) - su = [K(u, check=False) for u in self.__S_unit_data[0]] + self.__S_unit_data = pK.bnfunits(pS) else: - su = [] - self.__nsu = len(su) + self.__S_unit_data = pK.bnfunits() + # TODO: converting the factored matrix representation of bnfunits into polynomial + # form is a *big* waste of time + su_fu_tu = [pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0]] + + self.__nfu = len(pK.bnf_get_fu()) # number of fundamental units + self.__nsu = len(su_fu_tu) - self.__nfu - 1 # number of S-units + self.__ntu = pK.bnf_get_tu()[0] # order of torsion self.__rank = self.__nfu + self.__nsu - # compute a torsion generator and pick the 'simplest' one: - n, z = pK[7][3] # number of roots of unity and bnf.tu as in pari documentation - n = ZZ(n) - self.__ntu = n - z = K(z, check=False) - - # If we replaced z by another torsion generator we would need - # to allow for this in the dlog function! So we do not. + # Move the torsion unit first, then fundamental units then S-units + gens = [K(u, check=False) for u in su_fu_tu] + gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu] - # Store the actual generators (torsion first): - gens = [z] + fu + su - values = Sequence(gens, immutable=True, universe=self, check=False) # Construct the abtract group: - gens_orders = tuple([ZZ(n)]+[ZZ(0)]*(self.__rank)) - AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values, number_field) + gens_orders = tuple([ZZ(self.__ntu)]+[ZZ(0)]*(self.__rank)) + AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', gens, number_field) def _element_constructor_(self, u): """ @@ -394,8 +391,8 @@ def _element_constructor_(self, u): except TypeError: raise ValueError("%s is not an element of %s"%(u,K)) if self.__S: - m = pK.bnfissunit(self.__S_unit_data, pari(u)).mattranspose() - if m.ncols()==0: + m = pK.bnfisunit(pari(u), self.__S_unit_data).mattranspose() + if m.ncols() == 0: raise ValueError("%s is not an S-unit"%u) else: if not u.is_integral() or u.norm().abs() != 1: @@ -405,9 +402,8 @@ def _element_constructor_(self, u): # convert column matrix to a list: m = [ZZ(m[0,i].sage()) for i in range(m.ncols())] - # NB pari puts the torsion after the fundamental units, before - # the extra S-units but we have the torsion first: - m = [m[self.__nfu]] + m[:self.__nfu] + m[self.__nfu+1:] + # NOTE: pari ordering for the units is (S-units, fundamental units, torsion unit) + m = [m[-1]] + m[self.__nsu:-1] + m[:self.__nsu] return self.element_class(self, m) From 8879b2c4cbe3f6879b64f6dc03edcb3699ac9b37 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Nov 2020 10:17:10 +0100 Subject: [PATCH 020/706] Strchr -> strchr and .besseln -> bessely --- src/sage/libs/pari/tests.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index 2dface82e32..66be1181148 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -375,16 +375,13 @@ sage: pari('["bc","ab","bc"]').Set() ["ab", "bc"] - sage: pari([65,66,123]).Strchr() - doctest:warning - ... - DeprecationWarning: the PARI/GP function Strchr is obsolete (2018-10-01) + sage: pari([65,66,123]).strchr() "AB{" sage: pari('"Sage"').Vecsmall() Vecsmall([83, 97, 103, 101]) - sage: _.Strchr() + sage: _.strchr() "Sage" - sage: pari([83, 97, 103, 101]).Strchr() + sage: pari([83, 97, 103, 101]).strchr() "Sage" Basic functions:: @@ -766,10 +763,7 @@ sage: pari(2).besseli(3+i) 1.12539407613913 + 2.08313822670661*I sage: C. = ComplexField() - sage: pari(2+i).besseln(3) - doctest:warning - ... - DeprecationWarning: the PARI/GP function besseln is obsolete (2018-12-10) + sage: pari(2+i).bessely(3) -0.280775566958244 - 0.486708533223726*I sage: pari(1.5).cos() From f63e719c3074335c1fa491fd84bdddd43fe8908d Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Nov 2020 10:50:55 +0100 Subject: [PATCH 021/706] add primes to pari when maximize_at_primes is provided --- src/sage/rings/number_field/number_field.py | 53 ++++++++++++--------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 6de69f05e87..513bf129627 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -4134,13 +4134,13 @@ def pari_nf(self, important=True): a very long time without the ``maximize_at_primes`` option:: sage: K. = NumberField(x^2 - p*q, maximize_at_primes=[p]) - sage: K.pari_nf() # not tested # TODO: pari-2.13.0 + sage: K.pari_nf() [y^2 - 100000000000000000000...] Since the discriminant is square-free, this also works:: sage: K. = NumberField(x^2 - p*q, assume_disc_small=True) - sage: K.pari_nf() # not tested # TODO: pari-2.13.0 + sage: K.pari_nf() [y^2 - 100000000000000000000...] """ try: @@ -5930,28 +5930,37 @@ def _pari_integral_basis(self, v=None, important=True): try: return self._integral_basis_dict[v] except (AttributeError, KeyError): - f = self.pari_polynomial("y") - if v: - B = f.nfbasis(fa=v) - elif self._assume_disc_small: - B = f.nfbasis(1) - elif not important: - # Trial divide the discriminant with primes up to 10^6 - m = self.pari_polynomial().poldisc().abs().factor(limit=10**6) - # Since we only need a *squarefree* factorization for - # primes with exponent 1, we need trial division up to D^(1/3) - # instead of D^(1/2). - trialdivlimit2 = pari(10**12) - trialdivlimit3 = pari(10**18) - if all(p < trialdivlimit2 or (e == 1 and p < trialdivlimit3) or p.isprime() for p, e in zip(m[0], m[1])): - B = f.nfbasis(fa = m) - else: - raise RuntimeError("Unable to factor discriminant with trial division") + pass + + f = self.pari_polynomial("y") + if v: + # NOTE: here we make pari know about potentially big primes factors of + # the discriminant, see + # https://pari.math.u-bordeaux.fr/cgi-bin/bugreport.cgi?bug=2257 + primelimit = pari.default("primelimit") + primes = [p for p in v if p > primelimit] + if primes: + pari.addprimes(primes) + B = f.nfbasis(fa=v) + elif self._assume_disc_small: + B = f.nfbasis(1) + elif not important: + # Trial divide the discriminant with primes up to 10^6 + m = self.pari_polynomial().poldisc().abs().factor(limit=10**6) + # Since we only need a *squarefree* factorization for + # primes with exponent 1, we need trial division up to D^(1/3) + # instead of D^(1/2). + trialdivlimit2 = pari(10**12) + trialdivlimit3 = pari(10**18) + if all(p < trialdivlimit2 or (e == 1 and p < trialdivlimit3) or p.isprime() for p, e in zip(m[0], m[1])): + B = f.nfbasis(fa = m) else: - B = f.nfbasis() + raise RuntimeError("Unable to factor discriminant with trial division") + else: + B = f.nfbasis() - self._integral_basis_dict[v] = B - return B + self._integral_basis_dict[v] = B + return B def reduced_basis(self, prec=None): r""" From 7eac5c9977bde70ce3311e1b3dc9d62a242a8996 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Nov 2020 10:51:43 +0100 Subject: [PATCH 022/706] use bnf_get_fu instead of bnfunit --- src/sage/rings/number_field/number_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 513bf129627..f24a435207c 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6713,7 +6713,7 @@ def units(self, proof=None): pass # get PARI to compute the units - B = self.pari_bnf(proof).bnfunit() + B = self.pari_bnf(proof).bnf_get_fu() B = tuple(self(b, check=False) for b in B) if proof: # cache the provable results and return them From db1f7a448fe2ddea0d839aa2354a8d03b0384f89 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 5 Nov 2020 10:51:55 +0100 Subject: [PATCH 023/706] be more precise in documentation --- src/sage/rings/number_field/number_field.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index f24a435207c..d4586b9b64c 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -6646,7 +6646,8 @@ def units(self, proof=None): """ Return generators for the unit group modulo torsion. - ALGORITHM: Uses PARI's :pari:`bnfunit` command. + ALGORITHM: Uses PARI's :pari:`bnfinit` command (that computes fundamental units + among other things). INPUT: @@ -6728,7 +6729,7 @@ def unit_group(self, proof=None): """ Return the unit group (including torsion) of this number field. - ALGORITHM: Uses PARI's :pari:`bnfunit` command. + ALGORITHM: Uses PARI's :pari:`bnfinit` and :pari:`bnfunits`. INPUT: From b53bb878ff6cd78dd6e7c2344c994792087fc582 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sat, 7 Nov 2020 08:51:44 +0100 Subject: [PATCH 024/706] upgrade cypari2 to 2.1.2 --- build/pkgs/cypari/checksums.ini | 6 +++--- build/pkgs/cypari/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index 225fe05406b..1e316ce7594 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,4 +1,4 @@ tarball=cypari2-VERSION.tar.gz -sha1=d5029d83665eb09142dd880e951d810ffa8d8c23 -md5=5db3ba12aee6716d74beaceabe7e979a -cksum=3866847803 +sha1=2a3039aa6bd690206cb58d4c39aef21e736eacf1 +md5=6267c0dace847160763dc1777a390c0a +cksum=3639046443 diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index 1585c25cbf5..eca07e4c1a8 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -2.1.2b1 +2.1.2 From 1fc3d4cf8050c70e07906f56f88929556748519d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:01:43 -0800 Subject: [PATCH 025/706] build/pkgs/threejs: Install in /threejs-sage/rVERSION/ --- build/pkgs/threejs/package-version.txt | 2 +- build/pkgs/threejs/spkg-install.in | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/pkgs/threejs/package-version.txt b/build/pkgs/threejs/package-version.txt index 98ffb8e64ea..f149e1131d2 100644 --- a/build/pkgs/threejs/package-version.txt +++ b/build/pkgs/threejs/package-version.txt @@ -1 +1 @@ -r122 +r122.p0 diff --git a/build/pkgs/threejs/spkg-install.in b/build/pkgs/threejs/spkg-install.in index 579161d67d9..0ae18ac7347 100644 --- a/build/pkgs/threejs/spkg-install.in +++ b/build/pkgs/threejs/spkg-install.in @@ -1 +1,2 @@ -sdh_install src/* "${SAGE_SHARE}/threejs" +sdh_install src/version "${SAGE_SHARE}/threejs-sage/" +sdh_install -T src/build "${SAGE_SHARE}/threejs-sage/$(cat src/version)" From c8e707c031cea525aad4d2bfbad7c5e77eed4462 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:03:41 -0800 Subject: [PATCH 026/706] src/sage/repl/ipython_kernel/install.py: Change from threejs to threejs-sage --- src/sage/repl/ipython_kernel/install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index 073fae573c8..e9e5bac9d4e 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -159,12 +159,12 @@ def use_local_threejs(self): sage: from sage.repl.ipython_kernel.install import SageKernelSpec sage: spec = SageKernelSpec(prefix=tmp_dir()) sage: spec.use_local_threejs() - sage: threejs = os.path.join(spec.nbextensions_dir, 'threejs') + sage: threejs = os.path.join(spec.nbextensions_dir, 'threejs-sage') sage: os.path.isdir(threejs) True """ src = THREEJS_DIR - dst = os.path.join(self.nbextensions_dir, 'threejs') + dst = os.path.join(self.nbextensions_dir, 'threejs-sage') self.symlink(src, dst) def _kernel_cmd(self): From 5e1ca4e066517147edc2c43dc291e2ebe126225b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:08:32 -0800 Subject: [PATCH 027/706] src/sage/repl/ipython_kernel/install.py, src/sage/env.py, build/pkgs/sage_conf: Change from threejs to threejs-sage --- build/pkgs/sage_conf/src/sage_conf.py.in | 2 +- src/sage/env.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index b68efd38da5..ac0ea3155eb 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -16,7 +16,7 @@ CBLAS_PC_MODULES = "cblas" # Used in sage.repl.ipython_kernel.install JSMOL_DIR = "@prefix@/share/jsmol" MATHJAX_DIR = "@prefix@/share/mathjax" -THREEJS_DIR = "@prefix@/share/threejs" +THREEJS_DIR = "@prefix@/share/threejs-sage" # The following must not be used during build to determine source or installation # location of sagelib. See comments in SAGE_ROOT/src/Makefile.in diff --git a/src/sage/env.py b/src/sage/env.py index 83154eb6b1e..a17b1b08aa0 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -191,7 +191,7 @@ def var(key, *fallbacks, **kwds): var('JSMOL_DIR', join(SAGE_SHARE, 'jsmol')) var('MATHJAX_DIR', join(SAGE_SHARE, 'mathjax')) var('MTXLIB', join(SAGE_SHARE, 'meataxe')) -var('THREEJS_DIR', join(SAGE_SHARE, 'threejs')) +var('THREEJS_DIR', join(SAGE_SHARE, 'threejs-sage')) var('SINGULARPATH', join(SAGE_SHARE, 'singular')) var('PPLPY_DOCS', join(SAGE_SHARE, 'doc', 'pplpy')) var('MAXIMA', 'maxima') From 938bff40dbf6a3d5c96b5a90cffff6691c26db66 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:33:28 -0800 Subject: [PATCH 028/706] Read required threejs version from ext_data --- src/sage/ext_data/threejs/threejs-version.txt | 1 + src/sage/repl/rich_output/backend_ipython.py | 3 ++- src/sage/repl/rich_output/display_manager.py | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 src/sage/ext_data/threejs/threejs-version.txt diff --git a/src/sage/ext_data/threejs/threejs-version.txt b/src/sage/ext_data/threejs/threejs-version.txt new file mode 100644 index 00000000000..98ffb8e64ea --- /dev/null +++ b/src/sage/ext_data/threejs/threejs-version.txt @@ -0,0 +1 @@ +r122 diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 1400b175ca9..4f5d86bfb19 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -413,8 +413,9 @@ def threejs_offline_scripts(self): '... """.format(version) From f847738d8367ea931af8f955a72cf1a4f64913ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:38:18 -0800 Subject: [PATCH 029/706] src/sage/repl/rich_output/backend_ipython.py: Another change threejs -> threejs-sage --- src/sage/repl/rich_output/backend_ipython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 4f5d86bfb19..6090edf9fd0 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -597,12 +597,12 @@ def threejs_offline_scripts(self): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook sage: backend = BackendIPythonNotebook() sage: backend.threejs_offline_scripts() - '... + From d13177d1a89cc6bb643411b8bb8b233f9e1a54a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 21:22:09 -0800 Subject: [PATCH 030/706] src/sage/repl/rich_output/display_manager.py: Fix up imports --- src/sage/repl/rich_output/display_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index fd0818233b1..f8084ae09d5 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -52,6 +52,8 @@ def _required_threejs_version(): sage: _required_threejs_version() 'r...' """ + import os + import sage.env with open(os.path.join(sage.env.SAGE_EXTCODE, 'threejs', 'threejs-version.txt')) as f: return f.read().strip() @@ -758,9 +760,6 @@ def threejs_scripts(self, online): offline threejs graphics """ if online: - import sage.env - import re - import os version = _required_threejs_version() return """ From 47e795ba807db4eb4c742099f3d56cada76fc9b2 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 28 Nov 2020 18:45:41 -0800 Subject: [PATCH 031/706] Fix 404 fetching three.min.js from Jupyter/JupyterLab --- src/sage/repl/rich_output/backend_ipython.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 6090edf9fd0..bf56da3f5aa 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -597,13 +597,15 @@ def threejs_offline_scripts(self): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook sage: backend = BackendIPythonNotebook() sage: backend.threejs_offline_scripts() - '...', r'<\/script>').replace('\n', ' \\\n') return """ - + - """.format(CDN_script.replace('', r'<\/script>').replace('\n', ' \\\n')) + """.format(_required_threejs_version(), CDN_script) From e4f7e7a3e41c3b3ea77f93cccfd612005f30a17b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 09:47:52 +0100 Subject: [PATCH 032/706] temporary commit --- .../combinatorial_polyhedron/base.pxd | 2 +- .../combinatorial_polyhedron/base.pyx | 39 ++-- .../face_iterator.pxd | 5 +- .../face_iterator.pyx | 167 +++++++++++++++++- .../face_list_data_structure.pxd | 2 - 5 files changed, 193 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index f417e368f84..379ed5bdc62 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -62,7 +62,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple _mem_tuple cdef FaceIterator _face_iter(self, bint dual, int dimension) - cdef int _compute_f_vector(self) except -1 + cdef int _compute_f_vector(self, size_t num_threads, size_t parallelization_depth) except -1 cdef int _compute_edges(self, dual) except -1 cdef int _compute_ridges(self, dual) except -1 cdef int _compute_face_lattice_incidences(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 86175a23769..c8e3e959c76 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -104,6 +104,7 @@ from cysignals.signals cimport sig_check, sig_block, sig_unblock from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from .face_data_structure cimport face_len_atoms, face_init +from .face_iterator cimport iter_t, parallel_f_vector cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -1554,7 +1555,7 @@ cdef class CombinatorialPolyhedron(SageObject): return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True) @cached_method - def f_vector(self): + def f_vector(self, num_threads=1, parallelization_depth=0): r""" Compute the ``f_vector`` of the polyhedron. @@ -1584,7 +1585,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ if not self._f_vector: - self._compute_f_vector() + self._compute_f_vector(num_threads, parallelization_depth) if not self._f_vector: raise ValueError("could not determine f_vector") from sage.modules.free_module_element import vector @@ -2888,7 +2889,7 @@ cdef class CombinatorialPolyhedron(SageObject): # Internal methods. - cdef int _compute_f_vector(self) except -1: + cdef int _compute_f_vector(self, size_t num_threads, size_t parallelization_depth) except -1: r""" Compute the ``f_vector`` of the polyhedron. @@ -2897,6 +2898,18 @@ cdef class CombinatorialPolyhedron(SageObject): if self._f_vector: return 0 # There is no need to recompute the f_vector. + cdef int dim = self.dimension() + cdef int d # dimension of the current face of the iterator + cdef MemoryAllocator mem = MemoryAllocator() + + if num_threads == 0: + # No need to complain. + num_threads = 1 + + if parallelization_depth > dim - 1: + # Is a very bad choice anyway, but prevent segmenation faults. + parallelization_depth = dim - 1 + cdef bint dual if not self.is_bounded() or self.n_facets() <= self.n_Vrepresentation(): # In this case the non-dual approach is faster.. @@ -2904,25 +2917,21 @@ cdef class CombinatorialPolyhedron(SageObject): else: # In this case the dual approach is faster. dual = True - cdef FaceIterator face_iter = self._face_iter(dual, -2) - cdef int dim = self.dimension() - cdef int d # dimension of the current face of the iterator - cdef MemoryAllocator mem = MemoryAllocator() + face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] + cdef FaceIterator face_iter + cdef iter_t* structs = mem.allocarray(num_threads, sizeof(iter_t)) + cdef size_t i + for i in range(num_threads): + face_iter = face_iters[i] + structs[i][0] = face_iter.structure[0] # Initialize ``f_vector``. cdef size_t *f_vector = mem.calloc((dim + 2), sizeof(size_t)) f_vector[0] = 1 # Face iterator will only visit proper faces. f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. - # For each face in the iterator, add `1` to the corresponding entry in - # ``f_vector``. - if self.n_facets() > 0 and dim > 0: - d = face_iter.next_dimension() - while (d < dim): - sig_check() - f_vector[d+1] += 1 - d = face_iter.next_dimension() + parallel_f_vector(structs, parallelization_depth, num_threads, f_vector) # Copy ``f_vector``. if dual: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 56b062af495..c03d6ca7c8e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -45,6 +45,8 @@ cdef struct iter_s: # The number of elements in newfaces[current_dimension], # that have not been visited yet. size_t yet_to_visit + size_t current_job_id + size_t n_coatoms ctypedef iter_s iter_t[1] @@ -82,6 +84,7 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef int next_dimension(iter_t structure) nogil except -1 +cdef int next_dimension(iter_t structure, size_t parallelization_depth=?) nogil except -1 cdef int next_face_loop(iter_t structure) nogil except -1 cdef size_t n_atom_rep(iter_t structure) nogil except -1 +cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 6d79766950e..4b4b8533737 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,3 +1,5 @@ +# distutils: extra_compile_args = -fopenmp +# distutils: extra_link_args = -fopenmp r""" Face iterator for polyhedra @@ -183,6 +185,9 @@ from .base cimport CombinatorialPolyhedron from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace from .face_list_data_structure cimport * +from cython.parallel cimport prange +cimport openmp + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -308,6 +313,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + self.structure.current_job_id = 0 + self.structure.n_coatoms = self.coatoms.n_faces() + if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())): # We are in the comfortable situation that for our iterator # all intervals not containing the 0 element are boolean. @@ -351,6 +359,7 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + self.structure.current_job_id = 0 def __next__(self): r""" @@ -1187,11 +1196,14 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef inline int next_dimension(iter_t structure) nogil except -1: +cdef inline int next_dimension(iter_t structure, size_t parallelization_depth=0) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. + + ``parallelization_depth`` determines when to stop, + e.g. if it is ``1`` it will stop after having yield all faces of a facet """ - cdef int dim = structure.dimension + cdef int dim = structure.dimension - parallelization_depth structure.face_status = 0 while (not next_face_loop(structure)) and (structure.current_dimension < dim): sig_check() @@ -1205,7 +1217,8 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: if unlikely(structure.current_dimension == structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. - raise StopIteration + with gil: + raise StopIteration # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] @@ -1285,3 +1298,151 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") + +cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: + cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth + cdef size_t i + cdef size_t thread_id + for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): + _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, f_vector, i) + +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, size_t *f_vector, size_t job_id) nogil except -1: + cdef int max_dimension = structure.dimension - parallelization_depth + cdef int d + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, job_id, f_vector): + d = next_dimension(structure, parallelization_depth) + while (d < max_dimension): + sig_check() + f_vector[d+1] += 1 + d = next_dimension(structure, parallelization_depth) + +cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, size_t job_id, size_t* f_vector=NULL) nogil except -1: + if not structure.first_time[structure.current_dimension]: + # Act as if we had not visited this face. + # Thus the job id is correct again. + structure.first_time[structure.current_dimension] = True + structure.new_faces[structure.current_dimension].n_faces += 1 + cdef size_t n_coatoms = structure.n_coatoms + cdef size_t current_depth + cdef size_t job_id_i + cdef size_t current_job_id_i + cdef size_t test_id + cdef size_t i + for current_depth in range(1, parallelization_depth + 1): + job_id_i = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) + current_job_id_i = job_id_get(structure.current_job_id, current_depth - 1, parallelization_depth, n_coatoms) + if job_id_i > current_job_id_i: + job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + structure.current_dimension = structure.dimension - current_depth + for i in range(current_job_id_i, job_id_i): + if not skip_next_face(structure): + job_id_set(&structure.current_job_id, i, current_depth - 1, parallelization_depth, n_coatoms) + # This job id does not exist. + return 0 + + if job_id_i < current_job_id_i: + job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + structure.current_dimension = structure.dimension - current_depth + for i in range(job_id_i, current_job_id_i): + repeat_last_face(structure) + + # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. + test_id = job_id + job_id_set(&test_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + if test_id == job_id: + f_vector[structure.dimension - current_depth + 1] += 1 + + if structure.current_dimension == structure.dimension - current_depth: + structure.yet_to_visit = 0 + next_face_loop(structure) + if structure.current_dimension != structure.dimension - current_depth - 1: + return 0 + + if structure.current_dimension != structure.dimension - parallelization_depth - 1: + return 0 + + return 1 + +cdef inline int skip_next_face(iter_t structure) nogil except -1: + """ + Moves the iterator to the state it would be after having visited all + subfaces of ``structure.new_faces[structure.current_dimension]``. + + Return ``0`` if no farther faces of this dimension exist. + Return ``1`` otherwise. + + .. WARNING:: + + This is rather unsafe. It should be called only in two scenarios: + - ``structure.current_dimension`` was manually raised + - ``next_dimesion`` returned ``dimension - parallelization_depth`` + for ``parallelization_depth > 0`` + """ + cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] + cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + + if faces[0].n_faces == 0: + return 0 + + if not structure.first_time[structure.current_dimension]: + # In this case there exists ``faces[0].faces[n_faces]``, of which we + # have visited all faces, but which was not added to + # ``visited_all`` yet. + add_face_shallow(visited_all[0], faces[0].faces[faces[0].n_faces]) + else: + # Once we have visited all faces of ``faces[n_faces]``, we want + # to add it to ``visited_all``. + structure.first_time[structure.current_dimension] = False + faces[0].n_faces -= 1 + if faces[0].n_faces == 0: + return 0 + return 1 + +cdef inline int repeat_last_face(iter_t structure) nogil except -1: + """ + The inverse function to ``skip_next_face``. + """ + cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] + cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + if unlikely(faces[0].n_faces == faces[0].total_n_faces): + with gil: + raise MemoryError("wrong usage") + + if structure.first_time[structure.current_dimension]: + # faces[n_faces] is already in visited_all. + # As we want to revisit it, we should remove it from ``visited_all``. + visited_all[0].n_faces -= 1 + + faces[0].n_faces += 1 + # faces[n_faces] is already in visited_all and should not be readded. + structure.first_time[structure.current_dimension] = True + +# Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. +# We deal with this by obtaining/setting digits with base ``n_coatoms``. + +cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: + """ + Get the digit ``pos`` of ``job_id`` with base ``n_coatoms`` + padding the number of digits to ``parallelization_depth``. + + Digits are enumerated started with ``0``. + """ + # Remove the last ``parallelization_depth - pos - 1`` digits. + cdef size_t current_output = job_id // n_coatoms**(parallelization_depth - pos - 1) + + # Remove all digits before our current digit, which is digit ``pos``. + return current_output % n_coatoms + +cdef inline void job_id_set(size_t* job_id, size_t val, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: + """ + Set the digit ``pos`` of ``job_id`` with base ``n_coatoms`` + padding the number of digits to ``parallelization_depth``. + + Set all further digits to ``0``. + + Digits are enumerated started with ``0``. + """ + cdef size_t trailing = job_id[0] % n_coatoms**(parallelization_depth - pos) + job_id[0] -= trailing + job_id[0] += val * n_coatoms**(parallelization_depth - pos - 1) + diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 13c77a42060..8a88eaaf62e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -273,12 +273,10 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output - sig_on() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: output = get_next_level_fused(faces, new_faces, visited_all, 0) - sig_off() return output cdef inline size_t bit_rep_to_coatom_rep(face_t face, face_list_t coatoms, size_t *output): From bcb6607aed57923ff81583ba49310f6094a81f13 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 14:33:59 +0100 Subject: [PATCH 033/706] again a working parallel version --- .../face_iterator.pxd | 3 +- .../face_iterator.pyx | 214 +++++++++--------- .../face_list_data_structure.pxd | 21 +- 3 files changed, 118 insertions(+), 120 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index c03d6ca7c8e..0515e2551bb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -35,7 +35,7 @@ cdef struct iter_s: face_list_t* new_faces # After having visited a face completely, we want to add it to ``visited_all``. - # ``first_dim[i]`` will indicate, wether there is one more face in + # ``first_time[i]`` will indicate, wether there is one more face in # ``newfaces[i]`` then ``n_newfaces[i]`` suggests # that has to be added to ``visited_all``. # If ``first_time[i] == False``, we still need to @@ -45,7 +45,6 @@ cdef struct iter_s: # The number of elements in newfaces[current_dimension], # that have not been visited yet. size_t yet_to_visit - size_t current_job_id size_t n_coatoms ctypedef iter_s iter_t[1] diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 4b4b8533737..0a0e5ce2bbb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -313,7 +313,6 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 - self.structure.current_job_id = 0 self.structure.n_coatoms = self.coatoms.n_faces() if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())): @@ -359,7 +358,6 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 - self.structure.current_job_id = 0 def __next__(self): r""" @@ -1217,8 +1215,8 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: if unlikely(structure.current_dimension == structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. - with gil: - raise StopIteration + # Actually raising an error here results in a bad branch prediction. + return -1 # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] @@ -1272,7 +1270,7 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: # ``faces[n_faces]`` contains new faces. # We will visted them on next call, starting with codimension 1. - # Setting the variables correclty for next call of ``next_face_loop``. + # Setting the variables correctly for next call of ``next_face_loop``. structure.current_dimension -= 1 structure.first_time[structure.current_dimension] = True structure.visited_all[structure.current_dimension][0] = visited_all[0][0] @@ -1299,124 +1297,136 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") +cdef struct parallel_f_s: + size_t* f_vector + size_t* current_job_id + size_t* original_n_faces + size_t* original_n_visited_all + +ctypedef parallel_f_s parallel_f_t[1] + cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i + cdef int j + cdef int dim = structures[0].dimension cdef size_t thread_id + cdef MemoryAllocator mem = MemoryAllocator() + cdef parallel_f_t* parallel_structs = mem.allocarray(n_threads, sizeof(parallel_f_t)) + cdef parallel_f_s* parallel_structs_s = mem.allocarray(n_threads, sizeof(parallel_f_s)) + for i in range(n_threads): + parallel_structs[i][0] = parallel_structs_s[i] + parallel_structs[i].f_vector = mem.calloc(dim+2, sizeof(size_t)) + parallel_structs[i].current_job_id = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces[0] = structures[0].new_faces[dim - 1].n_faces + parallel_structs[i].original_n_visited_all = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_visited_all[0] = structures[0].visited_all[dim - 1].n_faces + for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): - _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, f_vector, i) + _parallel_f_vector(structures[openmp.omp_get_thread_num()], + parallelization_depth, + parallel_structs[openmp.omp_get_thread_num()], + i) -cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, size_t *f_vector, size_t job_id) nogil except -1: + # Gather the results. + for i in range(n_threads): + for j in range(structures[0].dimension + 2): + f_vector[j] += parallel_structs[i].f_vector[j] + +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: cdef int max_dimension = structure.dimension - parallelization_depth cdef int d - if prepare_face_iterator_for_partial_job(structure, parallelization_depth, job_id, f_vector): + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, parallel_struct, job_id): d = next_dimension(structure, parallelization_depth) while (d < max_dimension): - sig_check() - f_vector[d+1] += 1 + #sig_check() + parallel_struct.f_vector[d+1] += 1 d = next_dimension(structure, parallelization_depth) -cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, size_t job_id, size_t* f_vector=NULL) nogil except -1: - if not structure.first_time[structure.current_dimension]: - # Act as if we had not visited this face. - # Thus the job id is correct again. - structure.first_time[structure.current_dimension] = True - structure.new_faces[structure.current_dimension].n_faces += 1 - cdef size_t n_coatoms = structure.n_coatoms +cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: + cdef int d cdef size_t current_depth - cdef size_t job_id_i - cdef size_t current_job_id_i - cdef size_t test_id + if (not structure.first_time[structure.current_dimension] + and structure.current_dimension == structure.dimension - parallelization_depth): + # Act as if we had not visited faces in the last depth. + d = structure.dimension - parallelization_depth + current_depth = parallelization_depth + structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] + structure.new_faces[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] + parallel_struct.current_job_id[current_depth -1] = 0 + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True + structure.yet_to_visit = 0 # just to be on the safe side + + cdef size_t n_coatoms = structure.n_coatoms + cdef size_t job_id_c cdef size_t i - for current_depth in range(1, parallelization_depth + 1): - job_id_i = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) - current_job_id_i = job_id_get(structure.current_job_id, current_depth - 1, parallelization_depth, n_coatoms) - if job_id_i > current_job_id_i: - job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - structure.current_dimension = structure.dimension - current_depth - for i in range(current_job_id_i, job_id_i): - if not skip_next_face(structure): - job_id_set(&structure.current_job_id, i, current_depth - 1, parallelization_depth, n_coatoms) - # This job id does not exist. - return 0 - - if job_id_i < current_job_id_i: - job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - structure.current_dimension = structure.dimension - current_depth - for i in range(job_id_i, current_job_id_i): - repeat_last_face(structure) + cdef size_t diff + cdef size_t new_faces_counter - # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. - test_id = job_id - job_id_set(&test_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - if test_id == job_id: - f_vector[structure.dimension - current_depth + 1] += 1 + for current_depth in range(1, parallelization_depth + 1): + d = structure.dimension - current_depth + job_id_c = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) + if job_id_c != parallel_struct.current_job_id[current_depth -1]: + structure.current_dimension = d + structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] + structure.visited_all[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] + parallel_struct.current_job_id[current_depth -1] = 0 + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True + structure.yet_to_visit = 0 # just to be on the safe side + elif parallel_struct.current_job_id[current_depth] == -1: + # The job does not exist, we already settled this. + return 0 - if structure.current_dimension == structure.dimension - current_depth: - structure.yet_to_visit = 0 - next_face_loop(structure) - if structure.current_dimension != structure.dimension - current_depth - 1: + if job_id_c > parallel_struct.current_job_id[current_depth -1]: + if job_id_c >= structure.new_faces[d].n_faces: + # The job does not exist. return 0 - if structure.current_dimension != structure.dimension - parallelization_depth - 1: - return 0 + for i in range(job_id_c): + # Fast forwarding the jobs. + add_face_shallow(structure.visited_all[d], structure.new_faces[d].faces[structure.new_faces[d].n_faces -1]) + structure.new_faces[d].n_faces -= 1 - return 1 + parallel_struct.current_job_id[current_depth -1] = job_id_c -cdef inline int skip_next_face(iter_t structure) nogil except -1: - """ - Moves the iterator to the state it would be after having visited all - subfaces of ``structure.new_faces[structure.current_dimension]``. + # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. + if job_id == 0 or job_id_get(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: + parallel_struct.f_vector[structure.dimension - current_depth + 1] += 1 - Return ``0`` if no farther faces of this dimension exist. - Return ``1`` otherwise. + if structure.current_dimension == d: + structure.yet_to_visit = 0 - .. WARNING:: + if structure.new_faces[d].n_faces <= 1: + # The job will not exist. + parallel_struct.current_job_id[current_depth] = -1 + return 0 - This is rather unsafe. It should be called only in two scenarios: - - ``structure.current_dimension`` was manually raised - - ``next_dimesion`` returned ``dimension - parallelization_depth`` - for ``parallelization_depth > 0`` - """ - cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] - cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + new_faces_counter = get_next_level( + structure.new_faces[d], structure.new_faces[d-1], structure.visited_all[d]) + + if new_faces_counter: + # Setting the variables correctly, for the next call. + structure.current_dimension -= 1 + structure.first_time[d-1] = True + structure.visited_all[d-1][0] = structure.visited_all[d][0] + structure.yet_to_visit = new_faces_counter + for i in range(current_depth, parallelization_depth + 1): + parallel_struct.current_job_id[i] = 0 + parallel_struct.original_n_faces[current_depth] = new_faces_counter + parallel_struct.original_n_visited_all[current_depth] = structure.visited_all[d-1].n_faces + else: + # The job does not exist. + parallel_struct.current_job_id[current_depth] = -1 + return 0 - if faces[0].n_faces == 0: + if structure.current_dimension != structure.dimension - parallelization_depth - 1: return 0 - if not structure.first_time[structure.current_dimension]: - # In this case there exists ``faces[0].faces[n_faces]``, of which we - # have visited all faces, but which was not added to - # ``visited_all`` yet. - add_face_shallow(visited_all[0], faces[0].faces[faces[0].n_faces]) - else: - # Once we have visited all faces of ``faces[n_faces]``, we want - # to add it to ``visited_all``. - structure.first_time[structure.current_dimension] = False - faces[0].n_faces -= 1 - if faces[0].n_faces == 0: - return 0 return 1 -cdef inline int repeat_last_face(iter_t structure) nogil except -1: - """ - The inverse function to ``skip_next_face``. - """ - cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] - cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] - if unlikely(faces[0].n_faces == faces[0].total_n_faces): - with gil: - raise MemoryError("wrong usage") - - if structure.first_time[structure.current_dimension]: - # faces[n_faces] is already in visited_all. - # As we want to revisit it, we should remove it from ``visited_all``. - visited_all[0].n_faces -= 1 - - faces[0].n_faces += 1 - # faces[n_faces] is already in visited_all and should not be readded. - structure.first_time[structure.current_dimension] = True - # Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. # We deal with this by obtaining/setting digits with base ``n_coatoms``. @@ -1432,17 +1442,3 @@ cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_ # Remove all digits before our current digit, which is digit ``pos``. return current_output % n_coatoms - -cdef inline void job_id_set(size_t* job_id, size_t val, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: - """ - Set the digit ``pos`` of ``job_id`` with base ``n_coatoms`` - padding the number of digits to ``parallelization_depth``. - - Set all further digits to ``0``. - - Digits are enumerated started with ``0``. - """ - cdef size_t trailing = job_id[0] % n_coatoms**(parallelization_depth - pos) - job_id[0] -= trailing - job_id[0] += val * n_coatoms**(parallelization_depth - pos - 1) - diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 8a88eaaf62e..5beeb9da6d6 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -11,6 +11,9 @@ Inline cython methods for lists of faces. # https://www.gnu.org/licenses/ # **************************************************************************** +cdef extern from "Python.h": + int unlikely(int) nogil # Defined by Cython + from .face_data_structure cimport * from libc.string cimport memset from cysignals.signals cimport sig_on, sig_off @@ -89,9 +92,9 @@ cdef inline int add_face_shallow(face_list_t faces, face_t face) nogil except -1 """ Add a face to faces. """ - if not faces.total_n_faces >= faces.n_faces + 1: - with gil: - raise AssertionError + if unlikely(not faces.total_n_faces >= faces.n_faces + 1): + # Actually raising an error here results in a bad branch prediction. + return -1 faces.faces[faces.n_faces][0] = face[0] faces.n_faces += 1 @@ -184,12 +187,12 @@ cdef inline int face_list_intersection_fused(face_list_t dest, face_list_t A, fa """ Set ``dest`` to be the intersection of each face of ``A`` with ``b``. """ - if not dest.total_n_faces >= A.n_faces: - with gil: - raise AssertionError - if not dest.n_atoms >= A.n_atoms: - with gil: - raise AssertionError + if unlikely(not dest.total_n_faces >= A.n_faces): + # Actually raising an error here results in a bad branch prediction. + return -1 + if unlikely(not dest.n_atoms >= A.n_atoms): + # Actually raising an error here results in a bad branch prediction. + return -1 dest.n_faces = A.n_faces dest.polyhedron_is_simple = A.polyhedron_is_simple From 28c8cac07952466c3aaae4b4b6b614f21ce053fc Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 17:35:04 +0100 Subject: [PATCH 034/706] syntax error in documentation --- src/sage/parallel/map_reduce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index d51d5d872ee..10a770b6df0 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -50,7 +50,7 @@ How can I use all that stuff? ----------------------------- -First, you need to set the environment variable `SAGE_NUM_THREADS` to the +First, you need to set the environment variable ``SAGE_NUM_THREADS`` to the desired number of parallel threads to be used: sage: import os # not tested From 197eeeea4ef62610a34561403abcf832d608ab52 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 17:46:22 +0100 Subject: [PATCH 035/706] one more typo in documentation --- src/sage/parallel/map_reduce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 10a770b6df0..18ec7f41fe0 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -51,7 +51,7 @@ ----------------------------- First, you need to set the environment variable ``SAGE_NUM_THREADS`` to the -desired number of parallel threads to be used: +desired number of parallel threads to be used:: sage: import os # not tested sage: os.environ["SAGE_NUM_THREADS"] = '8' # not tested From 130b83f935b5c7f56dcfa99aaf055020233d5e1a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 15 Jan 2021 09:56:40 +0100 Subject: [PATCH 036/706] documentation and improvements to parallel f-vector --- src/sage/geometry/polyhedron/base.py | 28 +-- .../combinatorial_polyhedron/base.pyx | 39 ++++- .../face_iterator.pxd | 3 +- .../face_iterator.pyx | 164 ++++++++++++++---- .../face_list_data_structure.pxd | 5 +- 5 files changed, 184 insertions(+), 55 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 282f7e68aac..8b7fbb75b03 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -6892,10 +6892,18 @@ def facets(self): return self.faces(self.dimension()-1) @cached_method(do_pickle=True) - def f_vector(self): + def f_vector(self, num_threads=None, parallelization_depth=None): r""" Return the f-vector. + INPUT: + + - ``num_threads`` -- integer (optional); specify the number of threads; + otherwise determined by :func:`~sage.parallel.ncpus.ncpus` + + - ``parallelization_depth`` -- integer (optional); specify + how deep in the lattice the parallelization is done + OUTPUT: Return a vector whose `i`-th entry is the number of @@ -6951,7 +6959,7 @@ def f_vector(self): sage: Q.f_vector.is_in_cache() True """ - return self.combinatorial_polyhedron().f_vector() + return self.combinatorial_polyhedron().f_vector(num_threads, parallelization_depth) def flag_f_vector(self, *args): r""" @@ -10179,10 +10187,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index c8e3e959c76..f4379ccc0ff 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1555,13 +1555,20 @@ cdef class CombinatorialPolyhedron(SageObject): return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True) @cached_method - def f_vector(self, num_threads=1, parallelization_depth=0): + def f_vector(self, num_threads=None, parallelization_depth=None): r""" Compute the ``f_vector`` of the polyhedron. The ``f_vector`` contains the number of faces of dimension `k` for each `k` in ``range(-1, self.dimension() + 1)``. + INPUT: + + - ``num_threads`` -- integer (optional); specify the number of threads + + - ``parallelization_depth`` -- integer (optional); specify + how deep in the lattice the parallelization is done + .. NOTE:: To obtain edges and/or ridges as well, first do so. This might @@ -1579,11 +1586,33 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C.f_vector() (1, 10, 45, 120, 185, 150, 50, 1) + Using two threads:: + + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: C.f_vector(num_threads=2) + (1, 120, 240, 150, 30, 1) + TESTS:: sage: type(C.f_vector()) """ + if num_threads is None: + from sage.parallel.ncpus import ncpus + num_threads = ncpus() + + if parallelization_depth is None: + # Setting some reasonable defaults. + if num_threads == 0: + parallelization_depth = 0 + elif num_threads <= 3: + parallelization_depth = 1 + elif num_threads <= 8: + parallelization_depth = 2 + else: + parallelization_depth = 3 + if not self._f_vector: self._compute_f_vector(num_threads, parallelization_depth) if not self._f_vector: @@ -2918,20 +2947,20 @@ cdef class CombinatorialPolyhedron(SageObject): # In this case the dual approach is faster. dual = True - face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] cdef FaceIterator face_iter cdef iter_t* structs = mem.allocarray(num_threads, sizeof(iter_t)) cdef size_t i + + # For each thread an independent structure. + face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] for i in range(num_threads): face_iter = face_iters[i] structs[i][0] = face_iter.structure[0] # Initialize ``f_vector``. cdef size_t *f_vector = mem.calloc((dim + 2), sizeof(size_t)) - f_vector[0] = 1 # Face iterator will only visit proper faces. - f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. - parallel_f_vector(structs, parallelization_depth, num_threads, f_vector) + parallel_f_vector(structs, num_threads, parallelization_depth, f_vector) # Copy ``f_vector``. if dual: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 0515e2551bb..c92c0ace7b1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -81,9 +81,10 @@ cdef class FaceIterator_geom(FaceIterator_base): cdef object _requested_dim # Dimension requested on init. cdef readonly object P # The original polyhedron. +cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parallelization_depth, size_t *f_vector) except -1 + # Nogil definitions of crucial functions. cdef int next_dimension(iter_t structure, size_t parallelization_depth=?) nogil except -1 cdef int next_face_loop(iter_t structure) nogil except -1 cdef size_t n_atom_rep(iter_t structure) nogil except -1 -cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 0a0e5ce2bbb..b8183f2a232 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1297,68 +1297,145 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") +# Parallel iteration over the faces. +# Currently only the f-vector is implemented, but slight +# modifications would allow collecting other information as well. + cdef struct parallel_f_s: + # A structure carrying things that each thread should have exclusive access to. size_t* f_vector size_t* current_job_id + + # Keep track so that we can easily go from one job to the next. size_t* original_n_faces size_t* original_n_visited_all ctypedef parallel_f_s parallel_f_t[1] -cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: +cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parallelization_depth, size_t *f_vector) except -1: + """ + Compute the ``f_vector`` in parallel. + + INPUT: + + - ``structures`` -- one structure per thread + + - ``num_threads`` -- the number of threads to use + + - ``parallelization_depth`` -- the codimension at which the threads are released + + - ``f_vector`` -- where the ``f_vector`` is output + """ cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i cdef int j cdef int dim = structures[0].dimension + f_vector[0] = 1 # Face iterator will only visit proper faces. + f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. + if dim <= 0 or structures[0].n_coatoms == 0: + # Iterator assumes at least one face and at least dimension 1. + return 0 + + if num_threads == 0: + num_threads = 1 + cdef size_t thread_id cdef MemoryAllocator mem = MemoryAllocator() - cdef parallel_f_t* parallel_structs = mem.allocarray(n_threads, sizeof(parallel_f_t)) - cdef parallel_f_s* parallel_structs_s = mem.allocarray(n_threads, sizeof(parallel_f_s)) - for i in range(n_threads): + + # Setting up for each thread some storage space. + cdef parallel_f_t* parallel_structs = \ + mem.allocarray(num_threads, sizeof(parallel_f_t)) + cdef parallel_f_s* parallel_structs_s = \ + mem.allocarray(num_threads, sizeof(parallel_f_s)) + + for i in range(num_threads): parallel_structs[i][0] = parallel_structs_s[i] - parallel_structs[i].f_vector = mem.calloc(dim+2, sizeof(size_t)) - parallel_structs[i].current_job_id = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_faces = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_faces[0] = structures[0].new_faces[dim - 1].n_faces - parallel_structs[i].original_n_visited_all = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_visited_all[0] = structures[0].visited_all[dim - 1].n_faces - - for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): + + # Partial f-vectors. + parallel_structs[i].f_vector = \ + mem.calloc(dim+2, sizeof(size_t)) + parallel_structs[i].current_job_id = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + + # Keeping back of the original number of faces allows faster starting the next job. + parallel_structs[i].original_n_faces = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces[0] = \ + structures[0].new_faces[dim - 1].n_faces + + parallel_structs[i].original_n_visited_all = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_visited_all[0] = \ + structures[0].visited_all[dim - 1].n_faces + + for i in prange(n_jobs, schedule='dynamic', chunksize=1, + num_threads=num_threads, nogil=True): _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, parallel_structs[openmp.omp_get_thread_num()], i) # Gather the results. - for i in range(n_threads): + for i in range(num_threads): for j in range(structures[0].dimension + 2): f_vector[j] += parallel_structs[i].f_vector[j] -cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, + parallel_f_t parallel_struct, size_t job_id) nogil except -1: + """ + Set up a job and then visit all faces. + """ cdef int max_dimension = structure.dimension - parallelization_depth cdef int d - if prepare_face_iterator_for_partial_job(structure, parallelization_depth, parallel_struct, job_id): + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, + parallel_struct, job_id): d = next_dimension(structure, parallelization_depth) while (d < max_dimension): - #sig_check() parallel_struct.f_vector[d+1] += 1 d = next_dimension(structure, parallelization_depth) -cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: +cdef inline int prepare_face_iterator_for_partial_job( + iter_t structure, size_t parallelization_depth, + parallel_f_t parallel_struct, size_t job_id) nogil except -1: + """ + Set ``structure`` according to ``job_id``. + + ``job_id`` should be thought of as its digits with base ``structure.n_coatoms`` + padded to ``parallelization_depth``. + + The first digit determines which facet to visit. + The next digit determines which facet of the facet should be visited. + + OUTPUT: ``1`` if the job exists and ``0`` otherwise. + + In addition, the first job treating a face will "visit" this face + and increase the corresponding entry of the f-vector. + """ cdef int d cdef size_t current_depth if (not structure.first_time[structure.current_dimension] and structure.current_dimension == structure.dimension - parallelization_depth): # Act as if we had not visited faces in the last depth. + # Set ``current_job_id[parallelization_depth - 1] = 0``. d = structure.dimension - parallelization_depth current_depth = parallelization_depth - structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] - structure.new_faces[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] - parallel_struct.current_job_id[current_depth -1] = 0 - parallel_struct.current_job_id[current_depth] = 0 + + # Recover all faces. + structure.new_faces[d].n_faces = \ + parallel_struct.original_n_faces[current_depth -1] + structure.visited_all[d].n_faces = \ + parallel_struct.original_n_visited_all[current_depth -1] structure.first_time[d] = True structure.yet_to_visit = 0 # just to be on the safe side + parallel_struct.current_job_id[current_depth -1] = 0 + + # If the job does not exist, we will set the next value to ``-1``. + if parallel_struct.original_n_faces[current_depth -1] == 0: + parallel_struct.current_job_id[current_depth] = -1 + else: + parallel_struct.current_job_id[current_depth] = 0 + cdef size_t n_coatoms = structure.n_coatoms cdef size_t job_id_c cdef size_t i @@ -1367,17 +1444,30 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p for current_depth in range(1, parallelization_depth + 1): d = structure.dimension - current_depth - job_id_c = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) - if job_id_c != parallel_struct.current_job_id[current_depth -1]: + + # Get the corresponding digit of ``job_id``. + job_id_c = get_digit(job_id, current_depth - 1, parallelization_depth, n_coatoms) + + # Set ``current_job_id[current_depth - 1]`` to ``job_id_c`` if possible. + + if job_id_c != parallel_struct.current_job_id[current_depth - 1]: + # Set ``current_job_id[current_depth -1] = 0``. structure.current_dimension = d structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] structure.visited_all[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] parallel_struct.current_job_id[current_depth -1] = 0 - parallel_struct.current_job_id[current_depth] = 0 + + # If the job does not exist, we will set the next value to ``-1``. + if parallel_struct.original_n_faces[current_depth -1] == 0: + parallel_struct.current_job_id[current_depth] = -1 + else: + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True structure.yet_to_visit = 0 # just to be on the safe side - elif parallel_struct.current_job_id[current_depth] == -1: - # The job does not exist, we already settled this. + + if parallel_struct.current_job_id[current_depth] == -1: + # The job does not exist. return 0 if job_id_c > parallel_struct.current_job_id[current_depth -1]: @@ -1392,14 +1482,15 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p parallel_struct.current_job_id[current_depth -1] = job_id_c - # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. - if job_id == 0 or job_id_get(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: - parallel_struct.f_vector[structure.dimension - current_depth + 1] += 1 + # Apparently the face exists. We add it to the f-vector, if it is the very first job for the face. + if job_id == 0 or get_digit(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: + # Visit ``structure.new_faces[d].faces[structure.new_faces[d].n_faces - 1] + parallel_struct.f_vector[d + 1] += 1 if structure.current_dimension == d: structure.yet_to_visit = 0 - if structure.new_faces[d].n_faces <= 1: + if structure.new_faces[d].n_faces == 0: # The job will not exist. parallel_struct.current_job_id[current_depth] = -1 return 0 @@ -1427,18 +1518,15 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p return 1 -# Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. -# We deal with this by obtaining/setting digits with base ``n_coatoms``. - -cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: +cdef inline size_t get_digit(size_t job_id, size_t pos, size_t padto, size_t base) nogil: """ - Get the digit ``pos`` of ``job_id`` with base ``n_coatoms`` - padding the number of digits to ``parallelization_depth``. + Get the digit ``pos`` of ``job_id`` with base ``base`` + padding the number of digits to ``pad_to``. Digits are enumerated started with ``0``. """ # Remove the last ``parallelization_depth - pos - 1`` digits. - cdef size_t current_output = job_id // n_coatoms**(parallelization_depth - pos - 1) + cdef size_t current_output = job_id / base ** (padto - pos - 1) # Remove all digits before our current digit, which is digit ``pos``. - return current_output % n_coatoms + return current_output % base diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 5beeb9da6d6..19d5aa859ab 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -16,7 +16,7 @@ cdef extern from "Python.h": from .face_data_structure cimport * from libc.string cimport memset -from cysignals.signals cimport sig_on, sig_off +from cysignals.signals cimport sig_check cdef struct face_list_s: face_t* faces @@ -249,7 +249,9 @@ cdef inline size_t get_next_level_fused( face_list_intersection_fused(new_faces, faces, faces.faces[n_faces], algorithm) cdef size_t j + sig_check() for j in range(n_faces): + sig_check() if (is_not_maximal_fused(new_faces, j, algorithm) or # Step 2 is_contained_in_one_fused(new_faces.faces[j], visited_all, algorithm)): # Step 3 is_not_new_face[j] = True @@ -276,6 +278,7 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output + sig_check() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: From d051f283c08d117bd51d2167fb3d0ccf641dd620 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 15 Jan 2021 11:21:00 +0100 Subject: [PATCH 037/706] added a comment --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index b8183f2a232..58f7af086af 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1326,6 +1326,7 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall - ``f_vector`` -- where the ``f_vector`` is output """ + # One job per face of codimension ``parallelization_depth``. cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i cdef int j From 0dae7a061df9a2f05f9ef2d43602cc04fb351fb0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 17 Jan 2021 10:36:00 +0100 Subject: [PATCH 038/706] remove redundant sig_checks --- .../combinatorial_polyhedron/face_list_data_structure.pxd | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index 19d5aa859ab..5f7288560aa 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -249,7 +249,6 @@ cdef inline size_t get_next_level_fused( face_list_intersection_fused(new_faces, faces, faces.faces[n_faces], algorithm) cdef size_t j - sig_check() for j in range(n_faces): sig_check() if (is_not_maximal_fused(new_faces, j, algorithm) or # Step 2 @@ -278,7 +277,6 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output - sig_check() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: From 928ea608dc56698cd8df3d71b8266e75c28670eb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 17 Jan 2021 18:18:51 +0100 Subject: [PATCH 039/706] remove redundant allocation --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 156ab4682f5..9fdaca65c6d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1347,12 +1347,8 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall # Setting up for each thread some storage space. cdef parallel_f_t* parallel_structs = \ mem.allocarray(num_threads, sizeof(parallel_f_t)) - cdef parallel_f_s* parallel_structs_s = \ - mem.allocarray(num_threads, sizeof(parallel_f_s)) for i in range(num_threads): - parallel_structs[i][0] = parallel_structs_s[i] - # Partial f-vectors. parallel_structs[i].f_vector = \ mem.calloc(dim+2, sizeof(size_t)) From 772b9a3e7642d193625169b6c9edf78d6605d3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Jan 2021 15:23:45 +0100 Subject: [PATCH 040/706] fixing some of the flake8 E70* warning --- src/sage/coding/code_bounds.py | 3 ++- src/sage/coding/code_constructions.py | 12 ++++++++---- src/sage/coding/databases.py | 9 ++++++--- src/sage/coding/grs_code.py | 2 +- src/sage/coding/self_dual_codes.py | 4 +++- src/sage/combinat/ncsf_qsym/qsym.py | 8 ++++++-- src/sage/combinat/root_system/root_system.py | 3 ++- src/sage/combinat/root_system/type_super_A.py | 4 +++- src/sage/combinat/sf/elementary.py | 3 ++- src/sage/combinat/sf/hecke.py | 4 ++-- src/sage/combinat/sf/homogeneous.py | 4 ++-- src/sage/combinat/skew_partition.py | 2 +- .../endPN_automorphism_group.py | 4 ++-- .../dynamics/complex_dynamics/mandel_julia.py | 3 ++- src/sage/misc/temporary_file.py | 3 ++- src/sage/modules/fg_pid/fgp_element.py | 6 ++++-- src/sage/modules/fg_pid/fgp_morphism.py | 10 +++++++--- src/sage/modules/free_module.py | 19 ++++++++++++------- src/sage/numerical/optimize.py | 3 ++- src/sage/sets/integer_range.py | 9 ++++++--- src/sage/symbolic/relation.py | 4 +++- src/sage/symbolic/units.py | 6 ++++-- 22 files changed, 82 insertions(+), 43 deletions(-) diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index 6fb82fd4315..c6b37119336 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -472,7 +472,8 @@ def elias_upper_bound(n,q,d,algorithm=None): return QQ(ans) else: def ff(n,d,w,q): - return r*n*d*q**n/((w**2-2*r*n*w+r*n*d)*volume_hamming(n,q,w)); + return r*n*d*q**n/((w**2-2*r*n*w+r*n*d)*volume_hamming(n,q,w)) + def get_list(n,d,q): I = [] for i in range(1,int(r*n)+1): diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 44ecc8a33d7..e5b48049d11 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -596,12 +596,14 @@ def QuadraticResidueCodeEvenPair(n,F): n = Integer(n) if n <= 2 or not n.is_prime(): raise ValueError("the argument n must be an odd prime") - Q = quadratic_residues(n); Q.remove(0) # non-zero quad residues - N = [x for x in srange(1,n) if x not in Q] # non-zero quad non-residues + Q = quadratic_residues(n) + Q.remove(0) # non-zero quad residues + N = [x for x in srange(1, n) if x not in Q] # non-zero quad non-residues if q not in Q: raise ValueError("the order of the finite field must be a quadratic residue modulo n") return DuadicCodeEvenPair(F,Q,N) + def QuadraticResidueCodeOddPair(n,F): """ Quadratic residue codes of a given odd prime length and base ring @@ -654,12 +656,14 @@ def QuadraticResidueCodeOddPair(n,F): n = Integer(n) if n <= 2 or not n.is_prime(): raise ValueError("the argument n must be an odd prime") - Q = quadratic_residues(n); Q.remove(0) # non-zero quad residues - N = [x for x in srange(1,n) if x not in Q] # non-zero quad non-residues + Q = quadratic_residues(n) + Q.remove(0) # non-zero quad residues + N = [x for x in srange(1, n) if x not in Q] # non-zero quad non-residues if q not in Q: raise ValueError("the order of the finite field must be a quadratic residue modulo n") return DuadicCodeOddPair(F,Q,N) + def random_linear_code(F, length, dimension): r""" Generate a random linear code of length ``length``, dimension ``dimension`` diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index e0bce13871c..c2b1a2a9e23 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -297,17 +297,20 @@ def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, M = Matrix(FiniteField(2), [[1]*j]) if in_test(M): for N in self_orthogonal_binary_codes(n, k, d, M, BC, in_test=in_test): - if out_test(N): yield N + if out_test(N): + yield N else: C = LinearCode(parent) - if out_test(C): yield C + if out_test(C): + yield C if k == parent.nrows(): return for nn in range(parent.ncols()+1, n+1): if in_test(parent): for child in BC.generate_children(BinaryCode(parent), nn, d): for N in self_orthogonal_binary_codes(n, k, d, child, BC, in_test=in_test): - if out_test(N): yield N + if out_test(N): + yield N # Import the following function so that it is available as # sage.codes.databases.self_dual_binary_codes sage.codes.databases functions diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 9bc6248c4a9..1820b55a56c 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -215,7 +215,7 @@ def __init__(self, evaluation_points, dimension, column_multipliers=None): """ if column_multipliers: if len(evaluation_points) != len(column_multipliers): - raise ValueError("There must be the same number of evaluation points as column multipliers"); + raise ValueError("There must be the same number of evaluation points as column multipliers") try: common_points = vector(list(evaluation_points) + list(column_multipliers)) F = common_points.base_ring() diff --git a/src/sage/coding/self_dual_codes.py b/src/sage/coding/self_dual_codes.py index 2b12d5d90ed..132eb9b10cc 100644 --- a/src/sage/coding/self_dual_codes.py +++ b/src/sage/coding/self_dual_codes.py @@ -112,7 +112,9 @@ def _MS(n): sage: self_dual_codes._MS(8) Full MatrixSpace of 4 by 8 dense matrices over Finite Field of size 2 """ - n2 = ZZ(n)/2; return MatrixSpace(_F, n2, n) + n2 = ZZ(n)/2 + return MatrixSpace(_F, n2, n) + def _matA(n): r""" diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 44aae642397..fd2dcd506bf 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -3906,7 +3906,9 @@ def _from_Monomial_on_basis(self, I): """ R = self.base_ring() minus_one = -R.one() - def z(J): return R(J.to_partition().centralizer_size()) + + def z(J): + return R(J.to_partition().centralizer_size()) return self._from_dict({J: minus_one**(len(I)-len(J)) / z(J) * coeff_lp(I, J) for J in I.fatter()}) @@ -4046,7 +4048,9 @@ def _from_Monomial_on_basis(self, I): """ R = self.base_ring() minus_one = -R.one() - def z(J): return R(J.to_partition().centralizer_size()) + + def z(J): + return R(J.to_partition().centralizer_size()) return self._from_dict({J: minus_one**(len(I)-len(J)) * R.prod(J) / (coeff_ell(I, J) * z(J)) for J in I.fatter()}) diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index 83c209d6a09..628afb34833 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -329,7 +329,8 @@ def __init__(self, cartan_type, as_dual_of=None): self.dual_side = False # still fails for CartanType G2xA1 try: - self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self); + self.dual = RootSystem(self._cartan_type.dual(), + as_dual_of=self) except Exception: pass else: diff --git a/src/sage/combinat/root_system/type_super_A.py b/src/sage/combinat/root_system/type_super_A.py index 4f3522700f0..54b7a2eb147 100644 --- a/src/sage/combinat/root_system/type_super_A.py +++ b/src/sage/combinat/root_system/type_super_A.py @@ -585,7 +585,9 @@ def symmetrizer(self): Finite family {-2: 1, -1: 1, 0: 1, 1: -1, 2: -1, 3: -1} """ from sage.sets.family import Family - def ell(i): return ZZ.one() if i <= 0 else -ZZ.one() + + def ell(i): + return ZZ.one() if i <= 0 else -ZZ.one() return Family(self.index_set(), ell) def dynkin_diagram(self): diff --git a/src/sage/combinat/sf/elementary.py b/src/sage/combinat/sf/elementary.py index 02b4ab90c00..4be0083e782 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -91,7 +91,8 @@ def coproduct_on_generators(self, i): sage: e.coproduct_on_generators(0) e[] # e[] """ - def P(i): return Partition([i]) if i else Partition([]) + def P(i): + return Partition([i]) if i else Partition([]) T = self.tensor_square() return T.sum_of_monomials( (P(j), P(i-j)) for j in range(i+1) ) diff --git a/src/sage/combinat/sf/hecke.py b/src/sage/combinat/sf/hecke.py index 22bd90df969..fa911bbbd0b 100644 --- a/src/sage/combinat/sf/hecke.py +++ b/src/sage/combinat/sf/hecke.py @@ -289,10 +289,10 @@ def coproduct_on_generators(self, r): sage: qbar[2].coproduct() qbar[] # qbar[2] + (q-1)*qbar[1] # qbar[1] + qbar[2] # qbar[] """ - def P(i): return _Partitions([i]) if i else _Partitions([]) + def P(i): + return _Partitions([i]) if i else _Partitions([]) T = self.tensor_square() one = self.base_ring().one() q = self.q return T.sum_of_terms(((P(j), P(r-j)), one if j in [0,r] else q-one) for j in range(r+1)) - diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index 585a8fd7ffe..f6729bb9f6a 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -120,11 +120,11 @@ def coproduct_on_generators(self, i): sage: h.coproduct_on_generators(0) h[] # h[] """ - def P(i): return Partition([i]) if i else Partition([]) + def P(i): + return Partition([i]) if i else Partition([]) T = self.tensor_square() return T.sum_of_monomials( (P(j), P(i-j)) for j in range(i+1) ) - class Element(classical.SymmetricFunctionAlgebra_classical.Element): def omega(self): r""" diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 3087118648b..9be0a9b6237 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1516,7 +1516,7 @@ def from_row_and_column_length(self, rowL, colL): resIn.append(inP) resOut.append(len(colL_new)) for iCol in range(inP, len(colL_new)): - colL_new[iCol] -= 1; + colL_new[iCol] -= 1 if colL_new[iCol] < 0: raise ValueError("Incompatible row and column length : %s and %s"%(rowL, colL)) while colL_new and colL_new[-1] == 0: diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 4584510aeb4..d46c8601b25 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -712,7 +712,7 @@ def automorphism_group_QQ_CRT(rational_function, prime_lower_bound=4, return_fun MaxH = height_bound(h) congruence = 1 - primes = Primes(); + primes = Primes() p = primes.next(ZZ(prime_lower_bound)) primepowers = [] automorphisms = [] @@ -1563,7 +1563,7 @@ def automorphism_group_FF_alg3(rational_function): # Compute the set of distinct F-rational and F-quadratic # factors of the fixed point polynomial fix = R(f(z) - z*g(z)) - linear_fix = gcd(fix, z**q - z); + linear_fix = gcd(fix, z**q - z) quad_temp = fix.quo_rem(linear_fix)[0] residual = gcd(quad_temp, z**q - z) while residual.degree() > 0: diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia.py b/src/sage/dynamics/complex_dynamics/mandel_julia.py index 278f47656d8..19185da190e 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia.py +++ b/src/sage/dynamics/complex_dynamics/mandel_julia.py @@ -683,7 +683,8 @@ def julia_plot(f=None, **kwds): if f is not None and period is None: # f user-specified and no period given # try to coerce f to live in a polynomial ring - S = PolynomialRing(CC,names='z'); z = S.gen() + S = PolynomialRing(CC, names='z') + z = S.gen() try: f_poly = S(f) except TypeError: diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 25bf6d1372e..a6368f53a36 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -335,7 +335,8 @@ def __init__(self, target_filename, append=False, mode=0o666, self.tmpdir = os.path.dirname(self.target) self.append = append # Remove umask bits from mode - umask = os.umask(0); os.umask(umask) + umask = os.umask(0) + os.umask(umask) self.mode = mode & (~umask) # 'binary' mode is the default on Python 2, whereas 'text' mode is the diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 53857d31a07..aef837a8f81 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -76,7 +76,8 @@ def __init__(self, parent, x, check=DEBUG): For full documentation, see :class:`FGP_Element`. """ - if check: assert x in parent.V(), 'The argument x='+str(x)+' is not in the covering module!' + if check: + assert x in parent.V(), 'The argument x='+str(x)+' is not in the covering module!' ModuleElement.__init__(self, parent) self._x = x @@ -332,7 +333,8 @@ def vector(self): sage: x.vector().parent() Ambient free module of rank 2 over the principal ideal domain Integer Ring """ - try: return self.__vector + try: + return self.__vector except AttributeError: self.__vector = self.parent().coordinate_vector(self, reduce=True) self.__vector.set_immutable() diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index fbbd479c6e9..4edc9b9f55c 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -457,6 +457,8 @@ def lift(self, x): import sage.misc.weak_dict _fgp_homset = sage.misc.weak_dict.WeakValueDictionary() + + def FGP_Homset(X, Y): """ EXAMPLES:: @@ -469,9 +471,11 @@ def FGP_Homset(X, Y): sage: type(Q.Hom(Q)) """ - key = (X,Y) - try: return _fgp_homset[key] - except KeyError: pass + key = (X, Y) + try: + return _fgp_homset[key] + except KeyError: + pass H = FGP_Homset_class(X, Y) # Caching breaks tests in fgp_module. # _fgp_homset[key] = H diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 209a1195526..9ebefa47594 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1570,7 +1570,8 @@ def __iter__(self): return R = self.base_ring() iters = [iter(R) for _ in range(len(G))] - for x in iters: next(x) # put at 0 + for x in iters: + next(x) # put at 0 zero = R(0) v = [zero for _ in range(len(G))] n = 0 @@ -3001,9 +3002,11 @@ def intersection(self, other): # standard algorithm for computing intersection of general submodule if self.dimension() <= other.dimension(): - V1 = self; V2 = other + V1 = self + V2 = other else: - V1 = other; V2 = self + V1 = other + V2 = self A1 = V1.basis_matrix() A2 = V2.basis_matrix() S = A1.stack(A2) @@ -3846,13 +3849,15 @@ def intersection(self, other): # standard algorithm for computing intersection of general subspaces if self.dimension() <= other.dimension(): - V1 = self; V2 = other + V1 = self + V2 = other else: - V1 = other; V2 = self + V1 = other + V2 = self A1 = V1.basis_matrix() A2 = V2.basis_matrix() - S = A1.stack(A2) - K = S.kernel() + S = A1.stack(A2) + K = S.kernel() n = int(V1.dimension()) B = [A1.linear_combination_of_rows(v.list()[:n]) for v in K.basis()] return self.ambient_vector_space().submodule(B, check=False) diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index 6c6b1667b3d..f09634c3bf2 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -105,7 +105,8 @@ def find_root(f, a, b, xtol=10e-13, rtol=2.0**-50, maxiter=100, full_output=Fals return f.find_root(a=a,b=b,xtol=xtol,rtol=rtol,maxiter=maxiter,full_output=full_output) except AttributeError: pass - a = float(a); b = float(b) + a = float(a) + b = float(b) if a > b: a, b = b, a left = f(a) diff --git a/src/sage/sets/integer_range.py b/src/sage/sets/integer_range.py index 95e0c5d79fc..62138a9ecfb 100644 --- a/src/sage/sets/integer_range.py +++ b/src/sage/sets/integer_range.py @@ -222,9 +222,12 @@ def __classcall_private__(cls, begin, end=None, step=Integer(1), middle_point=No ... TypeError: end must be Integer or Infinity, not <... 'sage.rings.real_mpfr.RealLiteral'> """ - if isinstance(begin, int): begin = Integer(begin) - if isinstance(end, int): end = Integer(end) - if isinstance(step,int): step = Integer(step) + if isinstance(begin, int): + begin = Integer(begin) + if isinstance(end, int): + end = Integer(end) + if isinstance(step, int): + step = Integer(step) if end is None: end = begin diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index 0be478daff0..e4dd9223ee2 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -1611,10 +1611,12 @@ def _solve_mod_prime_power(eqns, p, m, vars): pairs = cartesian_product_iterator([shifts, ans]) possibles = (tuple(vector(t)+vector(shift)*(mrunning//p)) for shift, t in pairs) ans = list(t for t in possibles if all(e(*t) == 0 for e in eqns_mod)) - if not ans: return ans + if not ans: + return ans return ans + def solve_ineq_univar(ineq): """ Function solves rational inequality in one variable. diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index eff5394e98d..86db0b94ce4 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -509,11 +509,13 @@ def evalunitdict(): # Format the table for easier use. # for k, v in unitdict.items(): - for a in v: unit_to_type[a] = k + for a in v: + unit_to_type[a] = k for w in unitdict: for j in unitdict[w]: - if isinstance(unitdict[w][j], tuple): unitdict[w][j] = unitdict[w][j][0] + if isinstance(unitdict[w][j], tuple): + unitdict[w][j] = unitdict[w][j][0] value_to_unit[w] = {b: a for a, b in unitdict[w].items()} From c763df6d3f17b55def283e7b4909d2e20dee6b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 24 Jan 2021 15:47:48 +0100 Subject: [PATCH 041/706] more fixes for flake8 E70* --- .../free_bosons_lie_conformal_algebra.py | 4 ++-- .../categories/highest_weight_crystals.py | 2 +- .../geometry/polyhedron/representation.py | 6 +++--- .../triangulation/point_configuration.py | 15 +++++++------ src/sage/graphs/generators/smallgraphs.py | 4 ++-- src/sage/interacts/library.py | 6 ++++-- src/sage/interfaces/axiom.py | 5 ++--- src/sage/interfaces/frobby.py | 8 +++---- src/sage/interfaces/gap.py | 6 +++--- src/sage/interfaces/maxima_lib.py | 7 +++---- src/sage/interfaces/r.py | 2 +- .../modular/overconvergent/hecke_series.py | 6 +++--- src/sage/plot/hyperbolic_arc.py | 7 ++++--- .../quadratic_form__local_normal_form.py | 4 ++-- .../quadratic_form__siegel_product.py | 4 ++-- .../rings/function_field/function_field.py | 4 ++-- src/sage/rings/number_field/S_unit_solver.py | 4 ++-- .../rings/number_field/number_field_ideal.py | 5 ++--- .../rings/polynomial/multi_polynomial_ring.py | 4 ++-- src/sage/sandpiles/sandpile.py | 2 +- .../hyperelliptic_finite_field.py | 21 ++++++++----------- .../hyperelliptic_curves/jacobian_morphism.py | 5 +++-- src/sage/schemes/toric/variety.py | 2 +- 23 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py index 45849316534..2e37b63ef9a 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py @@ -121,8 +121,8 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, "{0} x {0} matrix, got {1}".format(ngens,gram_matrix)) else: if ngens is None: - ngens = 1; - gram_matrix = identity_matrix(R,ngens,ngens) + ngens = 1 + gram_matrix = identity_matrix(R, ngens, ngens) latex_names = None if (names is None) and (index_set is None): diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index 214b9d22733..6ce57f2217e 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -148,7 +148,7 @@ def highest_weight_vector(self): sage: C.highest_weight_vector() 1 """ - hw = self.highest_weight_vectors(); + hw = self.highest_weight_vectors() if len(hw) == 1: return hw[0] else: diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 95805d61d88..586cb5ede02 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -1382,7 +1382,7 @@ def _repr_(self): sage: v.__repr__() 'A vertex at (1, 0)' """ - return 'A vertex at ' + repr(self.vector()); + return 'A vertex at ' + repr(self.vector()) def homogeneous_vector(self, base_ring=None): """ @@ -1499,7 +1499,7 @@ def _repr_(self): sage: a._repr_() 'A ray in the direction (0, 1)' """ - return 'A ray in the direction ' + repr(self.vector()); + return 'A ray in the direction ' + repr(self.vector()) def homogeneous_vector(self, base_ring=None): """ @@ -1597,7 +1597,7 @@ def _repr_(self): sage: a.__repr__() 'A line in the direction (0, 1, 0)' """ - return 'A line in the direction ' + repr(self.vector()); + return 'A line in the direction ' + repr(self.vector()) def homogeneous_vector(self, base_ring=None): """ diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 593c3b6b560..d710fa1015a 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -646,8 +646,8 @@ def _TOPCOM_exec(cls, executable, input_string, verbose=True): if verbose: print('# Still running ' + str(executable)) continue - if len(line)==0: # EOF - break; + if len(line) == 0: # EOF + break if verbose: print("# " + line) sys.stdout.flush() @@ -1110,8 +1110,8 @@ def convex_hull(self): pass from sage.geometry.polyhedron.constructor import Polyhedron - pts = [ p.reduced_affine() for p in self.points() ]; - self._polyhedron = Polyhedron(vertices=pts); + pts = [p.reduced_affine() for p in self.points()] + self._polyhedron = Polyhedron(vertices=pts) return self._polyhedron @cached_method @@ -1206,14 +1206,13 @@ def face_codimension(self, point): try: p = vector(self.point(point).reduced_affine()) except TypeError: - p = vector(point); + p = vector(point) inequalities = [] for ieq in self.convex_hull().inequality_generator(): if (ieq.A()*p + ieq.b() == 0): - inequalities += [ ieq.vector() ]; - return matrix(inequalities).rank(); - + inequalities += [ ieq.vector() ] + return matrix(inequalities).rank() def face_interior(self, dim=None, codim=None): """ diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 888798ae9c1..92fb0c050b1 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -2816,8 +2816,8 @@ def GrotzschGraph(): g = Graph() g.add_vertices(range(11)) - edges = []; - for u in range(1,6): + edges = [] + for u in range(1, 6): edges.append( (0,u) ) edges.append( (10,6) ) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index d174cc7d9ef..829334a787b 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -192,7 +192,9 @@ def definite_integral( x = SR.var('x') f = symbolic_expression(f).function(x) g = symbolic_expression(g).function(x) - f_plot = Graphics(); g_plot = Graphics(); h_plot = Graphics(); + f_plot = Graphics() + g_plot = Graphics() + h_plot = Graphics() text = "" # Plot function f. @@ -1718,7 +1720,7 @@ def polar_prime_spiral( (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness = .3) #Pink Line b = 1 - c = c2; + c = c2 g = symbolic_expression(a*m**2+b*m+c).function(m) r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index 361b13c5a56..22ed05c8606 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -470,9 +470,8 @@ def _eval_line(self, line, reformat=True, allow_use_file=False, if line[i:] == "": i = 0 outs = outs[1:] - break; - out = "\n".join(line[i:] for line in outs[1:]) - return out + break + return "\n".join(line[i:] for line in outs[1:]) # define relational operators def _equality_symbol(self): diff --git a/src/sage/interfaces/frobby.py b/src/sage/interfaces/frobby.py index 69a5a3c2870..ed852f74efe 100644 --- a/src/sage/interfaces/frobby.py +++ b/src/sage/interfaces/frobby.py @@ -396,7 +396,7 @@ def _parse_4ti2_matrix(self, string): for i in range(term_count): exponents.append(ints[:var_count]) ints = ints[var_count:] - return exponents; + return exponents def _ideal_to_string(self, monomial_ideal): r""" @@ -424,10 +424,10 @@ def _ideal_to_string(self, monomial_ideal): if monomial_ideal.is_zero(): gens = [] else: - gens = monomial_ideal.gens(); - var_count = monomial_ideal.ring().ngens(); + gens = monomial_ideal.gens() + var_count = monomial_ideal.ring().ngens() first_row = str(len(gens)) + ' ' + str(var_count) + '\n' - rows = [self._monomial_to_string(_) for _ in gens]; + rows = [self._monomial_to_string(_) for _ in gens] return first_row + "".join(rows) def _monomial_to_string(self, monomial): diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 5855b7cf650..9937c8437db 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -630,15 +630,15 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False): elif x == 5: # @c completion, doesn't seem to happen when -p is in use warnings.warn("I didn't think GAP could do this") elif x == 6: # @f GAP error message - current_outputs = error_outputs; + current_outputs = error_outputs elif x == 7: # @h help text, but this stopped happening with new help warnings.warn("I didn't think GAP could do this") elif x == 8: # @i awaiting normal input - break; + break elif x == 9: # @m finished running a child pass # there is no need to do anything elif x==10: #@n normal output line - current_outputs = normal_outputs; + current_outputs = normal_outputs elif x==11: #@r echoing input current_outputs = terminal_echo elif x==12: #@sN shouldn't happen diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 40367c52426..dc77d4792d0 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -872,7 +872,7 @@ def sr_sum(self,*args): """ try: - return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_sum],([max_sum],[sr_to_max(SR(a)) for a in args])]])); + return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_sum],([max_sum],[sr_to_max(SR(a)) for a in args])]])) except RuntimeError as error: s = str(error) if "divergent" in s: @@ -900,17 +900,16 @@ def sr_prod(self,*args): """ try: - return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_prod],([max_prod],[sr_to_max(SR(a)) for a in args])]])); + return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_prod],([max_prod],[sr_to_max(SR(a)) for a in args])]])) except RuntimeError as error: s = str(error) if "divergent" in s: raise ValueError("Product is divergent.") - elif "Is" in s: # Maxima asked for a condition + elif "Is" in s: # Maxima asked for a condition self._missing_assumption(s) else: raise - def sr_limit(self, expr, v, a, dir=None): """ Helper function to wrap calculus use of Maxima's limits. diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 85184ad2ce6..24a2898b4c4 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -444,7 +444,7 @@ def _list_vector(vec): '_Names': rpy2py(names), # We don't give the rclass here because the old expect interface # didn't do that either and we want to maintain compatibility. - }; + } rpy2py.register(ListSexpVector, _list_vector) return cv diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 3320b6464f6..fe6147b0a07 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -446,7 +446,7 @@ def complementary_spaces_modp(N,p,k0,n,elldash,LWBModp,bound): """ CompSpacesCode = [] ell = dimension_modular_forms(N,k0 + n*(p-1)) - TotalBasisModp = matrix(GF(p),ell,elldash); # zero matrix + TotalBasisModp = matrix(GF(p), ell, elldash) # zero matrix for i in range(n+1): NewBasisCodemi = random_new_basis_modp(N,p,k0 + i*(p-1),LWBModp,TotalBasisModp,elldash,bound) @@ -751,8 +751,8 @@ def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): T = matrix(S,ell,elldash) for i in range(ell): ei = R(e[i].list()) - Gkdivei = Gkdiv*ei; # act by G^kdiv - for j in range(0, elldash): + Gkdivei = Gkdiv*ei # act by G^kdiv + for j in range(elldash): T[i,j] = Gkdivei[p*j] verbose("done steps 4b and 5", t) diff --git a/src/sage/plot/hyperbolic_arc.py b/src/sage/plot/hyperbolic_arc.py index 57661907545..1acc1e1fdba 100644 --- a/src/sage/plot/hyperbolic_arc.py +++ b/src/sage/plot/hyperbolic_arc.py @@ -54,7 +54,7 @@ def __init__(self, A, B, options): if B.imag()<0: raise ValueError("%s is not a valid point in the UHP model"%(B)) self.path = [] - self._hyperbolic_arc(A, B, True); + self._hyperbolic_arc(A, B, True) BezierPath.__init__(self, self.path, options) self.A, self.B = (A, B) @@ -100,12 +100,13 @@ def _hyperbolic_arc(self, z0, z3, first=False): self.path.append([(z0.real(), z0.imag()), (z1.real(), z1.imag()), (z2.real(), z2.imag()), - (z3.real(), z3.imag())]); + (z3.real(), z3.imag())]) first = False else: self.path.append([(z1.real(), z1.imag()), (z2.real(), z2.imag()), - (z3.real(), z3.imag())]); + (z3.real(), z3.imag())]) + @rename_keyword(color='rgbcolor') @options(alpha=1, fill=False, thickness=1, rgbcolor="blue", zorder=2, linestyle='solid') diff --git a/src/sage/quadratic_forms/quadratic_form__local_normal_form.py b/src/sage/quadratic_forms/quadratic_form__local_normal_form.py index ac11ea07e5b..9a69e16e7d8 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_normal_form.py +++ b/src/sage/quadratic_forms/quadratic_form__local_normal_form.py @@ -163,8 +163,8 @@ def local_normal_form(self, p): ## 1x1 => make upper left the smallest if (p != 2): - block_size = 1; - Q.add_symmetric(1, 0, 1, in_place = True) + block_size = 1 + Q.add_symmetric(1, 0, 1, in_place=True) ## 2x2 => replace it with the appropriate 2x2 matrix else: block_size = 2 diff --git a/src/sage/quadratic_forms/quadratic_form__siegel_product.py b/src/sage/quadratic_forms/quadratic_form__siegel_product.py index d380daaeb5b..fb2e72a0f4a 100644 --- a/src/sage/quadratic_forms/quadratic_form__siegel_product.py +++ b/src/sage/quadratic_forms/quadratic_form__siegel_product.py @@ -83,10 +83,10 @@ def siegel_product(self, u): n = self.dim() d = self.det() ## ??? Warning: This is a factor of 2^n larger than it should be! - ## DIAGNOSTIC + # DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) - verbose("In siegel_product: d = " + str(d) + "\n"); + verbose("In siegel_product: d = " + str(d) + "\n") ## Product of "bad" places to omit S = 2 * d * u diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index cf78a076a6c..4d1847d324b 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1870,7 +1870,7 @@ def free_module(self, base=None, basis=None, map=True): if base is None: base = self.base_field() degree = self.degree(base) - V = base**degree; + V = base**degree if not map: return V from_V = MapVectorSpaceToFunctionField(V, self) @@ -2074,7 +2074,7 @@ def hom(self, im_gens, base_morphism=None): base_morphism = self.base_field().hom(im_gens[1:], base_morphism) # the codomain of this morphism is the field containing all the im_gens - codomain = im_gens[0].parent(); + codomain = im_gens[0].parent() if base_morphism is not None: from sage.categories.pushout import pushout codomain = pushout(codomain, base_morphism.codomain()) diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 2a5dd11c950..81174a517fc 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -1422,11 +1422,11 @@ def defining_polynomial_for_Kp(prime, prec=106): # We are going to find which factor of f is related to the prime ideal 'prime' L = [g.change_ring(ZZ) for g, _ in factors] - A = [g for g in L if (g(theta)).valuation(prime) >= e*N/2]; + A = [g for g in L if (g(theta)).valuation(prime) >= e*N/2] # We narrow down the list unitl only one value remains - if len(A) == 1: + if len(A) == 1: return A[0].change_ring(Integers(p**prec)).change_ring(ZZ) else: N += 1 diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 2e6a368843e..aa8d96d265f 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -2510,16 +2510,15 @@ def _pari_bid_(self, flag=1): from sage.libs.pari.all import PariError try: bid = self._bid - if flag==2: + if flag == 2: # Try to access generators, we get PariError if this fails. - bid.bid_get_gen(); + bid.bid_get_gen() except (AttributeError, PariError): k = self.number_field() bid = k.pari_nf().idealstar(self.pari_hnf(), flag) self._bid = bid return bid - def idealstar(self, flag=1): r""" Returns the finite abelian group `(O_K/I)^*`, where I is the ideal self diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 91b350b61da..d26956b5724 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -115,12 +115,12 @@ def __init__(self, base_ring, n, names, order): self._base = base_ring # Construct the generators v = [0] * n - one = base_ring(1); + one = base_ring(1) self._gens = [] C = self._poly_class() for i in range(n): v[i] = 1 # int's! - self._gens.append(C(self, {tuple(v):one})) + self._gens.append(C(self, {tuple(v): one})) v[i] = 0 self._gens = tuple(self._gens) self._zero_tuple = tuple(v) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ad1cc562762..3650d56f0c6 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -2790,7 +2790,7 @@ def _set_points(self): True """ L = self._reduced_laplacian.transpose().dense_matrix() - n = self.num_verts()-1; + n = self.num_verts() - 1 D, U, V = L.smith_form() self._points = [] one = [1]*n diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 5313d2f74dd..445b22726f4 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -1501,14 +1501,13 @@ def _Cartier_matrix_cached(self): ... ValueError: curve is not smooth """ - #Compute the finite field and prime p. - Fq=self.base_ring(); - p=Fq.characteristic() + # Compute the finite field and prime p. + Fq = self.base_ring() + p = Fq.characteristic() #checks if p == 2: - raise ValueError("p must be odd"); - + raise ValueError("p must be odd") g = self.genus() @@ -1850,8 +1849,7 @@ def a_number(self): if E != self: self._Cartier_matrix_cached.clear_cache() M,Coeffs,g, Fq, p,E= self._Cartier_matrix_cached() - a=g-rank(M); - return a; + return g - rank(M) def p_rank(self): r""" @@ -1888,9 +1886,8 @@ def p_rank(self): #the last entry in A. If it does not match, clear A and compute Hasse Witt. # However, it seems a waste of time to manually analyse the cache # -- See Trac Ticket #11115 - N,E= self._Hasse_Witt_cached() - if E!=self: + N, E = self._Hasse_Witt_cached() + if E != self: self._Hasse_Witt_cached.clear_cache() - N,E= self._Hasse_Witt_cached() - pr=rank(N); - return pr + N, E = self._Hasse_Witt_cached() + return rank(N) diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py index f51c22ff0fb..cc4fe5f198d 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py @@ -147,16 +147,17 @@ def cantor_reduction_simple(a, b, f, genus): """ a2 = (f - b**2) // a a2 = a2.monic() - b2 = -b % (a2); + b2 = -b % (a2) if a2.degree() == a.degree(): # XXX - assert a2.degree() == genus+1 + assert a2.degree() == genus + 1 print("Returning ambiguous form of degree genus+1.") return (a2, b2) elif a2.degree() > genus: return cantor_reduction_simple(a2, b2, f, genus) return (a2, b2) + def cantor_reduction(a, b, f, h, genus): r""" Return the unique reduced divisor linearly equivalent to diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 1bcfd27f828..0bb34fa5848 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -2513,7 +2513,7 @@ def toric_divisor_group(self, base_ring=ZZ): Multivariate Polynomial Ring in x, u, y, v, z, w over Rational Field """ from sage.schemes.toric.divisor import ToricDivisorGroup - return ToricDivisorGroup(self, base_ring); + return ToricDivisorGroup(self, base_ring) def _semigroup_ring(self, cone=None, names=None): r""" From baf80deaca74b037c11e5eee914009e216f3beed Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 31 Jan 2021 16:27:58 +0100 Subject: [PATCH 042/706] try disabling CYSIGNALS_C_ATOMIC --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9fdaca65c6d..52123d92dbf 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,5 +1,6 @@ # distutils: extra_compile_args = -fopenmp # distutils: extra_link_args = -fopenmp +# distutils: undef_macros = CYSIGNALS_C_ATOMIC r""" Face iterator for polyhedra From 604dfe41ef8c5065eb90f1e3beb83ae9bbc15617 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 1 Mar 2021 12:15:19 +0100 Subject: [PATCH 043/706] Update pari to 2.13.1 --- build/pkgs/pari/checksums.ini | 6 +++--- build/pkgs/pari/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index 177cc0ea4e0..ec00516a1d6 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,5 +1,5 @@ tarball=pari-VERSION.tar.gz -sha1=357ba353a3ae66d9d9198a4a8976d9acb193172e -md5=401298e079ce11bc63ae25e2ac5784a0 -cksum=1349754076 +sha1=40731c850cc50fb4994148fac49fda4f68ce6106 +md5=826064cf75af268be8a482ade6e27501 +cksum=550849474 upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index fb2c0766b7c..94f15e9cc30 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.13.0 +2.13.1 From 574f98d5e069db4875b1b12851ed6b3b64b41813 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 1 Mar 2021 18:11:35 +0100 Subject: [PATCH 044/706] Fix merge --- src/sage/rings/number_field/unit_group.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index a2eb35a711c..3c481999bea 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -345,12 +345,9 @@ def __init__(self, number_field, proof=True, S=None): gens = [K(u, check=False) for u in su_fu_tu] gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu] - # Store the actual generators (torsion first): - gens = [z] + fu + su - values = Sequence(gens, immutable=True, universe=self, check=False) # Construct the abstract group: - gens_orders = tuple([ZZ(n)]+[ZZ(0)]*(self.__rank)) - AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values, number_field) + gens_orders = tuple([ZZ(self.__ntu)]+[ZZ(0)]*(self.__rank)) + AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', gens, number_field) def _element_constructor_(self, u): """ From 3b148fa480060296c09ffcb35fcba08a0ba07cbc Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 1 Mar 2021 18:45:06 +0100 Subject: [PATCH 045/706] Update doctests --- .../rings/finite_rings/finite_field_constructor.py | 8 ++++---- src/sage/rings/finite_rings/integer_mod_ring.py | 2 +- src/sage/rings/finite_rings/residue_field.pyx | 14 +++++++------- src/sage/rings/integer.pyx | 2 +- src/sage/rings/number_field/number_field_rel.py | 12 ++++++------ 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/finite_rings/finite_field_constructor.py b/src/sage/rings/finite_rings/finite_field_constructor.py index 81ff16b1b66..36b89005e7d 100644 --- a/src/sage/rings/finite_rings/finite_field_constructor.py +++ b/src/sage/rings/finite_rings/finite_field_constructor.py @@ -284,14 +284,14 @@ class FiniteFieldFactory(UniqueFactory): (a generator of the multiplicative group), use ``modulus="primitive"`` if you need this:: - sage: K. = GF(5^40) + sage: K. = GF(5^45) sage: a.multiplicative_order() - 189478062869360049565633138 + 7105427357601001858711242675781 sage: a.is_square() True - sage: K. = GF(5^40, modulus="primitive") + sage: K. = GF(5^45, modulus="primitive") sage: b.multiplicative_order() - 9094947017729282379150390624 + 28421709430404007434844970703124 The modulus must be irreducible:: diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 0dcef0d21ae..7b4f8b1b4b5 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -626,7 +626,7 @@ def multiplicative_subgroups(self): sage: Integers(5).multiplicative_subgroups() ((2,), (4,), ()) sage: Integers(15).multiplicative_subgroups() - ((11, 7), (4, 11), (8,), (11,), (14,), (7,), (4,), ()) + ((14, 13), (4, 11), (8,), (11,), (14,), (7,), (4,), ()) sage: Integers(2).multiplicative_subgroups() ((),) sage: len(Integers(341).multiplicative_subgroups()) diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index cd4c2212c3b..007a68e4c0f 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -1055,7 +1055,7 @@ cdef class ReductionMap(Map): sage: f = k.convert_map_from(K) sage: s = f.section(); s Lifting map: - From: Residue field in abar of Fractional ideal (14*a^4 - 24*a^3 - 26*a^2 + 58*a - 15) + From: Residue field in abar of Fractional ideal (-14*a^4 + 24*a^3 + 26*a^2 - 58*a + 15) To: Number Field in a with defining polynomial x^5 - 5*x + 2 sage: s(k.gen()) a @@ -1268,7 +1268,7 @@ cdef class ResidueFieldHomomorphism_global(RingHomomorphism): sage: f = k.coerce_map_from(K.ring_of_integers()) sage: s = f.section(); s Lifting map: - From: Residue field in abar of Fractional ideal (14*a^4 - 24*a^3 - 26*a^2 + 58*a - 15) + From: Residue field in abar of Fractional ideal (-14*a^4 + 24*a^3 + 26*a^2 - 58*a + 15) To: Maximal Order in Number Field in a with defining polynomial x^5 - 5*x + 2 sage: s(k.gen()) a @@ -1371,10 +1371,10 @@ cdef class LiftingMap(Section): sage: F = K.factor(7)[0][0].residue_field() sage: L = F.lift_map(); L Lifting map: - From: Residue field in abar of Fractional ideal (-2*a^4 + a^3 - 4*a^2 + 2*a - 1) + From: Residue field in abar of Fractional ideal (2*a^4 - a^3 + 4*a^2 - 2*a + 1) To: Maximal Order in Number Field in a with defining polynomial x^5 + 2 sage: L.domain() - Residue field in abar of Fractional ideal (-2*a^4 + a^3 - 4*a^2 + 2*a - 1) + Residue field in abar of Fractional ideal (2*a^4 - a^3 + 4*a^2 - 2*a + 1) sage: K. = CyclotomicField(7) sage: F = K.factor(5)[0][0].residue_field() @@ -1498,7 +1498,7 @@ cdef class LiftingMap(Section): sage: F. = K.factor(7)[0][0].residue_field() sage: F.lift_map() #indirect doctest Lifting map: - From: Residue field in tmod of Fractional ideal (-3*theta_12^2 + 1) + From: Residue field in tmod of Fractional ideal (theta_12^2 + 2) To: Maximal Order in Cyclotomic Field of order 12 and degree 4 """ return "Lifting" @@ -1515,7 +1515,7 @@ class ResidueFiniteField_prime_modn(ResidueField_generic, FiniteField_prime_modn sage: P = K.ideal(29).factor()[1][0] sage: k = ResidueField(P) sage: k - Residue field of Fractional ideal (a^2 + 2*a + 2) + Residue field of Fractional ideal (-a^2 - 2*a - 2) sage: k.order() 29 sage: OK = K.maximal_order() @@ -1597,7 +1597,7 @@ class ResidueFiniteField_prime_modn(ResidueField_generic, FiniteField_prime_modn sage: P = K.ideal(29).factor()[1][0] sage: k = ResidueField(P) sage: k - Residue field of Fractional ideal (a^2 + 2*a + 2) + Residue field of Fractional ideal (-a^2 - 2*a - 2) sage: OK = K.maximal_order() sage: c = OK(a) sage: b = k(a); b diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 77a3a18913f..421a90e10e7 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -5474,7 +5474,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: 3._bnfisnorm(QuadraticField(-1, 'i')) (1, 3) sage: 7._bnfisnorm(CyclotomicField(7)) - (-zeta7^5 - zeta7^4 - 2*zeta7^3 - zeta7^2 - zeta7 - 1, 1) + (zeta7^5 - zeta7^2, 1) """ from sage.rings.rational_field import QQ return QQ(self)._bnfisnorm(K, proof=proof, extra_primes=extra_primes) diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 312cbdf874a..436af50e875 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -396,18 +396,18 @@ def subfields(self, degree=0, name=None): sage: K. = F.extension(Y^2 - (1 + a)*(a + b)*a*b) sage: K.subfields(2) [ - (Number Field in c0 with defining polynomial x^2 - 24*x + 72, Ring morphism: - From: Number Field in c0 with defining polynomial x^2 - 24*x + 72 + (Number Field in c0 with defining polynomial x^2 - 24*x + 96, Ring morphism: + From: Number Field in c0 with defining polynomial x^2 - 24*x + 96 To: Number Field in c with defining polynomial Y^2 + (-2*b - 3)*a - 2*b - 6 over its base field - Defn: c0 |--> -6*a + 12, None), + Defn: c0 |--> -4*b + 12, None), (Number Field in c1 with defining polynomial x^2 - 24*x + 120, Ring morphism: From: Number Field in c1 with defining polynomial x^2 - 24*x + 120 To: Number Field in c with defining polynomial Y^2 + (-2*b - 3)*a - 2*b - 6 over its base field Defn: c1 |--> 2*b*a + 12, None), - (Number Field in c2 with defining polynomial x^2 - 24*x + 96, Ring morphism: - From: Number Field in c2 with defining polynomial x^2 - 24*x + 96 + (Number Field in c2 with defining polynomial x^2 - 24*x + 72, Ring morphism: + From: Number Field in c2 with defining polynomial x^2 - 24*x + 72 To: Number Field in c with defining polynomial Y^2 + (-2*b - 3)*a - 2*b - 6 over its base field - Defn: c2 |--> -4*b + 12, None) + Defn: c2 |--> -6*a + 12, None) ] sage: K.subfields(8, 'w') [ From b6c0c9a2c64ade403c829383e249694d6a5aa99e Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 1 Mar 2021 20:12:40 +0100 Subject: [PATCH 046/706] Fix doctest failures caused by different choices of generators --- src/sage/rings/number_field/S_unit_solver.py | 42 +++++++-------- src/sage/rings/number_field/class_group.py | 4 +- src/sage/rings/number_field/number_field.py | 2 +- .../number_field/number_field_element.pyx | 6 +-- .../rings/number_field/number_field_ideal.py | 6 +-- .../number_field/number_field_ideal_rel.py | 2 +- src/sage/rings/number_field/order.py | 2 +- src/sage/rings/number_field/unit_group.py | 52 +++++++++---------- .../polynomial/polynomial_quotient_ring.py | 2 +- 9 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 2a5dd11c950..14458b8062b 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -24,10 +24,10 @@ sage: from sage.rings.number_field.S_unit_solver import solve_S_unit_equation, eq_up_to_order sage: K. = NumberField(x^2+x+1) sage: S = K.primes_above(3) - sage: expected = [((2, 1), (4, 0), xi + 2, -xi - 1), - ....: ((5, -1), (4, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), - ....: ((5, 0), (1, 0), -xi, xi + 1), - ....: ((1, 1), (2, 0), -xi + 1, xi)] + sage: expected = [((0, 1), (4, 0), xi + 2, -xi - 1), + ....: ((1, -1), (0, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), + ....: ((1, 0), (5, 0), xi + 1, -xi), + ....: ((2, 0), (5, 1), xi, -xi + 1)] sage: sols = solve_S_unit_equation(K, S, 200) sage: eq_up_to_order(sols, expected) True @@ -1780,20 +1780,20 @@ def sieve_ordering(SUK, q): sage: SUK = K.S_unit_group(S=3) sage: sieve_data = list(sieve_ordering(SUK, 19)) sage: sieve_data[0] - (Fractional ideal (-2*xi^2 + 3), - Fractional ideal (xi - 3), - Fractional ideal (2*xi + 1)) + (Fractional ideal (xi - 3), + Fractional ideal (-2*xi^2 + 3), + Fractional ideal (2*xi + 1)) sage: sieve_data[1] - (Residue field of Fractional ideal (-2*xi^2 + 3), - Residue field of Fractional ideal (xi - 3), - Residue field of Fractional ideal (2*xi + 1)) + (Residue field of Fractional ideal (xi - 3), + Residue field of Fractional ideal (-2*xi^2 + 3), + Residue field of Fractional ideal (2*xi + 1)) sage: sieve_data[2] - ([18, 9, 16, 8], [18, 7, 10, 4], [18, 3, 12, 10]) + ([18, 7, 16, 4], [18, 9, 12, 8], [18, 3, 10, 10]) sage: sieve_data[3] - (972, 972, 3888) + (486, 648, 11664) """ K = SUK.number_field() @@ -2654,10 +2654,10 @@ def sieve_below_bound(K, S, bound=10, bump=10, split_primes_list=[], verbose=Fal sage: S = SUK.primes() sage: sols = sieve_below_bound(K, S, 10) sage: expected = [ - ....: ((5, -1), (4, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), - ....: ((2, 1), (4, 0), xi + 2, -xi - 1), - ....: ((2, 0), (1, 1), xi, -xi + 1), - ....: ((5, 0), (1, 0), -xi, xi + 1)] + ....: ((1, -1), (0, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), + ....: ((0, 1), (4, 0), xi + 2, -xi - 1), + ....: ((2, 0), (5, 1), xi, -xi + 1), + ....: ((1, 0), (5, 0), xi + 1, -xi)] sage: eq_up_to_order(sols, expected) True """ @@ -2715,10 +2715,10 @@ def solve_S_unit_equation(K, S, prec=106, include_exponents=True, include_bound= sage: S = K.primes_above(3) sage: sols = solve_S_unit_equation(K, S, 200) sage: expected = [ - ....: ((2, 1), (4, 0), xi + 2, -xi - 1), - ....: ((5, -1), (4, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), - ....: ((5, 0), (1, 0), -xi, xi + 1), - ....: ((1, 1), (2, 0), -xi + 1, xi)] + ....: ((0, 1), (4, 0), xi + 2, -xi - 1), + ....: ((1, -1), (0, -1), 1/3*xi + 2/3, -1/3*xi + 1/3), + ....: ((1, 0), (5, 0), xi + 1, -xi), + ....: ((2, 0), (5, 1), xi, -xi + 1)] sage: eq_up_to_order(sols, expected) True @@ -2726,7 +2726,7 @@ def solve_S_unit_equation(K, S, prec=106, include_exponents=True, include_bound= sage: solutions, bound = solve_S_unit_equation(K, S, 100, include_bound=True) sage: bound - 6 + 7 You can omit the exponent vectors:: diff --git a/src/sage/rings/number_field/class_group.py b/src/sage/rings/number_field/class_group.py index 46d0ca8c9d3..1ad6d583a8c 100644 --- a/src/sage/rings/number_field/class_group.py +++ b/src/sage/rings/number_field/class_group.py @@ -157,7 +157,7 @@ def __pow__(self, n): sage: C=K.class_group() sage: c = C(2, a) sage: c^2 - Fractional ideal class (2, a^2 + 2*a - 1) + Fractional ideal class (4, a) sage: c^3 Trivial principal fractional ideal class sage: c^1000 @@ -467,7 +467,7 @@ def _element_constructor_(self, *args, **kwds): sage: CK = K.class_group() sage: CL = L.class_group() sage: [CL(I).exponents() for I in CK] - [(0,), (4,), (2,)] + [(0,), (2,), (4,)] """ if isinstance(args[0], FractionalIdealClass): return self.element_class(self, None, self._number_field.ideal(args[0].ideal())) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 16544d2f458..49a6e31173a 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -7070,7 +7070,7 @@ def S_unit_solutions(self, S=[], prec=106, include_exponents=False, include_boun sage: solutions, bound = K.S_unit_solutions(S, prec=100, include_bound=True) sage: bound - 6 + 7 """ from .S_unit_solver import solve_S_unit_equation return solve_S_unit_equation(self, S, prec, include_exponents, include_bound, proof) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 562a76fa845..ca54b7f95f7 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -1628,7 +1628,7 @@ cdef class NumberFieldElement(FieldElement): sage: Q. = K[] sage: L. = NumberField(X^4 + a) sage: t = (-a).is_norm(L, element=True); t - (True, b^3 + 1) + (True, -b^3 - 1) sage: t[1].norm(K) -a @@ -1743,11 +1743,11 @@ cdef class NumberFieldElement(FieldElement): sage: Q. = K[] sage: L. = NumberField(X^4 + a) sage: t = (-a)._rnfisnorm(L); t - (b^3 + 1, 1) + (-b^3 - 1, 1) sage: t[0].norm(K) -a sage: t = K(3)._rnfisnorm(L); t - (-b^3 - a*b^2 - a^2*b + 1, 3*a^2 - 3*a + 6) + (b^3 + a*b^2 + a^2*b - 1, 3*a^2 - 3*a + 6) sage: t[0].norm(K)*t[1] 3 diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 2e6a368843e..aa2cc3c2978 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -231,7 +231,7 @@ def _richcmp_(self, other, op): sage: K. = NumberField(x^2 + 3); K Number Field in a with defining polynomial x^2 + 3 sage: f = K.factor(15); f - (Fractional ideal (-a))^2 * (Fractional ideal (5)) + (Fractional ideal (1/2*a + 3/2))^2 * (Fractional ideal (5)) sage: (f[0][0] < f[1][0]) True sage: (f[0][0] == f[0][0]) @@ -620,7 +620,7 @@ def free_module(self): sage: K. = CyclotomicField(7) sage: I = K.factor(11)[0][0]; I - Fractional ideal (-2*z^4 - 2*z^2 - 2*z + 1) + Fractional ideal (-3*z^4 - 2*z^3 - 2*z^2 - 2) sage: A = I.free_module() sage: A # warning -- choice of basis can be somewhat random Free module of degree 6 and rank 6 over Integer Ring @@ -3118,7 +3118,7 @@ def residue_class_degree(self): sage: K. = NumberField(x^5 + 2); K Number Field in a with defining polynomial x^5 + 2 sage: f = K.factor(19); f - (Fractional ideal (a^2 + a - 3)) * (Fractional ideal (-2*a^4 - a^2 + 2*a - 1)) * (Fractional ideal (a^2 + a - 1)) + (Fractional ideal (a^2 + a - 3)) * (Fractional ideal (2*a^4 + a^2 - 2*a + 1)) * (Fractional ideal (a^2 + a - 1)) sage: [i.residue_class_degree() for i, _ in f] [2, 2, 1] """ diff --git a/src/sage/rings/number_field/number_field_ideal_rel.py b/src/sage/rings/number_field/number_field_ideal_rel.py index 37c20150ad1..078682fa1fa 100644 --- a/src/sage/rings/number_field/number_field_ideal_rel.py +++ b/src/sage/rings/number_field/number_field_ideal_rel.py @@ -18,7 +18,7 @@ sage: G = [from_A(z) for z in I.gens()]; G [7, -2*b*a - 1] sage: K.fractional_ideal(G) - Fractional ideal (2*b*a + 1) + Fractional ideal ((1/2*b + 2)*a - 1/2*b + 2) sage: K.fractional_ideal(G).absolute_norm().factor() 7^2 """ diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index b5cdeb2a96f..be1548f7a2e 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -2180,7 +2180,7 @@ def EisensteinIntegers(names="omega"): sage: R Eisenstein Integers in Number Field in omega with defining polynomial x^2 + x + 1 with omega = -0.50000000000000000? + 0.866025403784439?*I sage: factor(3 + omega) - (omega) * (-3*omega - 2) + (-1) * (-omega - 3) sage: CC(omega) -0.500000000000000 + 0.866025403784439*I sage: omega.minpoly() diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index 3c481999bea..6dd6b68cb3d 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -100,29 +100,29 @@ sage: UL.zeta_order() 24 sage: UL.roots_of_unity() - [-b*a - b, - b^2*a, - b^3, - a + 1, - -b*a, - -b^2, - b^3*a + b^3, - a, - b, + [-b*a, -b^2*a - b^2, - b^3*a, - -1, - b*a + b, - -b^2*a, -b^3, - -a - 1, - b*a, - b^2, - -b^3*a - b^3, -a, + -b*a - b, + -b^2, + b^3*a, + -a - 1, -b, + b^2*a, + b^3*a + b^3, + -1, + b*a, b^2*a + b^2, + b^3, + a, + b*a + b, + b^2, -b^3*a, + a + 1, + b, + -b^2*a, + -b^3*a - b^3, 1] A relative extension example, which worked thanks to the code review by F.W.Clarke:: @@ -199,7 +199,7 @@ class UnitGroup(AbelianGroupWithValues_class): sage: UK.gen(5) u5 sage: UK.gen(5).value() - z^7 + z + -z^7 - z An S-unit group:: @@ -216,7 +216,7 @@ class UnitGroup(AbelianGroupWithValues_class): sage: SUK.zeta_order() 26 sage: SUK.log(21*z) - (12, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1) + (25, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1) """ # This structure is not a parent in the usual sense. The # "elements" are NumberFieldElement_absolute. Instead, they should @@ -250,7 +250,7 @@ def __init__(self, number_field, proof=True, S=None): sage: UK.gens() (u0, u1) sage: UK.gens_values() - [-1, 6*a - 37] + [-1, -6*a + 37] sage: K. = QuadraticField(-3) sage: UK = K.unit_group(); UK @@ -258,7 +258,7 @@ def __init__(self, number_field, proof=True, S=None): sage: UK.gens() (u,) sage: UK.gens_values() - [1/2*a + 1/2] + [-1/2*a + 1/2] sage: K. = CyclotomicField(13) sage: UK = K.unit_group(); UK @@ -372,7 +372,7 @@ def _element_constructor_(self, u): sage: UK.gens() (u0, u1) sage: UK.gens_values() - [-1, 6*a - 37] + [-1, -6*a + 37] sage: UK.ngens() 2 sage: [UK(u) for u in UK.gens()] @@ -523,9 +523,9 @@ def zeta(self, n=2, all=False): sage: U.zeta(2, all=True) [-1] sage: U.zeta(3) - 1/2*z - 1/2 + -1/2*z - 1/2 sage: U.zeta(3, all=True) - [1/2*z - 1/2, -1/2*z - 1/2] + [-1/2*z - 1/2, 1/2*z - 1/2] sage: U.zeta(4) Traceback (most recent call last): ... @@ -641,7 +641,7 @@ def log(self, u): sage: SUK = UnitGroup(K,S=2) sage: v = (3,1,4,1,5,9,2) sage: u = SUK.exp(v); u - -8732*z^11 + 15496*z^10 + 51840*z^9 + 68804*z^8 + 51840*z^7 + 15496*z^6 - 8732*z^5 + 34216*z^3 + 64312*z^2 + 64312*z + 34216 + 8732*z^11 - 15496*z^10 - 51840*z^9 - 68804*z^8 - 51840*z^7 - 15496*z^6 + 8732*z^5 - 34216*z^3 - 64312*z^2 - 64312*z - 34216 sage: SUK.log(u) (3, 1, 4, 1, 5, 9, 2) sage: SUK.log(u) == v @@ -688,7 +688,7 @@ def exp(self, exponents): sage: SUK = UnitGroup(K,S=2) sage: v = (3,1,4,1,5,9,2) sage: u = SUK.exp(v); u - -8732*z^11 + 15496*z^10 + 51840*z^9 + 68804*z^8 + 51840*z^7 + 15496*z^6 - 8732*z^5 + 34216*z^3 + 64312*z^2 + 64312*z + 34216 + 8732*z^11 - 15496*z^10 - 51840*z^9 - 68804*z^8 - 51840*z^7 - 15496*z^6 + 8732*z^5 - 34216*z^3 - 64312*z^2 - 64312*z - 34216 sage: SUK.log(u) (3, 1, 4, 1, 5, 9, 2) sage: SUK.log(u) == v diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 1ed1faaf150..269b8223823 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -1304,7 +1304,7 @@ def S_class_group(self, S, proof=True): `x^2 + 31` from 12 to 2, i.e. we lose a generator of order 6 (this was fixed in :trac:`14489`):: - sage: S.S_class_group([K.ideal(a)]) + sage: S.S_class_group([K.ideal(a)]) # representation varies, not tested [((1/4*xbar^2 + 31/4, (-1/8*a + 1/8)*xbar^2 - 31/8*a + 31/8, 1/16*xbar^3 + 1/16*xbar^2 + 31/16*xbar + 31/16, -1/16*a*xbar^3 + (1/16*a + 1/8)*xbar^2 - 31/16*a*xbar + 31/16*a + 31/8), 6), ((-1/4*xbar^2 - 23/4, (1/8*a - 1/8)*xbar^2 + 23/8*a - 23/8, -1/16*xbar^3 - 1/16*xbar^2 - 23/16*xbar - 23/16, 1/16*a*xbar^3 + (-1/16*a - 1/8)*xbar^2 + 23/16*a*xbar - 23/16*a - 23/8), 2)] Note that all the returned values live where we expect them to:: From e99a2034a92ac4f399500db73fc2ca357401e392 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 1 Mar 2021 20:22:37 +0100 Subject: [PATCH 047/706] Fix more tests --- src/sage/arith/misc.py | 6 +++--- src/sage/rings/polynomial/polynomial_element.pyx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 53d9683279b..564b99044fa 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1458,13 +1458,13 @@ def divisors(n): sage: K. = QuadraticField(7) sage: divisors(K.ideal(7)) - [Fractional ideal (1), Fractional ideal (a), Fractional ideal (7)] + [Fractional ideal (1), Fractional ideal (-a), Fractional ideal (7)] sage: divisors(K.ideal(3)) [Fractional ideal (1), Fractional ideal (3), Fractional ideal (-a + 2), Fractional ideal (-a - 2)] sage: divisors(K.ideal(35)) - [Fractional ideal (1), Fractional ideal (5), Fractional ideal (a), - Fractional ideal (7), Fractional ideal (5*a), Fractional ideal (35)] + [Fractional ideal (1), Fractional ideal (5), Fractional ideal (-a), + Fractional ideal (7), Fractional ideal (-5*a), Fractional ideal (35)] TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e1fc8972917..bfa66fc5707 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7550,7 +7550,7 @@ cdef class Polynomial(CommutativeAlgebraElement): [(-3.5074662110434039?e451, 1)] sage: p = bigc*x + 1 sage: p.roots(ring=RR) - [(0.000000000000000, 1)] + [(-2.85106096489671e-452, 1)] sage: p.roots(ring=AA) [(-2.8510609648967059?e-452, 1)] sage: p.roots(ring=QQbar) From da65d28f81ddb7a865d0af830d14bb4132c3c1cb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 5 Mar 2021 23:48:18 +0100 Subject: [PATCH 048/706] Revert "try disabling CYSIGNALS_C_ATOMIC" This reverts commit baf80deaca74b037c11e5eee914009e216f3beed. --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 52123d92dbf..9fdaca65c6d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,6 +1,5 @@ # distutils: extra_compile_args = -fopenmp # distutils: extra_link_args = -fopenmp -# distutils: undef_macros = CYSIGNALS_C_ATOMIC r""" Face iterator for polyhedra From 4d92bcae4f91005a1cd85a3509eaf6a159cdabe6 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 13 Nov 2020 12:44:45 +0000 Subject: [PATCH 049/706] do the bnfisunit check in Pari, not rely on output Output format changed in Pari 2.13, so we need to act. cf https://trac.sagemath.org/ticket/30801#comment:6 --- build/pkgs/pari/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 9a1b0c7db8c..1d5e8533693 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -78,8 +78,8 @@ SAGE_SPKG_CONFIGURE([pari], [ sage_spkg_install_pari=yes fi AC_MSG_CHECKING([whether bnfisunit bug of pari 2.11.3 is fixed]) - bug_check=`echo "bnf = bnfinit(y^4-y-1); bnfisunit(bnf,-y^3+2*y^2-1)" | $GP -qf 2>> config.log` - expected="[[0, 2, Mod(0, 2)]]~" + bug_check=`echo "bnf = bnfinit(y^4-y-1); bnfisunit(bnf,-y^3+2*y^2-1)==[[0,2,0]]~" | $GP -qf 2>> config.log` + expected="1" if test x"$bug_check" = x"$expected"; then AC_MSG_RESULT([yes]) else From 9c8e76e055939be74aadeb49a4d9ed47a00ade1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Mon, 8 Mar 2021 22:16:31 -0300 Subject: [PATCH 050/706] try to make smith_form() 'idempotent' --- src/sage/matrix/matrix_integer_dense.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index fd38eaef8d9..c00716288bd 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -2444,7 +2444,8 @@ cdef class Matrix_integer_dense(Matrix_dense): :meth:`elementary_divisors` """ - v = self.__pari__().matsnf(1).sage() + X = self.matrix_space()([self[i,j] for i in xrange(self._nrows-1,-1,-1) for j in xrange(self._ncols-1,-1,-1)]) + v = X.__pari__().matsnf(1).sage() # need to reverse order of rows of U, columns of V, and both of D. D = self.matrix_space()([v[2][i,j] for i in xrange(self._nrows-1,-1,-1) for j in xrange(self._ncols-1,-1,-1)]) @@ -2460,13 +2461,13 @@ cdef class Matrix_integer_dense(Matrix_dense): # silly special cases for matrices with 0 columns (PARI has a unique empty matrix) U = self.matrix_space(ncols = self._nrows)(1) else: - U = self.matrix_space(ncols = self._nrows)([v[0][i,j] for i in xrange(self._nrows-1,-1,-1) for j in xrange(self._nrows)]) + U = self.matrix_space(ncols = self._nrows)([v[0][i,j] for i in xrange(self._nrows-1,-1,-1) for j in xrange(self._nrows-1,-1,-1)]) if self._nrows == 0: # silly special cases for matrices with 0 rows (PARI has a unique empty matrix) V = self.matrix_space(nrows = self._ncols)(1) else: - V = self.matrix_space(nrows = self._ncols)([v[1][i,j] for i in xrange(self._ncols) for j in xrange(self._ncols-1,-1,-1)]) + V = self.matrix_space(nrows = self._ncols)([v[1][i,j] for i in xrange(self._ncols-1,-1,-1) for j in xrange(self._ncols-1,-1,-1)]) return D, U, V From b2eac8c9b53d3f9fd5fa6c0da46a14be35dc4bb4 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 9 Mar 2021 20:05:47 +0100 Subject: [PATCH 051/706] Update some tests for new Smith form transition matrices --- src/sage/groups/abelian_gps/abelian_group.py | 2 +- .../additive_abelian_group.py | 2 +- src/sage/groups/fqf_orthogonal.py | 34 +++++++++---------- src/sage/matrix/matrix_integer_dense.pyx | 16 ++++----- src/sage/matrix/matrix_integer_sparse.pyx | 16 ++++----- src/sage/quadratic_forms/extras.py | 6 ++-- .../quadratic_form__split_local_covering.py | 18 +++++----- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index e6d8e169038..a493506524b 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -1523,7 +1523,7 @@ def subgroup_reduced(self,elts, verbose=False): generated by {f0, f0*f1^2} sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ]) Multiplicative Abelian subgroup isomorphic to C2 x C4 - generated by {f1^2, f0} + generated by {f0^2*f1^2, f0^3} """ from sage.matrix.constructor import matrix d = self.ngens() diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index 3dcedeb7e36..2c76b75e0cd 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -43,7 +43,7 @@ def AdditiveAbelianGroup(invs, remember_generators = True): sage: H = AdditiveAbelianGroup([0, 2, 3], remember_generators = False); H Additive abelian group isomorphic to Z/6 + Z sage: H.gens() - ((0, 1, 2), (1, 0, 0)) + ((0, 1, 1), (1, 0, 0)) There are several ways to create elements of an additive abelian group. Realize that there are two sets of generators: the "obvious" ones composed diff --git a/src/sage/groups/fqf_orthogonal.py b/src/sage/groups/fqf_orthogonal.py index 3c5190589b5..ae9fc2fae2d 100644 --- a/src/sage/groups/fqf_orthogonal.py +++ b/src/sage/groups/fqf_orthogonal.py @@ -133,16 +133,16 @@ class FqfOrthogonalGroup(AbelianGroupAutomorphismGroup_subgroup): sage: T Finite quadratic module over Integer Ring with invariants (3, 3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0 0] + [2/3 0 0] [ 0 2/3 0] - [ 0 0 2/3] + [ 0 0 4/3] sage: T.orthogonal_group() Group of isometries of Finite quadratic module over Integer Ring with invariants (3, 3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0 0] + [2/3 0 0] [ 0 2/3 0] - [ 0 0 2/3] + [ 0 0 4/3] generated by 2 elements sage: q = matrix.diagonal(QQ, [3/2, 1/4, 1/4]) sage: T = TorsionQuadraticForm(q) @@ -158,7 +158,7 @@ class FqfOrthogonalGroup(AbelianGroupAutomorphismGroup_subgroup): sage: G = T.orthogonal_group() sage: g = G(matrix(ZZ, 2, [8, 0, 0, 1])) sage: Q.1 * g - (0, 2) + (0, 1) """ Element = FqfIsometry @@ -225,8 +225,8 @@ def _element_constructor_(self, x, check=True): sage: f = OL(f) sage: fbar = Oq(f) sage: fbar - [4 3] - [3 1] + [1 3] + [3 4] Note that the following does not work since it may lead to ambiguities, see :trac:`30669`:: @@ -307,12 +307,12 @@ def _get_action_(self, S, op, self_on_left): Right action by Group of isometries of Finite quadratic module over Integer Ring with invariants (3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0] - [ 0 2/3] + [2/3 0] + [ 0 4/3] generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0] - [ 0 2/3] + [2/3 0] + [ 0 4/3] """ import operator if op == operator.mul and not self_on_left: @@ -450,19 +450,19 @@ def _act_(self, g, a): Right action by Group of isometries of Finite quadratic module over Integer Ring with invariants (3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0] - [ 0 2/3] + [2/3 0] + [ 0 4/3] generated by 2 elements on Finite quadratic module over Integer Ring with invariants (3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0] - [ 0 2/3] + [2/3 0] + [ 0 4/3] sage: x = q.an_element() sage: g = G.an_element() sage: A(x, g).parent() Finite quadratic module over Integer Ring with invariants (3, 3) Gram matrix of the quadratic form with values in Q/2Z: - [4/3 0] - [ 0 2/3] + [2/3 0] + [ 0 4/3] sage: q = TorsionQuadraticForm(matrix.diagonal([2/3, 2/3, 6/8, 1/4])) sage: G = q.orthogonal_group() sage: q2 = q.primary_part(2) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index c00716288bd..59f7fcfab85 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -2399,13 +2399,13 @@ cdef class Matrix_integer_dense(Matrix_dense): [0 3 0] [0 0 0] sage: U - [ 0 1 0] + [ 0 2 -1] [ 0 -1 1] - [-1 2 -1] + [ 1 -2 1] sage: V - [-1 4 1] - [ 1 -3 -2] [ 0 0 1] + [-1 2 -2] + [ 1 -1 1] sage: U*A*V [1 0 0] [0 3 0] @@ -2420,12 +2420,12 @@ cdef class Matrix_integer_dense(Matrix_dense): [0 2] [0 0] sage: U - [ 0 1 0] + [ 0 2 -1] [ 0 -1 1] - [-1 2 -1] + [ 1 -2 1] sage: V - [-1 3] - [ 1 -2] + [-1 1] + [ 1 0] sage: U * A * V [1 0] [0 2] diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index c53c92602ed..8223fa8ab08 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -611,13 +611,13 @@ cdef class Matrix_integer_sparse(Matrix_sparse): [0 3 0] [0 0 0] sage: U - [ 0 1 0] + [ 0 2 -1] [ 0 -1 1] - [-1 2 -1] + [ 1 -2 1] sage: V - [-1 4 1] - [ 1 -3 -2] [ 0 0 1] + [-1 2 -2] + [ 1 -1 1] sage: U*A*V [1 0 0] [0 3 0] @@ -632,12 +632,12 @@ cdef class Matrix_integer_sparse(Matrix_sparse): [0 2] [0 0] sage: U - [ 0 1 0] + [ 0 2 -1] [ 0 -1 1] - [-1 2 -1] + [ 1 -2 1] sage: V - [-1 3] - [ 1 -2] + [-1 1] + [ 1 0] sage: U * A * V [1 0] [0 2] diff --git a/src/sage/quadratic_forms/extras.py b/src/sage/quadratic_forms/extras.py index 37c30e9afa5..2baa4e3cfa7 100644 --- a/src/sage/quadratic_forms/extras.py +++ b/src/sage/quadratic_forms/extras.py @@ -94,12 +94,12 @@ def extend_to_primitive(A_input): sage: A = Matrix(ZZ, 3, 2, range(6)) sage: extend_to_primitive(A) - [ 0 1 0] + [ 0 1 -1] [ 2 3 0] - [ 4 5 -1] + [ 4 5 0] sage: extend_to_primitive([vector([1,2,3])]) - [(1, 2, 3), (0, 1, 0), (0, 0, 1)] + [(1, 2, 3), (0, 1, 1), (-1, 0, 0)] """ ## Deal with a list of vectors diff --git a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py index 92102fcbd1e..85abc20a86c 100644 --- a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py +++ b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py @@ -318,25 +318,25 @@ def complementary_subform_to_vector(self, v): sage: Q1 = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q1.complementary_subform_to_vector([1,0,0,0]) Quadratic form in 3 variables over Integer Ring with coefficients: - [ 3 0 0 ] + [ 7 0 0 ] [ * 5 0 ] - [ * * 7 ] + [ * * 3 ] :: sage: Q1.complementary_subform_to_vector([1,1,0,0]) Quadratic form in 3 variables over Integer Ring with coefficients: - [ 12 0 0 ] + [ 7 0 0 ] [ * 5 0 ] - [ * * 7 ] + [ * * 12 ] :: sage: Q1.complementary_subform_to_vector([1,1,1,1]) Quadratic form in 3 variables over Integer Ring with coefficients: - [ 624 -480 -672 ] - [ * 880 -1120 ] - [ * * 1008 ] + [ 880 -480 -160 ] + [ * 624 -96 ] + [ * * 240 ] """ n = self.dim() @@ -417,8 +417,8 @@ def split_local_cover(self): sage: Q1.split_local_cover() Quadratic form in 3 variables over Integer Ring with coefficients: [ 3 0 0 ] - [ * 7 0 ] - [ * * 5 ] + [ * 5 0 ] + [ * * 7 ] """ ## 0. If a split local cover already exists, then return it. From 6cc043fa3e8b51ca11c68fb13f4bd71c3fbb027b Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 13 Mar 2021 19:40:20 +0100 Subject: [PATCH 052/706] Update some tests for different ideal and unit group generators --- src/sage/modular/local_comp/liftings.py | 6 +++--- src/sage/modular/local_comp/smoothchar.py | 4 ++-- src/sage/modular/modsym/p1list_nf.py | 2 +- src/sage/structure/factorization.py | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/modular/local_comp/liftings.py b/src/sage/modular/local_comp/liftings.py index 91bfd2448d3..f629bc659b0 100644 --- a/src/sage/modular/local_comp/liftings.py +++ b/src/sage/modular/local_comp/liftings.py @@ -222,9 +222,9 @@ def lift_for_SL(A, N=None): TESTS:: sage: lift_for_SL(matrix(3,3,[1,2,0,3,4,0,0,0,1]),3) - [10 14 3] - [ 9 10 3] - [ 3 3 1] + [-2 -1 0] + [ 0 1 -3] + [ 3 0 4] sage: A = matrix(Zmod(7), 2, [1,0,0,1]) sage: L = lift_for_SL(A) diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index 8bb3d46a009..3bce61f4ebf 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -1624,8 +1624,8 @@ def discrete_log(self, level, x): sage: G = SmoothCharacterGroupRamifiedQuadratic(3, 1, QQ) sage: s = G.number_field().gen() sage: G.discrete_log(4, 3 + 2*s) - [5, 1, 1, 1] - sage: gs = G.unit_gens(4); gs[0]^5 * gs[1] * gs[2] * gs[3] - (3 + 2*s) in G.ideal(4) + [1, 2, 2, 1] + sage: gs = G.unit_gens(4); gs[0] * gs[1]^2 * gs[2]^2 * gs[3] - (3 + 2*s) in G.ideal(4) True """ x = self.number_field().coerce(x) diff --git a/src/sage/modular/modsym/p1list_nf.py b/src/sage/modular/modsym/p1list_nf.py index 7a91d353e3c..b71fc8aac41 100644 --- a/src/sage/modular/modsym/p1list_nf.py +++ b/src/sage/modular/modsym/p1list_nf.py @@ -956,7 +956,7 @@ def apply_J_epsilon(self, i, e1, e2=1): sage: N = k.ideal(a + 1) sage: P = P1NFList(N) sage: u = k.unit_group().gens_values(); u - [-1, a^3 + a^2 + a + 12, a^3 + 3*a^2 - 1] + [-1, -a^3 - a^2 - a - 12, -a^3 - 3*a^2 + 1] sage: P.apply_J_epsilon(3, u[2]^2)==P.apply_J_epsilon(P.apply_J_epsilon(3, u[2]),u[2]) True """ diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py index 1d32db08422..7636f1a9ba7 100644 --- a/src/sage/structure/factorization.py +++ b/src/sage/structure/factorization.py @@ -133,17 +133,17 @@ sage: K. = NumberField(x^2 + 3); K Number Field in a with defining polynomial x^2 + 3 sage: f = K.factor(15); f - (Fractional ideal (-a))^2 * (Fractional ideal (5)) + (Fractional ideal (1/2*a + 3/2))^2 * (Fractional ideal (5)) sage: f.universe() Monoid of ideals of Number Field in a with defining polynomial x^2 + 3 sage: f.unit() Fractional ideal (1) sage: g=K.factor(9); g - (Fractional ideal (-a))^4 + (Fractional ideal (1/2*a + 3/2))^4 sage: f.lcm(g) - (Fractional ideal (-a))^4 * (Fractional ideal (5)) + (Fractional ideal (1/2*a + 3/2))^4 * (Fractional ideal (5)) sage: f.gcd(g) - (Fractional ideal (-a))^2 + (Fractional ideal (1/2*a + 3/2))^2 sage: f.is_integral() True From 52d48d5105dad1b24c332db0ee73be01538c57c2 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 13 Mar 2021 21:23:04 +0100 Subject: [PATCH 053/706] Fix some tests in sage.schemes.elliptic_curves --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 +++--- src/sage/schemes/elliptic_curves/ell_generic.py | 6 +++--- src/sage/schemes/elliptic_curves/ell_rational_field.py | 10 +++++----- .../schemes/elliptic_curves/isogeny_small_degree.py | 4 ++-- src/sage/schemes/elliptic_curves/period_lattice.py | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 87fbec3b69b..adf0d2c73fc 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -785,11 +785,11 @@ def gens(self): sage: len(E.gens()) 2 sage: E.cardinality() - 867361737988403547207212930746733987710588 + 867361737988403547206134229616487867594472 sage: E.gens()[0].order() - 433680868994201773603606465373366993855294 + 433680868994201773603067114808243933797236 sage: E.gens()[1].order() - 433680868994201773603606465373366993855294 + 433680868994201773603067114808243933797236 """ G = self.__pari__().ellgroup(flag=1) return tuple(self.point(list(pt)) for pt in G[2]) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 7b02e8f6635..a1a48d2397f 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -527,7 +527,7 @@ def __call__(self, *args, **kwds): sage: E = EllipticCurve([0,0,0,-49,0]) sage: T = E.torsion_subgroup() sage: [E(t) for t in T] - [(0 : 1 : 0), (-7 : 0 : 1), (0 : 0 : 1), (7 : 0 : 1)] + [(0 : 1 : 0), (0 : 0 : 1), (-7 : 0 : 1), (7 : 0 : 1)] :: @@ -2951,8 +2951,8 @@ def pari_curve(self): Mod(-928, y^2 - 2), Mod(3456/29, y^2 - 2), Vecsmall([5]), [[y^2 - 2, [2, 0], 8, 1, [[1, -1.41421356237310; 1, 1.41421356237310], [1, -1.41421356237310; 1, 1.41421356237310], - [1, -1; 1, 1], [2, 0; 0, 4], [4, 0; 0, 2], [2, 0; 0, 1], - [2, [0, 2; 1, 0]], []], [-1.41421356237310, 1.41421356237310], + [16, -23; 16, 23], [2, 0; 0, 4], [4, 0; 0, 2], [2, 0; 0, 1], + [2, [0, 2; 1, 0]], [2]], [-1.41421356237310, 1.41421356237310], [1, y], [1, 0; 0, 1], [1, 0, 0, 2; 0, 1, 1, 0]]], [0, 0, 0, 0, 0]] PARI no longer requires that the `j`-invariant has negative `p`-adic valuation:: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 2953e80cac1..bf552a25f5c 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -4083,7 +4083,7 @@ def torsion_subgroup(self): sage: G.1 (282 : 0 : 1) sage: list(G) - [(0 : 1 : 0), (147 : 12960 : 1), (2307 : 97200 : 1), (-933 : 29160 : 1), (1011 : 0 : 1), (-933 : -29160 : 1), (2307 : -97200 : 1), (147 : -12960 : 1), (282 : 0 : 1), (8787 : 816480 : 1), (-285 : 27216 : 1), (1227 : 22680 : 1), (-1293 : 0 : 1), (1227 : -22680 : 1), (-285 : -27216 : 1), (8787 : -816480 : 1)] + [(0 : 1 : 0), (147 : -12960 : 1), (2307 : -97200 : 1), (-933 : -29160 : 1), (1011 : 0 : 1), (-933 : 29160 : 1), (2307 : 97200 : 1), (147 : 12960 : 1), (-1293 : 0 : 1), (1227 : 22680 : 1), (-285 : 27216 : 1), (8787 : 816480 : 1), (282 : 0 : 1), (8787 : -816480 : 1), (-285 : -27216 : 1), (1227 : -22680 : 1)] """ try: G = self.__torsion_subgroup @@ -5406,10 +5406,10 @@ def eval_modular_form(self, points, order): EXAMPLES:: sage: E = EllipticCurve('37a1') - sage: E.eval_modular_form([1.5+I,2.0+I,2.5+I],100) # abs tol 1e-20 - [-0.0018743978548152085771342944989052703431, - 0.0018604485340371083710285594393397945456, - -0.0018743978548152085771342944989052703431] + sage: E.eval_modular_form([1.5+I,2.0+I,2.5+I],100) + [-0.0018743978548152085..., + 0.0018604485340371083..., + -0.0018743978548152085...] sage: E.eval_modular_form(2.1+I, 100) # abs tol 1e-16 [0.00150864362757267079 + 0.00109100341113449845*I] diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index 7178da3685a..8342d92ed34 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -801,8 +801,8 @@ def isogenies_5_0(E, minimal_models=True): sage: K. = NumberField(x**6-320*x**3-320) sage: E = EllipticCurve(K,[0,0,1,0,0]) sage: isogenies_5_0(E) - [Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 + y = x^3 + (643/8*a^5-15779/48*a^4-32939/24*a^3-71989/2*a^2+214321/6*a-112115/3)*x + (2901961/96*a^5+4045805/48*a^4+12594215/18*a^3-30029635/6*a^2+15341626/3*a-38944312/9) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320, - Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 + y = x^3 + (-1109/8*a^5-53873/48*a^4-180281/24*a^3-14491/2*a^2+35899/6*a-43745/3)*x + (-17790679/96*a^5-60439571/48*a^4-77680504/9*a^3+1286245/6*a^2-4961854/3*a-73854632/9) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320] + [Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 + y = x^3 + (241565/32*a^5-362149/48*a^4+180281/24*a^3-9693307/4*a^2+14524871/6*a-7254985/3)*x + (1660391123/192*a^5-829315373/96*a^4+77680504/9*a^3-66622345345/24*a^2+33276655441/12*a-24931615912/9) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320, + Isogeny of degree 5 from Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^6 - 320*x^3 - 320 to Elliptic Curve defined by y^2 + y = x^3 + (47519/32*a^5-72103/48*a^4+32939/24*a^3-1909753/4*a^2+2861549/6*a-1429675/3)*x + (-131678717/192*a^5+65520419/96*a^4-12594215/18*a^3+5280985135/24*a^2-2637787519/12*a+1976130088/9) over Number Field in a with defining polynomial x^6 - 320*x^3 - 320] """ F = E.base_field() if E.j_invariant() != 0: diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 0f1d17f9955..fe1899f647c 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1625,7 +1625,7 @@ def elliptic_logarithm(self, P, prec=None, reduce=True): sage: P,Q = T[2] sage: embs = K.embeddings(CC) sage: Lambda = E.period_lattice(embs[0]) - sage: Lambda.elliptic_logarithm(P+3*Q, 100) + sage: Lambda.elliptic_logarithm(P, 100) 4.7100131126199672766973600998 sage: R. = QQ[] sage: K. = NumberField(x^2 + x + 5) From 27baa071a64d5f93ca49d5ca4c18ae8ca7fa892a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Mar 2021 12:58:13 +0100 Subject: [PATCH 054/706] use OPENMP_CFLAGS --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 7c5fb7efafc..fd6280b48d7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,5 +1,5 @@ -# distutils: extra_compile_args = -fopenmp -# distutils: extra_link_args = -fopenmp +# distutils: extra_compile_args = OPENMP_CFLAGS +# distutils: extra_link_args = OPENMP_CFLAGS r""" Face iterator for polyhedra From 1f0b74f20bb3e444fc1fdfa3fb92b33c5b5cb06e Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 10 Mar 2021 09:47:38 +0000 Subject: [PATCH 055/706] #31443: update eclib package metadata --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 4daaf05d8b5..4e8b74dd38b 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ -tarball=eclib-20190909.tar.bz2 -sha1=0e994c0de95ef03ef19ad5030a2cacbb83c76bbd -md5=1a67217a7fa762646d43c7bec8a73028 -cksum=4240278408 +tarball=eclib-20210308.tar.bz2 +sha1=1ffefad598683ff95c6ad90be42488a8255e8e98 +md5=b284ea7df077ca680c9cd93fe3bd353b +cksum=2985037614 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 4defdea11fc..73aebd5f103 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20190909 +20210308 From 5fce79f65d4a5071a5fccd210a99cd07bdc8b30d Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:02:47 +0000 Subject: [PATCH 056/706] #31443: update eclib package metadata again --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 4e8b74dd38b..b37bff93c05 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ -tarball=eclib-20210308.tar.bz2 -sha1=1ffefad598683ff95c6ad90be42488a8255e8e98 -md5=b284ea7df077ca680c9cd93fe3bd353b -cksum=2985037614 +tarball=eclib-20210310.tar.bz2 +sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 +md5=4ac988bc46869866f076f7bea0fdaa6b +cksum=2130748042 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 73aebd5f103..3384f6c7325 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210308 +20210310 From a46b6f9d9b3b2fb8a9ba0587dd6b71991cd0fec9 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:04:30 +0000 Subject: [PATCH 057/706] #31443: adapt eclib library interface for version v20210310 --- src/sage/libs/eclib/__init__.pxd | 8 +- src/sage/libs/eclib/interface.py | 397 ++++++++++++++----------------- src/sage/libs/eclib/mwrank.pyx | 227 +++++++++--------- src/sage/libs/eclib/newforms.pyx | 1 + src/sage/libs/eclib/t | 0 src/sage/libs/eclib/wrap.cpp | 10 +- 6 files changed, 313 insertions(+), 330 deletions(-) create mode 100644 src/sage/libs/eclib/t diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index 3f99f998a50..d44d4fba865 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -12,9 +12,11 @@ from libcpp.pair cimport pair from sage.libs.ntl.types cimport ZZ_c -# NOTE: eclib includes have specific dependencies and must be included -# in a specific order. So we start by listing all relevant include files -# in the correct order. +# NOTE: eclib used to have specific dependencies, so that they had to +# be included in a specific order. Although this is no longer the +# case, we start by listing all relevant include files in the correct +# order. + cdef extern from "eclib/vector.h": pass cdef extern from "eclib/xmod.h": pass cdef extern from "eclib/svector.h": pass diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index e8984567205..493b5f13470 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -21,17 +21,16 @@ sage: [k for k in sys.modules if k.startswith("sage.libs.eclib")] [] sage: EllipticCurve('11a1').mwrank_curve() - y^2+ y = x^3 - x^2 - 10*x - 20 + y^2 + y = x^3 - x^2 - 10 x - 20 sage: [k for k in sys.modules if k.startswith("sage.libs.eclib")] ['...'] """ - +import sys from sage.structure.sage_object import SageObject from sage.rings.all import Integer from sage.rings.integer_ring import IntegerRing -from .mwrank import _Curvedata, _two_descent, _mw - +from .mwrank import _Curvedata, _two_descent, _mw, parse_point_list class mwrank_EllipticCurve(SageObject): r""" @@ -67,7 +66,7 @@ class mwrank_EllipticCurve(SageObject): sage: e = mwrank_EllipticCurve([3, -4]) sage: e - y^2 = x^3 + 3*x - 4 + y^2 = x^3 + 3 x - 4 sage: e.ainvs() [0, 0, 0, 3, -4] @@ -127,6 +126,7 @@ def __init__(self, ainvs, verbose=False): # place holders self.__saturate = -2 # not yet saturated + self.__descent = None def __reduce__(self): r""" @@ -137,12 +137,9 @@ def __reduce__(self): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: E.__reduce__() (, ([0, 0, 1, -7, 6], False)) - - """ return mwrank_EllipticCurve, (self.__ainvs, self.__verbose) - def set_verbose(self, verbose): """ Set the verbosity of printing of output by the :meth:`two_descent()` and @@ -247,53 +244,27 @@ def __repr__(self): sage: E = mwrank_EllipticCurve([0,-1,1,0,0]) sage: E.__repr__() - 'y^2+ y = x^3 - x^2 ' + 'y^2 + y = x^3 - x^2' """ - # TODO: Is the use (or omission) of spaces here intentional? - a = self.ainvs() - s = "y^2" - if a[0] == -1: - s += "- x*y " - elif a[0] == 1: - s += "+ x*y " - elif a[0] != 0: - s += "+ %s*x*y "%a[0] - if a[2] == -1: - s += " - y" - elif a[2] == 1: - s += "+ y" - elif a[2] != 0: - s += "+ %s*y"%a[2] - s += " = x^3 " - if a[1] == -1: - s += "- x^2 " - elif a[1] == 1: - s += "+ x^2 " - elif a[1] != 0: - s += "+ %s*x^2 "%a[1] - if a[3] == -1: - s += "- x " - elif a[3] == 1: - s += "+ x " - elif a[3] != 0: - s += "+ %s*x "%a[3] - if a[4] == -1: - s += "-1" - elif a[4] == 1: - s += "+1" - elif a[4] != 0: - s += "+ %s"%a[4] - s = s.replace("+ -","- ") - return s - + a1, a2, a3, a4, a6 = self.__ainvs + # we do not assume a1, a2, a3 are reduced to {0,1}, {-1,0,1}, {0,1} + coeff = lambda a: ''.join([" +" if a > 0 else " -", + " " + str(abs(a)) if abs(a) > 1 else ""]) + return ''.join(['y^2', + ' '.join([coeff(a1), 'xy']) if a1 else '', + ' '.join([coeff(a3), 'y']) if a3 else '', + ' = x^3', + ' '.join([coeff(a2), 'x^2']) if a2 else '', + ' '.join([coeff(a4), 'x']) if a4 else '', + ' '.join([" +" if a6 > 0 else " -", str(abs(a6))]) if a6 else '']) def two_descent(self, - verbose = True, - selmer_only = False, - first_limit = 20, - second_limit = 8, - n_aux = -1, - second_descent = True): + verbose=True, + selmer_only=False, + first_limit=20, + second_limit=8, + n_aux=-1, + second_descent=True): r""" Compute 2-descent data for this curve. @@ -374,16 +345,14 @@ def two_descent(self, second_limit = int(second_limit) n_aux = int(n_aux) second_descent = int(second_descent) # convert from bool to (int) 0 or 1 - # TODO: Don't allow limits above some value...??? - # (since otherwise mwrank just sets limit tiny) self.__descent = _two_descent() self.__descent.do_descent(self.__curve, - verbose, - selmer_only, - first_limit, - second_limit, - n_aux, - second_descent) + verbose, + selmer_only, + first_limit, + second_limit, + n_aux, + second_descent) if not self.__descent.ok(): raise RuntimeError("A 2-descent did not complete successfully.") self.__saturate = -2 # not yet saturated @@ -398,11 +367,9 @@ def __two_descent_data(self): sage: E._mwrank_EllipticCurve__two_descent_data() """ - try: - return self.__descent - except AttributeError: + if self.__descent is None: self.two_descent(self.__verbose) - return self.__descent + return self.__descent def conductor(self): """ @@ -565,22 +532,24 @@ def regulator(self): R = self.__two_descent_data().regulator() return float(R) - def saturate(self, bound=-1): + def saturate(self, bound=-1, lower=2): """ - Compute the saturation of the Mordell-Weil group at all - primes up to ``bound``. + Compute the saturation of the Mordell-Weil group. INPUT: - - ``bound`` (int, default -1) -- Use `-1` (the default) to - saturate at *all* primes, `0` for no saturation, or `n` (a - positive integer) to saturate at all primes up to `n`. + - ``bound`` (int, default -1) -- If `-1`, saturate at *all* + primes by computing a bound on the saturation index, + otherwise saturate at all primes up to the minimum of + ``bound`` and the saturation index bound. + + - ``lower`` (int, default 2) -- Only saturate at primes not + less than this. EXAMPLES: Since the 2-descent automatically saturates at primes up to - 20, it is not easy to come up with an example where saturation - has any effect:: + 20, further saturation often has no effect:: sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() @@ -599,7 +568,7 @@ def saturate(self, bound=-1): """ bound = int(bound) if self.__saturate < bound: - self.__two_descent_data().saturate(bound) + self.__two_descent_data().saturate(bound, lower) self.__saturate = bound def gens(self): @@ -613,8 +582,7 @@ def gens(self): [[0, -1, 1]] """ self.saturate() - L = eval(self.__two_descent_data().getbasis().replace(":",",")) - return [[Integer(x), Integer(y), Integer(z)] for (x,y,z) in L] + return parse_point_list(self.__two_descent_data().getbasis()) def certain(self): r""" @@ -760,65 +728,37 @@ class mwrank_MordellWeil(SageObject): sage: EQ.search(1) P1 = [0:1:0] is torsion point, order 1 P1 = [-3:0:1] is generator number 1 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 7) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 7) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 23) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 41) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 17) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 43) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 31) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 37) + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 7) + Checking 3-saturation + Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 - saturating up to 20...Checking 2-saturation + saturating up to 20...Saturation index bound (for points of good reduction) = 4 + Reducing saturation bound from given value 20 to computed index bound 4 + Checking saturation at [ 2 3 ] + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] + Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) Index gain = 2^1 - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 67) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 53) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 73) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 103) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 113) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 47) - done (index = 2). + done, index = 2. Gained index 2, new generators = [ [1:-1:1] [-2:3:1] ] P3 = [-14:25:8] is generator number 3 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 11) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 71) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 101) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 127) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 151) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 139) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 179) - done (index = 1). + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) P4 = [0:2:1] = 2*P1 + 0*P2 + 1*P3 (mod torsion) P4 = [2:13:8] = -3*P1 + 1*P2 + -1*P3 (mod torsion) @@ -878,7 +818,7 @@ def __reduce__(self): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) sage: EQ.__reduce__() - (, (y^2+ y = x^3 - 7*x + 6, True, 1, 999)) + (, (y^2 + y = x^3 - 7 x + 6, True, 1, 999)) """ return mwrank_MordellWeil, (self.__curve, self.__verbose, self.__pp, self.__maxr) @@ -902,12 +842,10 @@ def __repr__(self): """ return "Subgroup of Mordell-Weil group: %s"%self.__mw - def process(self, v, sat=0): - """ - This function allows one to add points to a :class:`mwrank_MordellWeil` object. + def process(self, v, saturation_bound=0): + """Process points in the list ``v``. - Process points in the list ``v``, with saturation at primes up to - ``sat``. If ``sat`` is zero (the default), do no saturation. + This function allows one to add points to a :class:`mwrank_MordellWeil` object. INPUT: @@ -915,8 +853,9 @@ def process(self, v, sat=0): list of triples of integers, which define points on the curve. - - ``sat`` (int, default 0) -- saturate at primes up to ``sat``, or at - *all* primes if ``sat`` is zero. + - ``saturation_bound`` (int, default 0) -- saturate at primes up to + ``saturation_bound``, or at *all* primes if ``saturation_bound`` is -1; when ``saturation_bound`` + is 0 (the default), do no saturation.. OUTPUT: @@ -939,11 +878,11 @@ def process(self, v, sat=0): sage: EQ.points() [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] - Example to illustrate the saturation parameter ``sat``:: + Example to illustrate the saturation parameter ``saturation_bound``:: sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=20) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=20) P1 = [1547:-2967:343] is generator number 1 ... Gained index 5, new generators = [ [-2:3:1] [-14:25:8] [1:-1:1] ] @@ -956,7 +895,7 @@ def process(self, v, sat=0): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=0) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=0) P1 = [1547:-2967:343] is generator number 1 P2 = [2707496766203306:864581029138191:2969715140223272] is generator number 2 P3 = [-13422227300:-49322830557:12167000000] is generator number 3 @@ -965,55 +904,92 @@ def process(self, v, sat=0): sage: EQ.regulator() 375.42920288254555 sage: EQ.saturate(2) # points were not 2-saturated - saturating basis...Saturation index bound = 93 - WARNING: saturation at primes p > 2 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 93 + Only p-saturating for p up to given value 2. + The resulting points may not be p-saturated for p between this and the computed index bound 93 + Checking saturation at [ 2 ] + Checking 2-saturation + possible kernel vector = [1,0,0] + This point may be in 2E(Q): [1547:-2967:343] + ...and it is! + Replacing old generator #1 with new generator [-2:3:1] + Reducing index bound from 93 to 46 + Points have successfully been 2-saturated (max q used = 11) + Index gain = 2^1 + done Gained index 2 - New regulator = 93.857... - (False, 2, '[ ]') + New regulator = 93.85730072 + (True, 2, '[ ]') sage: EQ.points() [[-2, 3, 1], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]] sage: EQ.regulator() 93.85730072063639 sage: EQ.saturate(3) # points were not 3-saturated - saturating basis...Saturation index bound = 46 - WARNING: saturation at primes p > 3 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 46 + Only p-saturating for p up to given value 3. + The resulting points may not be p-saturated for p between this and the computed index bound 46 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + possible kernel vector = [0,1,0] + This point may be in 3E(Q): [2707496766203306:864581029138191:2969715140223272] + ...and it is! + Replacing old generator #2 with new generator [-14:25:8] + Reducing index bound from 46 to 15 + Points have successfully been 3-saturated (max q used = 13) + Index gain = 3^1 + done Gained index 3 - New regulator = 10.428... - (False, 3, '[ ]') + New regulator = 10.42858897 + (True, 3, '[ ]') sage: EQ.points() [[-2, 3, 1], [-14, 25, 8], [-13422227300, -49322830557, 12167000000]] sage: EQ.regulator() 10.4285889689596 sage: EQ.saturate(5) # points were not 5-saturated - saturating basis...Saturation index bound = 15 - WARNING: saturation at primes p > 5 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 15 + Only p-saturating for p up to given value 5. + The resulting points may not be p-saturated for p between this and the computed index bound 15 + Checking saturation at [ 2 3 5 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + Checking 5-saturation + possible kernel vector = [0,0,1] + This point may be in 5E(Q): [-13422227300:-49322830557:12167000000] + ...and it is! + Replacing old generator #3 with new generator [1:-1:1] + Reducing index bound from 15 to 3 + Points have successfully been 5-saturated (max q used = 71) + Index gain = 5^1 + done Gained index 5 - New regulator = 0.417... - (False, 5, '[ ]') + New regulator = 0.4171435588 + (True, 5, '[ ]') sage: EQ.points() [[-2, 3, 1], [-14, 25, 8], [1, -1, 1]] sage: EQ.regulator() 0.417143558758384 sage: EQ.saturate() # points are now saturated - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done (True, 1, '[ ]') """ if not isinstance(v, list): raise TypeError("v (=%s) must be a list"%v) - sat = int(sat) + saturation_bound = int(saturation_bound) for P in v: - if not isinstance(P, (list,tuple)) or len(P) != 3: + if not isinstance(P, (list, tuple)) or len(P) != 3: raise TypeError("v (=%s) must be a list of 3-tuples (or 3-element lists) of ints"%v) - self.__mw.process(P, sat) + self.__mw.process(P, saturation_bound) def regulator(self): """ @@ -1091,23 +1067,21 @@ def rank(self): """ return self.__mw.rank() - def saturate(self, max_prime=-1, odd_primes_only=False): - r""" - Saturate this subgroup of the Mordell-Weil group. + def saturate(self, max_prime=-1, min_prime=2): + r"""Saturate this subgroup of the Mordell-Weil group. INPUT: - - ``max_prime`` (int, default -1) -- saturation is performed for - all primes up to ``max_prime``. If `-1` (the default), an + - ``max_prime`` (int, default -1) -- If `-1` (the default), an upper bound is computed for the primes at which the subgroup - may not be saturated, and this is used; however, if the - computed bound is greater than a value set by the ``eclib`` - library (currently 97) then no saturation will be attempted - at primes above this. + may not be saturated, and saturation is performed for all + primes up to this bound. Otherwise, the bound used is the + minimum of ``max_prime`` and the computed bound. - - ``odd_primes_only`` (bool, default ``False``) -- only do - saturation at odd primes. (If the points have been found - via :meth:`two_descent` they should already be 2-saturated.) + - ``min_prime`` (int, default 2) -- only do saturation at + primes no less than this. (For example, if the points have + been found via :meth:`two_descent` they should already be + 2-saturated so a value of 3 is appropriate.) OUTPUT: @@ -1115,40 +1089,35 @@ def saturate(self, max_prime=-1, odd_primes_only=False): - ``ok`` (bool) -- ``True`` if and only if the saturation was provably successful at all primes attempted. If the default - was used for ``max_prime`` and no warning was output about - the computed saturation bound being too high, then ``True`` - indicates that the subgroup is saturated at *all* - primes. + was used for ``max_prime``, then ``True`` indicates that the + subgroup is saturated at *all* primes. - ``index`` (int) -- the index of the group generated by the original points in their saturation. - ``unsatlist`` (list of ints) -- list of primes at which - saturation could not be proved or achieved. Increasing the - precision should correct this, since it happens when - a linear combination of the points appears to be a multiple - of `p` but cannot be divided by `p`. (Note that ``eclib`` - uses floating point methods based on elliptic logarithms to - divide points.) + saturation could not be proved or achieved. .. note:: - We emphasize that if this function returns ``True`` as the - first return argument (``ok``), and if the default was used for the - parameter ``max_prime``, then the points in the basis after - calling this function are saturated at *all* primes, - i.e., saturating at the primes up to ``max_prime`` are - sufficient to saturate at all primes. Note that the - function might not have needed to saturate at all primes up - to ``max_prime``. It has worked out what prime you need to - saturate up to, and that prime might be smaller than ``max_prime``. + In versions up to v20190909, ``eclib`` used floating point + methods based on elliptic logarithms to divide points, and + did not compute the precision necessary, which could casue + failures. Since v20210310, ``eclib`` uses exact method based + on division polynomials, which should mean that such + failures does not happen. .. note:: - Currently (May 2010), this does not remember the result of - calling :meth:`search()`. So calling :meth:`search()` up - to height 20 then calling :meth:`saturate()` results in - another search up to height 18. + We emphasize that if this function returns ``True`` as the + first return argument (``ok``), and if the default was used + for the parameter ``max_prime``, then the points in the + basis after calling this function are saturated at *all* + primes, i.e., saturating at the primes up to ``max_prime`` + are sufficient to saturate at all primes. Note that the + function computes an upper bound for the index of + saturation, and does no work for primes greater than this + even if ``max_prime`` is larger. EXAMPLES:: @@ -1160,7 +1129,7 @@ def saturate(self, max_prime=-1, odd_primes_only=False): automatic saturation at this stage we set the parameter ``sat`` to 0 (which is in fact the default):: - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=0) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=0) P1 = [1547:-2967:343] is generator number 1 P2 = [2707496766203306:864581029138191:2969715140223272] is generator number 2 P3 = [-13422227300:-49322830557:12167000000] is generator number 3 @@ -1172,12 +1141,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=2`, and gain index 2:: sage: EQ.saturate(2) # points were not 2-saturated - saturating basis...Saturation index bound = 93 - WARNING: saturation at primes p > 2 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 93 + Only p-saturating for p up to given value 2. ... Gained index 2 New regulator = 93.857... - (False, 2, '[ ]') + (True, 2, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [2707496766203306:864581029138191:2969715140223272], [-13422227300:-49322830557:12167000000]] sage: EQ.regulator() @@ -1186,12 +1155,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=3`, and gain index 3:: sage: EQ.saturate(3) # points were not 3-saturated - saturating basis...Saturation index bound = 46 - WARNING: saturation at primes p > 3 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 46 + Only p-saturating for p up to given value 3. ... Gained index 3 New regulator = 10.428... - (False, 3, '[ ]') + (True, 3, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [-14:25:8], [-13422227300:-49322830557:12167000000]] sage: EQ.regulator() @@ -1200,12 +1169,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=5`, and gain index 5:: sage: EQ.saturate(5) # points were not 5-saturated - saturating basis...Saturation index bound = 15 - WARNING: saturation at primes p > 5 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 15 + Only p-saturating for p up to given value 5. ... Gained index 5 New regulator = 0.417... - (False, 5, '[ ]') + (True, 5, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [-14:25:8], [1:-1:1]] sage: EQ.regulator() @@ -1215,7 +1184,8 @@ def saturate(self, max_prime=-1, odd_primes_only=False): the points are now provably saturated at all primes:: sage: EQ.saturate() # points are now saturated - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1229,7 +1199,7 @@ def saturate(self, max_prime=-1, odd_primes_only=False): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=5) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=5) P1 = [1547:-2967:343] is generator number 1 ... Gained index 5, new generators = [ [-2:3:1] [-14:25:8] [1:-1:1] ] @@ -1242,7 +1212,8 @@ def saturate(self, max_prime=-1, odd_primes_only=False): verify that full saturation has been done:: sage: EQ.saturate() - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1255,8 +1226,9 @@ def saturate(self, max_prime=-1, odd_primes_only=False): index of the points in their saturation is at most 3, then proves saturation at 2 and at 3, by reducing the points modulo all primes of good reduction up to 11, respectively 13. + """ - ok, index, unsat = self.__mw.saturate(int(max_prime), odd_primes_only) + ok, index, unsat = self.__mw.saturate(int(max_prime), int(min_prime)) return bool(ok), int(str(index)), unsat def search(self, height_limit=18, verbose=False): @@ -1271,9 +1243,9 @@ def search(self, height_limit=18, verbose=False): .. note:: - On 32-bit machines, this *must* be < 21.48 else + On 32-bit machines, this *must* be < 21.48 (`31\log(2)`) else `\exp(h_{\text{lim}}) > 2^{31}` and overflows. On 64-bit machines, it - must be *at most* 43.668. However, this bound is a logarithmic + must be *at most* 43.668 (`63\log(2)`) . However, this bound is a logarithmic bound and increasing it by just 1 increases the running time by (roughly) `\exp(1.5)=4.5`, so searching up to even 20 takes a very long time. @@ -1320,8 +1292,10 @@ def search(self, height_limit=18, verbose=False): Subgroup of Mordell-Weil group: [[4413270:10381877:27000]] """ height_limit = float(height_limit) - if height_limit >= 21.4: # TODO: docstring says 21.48 (for 32-bit machines; what about 64-bit...?) - raise ValueError("The height limit must be < 21.4.") + int_bits = sys.maxsize.bit_length() + max_height_limit = int_bits * 0.693147 # log(2.0) = 0.693147 approx + if height_limit >= max_height_limit: + raise ValueError("The height limit must be < {} = {}log(2) on a {}-bit machine.".format(max_height_limit, int_bits, int_bits+1)) moduli_option = 0 # Use Stoll's sieving program... see strategies in ratpoints-1.4.c @@ -1352,5 +1326,4 @@ def points(self): [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] """ - L = eval(self.__mw.getbasis().replace(":",",")) - return [[Integer(x), Integer(y), Integer(z)] for (x,y,z) in L] + return self.__mw.getbasis() diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index b82831dc5fd..ce5090c80d5 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -28,6 +28,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.cpython.string cimport char_to_str, str_to_bytes from sage.cpython.string import FS_ENCODING from sage.libs.eclib cimport bigint, Curvedata, mw, two_descent +from sage.rings.all import Integer cdef extern from "wrap.cpp": ### misc functions ### @@ -55,8 +56,8 @@ cdef extern from "wrap.cpp": char* mw_getbasis(mw* m) double mw_regulator(mw* m) int mw_rank(mw* m) - int mw_saturate(mw* m, bigint* index, char** unsat, - long sat_bd, int odd_primes_only) + int mw_saturate(mw* m, long* index, char** unsat, + long sat_bd, long sat_low_bd) void mw_search(mw* m, char* h_lim, int moduli_option, int verb) ### two_descent ### @@ -67,8 +68,7 @@ cdef extern from "wrap.cpp": long two_descent_get_rank(two_descent* t) long two_descent_get_rank_bound(two_descent* t) long two_descent_get_selmer_rank(two_descent* t) - void two_descent_saturate(two_descent* t, long sat_bd) - + void two_descent_saturate(two_descent* t, long sat_bd, long sat_low_bd) cdef object string_sigoff(char* s): sig_off() @@ -445,7 +445,6 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class -1269581104000000 """ sig_on() - from sage.rings.all import Integer return Integer(string_sigoff(Curvedata_getdiscr(self.x))) def conductor(self): @@ -467,7 +466,6 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class 126958110400 """ sig_on() - from sage.rings.all import Integer return Integer(string_sigoff(Curvedata_conductor(self.x))) def isogeny_class(self, verbose=False): @@ -503,6 +501,36 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class ############# _mw ################# +def parse_point_list(s): + r""" + Parse a string representing a list of points. + + INPUT: + + - ``s`` (string) -- string representation of a list of points, for + example '[]', '[[1:2:3]]', or '[[1:2:3],[4:5:6]]'. + + OUTPUT: + + (list) a list of triples of integers, for example [], [[1,2,3]], [[1,2,3],[4,5,6]]. + + EXAMPLES:: + + sage: from sage.libs.eclib.mwrank import parse_point_list + sage: parse_point_list('[]') + [] + sage: parse_point_list('[[1:2:3]]') + [[1, 2, 3]] + sage: parse_point_list('[[1:2:3],[4:5:6]]') + [[1, 2, 3], [4, 5, 6]] + + """ + s = s.replace(":", ",").replace(" ", "") + if s == '[]': + return [] + pts = s[2:-2].split('],[') + return [[Integer(x) for x in pt.split(",")] for pt in pts] + cdef class _mw: """ Cython class wrapping eclib's mw class. @@ -561,72 +589,37 @@ cdef class _mw: sage: EQ.search(1) P1 = [0:1:0] is torsion point, order 1 P1 = [-3:0:1] is generator number 1 - ... - P4 = [12:35:27] = 1*P1 + -1*P2 + -1*P3 (mod torsion) - - The previous command produces the following output:: - - P1 = [0:1:0] is torsion point, order 1 - P1 = [-3:0:1] is generator number 1 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 7) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 7) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 23) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 41) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 17) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 43) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 31) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 37) + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 7) + Checking 3-saturation + Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 - saturating up to 20...Checking 2-saturation + saturating up to 20...Saturation index bound (for points of good reduction) = 4 + Reducing saturation bound from given value 20 to computed index bound 4 + Checking saturation at [ 2 3 ] + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] + Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) Index gain = 2^1 - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 67) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 53) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 73) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 103) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 113) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 47) - done (index = 2). + done, index = 2. Gained index 2, new generators = [ [1:-1:1] [-2:3:1] ] P3 = [-14:25:8] is generator number 3 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 11) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 71) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 101) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 127) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 151) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 139) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 179) - done (index = 1). + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) P4 = [0:2:1] = 2*P1 + 0*P2 + 1*P3 (mod torsion) P4 = [2:13:8] = -3*P1 + 1*P2 + -1*P3 (mod torsion) @@ -687,7 +680,7 @@ cdef class _mw: sig_on() return string_sigoff(mw_getbasis(self.x)) - def process(self, point, sat=0): + def process(self, point, saturation_bound=0): """ Processes the given point, adding it to the mw group. @@ -697,10 +690,12 @@ cdef class _mw: An ``ArithmeticError`` is raised if the point is not on the curve. - - ``sat`` (int, default 0) --saturate at primes up to ``sat``. - No saturation is done if ``sat=0``. (Note that it is more - efficient to add several points at once and then saturate - just once at the end). + - ``saturation_bound`` (int, default 0) --saturate at primes up to ``saturation_bound``. + No saturation is done if ``saturation_bound=0``. If ``saturation_bound=-1`` then + saturation is done at all primes, by computing a bound on + the saturation index. Note that it is more efficient to add + several points at once and then saturate just once at the + end. .. NOTE:: @@ -746,7 +741,7 @@ cdef class _mw: cdef _bigint x,y,z sig_on() x,y,z = _bigint(point[0]), _bigint(point[1]), _bigint(point[2]) - r = mw_process(self.curve, self.x, x.x, y.x, z.x, sat) + r = mw_process(self.curve, self.x, x.x, y.x, z.x, saturation_bound) sig_off() if r != 0: raise ArithmeticError("point (=%s) not on curve." % point) @@ -757,8 +752,8 @@ cdef class _mw: OUTPUT: - (string) String representation of the points in the basis of - the mw group. + (list) list of integer triples giving the projective + coordinates of the points in the basis. EXAMPLES:: @@ -768,13 +763,13 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 """ sig_on() s = string_sigoff(mw_getbasis(self.x)) - return s + return parse_point_list(s) def regulator(self): """ @@ -797,7 +792,7 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 sage: EQ.regulator() @@ -824,39 +819,54 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 """ sig_on() r = mw_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) - def saturate(self, int sat_bd=-1, int odd_primes_only=0): + def saturate(self, int sat_bd=-1, int sat_low_bd=2): """ Saturates the current subgroup of the mw group. INPUT: - - ``sat_bnd`` (int, default -1) -- bound on primes at which to - saturate. If -1 (default), compute a bound for the primes - which may not be saturated, and use that. + - ``sat_bnd`` (int, default -1) -- upper bound on primes at + which to saturate. If -1 (default), compute a bound for the + primes which may not be saturated, and use that. Otherwise, + the bound used is the minumum of the value of ``sat_bnd`` + and the computed bound. - - ``odd_primes_only`` (bool, default ``False``) -- only do - saturation at odd primes. (If the points have been found - via 2-descent they should already be 2-saturated.) + - ``sat_low_bd`` (int, default 2) -- only do saturation at + prime not less than this. For exampe, if the points have + been found via 2-descent they should already be 2-saturated, + and ``sat_low_bd=3`` is appropriate. OUTPUT: (tuple) (success flag, index, list) The success flag will be 1 unless something failed (usually an indication that the points - were not saturated but the precision is not high enough to - divide out successfully). The index is the index of the mw - group before saturation in the mw group after. The list is a - string representation of the primes at which saturation was - not proved or achieved. + were not saturated but eclib was not able to divide out + successfully). The index is the index of the mw group before + saturation in the mw group after. The list is a string + representation of the primes at which saturation was not + proved or achieved. + + .. NOTE:: + + ``eclib`` will compute a bound on the saturation index. If + the computed saturation bound is very large and ``sat_bnd`` is + -1, ``eclib`` may output a warning, but will still attempt to + saturate up to the computed bound. If a positive value of + ``sat_bnd`` is given which is greater than the computed bound, + `p`-saturation will only be carried out for primes up to the + compated bound. Setting ``sat_low_bnd`` to a value greater + than 2 allows for saturation to be done incrementally, or for + exactly one prime `p` by setting both ``sat_bd`` and + ``sat_low_bd`` to `p`. EXAMPLES:: @@ -872,34 +882,23 @@ cdef class _mw: sage: EQ [[-1:1:1]] - If we set the saturation bound at 2, then saturation will fail:: + If we set the saturation bound at 2, then saturation will not + enlarge the basis, but the success flag is still 1 (True) + since we did not ask to check 3-saturation:: sage: EQ = _mw(E) sage: EQ.process([494, -5720, 6859]) # 3 times another point sage: EQ.saturate(sat_bd=2) - Saturation index bound = 10 - WARNING: saturation at primes p > 2 will not be done; - points may be unsaturated at primes between 2 and index bound - Failed to saturate MW basis at primes [ ] - (0, 1, '[ ]') + (1, 1, '[ ]') sage: EQ [[494:-5720:6859]] - The following output is also seen in the preceding example:: - - Saturation index bound = 10 - WARNING: saturation at primes p > 2 will not be done; - points may be unsaturated at primes between 2 and index bound - Failed to saturate MW basis at primes [ ] - - """ - cdef _bigint index + cdef long index cdef char* s cdef int ok sig_on() - index = _bigint() - ok = mw_saturate(self.x, index.x, &s, sat_bd, odd_primes_only) + ok = mw_saturate(self.x, &index, &s, sat_bd, sat_low_bd) unsat = string_sigoff(s) return ok, index, unsat @@ -1094,7 +1093,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def getrankbound(self): @@ -1128,7 +1126,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_rank_bound(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def getselmer(self): @@ -1161,7 +1158,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_selmer_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def ok(self): @@ -1222,10 +1218,21 @@ cdef class _two_descent: """ return two_descent_get_certain(self.x) - def saturate(self, saturation_bound=0): + def saturate(self, saturation_bound=0, lower=3): """ Carries out saturation of the points found by a 2-descent. + INPUT: + + - ``saturation_bound`` (int) -- an upper bound on the primes + `p` at which `p`-saturation will be carried out, or -1, in + which case ``eclib`` will compute an upper bound on the + saturation index. + + - ``lower`` (int, default 3) -- do no `p`-saturation for `p` + less than this. The default is 3 since the points found + during 2-descent will be 2-saturated. + OUTPUT: None. @@ -1257,7 +1264,7 @@ cdef class _two_descent: '[[1:-1:1], [-2:3:1], [-14:25:8]]' """ sig_on() - two_descent_saturate(self.x, saturation_bound) + two_descent_saturate(self.x, saturation_bound, 3) sig_off() def getbasis(self): diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index b50b6061aa2..96263cd0be9 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -140,6 +140,7 @@ cdef class ECModularSymbol: - ``nap`` - (int, default 1000): the number of ap of E to use in determining the normalisation of the modular symbols. + Note that eclib will increase this to 100*sqrt(N) if necessary. EXAMPLES:: diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/eclib/wrap.cpp b/src/sage/libs/eclib/wrap.cpp index 58c18ab67b6..28e6da869b4 100644 --- a/src/sage/libs/eclib/wrap.cpp +++ b/src/sage/libs/eclib/wrap.cpp @@ -178,11 +178,11 @@ int mw_rank(struct mw* m) } /* Returns index and unsat long array, which user must deallocate */ -int mw_saturate(struct mw* m, bigint* index, char** unsat, - long sat_bd, int odd_primes_only) +int mw_saturate(struct mw* m, long* index, char** unsat, + long sat_bd, long sat_low_bd) { vector v; - int s = m->saturate(*index, v, sat_bd, odd_primes_only); + int s = m->saturate(*index, v, sat_bd, sat_low_bd); ostringstream instore; instore << v; *unsat = stringstream_to_char(instore); @@ -236,9 +236,9 @@ long two_descent_get_certain(const two_descent* t) return t->getcertain(); } -void two_descent_saturate(struct two_descent* t, long sat_bd) +void two_descent_saturate(struct two_descent* t, long sat_bd, long sat_low_bd) { - t->saturate(sat_bd); + t->saturate(sat_bd, sat_low_bd); } double two_descent_regulator(struct two_descent* t) From 453c793d1f17096695d7ee2cf53d6e0c9c462338 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:06:15 +0000 Subject: [PATCH 058/706] #31443: simplify/improve saturation method for elliptic curves over Q --- .../elliptic_curves/ell_rational_field.py | 93 ++++++++++--------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index a792afce8dc..c894cbc766b 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -779,7 +779,7 @@ def mwrank_curve(self, verbose=False): sage: E = EllipticCurve('11a1') sage: EE = E.mwrank_curve() sage: EE - y^2+ y = x^3 - x^2 - 10*x - 20 + y^2 + y = x^3 - x^2 - 10 x - 20 sage: type(EE) sage: EE.isogeny_class() @@ -2525,7 +2525,7 @@ def regulator(self, proof=None, precision=53, **kwds): assert reg.parent() is R return reg - def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): + def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): """ Given a list of rational points on E, compute the saturation in E(Q) of the subgroup they generate. @@ -2538,17 +2538,24 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): - ``verbose (bool)`` - (default: ``False``), if ``True``, give verbose output - - ``max_prime (int)`` - (default: 0), saturation is - performed for all primes up to max_prime. If max_prime==0, - perform saturation at *all* primes, i.e., compute the true - saturation. + - ``max_prime`` (int, default -1) -- If `-1` (the default), an + upper bound is computed for the primes at which the subgroup + may not be saturated, and saturation is performed for all + primes up to this bound. Otherwise, the bound used is the + minimum of ``max_prime`` and the computed bound. - - ``odd_primes_only (bool)`` - only do saturation at - odd primes + - ``min_prime (int)`` - (default: 2), only do `p`-saturation + at primes `p` greater than or equal to this. + .. note:: - OUTPUT: + To saturate at a single prime `p`, set ``max_prime`` and + ``min_prime`` both to `p`. One situation where this is + useful is after mapping saturated points from another + elliptic curve by a `p`-isogeny, since the images may not + be `p`-saturated but with be saturated at all other primes. + OUTPUT: - ``saturation (list)`` - points that form a basis for the saturation @@ -2559,12 +2566,32 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): - ``regulator (real with default precision)`` - regulator of saturated points. + ALGORITHM: Uses Cremona's ``eclib`` package, which computes a + bound on the saturation index. To `p`-saturate, or prove + `p`-saturation, we consider the reductions of the points + modulo primes `q` of good reduction such that `E(\FF_q)` has + order divisible by `p`. + + .. note:: + + In versons of ``eclib`` up to ``v20190909``, division of + points in ``eclib`` was done using floating point methods, + without automatic handling of precision, so that + `p`-saturation sometimes failed unless + ``mwrank_set_precision()`` was called in advance with a + suitably high bit precision. Since version ``v20210310`` + of ``eclib``, division is done using exact methods based on + division polynomials, and `p`-saturation cannot fail in + this way. + + .. note:: + + The computed index of saturation may be large, in which + case saturation may take a long time. For example, the + rank 4 curve ``EllipticCurve([0,1,1,-9872,374262])`` has a + saturation index bound of 86682 and takes around 15 minutes + to prove saturation. - ALGORITHM: Uses Cremona's ``mwrank`` package. With ``max_prime=0``, - we call ``mwrank`` with successively larger prime bounds until the full - saturation is provably found. The results of saturation at the - previous primes is stored in each case, so this should be - reasonably fast. EXAMPLES:: @@ -2577,7 +2604,9 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): TESTS: - See :trac:`10590`. This example would loop forever at default precision:: + See :trac:`10590`. With ``eclib`` versions up to + ``v20190909``, this example would loop forever at default + precision. Since version ``v20210310`` it runs fine:: sage: E = EllipticCurve([1, 0, 1, -977842, -372252745]) sage: P = E([-192128125858676194585718821667542660822323528626273/336995568430319276695106602174283479617040716649, 70208213492933395764907328787228427430477177498927549075405076353624188436/195630373799784831667835900062564586429333568841391304129067339731164107, 1]) @@ -2585,7 +2614,7 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): 113.302910926080 sage: E.saturation([P]) ([(-192128125858676194585718821667542660822323528626273/336995568430319276695106602174283479617040716649 : 70208213492933395764907328787228427430477177498927549075405076353624188436/195630373799784831667835900062564586429333568841391304129067339731164107 : 1)], 1, 113.302910926080) - sage: (Q,), ind, reg = E.saturation([2*P]) # needs higher precision, handled by eclib + sage: (Q,), ind, reg = E.saturation([2*P]) sage: 2*Q == 2*P True sage: ind @@ -2634,36 +2663,16 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): c = Emin.mwrank_curve() from sage.libs.eclib.all import mwrank_MordellWeil mw = mwrank_MordellWeil(c, verbose) - mw.process(v) - repeat_until_saturated = False - if max_prime == 0: - repeat_until_saturated = True - max_prime = 9973 - from sage.libs.all import mwrank_get_precision, mwrank_set_precision - prec0 = mwrank_get_precision() - prec = 100 - if prec0 Date: Tue, 16 Mar 2021 14:26:10 +0000 Subject: [PATCH 059/706] #31443: fix two doctest output now that eclib no longer gives incorrect results --- .../elliptic_curves/ell_modular_symbols.py | 14 +++++++++++--- .../elliptic_curves/ell_rational_field.py | 17 ++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a32f64ea47b..30a61e1635c 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -298,19 +298,27 @@ def __init__(self, E, sign, nap=1000): sage: m(0) 1/5 - If ``nap`` is too small, the normalization in eclib may be incorrect. See :trac:`31317`:: + If ``nap`` is too small, the normalization in eclib used to be + incorrect (see :trac:`31317`), but since ``eclib`` version + v20210310 the value of ``nap`` is increased automatically by + ``eclib``:: sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB sage: E = EllipticCurve('1590g1') sage: m = ModularSymbolECLIB(E, sign=+1, nap=300) sage: [m(a/5) for a in [1..4]] - [1001/153, -1001/153, -1001/153, 1001/153] + [13/2, -13/2, -13/2, 13/2] - Those values are incorrect. The correct values are:: + These values are correct, and increasing ``nap`` has no + effect. The correct values may verified by the numerical + implementation:: sage: m = ModularSymbolECLIB(E, sign=+1, nap=400) sage: [m(a/5) for a in [1..4]] [13/2, -13/2, -13/2, 13/2] + sage: m = E.modular_symbol(implementation='num') + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] """ from sage.libs.eclib.newforms import ECModularSymbol diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index c894cbc766b..5a563898e68 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1283,22 +1283,21 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0) sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] - With the default 'eclib' implementation, if ``nap`` is too - small, the normalization may be computed incorrectly. See - :trac:`31317`:: + With older version of eclib, in the default 'eclib' + implementation, if ``nap`` is too small, the normalization may + be computed incorrectly (see :trac:`31317`). This was fixed + in eclib version v20210310, since now eclib increase ``nap`` + automatically. The following used to give incorrect results. + See :trac:`31443`:: sage: E = EllipticCurve('1590g1') sage: m = E.modular_symbol(nap=300) sage: [m(a/5) for a in [1..4]] - [1001/153, -1001/153, -1001/153, 1001/153] + [13/2, -13/2, -13/2, 13/2] - Those values are incorrect. The correct values may be - obtained by increasing ``nap``, as verified by the numerical + These values are correct, as verified by the numerical implementation:: - sage: m = E.modular_symbol(nap=400) - sage: [m(a/5) for a in [1..4]] - [13/2, -13/2, -13/2, 13/2] sage: m = E.modular_symbol(implementation='num') sage: [m(a/5) for a in [1..4]] [13/2, -13/2, -13/2, 13/2] From f4752618381fc589d3b992d67cd29e9a9c7086d5 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 17 Mar 2021 10:14:23 +0000 Subject: [PATCH 060/706] added upstream_url to eclib's checksums.ini --- build/pkgs/eclib/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index b37bff93c05..e639bc3f395 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,3 +2,4 @@ tarball=eclib-20210310.tar.bz2 sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 md5=4ac988bc46869866f076f7bea0fdaa6b cksum=2130748042 +upstream_url=https://github.com/JohnCremona/eclib/archive/VERSION.tar.gz From 1bac7138020240bd3b6944e85bfc28676dcca6f1 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 17 Mar 2021 15:46:48 +0000 Subject: [PATCH 061/706] more changes to eclib build setup --- build/pkgs/eclib/checksums.ini | 10 +++++----- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 28 ++++++++-------------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index e639bc3f395..7f3e3b9233c 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ -tarball=eclib-20210310.tar.bz2 -sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 -md5=4ac988bc46869866f076f7bea0fdaa6b -cksum=2130748042 -upstream_url=https://github.com/JohnCremona/eclib/archive/VERSION.tar.gz +tarball=eclib-VERSION.tar.bz2 +sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 +md5=9da2bba60ec2920b4524b355aff9333a +cksum=1435512890 +upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 3384f6c7325..d52e9bed28f 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210310 +v20210317 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 8cb8ed01ea7..e55566b1e73 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,23 +1,11 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ - dnl header types.h appeared in v20180710 - AC_CHECK_HEADER([eclib/types.h], [ - AC_MSG_CHECKING([whether we can link and run a program using eclib]) - ECLIB_SAVED_LIBS="$LIBS" - LIBS="$LIBS -lec" - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[#include ] - [#include ]], - [[set_bit_precision(42); /* test for versions >= v20190226 */ - show_version(); - return 0;]] - )], [AC_MSG_RESULT([yes; use eclib from the system])], [ - AC_MSG_RESULT([no; install eclib]) - sage_spkg_install_eclib=yes - LIBS="$ECLIB_SAVED_LIBS" - ]) - ], [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) - ]) + dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct + PKG_CHECK_MODULES([ECLIB], + [eclib = v20210317], + [sage_spkg_install_eclib=no], + [sage_spkg_install_eclib=yes]) + AC_PATH_PROG([MWRANK], [mwrank]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ]) ]) From bb3f783b30a02fb09113950d12b7cb73df4311d5 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 17 Mar 2021 21:02:20 +0100 Subject: [PATCH 062/706] Fix a few more tests --- .../nf_galois_groups.rst | 12 ++++++------ .../linalg_doctest.py | 10 +++++----- .../books/judson-abstract-algebra/galois-sage.py | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst index 07e84de55ea..1d789228655 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/nf_galois_groups.rst @@ -305,7 +305,7 @@ ideal classes containing :math:`(5,\sqrt{-30})` and sage: category(C) Category of finite enumerated commutative groups sage: C.gens() - (Fractional ideal class (2, a), Fractional ideal class (3, a)) + (Fractional ideal class (5, a), Fractional ideal class (3, a)) Arithmetic in the class group @@ -322,17 +322,17 @@ means "the product of the 0th and 1st generators of the class group sage: K. = QuadraticField(-30) sage: C = K.class_group() sage: C.0 - Fractional ideal class (2, a) + Fractional ideal class (5, a) sage: C.0.ideal() - Fractional ideal (2, a) + Fractional ideal (5, a) sage: I = C.0 * C.1 sage: I - Fractional ideal class (5, a) + Fractional ideal class (2, a) Next we find that the class of the fractional ideal :math:`(2,\sqrt{-30}+4/3)` is equal to the ideal class -:math:`C.0`. +:math:`C.0*C.1`. .. link @@ -342,7 +342,7 @@ Next we find that the class of the fractional ideal sage: J = C(A) sage: J Fractional ideal class (2/3, 1/3*a) - sage: J == C.0 + sage: J == C.0*C.1 True diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py index d67a33e7c4a..e3c78e66202 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/linalg_doctest.py @@ -233,11 +233,11 @@ ....: [-1,-1,-1,-2,-2,-2,1,1,-1,2,2,2,2,2,-1,2,2,2,2,2]) sage: S,U,V = A.smith_form(); S,U,V ( - [ 0 -2 -1 -5 0] - [1 0 0 0 0] [ 1 0 0 0] [ 1 0 1 -1 -1] - [0 1 0 0 0] [ 0 0 1 0] [ 0 0 0 0 1] - [0 0 3 0 0] [-2 1 0 0] [-1 2 0 5 0] - [0 0 0 6 0], [ 0 0 -2 -1], [ 0 -1 0 -2 0] + [ 3 1 2 -1 0] + [1 0 0 0 0] [ 0 0 1 0] [ 0 0 0 0 1] + [0 1 0 0 0] [ 0 1 0 0] [ 1 1 1 1 -1] + [0 0 3 0 0] [ 1 -2 -4 1] [-3 -2 -3 -1 0] + [0 0 0 6 0], [ 0 0 4 -1], [ 1 0 0 -2 0] ) Sage example in ./linalg.tex, line 1674:: diff --git a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py index ac15dad7a9d..993015df604 100644 --- a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py @@ -414,18 +414,18 @@ To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 Defn: c4 |--> 2*c^2, None), - (Number Field in c5 with defining polynomial x^4 + 648, - Ring morphism: - From: Number Field in c5 with defining polynomial x^4 + 648 - To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 - Defn: c5 |--> 1/80*c^5 + 79/40*c, - None), (Number Field in c6 with defining polynomial x^4 + 8, Ring morphism: From: Number Field in c6 with defining polynomial x^4 + 8 To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 Defn: c6 |--> -1/80*c^5 + 1/40*c, None), + (Number Field in c5 with defining polynomial x^4 + 648, + Ring morphism: + From: Number Field in c5 with defining polynomial x^4 + 648 + To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 + Defn: c5 |--> 1/80*c^5 + 79/40*c, + None), (Number Field in c7 with defining polynomial x^4 - 512, Ring morphism: From: Number Field in c7 with defining polynomial x^4 - 512 From 7db4910ebff9e37ccbdadebf0a4e365e064d0c3a Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 14:01:20 +0000 Subject: [PATCH 063/706] remove dud empty file --- src/sage/libs/eclib/t | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/sage/libs/eclib/t diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t deleted file mode 100644 index e69de29bb2d..00000000000 From 28408fc22d9d9525460e74492f9193e54a20bb15 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 14:05:41 +0000 Subject: [PATCH 064/706] correct upstream_url for eclib --- build/pkgs/eclib/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..80f452946bc 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 md5=9da2bba60ec2920b4524b355aff9333a cksum=1435512890 -upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 +upstream_url=https://johncremona.github.io/ftp/eclib-v20210317.tar.bz2 From 573bcf604b3ab69d19716b47627f3b378b0c081f Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 18 Mar 2021 14:33:31 +0000 Subject: [PATCH 065/706] fix m4 and tar URL, remove junk --- build/pkgs/eclib/checksums.ini | 2 +- build/pkgs/eclib/spkg-configure.m4 | 22 +++++++++++++++------- src/sage/libs/eclib/t | 0 3 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 src/sage/libs/eclib/t diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..94203b25d9d 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 md5=9da2bba60ec2920b4524b355aff9333a cksum=1435512890 -upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 +upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index e55566b1e73..377b66b0711 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,11 +1,19 @@ SAGE_SPKG_CONFIGURE([eclib], [ - SAGE_SPKG_DEPCHECK([ntl pari flint], [ + SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - PKG_CHECK_MODULES([ECLIB], - [eclib = v20210317], - [sage_spkg_install_eclib=no], - [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ + AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ + AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ + mwrank_version=`$ac_path_MWRANK -V 2>&1` + AX_COMPARE_VERSION([$mwrank_version], [eq], [SAGE_ECLIB_VER], [ + ac_cv_path_MWRANK="$ac_path_MWRANK" + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ], [ + sage_spkg_install_eclib=yes]) ]) + m4_popdef([SAGE_ECLIB_VER]) ]) diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t deleted file mode 100644 index e69de29bb2d..00000000000 From d32295ffeab20af1781b903fad1139c50527d94d Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 15:03:17 +0000 Subject: [PATCH 066/706] fix spkg-configure for eclib --- build/pkgs/eclib/spkg-configure.m4 | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index e55566b1e73..377b66b0711 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,11 +1,19 @@ SAGE_SPKG_CONFIGURE([eclib], [ - SAGE_SPKG_DEPCHECK([ntl pari flint], [ + SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - PKG_CHECK_MODULES([ECLIB], - [eclib = v20210317], - [sage_spkg_install_eclib=no], - [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ + AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ + AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ + mwrank_version=`$ac_path_MWRANK -V 2>&1` + AX_COMPARE_VERSION([$mwrank_version], [eq], [SAGE_ECLIB_VER], [ + ac_cv_path_MWRANK="$ac_path_MWRANK" + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ], [ + sage_spkg_install_eclib=yes]) ]) + m4_popdef([SAGE_ECLIB_VER]) ]) From 5a90432220bf69426b4f5c82562891292b07a1b2 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 15:32:43 +0000 Subject: [PATCH 067/706] updated eclib build files to 20210318 --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 80f452946bc..1a8e002b78b 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 -md5=9da2bba60ec2920b4524b355aff9333a -cksum=1435512890 -upstream_url=https://johncremona.github.io/ftp/eclib-v20210317.tar.bz2 +sha1=6954aa9553e61c48ba3f8565ac4ac88b31e761a4 +md5=26f7d78a4ba7263dbf59ef7466bbce34 +cksum=4022275109 +upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index d52e9bed28f..5a5e7e1b211 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -v20210317 +20210318 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 377b66b0711..748e6b8bf3e 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + m4_pushdef([SAGE_ECLIB_VER],["20210318"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From d66a90523f5aaaff7d559d21497872b2eb866178 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 19 Mar 2021 19:25:40 +0100 Subject: [PATCH 068/706] Fix some more tests --- src/doc/en/thematic_tutorials/sandpile.rst | 8 ++++---- src/sage/lfunctions/pari.py | 2 +- src/sage/rings/finite_rings/integer_mod_ring.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst index 75ac904ec58..487d2f4d29a 100644 --- a/src/doc/en/thematic_tutorials/sandpile.rst +++ b/src/doc/en/thematic_tutorials/sandpile.rst @@ -324,9 +324,9 @@ sink. [1, 1, 3] sage: S.reduced_laplacian().dense_matrix().smith_form() ( - [1 0 0] [ 0 0 1] [3 1 4] - [0 1 0] [ 1 0 0] [4 1 6] - [0 0 3], [ 0 1 -1], [4 1 5] + [1 0 0] [ 1 0 0] [1 3 5] + [0 1 0] [ 0 1 0] [1 4 6] + [0 0 3], [ 0 -1 1], [1 4 7] ) Adding the identity to any recurrent configuration and stabilizing yields @@ -687,7 +687,7 @@ Approximation to the zero set (setting ``x_0 = 1``):: The zeros are generated as a group by a single vector:: sage: S.points() - [[(1/2*I + 1/2)*sqrt(2), -(1/2*I + 1/2)*sqrt(2)]] + [[-(1/2*I + 1/2)*sqrt(2), (1/2*I + 1/2)*sqrt(2)]] Resolutions diff --git a/src/sage/lfunctions/pari.py b/src/sage/lfunctions/pari.py index f810157b3e4..f621b57c67c 100644 --- a/src/sage/lfunctions/pari.py +++ b/src/sage/lfunctions/pari.py @@ -421,7 +421,7 @@ class LFunction(SageObject): sage: L.derivative(1,E.rank()) 1.51863300057685 sage: L.taylor_series(1,4) - -3...e-19 + (...e-19)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) + ...e-19 + (...e-19)*z + 0.759316500288427*z^2 - 0.430302337583362*z^3 + O(z^4) .. RUBRIC:: Number field diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 7b4f8b1b4b5..8380acc6533 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -626,7 +626,7 @@ def multiplicative_subgroups(self): sage: Integers(5).multiplicative_subgroups() ((2,), (4,), ()) sage: Integers(15).multiplicative_subgroups() - ((14, 13), (4, 11), (8,), (11,), (14,), (7,), (4,), ()) + ((11, 7), (11, 4), (2,), (11,), (14,), (7,), (4,), ()) sage: Integers(2).multiplicative_subgroups() ((),) sage: len(Integers(341).multiplicative_subgroups()) From 55afe2e7590b822b2d901a7fe08040d4aa9196fb Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 19 Mar 2021 19:27:38 +0100 Subject: [PATCH 069/706] Fix timeout in sage/tests/parigp.py --- src/sage/tests/parigp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/tests/parigp.py b/src/sage/tests/parigp.py index c118b6eb374..4692b613de4 100644 --- a/src/sage/tests/parigp.py +++ b/src/sage/tests/parigp.py @@ -6,9 +6,7 @@ self-test :pari:`rnfkummer` but was modified such that the answer is canonical:: - sage: pari('addprimes([31438243, 238576291, 18775387483, 24217212463267, 1427657500359111961, 135564809928627323997297867381959])') - [31438243, 238576291, 18775387483, 24217212463267, 1427657500359111961, 135564809928627323997297867381959] - sage: pari('K = bnfinit(y^4-52*y^2+26,1); pol = rnfkummer(bnrinit(K,3,1),Mat(5)); L = rnfinit(K, pol); polredabs(polredbest(L.polabs))') # long time + sage: pari('K = bnfinit(y^4-52*y^2+26,1); pol = rnfkummer(bnrinit(K,3,1),Mat(5)); L = rnfinit(K, [pol, 10^6]); polredabs(polredbest(L.polabs))') # long time x^20 - 112*x^18 + 5108*x^16 - 123460*x^14 + 1724337*x^12 - 14266996*x^10 + 69192270*x^8 - 188583712*x^6 + 260329852*x^4 - 141461008*x^2 + 19860776 Check that :trac:`10195` (PARI bug 1153) has been fixed:: From a476ca3f24fea5afaef64a1f6225719050b79d76 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 19 Mar 2021 21:03:19 +0100 Subject: [PATCH 070/706] Fix some tests in sage.schemes.toric --- src/sage/geometry/polyhedron/ppl_lattice_polytope.py | 2 +- src/sage/schemes/toric/divisor_class.pyx | 6 +++--- src/sage/schemes/toric/homset.py | 4 ++-- src/sage/schemes/toric/morphism.py | 6 +++--- src/sage/schemes/toric/points.py | 2 +- src/sage/schemes/toric/variety.py | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py index 65d261d4283..583101ad7ec 100644 --- a/src/sage/geometry/polyhedron/ppl_lattice_polytope.py +++ b/src/sage/geometry/polyhedron/ppl_lattice_polytope.py @@ -863,7 +863,7 @@ def base_projection_matrix(self, fiber): sage: proj = poly.base_projection(fiber) sage: proj_matrix = poly.base_projection_matrix(fiber) sage: [ proj(p) for p in poly.integral_points() ] - [(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (1, 0), (0, 1)] + [(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)] sage: [ proj_matrix*p for p in poly.integral_points() ] [(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)] """ diff --git a/src/sage/schemes/toric/divisor_class.pyx b/src/sage/schemes/toric/divisor_class.pyx index f0e6eeca7ef..52874594a51 100644 --- a/src/sage/schemes/toric/divisor_class.pyx +++ b/src/sage/schemes/toric/divisor_class.pyx @@ -39,9 +39,9 @@ The only special method is :meth:`~ToricRationalDivisorClass.lift` to get a divisor representing a divisor class:: sage: D.lift() - V(x) - 2*V(u) + 3*V(y) - 4*V(v) + -3*V(x) - 9*V(u) + 7*V(z) + 3*V(w) sage: E.lift() - 1/2*V(x) - 2/3*V(u) + 3/4*V(y) - 4/5*V(v) + -3/10*V(x) - 133/60*V(u) + 31/20*V(z) + 3/4*V(w) """ @@ -279,7 +279,7 @@ cdef class ToricRationalDivisorClass(Vector_rational_dense): sage: D.divisor_class() Divisor class [29, 6, 8, 10, 0] sage: Dequiv = D.divisor_class().lift(); Dequiv - 6*V(z1) - 17*V(z2) - 22*V(z3) - 7*V(z4) + 25*V(z6) + 32*V(z7) + 15*V(z1) - 11*V(z2) - 9*V(z5) + 19*V(z6) + 10*V(z7) sage: Dequiv == D False sage: Dequiv.divisor_class() == D.divisor_class() diff --git a/src/sage/schemes/toric/homset.py b/src/sage/schemes/toric/homset.py index 4bff92bcb01..f13482bae0c 100644 --- a/src/sage/schemes/toric/homset.py +++ b/src/sage/schemes/toric/homset.py @@ -641,7 +641,7 @@ def __iter__(self): sage: P2. = toric_varieties.P2(base_ring=GF(5)) sage: cubic = P2.subscheme([x^3 + y^3 + z^3]) sage: list(cubic.point_set()) - [[0 : 1 : 4], [1 : 0 : 4], [1 : 4 : 0], [1 : 2 : 1], [1 : 1 : 2], [1 : 3 : 3]] + [[0 : 1 : 4], [1 : 0 : 4], [1 : 4 : 0], [1 : 1 : 2], [1 : 2 : 1], [1 : 3 : 3]] sage: cubic.point_set().cardinality() 6 """ @@ -661,7 +661,7 @@ def cardinality(self): sage: P2. = toric_varieties.P2(base_ring=GF(5)) sage: cubic = P2.subscheme([x^3 + y^3 + z^3]) sage: list(cubic.point_set()) - [[0 : 1 : 4], [1 : 0 : 4], [1 : 4 : 0], [1 : 2 : 1], [1 : 1 : 2], [1 : 3 : 3]] + [[0 : 1 : 4], [1 : 0 : 4], [1 : 4 : 0], [1 : 1 : 2], [1 : 2 : 1], [1 : 3 : 3]] sage: cubic.point_set().cardinality() 6 """ diff --git a/src/sage/schemes/toric/morphism.py b/src/sage/schemes/toric/morphism.py index 94be447f9ce..6a001ee74a2 100644 --- a/src/sage/schemes/toric/morphism.py +++ b/src/sage/schemes/toric/morphism.py @@ -1692,7 +1692,7 @@ class SchemeMorphism_fan_fiber_component_toric_variety(SchemeMorphism): From: 2-d toric variety covered by 4 affine patches To: 4-d toric variety covered by 23 affine patches Defn: Defined on coordinates by sending [z0 : z1 : z2 : z3] to - [1 : 1 : 1 : 1 : z3 : 0 : 1 : z2 : 1 : 1 : 1 : z1 : z0 : 1 : 1] + [1 : 1 : 1 : 1 : z2 : 0 : 1 : z3 : 1 : 1 : 1 : z1 : z0 : 1 : 1] sage: type(fiber_component.embedding_morphism()) """ @@ -1775,7 +1775,7 @@ def as_polynomial_map(self): From: 2-d toric variety covered by 4 affine patches To: 4-d toric variety covered by 23 affine patches Defn: Defined on coordinates by sending [z0 : z1 : z2 : z3] to - [1 : 1 : 1 : 1 : z3 : 0 : 1 : z2 : 1 : 1 : 1 : z1 : z0 : 1 : 1] + [1 : 1 : 1 : 1 : z2 : 0 : 1 : z3 : 1 : 1 : 1 : z1 : z0 : 1 : 1] sage: primitive_cone = Cone([(-1, 2, -1, 0)]) sage: f = fibration.fiber_component(primitive_cone).embedding_morphism() @@ -1997,7 +1997,7 @@ def pullback_divisor(self, divisor): V(z0) + V(z1) + 3*V(z2) + 4*V(z3) sage: fc = f.fiber_component(Cone([(1,1,0)])) sage: fc.embedding_morphism().pullback_divisor(D) - 2*V(z0) + 3*V(z1) + 4*V(z0) + V(z1) + 4*V(z2) sage: fc = f.fiber_component(Cone([(1,0,0)])) sage: fc.embedding_morphism().pullback_divisor(D) -V(z0) - 3*V(z1) - 3*V(z2) diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py index 31e7769ede7..2ea3067e8ab 100644 --- a/src/sage/schemes/toric/points.py +++ b/src/sage/schemes/toric/points.py @@ -986,7 +986,7 @@ def __iter__(self): sage: point_set = X.point_set() sage: ffe = point_set._enumerator() sage: list(ffe) # indirect doctest - [(1, 4, 3), (1, 1, 6), (1, 2, 5)] + [(1, 1, 6), (1, 2, 5), (1, 4, 3)] """ for cone, nonzero_coordinates, cokernel in self.ambient.cone_points_iter(): R = PolynomialRing(self.ambient.ring, cokernel.ngens(), 't') diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 1bcfd27f828..56504880459 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -1498,7 +1498,7 @@ def Kaehler_cone(self): in Basis lattice of The toric rational divisor class group of a 2-d CPR-Fano toric variety covered by 4 affine patches sage: [ divisor_class.lift() for divisor_class in Kc.rays() ] - [V(x), V(s)] + [V(y), V(t)] sage: Kc.lattice() Basis lattice of The toric rational divisor class group of a 2-d CPR-Fano toric variety covered by 4 affine patches @@ -1654,7 +1654,7 @@ def Chow_group(self, base_ring=ZZ): sage: A = toric_varieties.P2().Chow_group(); A Chow group of 2-d CPR-Fano toric variety covered by 3 affine patches sage: A.gens() - (( 1 | 0 | 0 ), ( 0 | 1 | 0 ), ( 0 | 0 | 1 )) + (( 0 | 0 | 1 ), ( 0 | 1 | 0 ), ( 1 | 0 | 0 )) """ from sage.schemes.toric.chow_group import ChowGroup return ChowGroup(self,base_ring) From 614463165bc3d28e099537dd7cf7ab0af31bf3e7 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 20 Mar 2021 13:37:36 +0100 Subject: [PATCH 071/706] Fix doctests in sage.modular for new basis choices --- src/sage/modular/etaproducts.py | 20 ++++++++++---------- src/sage/modular/modform/find_generators.py | 3 +-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index d3f05ca8332..dbb8c24bd70 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -146,7 +146,7 @@ def _mul_(self, other): sage: eta1, eta2 = EtaGroup(4).basis() # indirect doctest sage: eta1 * eta2 - Eta product of level 4 : (eta_1)^8 (eta_4)^-8 + Eta product of level 4 : (eta_1)^24 (eta_2)^-48 (eta_4)^24 """ newdict = {d: self._rdict.get(d, 0) + other._rdict.get(d, 0) for d in union(self._rdict, other._rdict)} @@ -161,7 +161,7 @@ def _div_(self, other): sage: eta1, eta2 = EtaGroup(4).basis() sage: eta1 / eta2 # indirect doctest - Eta product of level 4 : (eta_1)^-24 (eta_2)^48 (eta_4)^-24 + Eta product of level 4 : (eta_1)^-8 (eta_4)^8 sage: (eta1 / eta2) * eta2 == eta1 True """ @@ -509,16 +509,16 @@ def basis(self, reduce=True): sage: EtaGroup(5).basis() [Eta product of level 5 : (eta_1)^6 (eta_5)^-6] sage: EtaGroup(12).basis() - [Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3, + [Eta product of level 12 : (eta_1)^-3 (eta_2)^2 (eta_3)^1 (eta_4)^-1 (eta_6)^-2 (eta_12)^3, Eta product of level 12 : (eta_1)^-4 (eta_2)^2 (eta_3)^4 (eta_6)^-2, + Eta product of level 12 : (eta_1)^6 (eta_2)^-9 (eta_3)^-2 (eta_4)^3 (eta_6)^3 (eta_12)^-1, Eta product of level 12 : (eta_1)^-1 (eta_2)^3 (eta_3)^3 (eta_4)^-2 (eta_6)^-9 (eta_12)^6, - Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2, - Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1] + Eta product of level 12 : (eta_1)^3 (eta_3)^-1 (eta_4)^-3 (eta_12)^1] sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients - [Eta product of level 12 : (eta_2)^24 (eta_12)^-24, - Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144, - Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8, - Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4, + [Eta product of level 12 : (eta_1)^384 (eta_2)^-576 (eta_3)^-696 (eta_4)^216 (eta_6)^576 (eta_12)^96, + Eta product of level 12 : (eta_2)^24 (eta_12)^-24, + Eta product of level 12 : (eta_1)^-40 (eta_2)^116 (eta_3)^96 (eta_4)^-30 (eta_6)^-80 (eta_12)^-62, + Eta product of level 12 : (eta_1)^-4 (eta_2)^-33 (eta_3)^-4 (eta_4)^1 (eta_6)^3 (eta_12)^37, Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5] ALGORITHM: An eta product of level `N` is uniquely @@ -1030,7 +1030,7 @@ def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose): sage: from sage.modular.etaproducts import _eta_relations_helper sage: r,s = EtaGroup(4).basis() sage: _eta_relations_helper(r,s,4,100,['a','b'],False) - [a*b - a + 16] + [a + 1/16*b - 1/16] sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish [1] """ diff --git a/src/sage/modular/modform/find_generators.py b/src/sage/modular/modform/find_generators.py index 18055ef00f8..40f4f7c8580 100644 --- a/src/sage/modular/modform/find_generators.py +++ b/src/sage/modular/modform/find_generators.py @@ -400,8 +400,7 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): sage: ModularFormsRing(Gamma0(13)).generators(maxweight=12, prec=4) [(2, 1 + 2*q + 6*q^2 + 8*q^3 + O(q^4)), (4, 1 + O(q^4)), (4, q + O(q^4)), (4, q^2 + O(q^4)), (4, q^3 + O(q^4)), (6, 1 + O(q^4)), (6, q + O(q^4))] sage: ModularFormsRing(Gamma0(13),base_ring=ZZ).generators(maxweight=12, prec=4) - [(2, 1 + 2*q + 6*q^2 + 8*q^3 + O(q^4)), (4, O(q^4)), (4, q^3 + O(q^4)), (4, q^2 + O(q^4)), (4, q + O(q^4)), (6, O(q^4)), (6, O(q^4)), (12, O(q^4))] - + [(2, 1 + 2*q + 6*q^2 + 8*q^3 + O(q^4)), (4, q + 4*q^2 + 10*q^3 + O(q^4)), (4, 2*q^2 + 5*q^3 + O(q^4)), (4, q^2 + O(q^4)), (4, -2*q^3 + O(q^4)), (6, O(q^4)), (6, O(q^4)), (12, O(q^4))] sage: [k for k,f in ModularFormsRing(1, QQ).generators(maxweight=12)] [4, 6] sage: [k for k,f in ModularFormsRing(1, ZZ).generators(maxweight=12)] From 273b2b2868c0cd7163b6676d2b769c582bbd3f98 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 20 Mar 2021 13:54:47 +0100 Subject: [PATCH 072/706] Fix some tests in sage/schemes/elliptic_curves --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 +- src/sage/schemes/elliptic_curves/ell_torsion.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 9c0195bdcbb..e477c93c147 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -789,7 +789,7 @@ def gens(self): sage: E.gens()[0].order() 433680868994201773603067114808243933797236 sage: E.gens()[1].order() - 433680868994201773603067114808243933797236 + 30977204928157269543076222486303138128374 """ G = self.__pari__().ellgroup(flag=1) return tuple(self.point(list(pt)) for pt in G[2]) diff --git a/src/sage/schemes/elliptic_curves/ell_torsion.py b/src/sage/schemes/elliptic_curves/ell_torsion.py index 7f6f0f9701b..33265f11d7d 100644 --- a/src/sage/schemes/elliptic_curves/ell_torsion.py +++ b/src/sage/schemes/elliptic_curves/ell_torsion.py @@ -84,7 +84,7 @@ class EllipticCurveTorsionSubgroup(groups.AdditiveAbelianGroupWrapper): sage: E = EllipticCurve([0,0,0,-49,0]) sage: T = E.torsion_subgroup() sage: [E(t) for t in T] - [(0 : 1 : 0), (-7 : 0 : 1), (0 : 0 : 1), (7 : 0 : 1)] + [(0 : 1 : 0), (0 : 0 : 1), (-7 : 0 : 1), (7 : 0 : 1)] An example where the torsion subgroup is trivial:: @@ -256,7 +256,7 @@ def points(self): sage: E = EllipticCurve(K,[0,0,0,1,0]) sage: tor = E.torsion_subgroup() sage: tor.points() - [(0 : 1 : 0), (-i : 0 : 1), (0 : 0 : 1), (i : 0 : 1)] + [(0 : 1 : 0), (0 : 0 : 1), (-i : 0 : 1), (i : 0 : 1)] """ return [x.element() for x in self] From e3be5e5d1c47658dcd41a7e3585a9b4d092b3acf Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 20 Mar 2021 19:49:28 +0100 Subject: [PATCH 073/706] Update tests for new precision() function --- src/sage/libs/pari/__init__.py | 6 +++--- src/sage/libs/pari/tests.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/libs/pari/__init__.py b/src/sage/libs/pari/__init__.py index 77eda66097d..e9112b2e3e1 100644 --- a/src/sage/libs/pari/__init__.py +++ b/src/sage/libs/pari/__init__.py @@ -161,12 +161,12 @@ call individually:: sage: e = pari([0,0,0,-82,0]).ellinit() - sage: eta1 = e.elleta(precision=100)[0] + sage: eta1 = e.elleta(precision=50)[0] sage: eta1.sage() 3.6054636014326520859158205642077267748 - sage: eta1 = e.elleta(precision=180)[0] + sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() - 3.60546360143265208591582056420772677481026899659802474544 + 3.605463601432652085915820564207726774810268996598024745444380641429820491740 """ diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index cdc5113ae03..c6e7ee7fbd9 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -1760,12 +1760,12 @@ library:: sage: e = pari([0,0,0,-82,0]).ellinit() - sage: eta1 = e.elleta(precision=100)[0] + sage: eta1 = e.elleta(precision=50)[0] sage: eta1.sage() 3.6054636014326520859158205642077267748 - sage: eta1 = e.elleta(precision=180)[0] + sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() - 3.60546360143265208591582056420772677481026899659802474544 + 3.605463601432652085915820564207726774810268996598024745444380641429820491740 sage: from cypari2 import Pari sage: pari = Pari() From c3f2aad8fe17d92629994a395e9982b95aaff1e9 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 20 Mar 2021 19:50:32 +0100 Subject: [PATCH 074/706] Update tests for updated quadhilbert() output in pari --- src/sage/libs/pari/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index c6e7ee7fbd9..2aa382269a4 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -1737,7 +1737,7 @@ sage: pari(-23).quadhilbert() x^3 - x^2 + 1 sage: pari(145).quadhilbert() - x^4 - 6*x^2 - 5*x - 1 + x^4 - x^3 - 5*x^2 - x + 1 sage: pari(-12).quadhilbert() # Not fundamental Traceback (most recent call last): ... From 5d9e70a118b4cce4d2b526129a880ca7f4822485 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Mon, 22 Mar 2021 08:41:23 +0000 Subject: [PATCH 075/706] #31443: updated upstream tarball URL --- build/pkgs/eclib/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 1a8e002b78b..824f44f53cd 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=6954aa9553e61c48ba3f8565ac4ac88b31e761a4 md5=26f7d78a4ba7263dbf59ef7466bbce34 cksum=4022275109 -upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 +upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 \ No newline at end of file From 7781f3af64d2b509f4a7ebaaba927fb939ac31b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 24 Mar 2021 18:50:20 +0100 Subject: [PATCH 076/706] fix some NOTE and EXAMPLES --- src/sage/categories/sets_cat.py | 3 +-- src/sage/combinat/designs/covering_design.py | 16 +++++------ src/sage/combinat/sloane_functions.py | 2 +- src/sage/functions/prime_pi.pyx | 14 +++++----- .../geometry/polyhedron/representation.py | 26 +++++++++--------- .../graphs/graph_decompositions/rankwidth.pyx | 8 +++--- src/sage/matrix/matrix2.pyx | 27 ++++++++++--------- .../number_field/number_field_element.pyx | 11 ++++---- src/sage/rings/number_field/totallyreal.pyx | 7 ++--- .../rings/number_field/totallyreal_phc.py | 17 ++++++------ .../rings/padics/local_generic_element.pyx | 8 ++++-- src/sage/rings/padics/padic_generic.py | 13 ++++----- .../rings/padics/padic_generic_element.pyx | 10 +++---- src/sage/schemes/elliptic_curves/ell_point.py | 27 ++++++++++--------- .../elliptic_curves/ell_rational_field.py | 24 ++++++++--------- .../schemes/elliptic_curves/lseries_ell.py | 2 +- 16 files changed, 111 insertions(+), 104 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..0efd3bf94dc 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1458,7 +1458,7 @@ def _test_construction(self, **options): and if it returns the latter, then it is supposed that ``F(O)==self`. The test verifies this assumption. - EXAMPLE: + EXAMPLES: We create a parent that returns a wrong construction (its construction returns the rational field rather than the parent itself):: @@ -2601,7 +2601,6 @@ def example(self, base_ring = None, set = None): from sage.categories.examples.with_realizations import SubsetAlgebra return SubsetAlgebra(base_ring, set) - class ParentMethods: def _test_with_realizations(self, **options): diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index f6b69cc8180..8592a005ca6 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -128,18 +128,18 @@ def trivial_covering_design(v, k, t): 1 3 4 2 3 4 - NOTES: + .. NOTE:: - Cases are: + Cases are: - * `t=0`: This could be empty, but it's a useful convention to have - one block (which is empty if $k=0$). + * `t=0`: This could be empty, but it's a useful convention to have + one block (which is empty if $k=0$). - * `t=1` : This contains `\lceil v/k \rceil` blocks: - `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around if - `k` does not divide `v`. + * `t=1` : This contains `\lceil v/k \rceil` blocks: + `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around + if `k` does not divide `v`. - * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. + * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. """ if t == 0: # single block [0, ..., k-1] diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index ada6bf23094..bdeb3be26c1 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -84,7 +84,7 @@ # 1. Add a new class to Section II below, which you should # do by copying an existing class and modifying it. # Make sure to at least define _eval and _repr_. -# NOTES: (a) define the _eval method only, which you may +# NOTE: (a) define the _eval method only, which you may # assume has as input a *positive* Sage integer (offset > 0). # Each sequence in the OEIS has an offset >= 0, indicating the # value of the first index. The default offset = 1. diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index 60ddf604af1..7d70e1edcb1 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -1,4 +1,4 @@ -""" +r""" Counting Primes AUTHORS: @@ -121,10 +121,10 @@ cdef class PrimePi(BuiltinFunction): sage: P = plot(prime_pi, 50, 100) - NOTES: + .. NOTE:: - Uses a recursive implementation, using the optimizations described in - [Oha2011]_. + This uses a recursive implementation, using the optimizations + described in [Oha2011]_. AUTHOR: @@ -504,10 +504,10 @@ cpdef Integer legendre_phi(x, a): sage: legendre_phi(4215701455, 6450023226) 1 - NOTES: + .. NOTE:: - Uses a recursive implementation, using the optimizations described in - [Oha2011]_. + This uses a recursive implementation, using the optimizations + described in [Oha2011]_. AUTHOR: diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 3c73a959ac7..eb752542378 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -280,13 +280,13 @@ def index(self): Return an arbitrary but fixed number according to the internal storage order. - NOTES: + .. NOTE:: - H-representation and V-representation objects are enumerated - independently. That is, amongst all vertices/rays/lines there - will be one with ``index()==0``, and amongst all - inequalities/equations there will be one with ``index()==0``, - unless the polyhedron is empty or spans the whole space. + H-representation and V-representation objects are enumerated + independently. That is, amongst all vertices/rays/lines there + will be one with ``index()==0``, and amongst all + inequalities/equations there will be one with ``index()==0``, + unless the polyhedron is empty or spans the whole space. EXAMPLES:: @@ -598,16 +598,16 @@ def __mul__(self, Vobj): def eval(self, Vobj): r""" - Evaluates the left hand side `A\vec{x}+b` on the given + Evaluate the left hand side `A\vec{x}+b` on the given vertex/ray/line. - NOTES: + .. NOTE:: - * Evaluating on a vertex returns `A\vec{x}+b` - * Evaluating on a ray returns `A\vec{r}`. Only the sign or - whether it is zero is meaningful. - * Evaluating on a line returns `A\vec{l}`. Only whether it - is zero or not is meaningful. + * Evaluating on a vertex returns `A\vec{x}+b` + * Evaluating on a ray returns `A\vec{r}`. Only the sign or + whether it is zero is meaningful. + * Evaluating on a line returns `A\vec{l}`. Only whether it + is zero or not is meaningful. EXAMPLES:: diff --git a/src/sage/graphs/graph_decompositions/rankwidth.pyx b/src/sage/graphs/graph_decompositions/rankwidth.pyx index 093d0121ac8..1a907e4bebe 100644 --- a/src/sage/graphs/graph_decompositions/rankwidth.pyx +++ b/src/sage/graphs/graph_decompositions/rankwidth.pyx @@ -30,7 +30,7 @@ achieving the minimal *rank-width*. **RW -- The original source code :** RW is a program that calculates rank-width and -rank-decompositions. It is based on ideas from : +rank-decompositions. It is based on ideas from: * "Computing rank-width exactly" by Sang-il Oum [Oum2009]_ * "Sopra una formula numerica" by Ernesto Pascal @@ -94,15 +94,15 @@ Methods ------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Nathann Cohen # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport * diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 885fe8572e3..5497985083c 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10665,19 +10665,19 @@ cdef class Matrix(Matrix1): correctness. Set this to ``False`` for a speedup if the eigenvalues are known to be correct. - NOTES: + .. NOTE:: - Currently, the Jordan normal form is not computed over inexact rings - in any but the trivial cases when the matrix is either `0 \times 0` - or `1 \times 1`. + Currently, the Jordan normal form is not computed over + inexact rings in any but the trivial cases when the matrix + is either `0 \times 0` or `1 \times 1`. - In the case of exact rings, this method does not compute any - generalized form of the Jordan normal form, but is only able to - compute the result if the characteristic polynomial of the matrix - splits over the specific base ring. + In the case of exact rings, this method does not compute any + generalized form of the Jordan normal form, but is only able to + compute the result if the characteristic polynomial of the matrix + splits over the specific base ring. - Note that the base ring must be a field or a ring with an implemented - fraction field. + Note that the base ring must be a field or a ring with an + implemented fraction field. EXAMPLES:: @@ -17589,16 +17589,17 @@ def _binomial(Py_ssize_t n, Py_ssize_t k): i, n, k = i + 1, n - 1, k - 1 return result + def _jordan_form_vector_in_difference(V, W): r""" Given two lists of vectors ``V`` and ``W`` over the same base field, returns a vector in the difference ``V - W``. If the difference is empty, returns ``None``. - NOTES: + .. NOTE:: - This is meant to be a private helper method for the ``jordan_form`` method - in the above class. + This is meant to be a private helper method for the + ``jordan_form`` method in the above class. TESTS:: diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 8167c524522..7462f0d986b 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -2494,9 +2494,9 @@ cdef class NumberFieldElement(FieldElement): x._reduce_c_() return x - #NOTES: In LiDIA, they build a multiplication table for the - #number field, so it's not necessary to reduce modulo the - #defining polynomial every time: + # NOTE: In LiDIA, they build a multiplication table for the + # number field, so it's not necessary to reduce modulo the + # defining polynomial every time: # src/number_fields/algebraic_num/order.cc: compute_table # but asymptotically fast poly multiplication means it's # actually faster to *not* build a table!?! @@ -4488,7 +4488,7 @@ cdef class NumberFieldElement(FieldElement): This means the different with respect to the base field `\QQ`. - EXAMPLE:: + EXAMPLES:: sage: K. = NumberFieldTower([x^2 - 17, x^3 - 2]) sage: a.absolute_different() @@ -4500,6 +4500,7 @@ cdef class NumberFieldElement(FieldElement): """ return self.different(K=QQ) + cdef class NumberFieldElement_absolute(NumberFieldElement): def _magma_init_(self, magma): @@ -5520,5 +5521,3 @@ cdef void _ntl_poly(f, ZZX_c *num, ZZ_c *den): for i from 0 <= i <= __num.degree(): mpz_to_ZZ(&coeff, (ZZ(__num[i])).value) ZZX_SetCoeff( num[0], i, coeff ) - - diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 4ac05950d71..0512f0559fc 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -1,4 +1,4 @@ -""" +r""" Enumeration of Primitive Totally Real Fields This module contains functions for enumerating all primitive @@ -138,8 +138,9 @@ cpdef double odlyzko_bound_totallyreal(int n): - John Voight (2007-09-03) - NOTES: - The values are calculated by Martinet [Mar1980]_. + .. NOTE:: + + The values are calculated by Martinet [Mar1980]_. """ if n <= 10: diff --git a/src/sage/rings/number_field/totallyreal_phc.py b/src/sage/rings/number_field/totallyreal_phc.py index 6dbc0b60576..59467e5af40 100644 --- a/src/sage/rings/number_field/totallyreal_phc.py +++ b/src/sage/rings/number_field/totallyreal_phc.py @@ -7,15 +7,15 @@ * Zeroth attempt. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein and John Voight # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import os import sage.misc.misc @@ -36,9 +36,9 @@ def coefficients_to_power_sums(n, m, a): list of integers. - NOTES: + .. NOTE:: - Uses Newton's relations, which are classical. + This uses Newton's relations, which are classical. AUTHORS: @@ -62,6 +62,7 @@ def __lagrange_bounds_phc(n, m, a, tmpfile=None): r""" This function determines the bounds on the roots in the enumeration of totally real fields via Lagrange multipliers. + It is used internally by the main function enumerate_totallyreal_fields_prim(), which should be consulted for further information. @@ -75,10 +76,10 @@ def __lagrange_bounds_phc(n, m, a, tmpfile=None): the lower and upper bounds as real numbers. - NOTES: + .. NOTE:: - See Cohen [Coh2000]_ for the general idea and unpublished work of the - author for more detail. + See Cohen [Coh2000]_ for the general idea and unpublished work of the + author for more detail. AUTHORS: diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 71dfbc923c1..9cba6678cc4 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -612,9 +612,13 @@ cdef class LocalGenericElement(CommutativeRingElement): - boolean -- whether ``self`` is a unit - NOTES: + .. NOTE:: - For fields all nonzero elements are units. For DVR's, only those elements of valuation 0 are. An older implementation ignored the case of fields, and returned always the negation of self.valuation()==0. This behavior is now supported with self.is_padic_unit(). + For fields all nonzero elements are units. For DVR's, only + those elements of valuation 0 are. An older implementation + ignored the case of fields, and returned always the + negation of self.valuation()==0. This behavior is now + supported with self.is_padic_unit(). EXAMPLES:: diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 650b5137bf5..a5facdeb950 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -12,7 +12,7 @@ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # Julian Rueth @@ -21,8 +21,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.misc import some_tuples from copy import copy @@ -606,13 +606,14 @@ def teichmuller_system(self): sage: F.teichmuller_system()[3] (2*a + 2) + (4*a + 1)*5 + 4*5^2 + (2*a + 1)*5^3 + (4*a + 1)*5^4 + (2*a + 3)*5^5 + O(5^6) - NOTES: + .. NOTE:: - Should this return 0 as well? + Should this return 0 as well? """ R = self.residue_class_field() prec = self.precision_cap() - return [self.teichmuller(self(i).lift_to_precision(prec)) for i in R if i != 0] + return [self.teichmuller(self(i).lift_to_precision(prec)) + for i in R if i != 0] # def different(self): # raise NotImplementedError diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index deede842333..4cd227ec49f 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2552,14 +2552,14 @@ cdef class pAdicGenericElement(LocalGenericElement): By default, we use the binary splitting if it is available. Otherwise we switch to the generic algorithm. - NOTES: + .. NOTE:: - What some other systems do: + What some other systems do: - - PARI: Seems to define the logarithm for units not congruent - to 1 as we do. + - PARI: Seems to define the logarithm for units not congruent + to 1 as we do. - - MAGMA: Only implements logarithm for 1-units (as of version 2.19-2) + - MAGMA: Only implements logarithm for 1-units (version 2.19-2) .. TODO:: diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 038d105e1a7..dc14aa5caff 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -1748,14 +1748,14 @@ def tate_pairing(self, Q, n, k, q=None): sage: Px.weil_pairing(Qx, 41)^e == num/den True - NOTES: + .. NOTE:: - This function uses Miller's algorithm, followed by a naive - exponentiation. It does not do anything fancy. In the case - that there is an issue with `Q` being on one of the lines - generated in the `r*P` calculation, `Q` is offset by a random - point `R` and P.tate_pairing(Q+R,n,k)/P.tate_pairing(R,n,k) - is returned. + This function uses Miller's algorithm, followed by a naive + exponentiation. It does not do anything fancy. In the case + that there is an issue with `Q` being on one of the lines + generated in the `r*P` calculation, `Q` is offset by a random + point `R` and P.tate_pairing(Q+R,n,k)/P.tate_pairing(R,n,k) + is returned. AUTHORS: @@ -1945,13 +1945,14 @@ def ate_pairing(self, Q, n, k, t, q=None): ... ValueError: This point (14 : 10*a : 1) is not in Ker(pi - 1) - NOTES: + .. NOTE:: - First defined in the paper of [HSV2006]_, the ate pairing can be - computationally effective in those cases when the trace of the curve - over the base field is significantly smaller than the expected - value. This implementation is simply Miller's algorithm followed by a - naive exponentiation, and makes no claims towards efficiency. + First defined in the paper of [HSV2006]_, the ate pairing + can be computationally effective in those cases when the + trace of the curve over the base field is significantly + smaller than the expected value. This implementation is + simply Miller's algorithm followed by a naive + exponentiation, and makes no claims towards efficiency. AUTHORS: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index eea4c883303..e9a51903725 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2727,21 +2727,21 @@ def silverman_height_bound(self, algorithm='default'): - 'mwrank' -- use a C++ implementation in the mwrank library - NOTES: + .. NOTE:: - - The CPS_height_bound is often better (i.e. smaller) than - the Silverman bound, but it only applies for points over - the base field, whereas the Silverman bound works over - all number fields. + - The CPS_height_bound is often better (i.e. smaller) than + the Silverman bound, but it only applies for points over + the base field, whereas the Silverman bound works over + all number fields. - - The Silverman bound is also fairly straightforward to - compute over number fields, but isn't implemented here. + - The Silverman bound is also fairly straightforward to + compute over number fields, but isn't implemented here. - - Silverman's paper is 'The Difference Between the Weil - Height and the Canonical Height on Elliptic Curves', - Math. Comp., Volume 55, Number 192, pages 723-743. We - use a correction by Bremner with 0.973 replaced by 0.961, - as explained in the source code to mwrank (htconst.cc). + - Silverman's paper is 'The Difference Between the Weil + Height and the Canonical Height on Elliptic Curves', + Math. Comp., Volume 55, Number 192, pages 723-743. We + use a correction by Bremner with 0.973 replaced by 0.961, + as explained in the source code to mwrank (htconst.cc). EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 9aa647093f4..1fcd02f9ec2 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -871,7 +871,7 @@ def L_ratio(self): # and is a multiple of the degree of an isogeny between E # and the optimal curve. # - # NOTES: We *do* have to worry about the Manin constant, since + # NOTE: We *do* have to worry about the Manin constant, since # we are using the Neron model to compute omega, not the # newform. My theorem replaces the omega above by omega/c, # where c is the Manin constant, and the bound must be From 88d43cf69133344b59bb7a546fac2e84391f259a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 26 Mar 2021 09:48:20 +0000 Subject: [PATCH 077/706] update mpmath to 1.2.0 --- build/pkgs/mpmath/checksums.ini | 7 ++++--- build/pkgs/mpmath/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/mpmath/checksums.ini b/build/pkgs/mpmath/checksums.ini index 6c532f911b9..e01d50166a5 100644 --- a/build/pkgs/mpmath/checksums.ini +++ b/build/pkgs/mpmath/checksums.ini @@ -1,4 +1,5 @@ tarball=mpmath-VERSION.tar.gz -sha1=3f479408ea65b08bc23eeebe5dac2f2293dfec9d -md5=acb1cdddf38e16084628065b174ddbfe -cksum=3387899135 +sha1=7376e4a8b8c5a75e736ffc79e9680a93b78c126f +md5=d02f3913ea3c937ab49d8cb0a519e2ae +cksum=230710504 +upstream_url=https://mpmath.org/files/mpmath-1.2.0.tar.gz diff --git a/build/pkgs/mpmath/package-version.txt b/build/pkgs/mpmath/package-version.txt index 9084fa2f716..26aaba0e866 100644 --- a/build/pkgs/mpmath/package-version.txt +++ b/build/pkgs/mpmath/package-version.txt @@ -1 +1 @@ -1.1.0 +1.2.0 From ed5289033e26b2f06a3afe9c548a4ee5ed3aceed Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 26 Mar 2021 11:47:50 +0000 Subject: [PATCH 078/706] disable scm use by setuptools --- build/pkgs/mpmath/patches/no_scm.patch | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 build/pkgs/mpmath/patches/no_scm.patch diff --git a/build/pkgs/mpmath/patches/no_scm.patch b/build/pkgs/mpmath/patches/no_scm.patch new file mode 100644 index 00000000000..597c5689d47 --- /dev/null +++ b/build/pkgs/mpmath/patches/no_scm.patch @@ -0,0 +1,10 @@ +diff --git a/setup.py b/setup.py +index d3b4336..b5e6af3 100644 +--- a/setup.py ++++ b/setup.py +@@ -3,4 +3,4 @@ + import setuptools + + +-setuptools.setup(use_scm_version=True) ++setuptools.setup(use_scm_version=False) From f4a60740a7d6f47ec0cbf609b56d596f41a769f9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 26 Mar 2021 21:51:11 +0000 Subject: [PATCH 079/706] update mpmath to 1.2.1 --- build/pkgs/mpmath/checksums.ini | 8 ++++---- build/pkgs/mpmath/package-version.txt | 2 +- build/pkgs/mpmath/patches/no_scm.patch | 10 ---------- 3 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 build/pkgs/mpmath/patches/no_scm.patch diff --git a/build/pkgs/mpmath/checksums.ini b/build/pkgs/mpmath/checksums.ini index e01d50166a5..1313275f20e 100644 --- a/build/pkgs/mpmath/checksums.ini +++ b/build/pkgs/mpmath/checksums.ini @@ -1,5 +1,5 @@ tarball=mpmath-VERSION.tar.gz -sha1=7376e4a8b8c5a75e736ffc79e9680a93b78c126f -md5=d02f3913ea3c937ab49d8cb0a519e2ae -cksum=230710504 -upstream_url=https://mpmath.org/files/mpmath-1.2.0.tar.gz +sha1=ce8bd24606eeb02218b26304e6d99228919021f8 +md5=ef8a6449851755319673b06f71731d52 +cksum=3549837503 +upstream_url=https://files.pythonhosted.org/packages/source/m/mpmath/mpmath-VERSION.tar.gz diff --git a/build/pkgs/mpmath/package-version.txt b/build/pkgs/mpmath/package-version.txt index 26aaba0e866..6085e946503 100644 --- a/build/pkgs/mpmath/package-version.txt +++ b/build/pkgs/mpmath/package-version.txt @@ -1 +1 @@ -1.2.0 +1.2.1 diff --git a/build/pkgs/mpmath/patches/no_scm.patch b/build/pkgs/mpmath/patches/no_scm.patch deleted file mode 100644 index 597c5689d47..00000000000 --- a/build/pkgs/mpmath/patches/no_scm.patch +++ /dev/null @@ -1,10 +0,0 @@ -diff --git a/setup.py b/setup.py -index d3b4336..b5e6af3 100644 ---- a/setup.py -+++ b/setup.py -@@ -3,4 +3,4 @@ - import setuptools - - --setuptools.setup(use_scm_version=True) -+setuptools.setup(use_scm_version=False) From ec66c3c0c839b07523daf9b472acdc72d3a7acf7 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 18:33:59 +0100 Subject: [PATCH 080/706] Change example to one where the is actually no coercion map --- src/sage/rings/finite_rings/finite_field_base.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 972f07e6de8..124d9cfd56c 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1621,14 +1621,14 @@ cdef class FiniteField(Field): We check that :trac:`23801` is resolved:: - sage: k. = GF(3^240) + sage: k. = GF(5^240) sage: l, inc = k.subfield(3, 'z', map=True); l - Finite Field in z of size 3^3 + Finite Field in z of size 5^3 sage: inc Ring morphism: - From: Finite Field in z of size 3^3 - To: Finite Field in a of size 3^240 - Defn: z |--> a^239 + a^238 + ... + a^3 + 2 + From: Finite Field in z of size 5^3 + To: Finite Field in a of size 5^240 + Defn: z |--> 2*a^235 + a^231 + ... + a + 4 There is no coercion since we can't ensure compatibility with larger fields in this case:: @@ -1639,7 +1639,7 @@ cdef class FiniteField(Field): But there is still a compatibility among the generators chosen for the subfields:: sage: ll, iinc = k.subfield(12, 'w', map=True) - sage: x = iinc(ll.gen())^((3^12-1)/(3^3-1)) + sage: x = iinc(ll.gen())^((5^12-1)/(5^3-1)) sage: x.minimal_polynomial() == l.modulus() True From 7c7843d805dd56e5f1ed54eb3fbbf51c33534393 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 19:19:49 +0100 Subject: [PATCH 081/706] Fix tests in sage.geometry for new lattice generators --- src/sage/geometry/cone.py | 52 +++++++++++++-------------- src/sage/geometry/fan.py | 8 ++--- src/sage/geometry/fan_isomorphism.py | 18 +++++----- src/sage/geometry/integral_points.pyx | 4 +-- src/sage/geometry/toric_lattice.py | 14 ++++---- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..5b2326a8e7f 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3504,8 +3504,8 @@ def solid_restriction(self): sage: K = Cone([(1,0,0), (0,1,0)]) sage: K.solid_restriction().rays() - N(1, 0), - N(0, 1) + N(0, 1), + N(1, 0) in 2-d lattice N The solid restriction of a single ray has the same @@ -3624,7 +3624,7 @@ def _split_ambient_lattice(self): sage: C2_Z2 = Cone([(1,0),(1,2)]) sage: C2_Z2._split_ambient_lattice() sage: C2_Z2._sublattice - Sublattice + Sublattice Trivial cone:: @@ -3695,9 +3695,9 @@ def sublattice(self, *args, **kwds): sage: cone.rays().basis().matrix().det() -4 sage: cone.sublattice() - Sublattice + Sublattice sage: matrix( cone.sublattice().gens() ).det() - 1 + -1 Another example:: @@ -3709,7 +3709,7 @@ def sublattice(self, *args, **kwds): N(4, -5, 1) in 3-d lattice N sage: c.sublattice() - Sublattice + Sublattice sage: c.sublattice(5, -3, 4) N(5, -3, 4) sage: c.sublattice(1, 0, 0) @@ -3805,14 +3805,14 @@ def sublattice_complement(self, *args, **kwds): sage: c = Cone([(1,2,3), (4,-5,1)]) sage: c.sublattice() - Sublattice + Sublattice sage: c.sublattice_complement() - Sublattice + Sublattice sage: m = matrix( c.sublattice().gens() + c.sublattice_complement().gens() ) sage: m - [ 1 2 3] [ 4 -5 1] - [ 0 -6 -5] + [ 1 2 3] + [ 2 -3 0] sage: m.det() -1 """ @@ -3856,7 +3856,7 @@ def orthogonal_sublattice(self, *args, **kwds): Sublattice <> sage: c12 = Cone([(1,1,1), (1,-1,1)]) sage: c12.sublattice() - Sublattice + Sublattice sage: c12.orthogonal_sublattice() Sublattice @@ -3922,15 +3922,15 @@ def relative_quotient(self, subcone): sage: sigma = Cone([(1,1,1,3),(1,-1,1,3),(-1,-1,1,3),(-1,1,1,3)]) sage: rho = Cone([(-1, -1, 1, 3), (-1, 1, 1, 3)]) sage: sigma.sublattice() - Sublattice + Sublattice sage: rho.sublattice() - Sublattice + Sublattice sage: sigma.relative_quotient(rho) 1-d lattice, quotient - of Sublattice + of Sublattice by Sublattice sage: sigma.relative_quotient(rho).gens() - (N[1, 1, 0, 0],) + (N[1, 0, 0, 0],) More complicated example:: @@ -3938,12 +3938,12 @@ def relative_quotient(self, subcone): sage: sigma = Cone([(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1)]) sage: N_sigma = sigma.sublattice() sage: N_sigma - Sublattice + Sublattice sage: N_rho = rho.sublattice() sage: N_rho Sublattice sage: sigma.relative_quotient(rho).gens() - (N[0, 1, 1],) + (N[-1, -1, -2],) sage: N = rho.lattice() sage: N_sigma == N.span(N_rho.gens() + tuple(q.lift() ....: for q in sigma.relative_quotient(rho).gens())) @@ -3958,12 +3958,12 @@ def relative_quotient(self, subcone): Sublattice sage: sigma1.relative_quotient(rho) 1-d lattice, quotient - of Sublattice + of Sublattice by Sublattice sage: sigma1.relative_quotient(rho).gens() - (N[0, 1, 1],) + (N[-1, -1, -2],) sage: sigma2.relative_quotient(rho).gens() - (N[-1, 0, -2],) + (N[0, 2, 1],) """ try: cached_values = self._relative_quotient @@ -4263,9 +4263,9 @@ def Hilbert_basis(self): sage: Cone([[1,0],[3,4]]).dual().Hilbert_basis() M(0, 1), M(4, -3), - M(3, -2), + M(1, 0), M(2, -1), - M(1, 0) + M(3, -2) in 2-d lattice M sage: cone = Cone([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]]).dual() sage: cone.Hilbert_basis() # long time @@ -4275,16 +4275,16 @@ def Hilbert_basis(self): M(15, -63, 25, 9), M( 2, -3, 0, 1), M( 1, -4, 1, 1), - M(-1, 3, 0, 0), M( 4, -4, 0, 1), + M(-1, 3, 0, 0), M( 1, -5, 2, 1), M( 3, -5, 1, 1), M( 6, -5, 0, 1), M( 3, -13, 5, 2), M( 2, -6, 2, 1), M( 5, -6, 1, 1), - M( 0, 1, 0, 0), M( 8, -6, 0, 1), + M( 0, 1, 0, 0), M(-2, 8, 0, -1), M(10, -42, 17, 6), M( 7, -28, 11, 4), @@ -4295,9 +4295,9 @@ def Hilbert_basis(self): M( 4, -7, 2, 1), M( 7, -7, 1, 1), M( 0, 0, 1, 0), - M(-3, 14, 0, -2), + M( 1, 0, 0, 0), M(-1, 7, 0, -1), - M( 1, 0, 0, 0) + M(-3, 14, 0, -2) in 4-d lattice M Not a strictly convex cone:: diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index c5c3eb646e2..23800869037 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -3008,8 +3008,8 @@ def virtual_rays(self, *args): sage: f = Fan([Cone([(1,0,1,0), (0,1,1,0)])]) sage: f.virtual_rays() - N(0, 0, 0, 1), - N(0, 0, 1, 0) + N(1, 0, 0, 0), + N(0, 0, 0, 1) in 4-d lattice N sage: f.rays() @@ -3018,14 +3018,14 @@ def virtual_rays(self, *args): in 4-d lattice N sage: f.virtual_rays([0]) - N(0, 0, 0, 1) + N(1, 0, 0, 0) in 4-d lattice N You can also give virtual ray indices directly, without packing them into a list:: sage: f.virtual_rays(0) - N(0, 0, 0, 1) + N(1, 0, 0, 0) in 4-d lattice N Make sure that :trac:`16344` is fixed and one can compute diff --git a/src/sage/geometry/fan_isomorphism.py b/src/sage/geometry/fan_isomorphism.py index 04732ab0c20..18a6c9b199a 100644 --- a/src/sage/geometry/fan_isomorphism.py +++ b/src/sage/geometry/fan_isomorphism.py @@ -96,9 +96,9 @@ def fan_isomorphism_generator(fan1, fan2): ....: Cone([m2*vector([-1,-14]), m2*vector([-100, -5])])]) sage: sorted(fan_isomorphism_generator(fan1, fan2)) [ - [18 1 -5] - [ 4 0 -1] - [ 5 0 -1] + [-12 1 -5] + [ -4 0 -1] + [ -5 0 -1] ] sage: m0 = identity_matrix(ZZ, 2) @@ -125,15 +125,15 @@ def fan_isomorphism_generator(fan1, fan2): ] sage: sorted(fan_isomorphism_generator(fan1, fan2)) [ - [ 6 -3 7] [18 1 -5] - [ 1 -1 2] [ 4 0 -1] - [ 2 -1 2], [ 5 0 -1] + [-24 -3 7] [-12 1 -5] + [ -7 -1 2] [ -4 0 -1] + [ -8 -1 2], [ -5 0 -1] ] sage: sorted(fan_isomorphism_generator(fan2, fan1)) [ - [ 0 -1 1] [ 0 -1 1] - [ 1 -7 2] [ 2 -2 -5] - [ 0 -5 4], [ 1 0 -3] + [ 0 1 -1] [ 0 1 -1] + [ 1 -13 8] [ 2 -8 1] + [ 0 -5 4], [ 1 0 -3] ] """ if not fan_isomorphic_necessary_conditions(fan1, fan2): diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index 765b0a5bb55..37e1d23339d 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -108,13 +108,13 @@ cpdef tuple parallelotope_points(spanning_points, lattice): sage: from sage.geometry.integral_points import parallelotope_points sage: rays = list(map(vector, [(2,0), (0,2)])) sage: parallelotope_points(rays, ZZ^2) - ((0, 0), (1, 0), (0, 1), (1, 1)) + ((0, 0), (0, 1), (1, 0), (1, 1)) The rays can also be toric lattice points:: sage: rays = list(map(ToricLattice(2), [(2,0), (0,2)])) sage: parallelotope_points(rays, ToricLattice(2)) - (N(0, 0), N(1, 0), N(0, 1), N(1, 1)) + (N(0, 0), N(0, 1), N(1, 0), N(1, 1)) A non-smooth cone:: diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index 4444d662be5..4d8ef3fba62 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -425,7 +425,7 @@ def __call__(self, *args, **kwds): sage: N3 = ToricLattice(3, 'N3') sage: Q = N3 / N3.span([ N3(1,2,3) ]) sage: Q.an_element() - N3[0, 0, 1] + N3[1, 0, 0] sage: N2 = ToricLattice(2, 'N2') sage: N2( Q.an_element() ) N2(1, 0) @@ -1297,9 +1297,9 @@ class ToricLattice_quotient_element(FGP_Element): sage: e == e2 True sage: e.vector() - (4) + (-4) sage: e2.vector() - (4) + (-4) """ def _latex_(self): @@ -1407,7 +1407,7 @@ class ToricLattice_quotient(FGP_Module_class): sage: Q 1-d lattice, quotient of 3-d lattice N by Sublattice sage: Q.gens() - (N[0, 0, 1],) + (N[1, 0, 0],) Here, ``sublattice`` happens to be of codimension one in ``N``. If you want to prescribe the sign of the quotient generator, you can @@ -1416,15 +1416,15 @@ class ToricLattice_quotient(FGP_Module_class): sage: Q = N.quotient(sublattice, positive_point=N(0,0,-1)); Q 1-d lattice, quotient of 3-d lattice N by Sublattice sage: Q.gens() - (N[0, 0, -1],) + (N[1, 0, 0],) or:: sage: M = N.dual() - sage: Q = N.quotient(sublattice, positive_dual_point=M(0,0,-1)); Q + sage: Q = N.quotient(sublattice, positive_dual_point=M(1,0,0)); Q 1-d lattice, quotient of 3-d lattice N by Sublattice sage: Q.gens() - (N[0, 0, -1],) + (N[1, 0, 0],) TESTS:: From 3dc3251626e71237c2d2bda6ff54d4310639356e Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 19:54:20 +0100 Subject: [PATCH 082/706] Fix tests in sage.schemes.toric for new lattice generators --- src/sage/schemes/toric/chow_group.py | 42 ++++++++++++++-------------- src/sage/schemes/toric/morphism.py | 6 ++-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/sage/schemes/toric/chow_group.py b/src/sage/schemes/toric/chow_group.py index 89e82467b05..84d32fe373e 100644 --- a/src/sage/schemes/toric/chow_group.py +++ b/src/sage/schemes/toric/chow_group.py @@ -62,7 +62,7 @@ 7 sage: a = sum( A.gen(i) * (i+1) for i in range(A.ngens()) ) # an element of A sage: a # long time (2s on sage.math, 2011) - ( 3 | 1 mod 7 | 0 mod 2, 1 mod 2, 4, 5, 6, 7, 8 | 9 ) + ( 9 | 1 mod 7 | 1 mod 2, 0 mod 2, 4, 5, 6, 7, 8 | 3 ) The Chow group elements are printed as ``( a0 | a1 mod 7 | a2 mod 2, a3 mod 2, a4, a5, a6, a7, a8 | a9 )``, which denotes the element of @@ -93,13 +93,13 @@ sage: cone = X.fan(dim=2)[3]; cone 2-d cone of Rational polyhedral fan in 3-d lattice N sage: A_cone = A(cone); A_cone - ( 0 | 1 mod 7 | 0 mod 2, 0 mod 2, 0, 0, 0, 0, 0 | 0 ) + ( 0 | 6 mod 7 | 0 mod 2, 0 mod 2, 0, 0, 0, 0, 0 | 0 ) sage: A_cone.degree() 1 sage: 2 * A_cone - ( 0 | 2 mod 7 | 0 mod 2, 0 mod 2, 0, 0, 0, 0, 0 | 0 ) + ( 0 | 5 mod 7 | 0 mod 2, 0 mod 2, 0, 0, 0, 0, 0 | 0 ) sage: A_cone + A.gen(0) - ( 0 | 1 mod 7 | 0 mod 2, 1 mod 2, 0, 0, 0, 0, 0 | 0 ) + ( 0 | 6 mod 7 | 1 mod 2, 0 mod 2, 0, 0, 0, 0, 0 | 0 ) Chow cycles can be of mixed degrees:: @@ -151,7 +151,7 @@ class ChowCycle(FGP_Element): sage: P2 = toric_varieties.P2() sage: A = P2.Chow_group() sage: A.gens() - (( 1 | 0 | 0 ), ( 0 | 1 | 0 ), ( 0 | 0 | 1 )) + (( 0 | 0 | 1 ), ( 0 | 1 | 0 ), ( 1 | 0 | 0 )) sage: cone = P2.fan(1)[0] sage: A(cone) ( 0 | 1 | 0 ) @@ -199,7 +199,7 @@ def _repr_(self): sage: A.degree() (Z, Z, Z) sage: A.an_element()._repr_() - '( 1 | 0 | 0 )' + '( 0 | 0 | 1 )' A more complicated example with torsion:: @@ -208,7 +208,7 @@ def _repr_(self): sage: A.degree() (Z, 0, C2 x Z^5, Z) sage: sum( A.gen(i) * (i+1) for i in range(A.ngens()) ) - ( 2 || 1 mod 2, 3, 4, 5, 6, 7 | 8 ) + ( 8 || 1 mod 2, 3, 4, 5, 6, 7 | 2 ) """ A = self.parent() s = '(' @@ -245,7 +245,7 @@ def degree(self): sage: P2 = toric_varieties.P2() sage: A = P2.Chow_group() sage: [ a.degree() for a in A.gens() ] - [0, 1, 2] + [2, 1, 0] """ if '_dim' in self.__dict__: return self._dim @@ -279,9 +279,9 @@ def project_to_degree(self, degree): sage: A = toric_varieties.P2().Chow_group() sage: cycle = 10*A.gen(0) + 11*A.gen(1) + 12*A.gen(2) sage: cycle - ( 10 | 11 | 12 ) + ( 12 | 11 | 10 ) sage: cycle.project_to_degree(2) - ( 0 | 0 | 12 ) + ( 0 | 0 | 10 ) """ ambient_dim = self.parent()._variety.dimension() v = list(self.lift()) @@ -307,7 +307,7 @@ def count_points(self): sage: P2 = toric_varieties.P2() sage: A = P2.Chow_group() - sage: a = 5*A.gen(0) + 7*A.gen(1); a + sage: a = 5*A.gen(2) + 7*A.gen(1); a ( 5 | 7 | 0 ) sage: a.count_points() 5 @@ -373,7 +373,7 @@ def intersection_with_divisor(self, divisor): V(y) sage: A = dP6.Chow_group() sage: A(cone) - ( 0 | 0, 0, 0, 1 | 0 ) + ( 0 | 0, 0, 1, 0 | 0 ) sage: intersection = A(cone).intersection_with_divisor(D); intersection ( -1 | 0, 0, 0, 0 | 0 ) sage: intersection.count_points() @@ -405,8 +405,8 @@ def intersection_with_divisor(self, divisor): ( 0 | 0, 0, 0, 0 | 0 ), ( 0 | 0, 0, 0, 0 | 0 ), ( 0 | 0, 0, 0, 0 | 0 )] sage: [ r.intersection_with_divisor(D).lift() for r in dP6.Chow_group().relation_gens() ] - [(0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), @@ -504,13 +504,13 @@ def cohomology_class(self): sage: HH = WP4.cohomology_ring() sage: cone3d = Cone([(0,0,1,0), (0,0,0,1), (-9,-6,-1,-1)]) sage: A(cone3d) - ( 0 | 1 | 0 | 0 | 0 ) + ( 0 | -1 | 0 | 0 | 0 ) sage: HH(cone3d) [3*z4^3] sage: D = -WP4.K() # the anticanonical divisor sage: A(D) - ( 0 | 0 | 0 | 18 | 0 ) + ( 0 | 0 | 0 | -18 | 0 ) sage: HH(D) [18*z4] @@ -605,7 +605,7 @@ class ChowGroup_class(FGP_Module_class, WithEqualityById): sage: A = ChowGroup_class(P2,ZZ,True); A Chow group of 2-d CPR-Fano toric variety covered by 3 affine patches sage: A.an_element() - ( 1 | 0 | 0 ) + ( 0 | 0 | 1 ) """ Element = ChowCycle @@ -627,7 +627,7 @@ def __init__(self, toric_variety, base_ring, check): sage: A_ZZ = P2.Chow_group() sage: 2 * A_ZZ.an_element() * 3 - ( 6 | 0 | 0 ) + ( 0 | 0 | 6 ) sage: 1/2 * A_ZZ.an_element() * 1/3 Traceback (most recent call last): ... @@ -705,9 +705,9 @@ def _element_constructor_(self, x, check=True): sage: A = dP6.Chow_group() sage: cone = dP6.fan(dim=1)[4] sage: A(cone) - ( 0 | 0, 1, 0, 0 | 0 ) + ( 0 | 1, 1, 0, -1 | 0 ) sage: A(Cone(cone)) # isomorphic but not identical to a cone of the fan! - ( 0 | 0, 1, 0, 0 | 0 ) + ( 0 | 1, 1, 0, -1 | 0 ) sage: A( dP6.K() ) ( 0 | -1, -2, -2, -1 | 0 ) """ @@ -1003,7 +1003,7 @@ def gens(self, degree=None): sage: A = toric_varieties.P2().Chow_group() sage: A.gens() - (( 1 | 0 | 0 ), ( 0 | 1 | 0 ), ( 0 | 0 | 1 )) + (( 0 | 0 | 1 ), ( 0 | 1 | 0 ), ( 1 | 0 | 0 )) sage: A.gens(degree=1) (( 0 | 1 | 0 ),) """ diff --git a/src/sage/schemes/toric/morphism.py b/src/sage/schemes/toric/morphism.py index 6a001ee74a2..0d8ed6f20d0 100644 --- a/src/sage/schemes/toric/morphism.py +++ b/src/sage/schemes/toric/morphism.py @@ -1949,11 +1949,11 @@ def _image_ray_multiplicity(self, fiber_ray): sage: f = fc.embedding_morphism() sage: for r in fc.fan().rays(): ....: print("{} {}".format(r, f._image_ray_multiplicity(r))) - N(-1, 2) (11, 1) + N(-1, -1) (9, 2) N(0, 1) (5, 1) - N(1, -3) (9, 2) + N(1, 0) (11, 1) sage: f._ray_index_map - {N(-3, 4): 10, N(-1, 2): 11, N(0, 1): 5, N(1, 0): 4, N(2, -6): 9} + {N(-2, -2): 9, N(-1, 2): 4, N(0, 1): 5, N(1, 0): 11, N(3, -2): 10} """ try: image_ray_index = self._ray_index_map[fiber_ray] From 530914d0f848e6e894361d13a9919f40523f34c4 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 19:58:45 +0100 Subject: [PATCH 083/706] Fix one test in judson-abstract-algebra --- .../books/judson-abstract-algebra/galois-sage.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py index 993015df604..6996d34012d 100644 --- a/src/sage/tests/books/judson-abstract-algebra/galois-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/galois-sage.py @@ -414,17 +414,17 @@ To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 Defn: c4 |--> 2*c^2, None), - (Number Field in c6 with defining polynomial x^4 + 8, + (Number Field in c5 with defining polynomial x^4 + 8, Ring morphism: - From: Number Field in c6 with defining polynomial x^4 + 8 + From: Number Field in c5 with defining polynomial x^4 + 8 To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 - Defn: c6 |--> -1/80*c^5 + 1/40*c, + Defn: c5 |--> -1/80*c^5 + 1/40*c, None), - (Number Field in c5 with defining polynomial x^4 + 648, + (Number Field in c6 with defining polynomial x^4 + 648, Ring morphism: - From: Number Field in c5 with defining polynomial x^4 + 648 + From: Number Field in c6 with defining polynomial x^4 + 648 To: Number Field in c with defining polynomial x^8 + 28*x^4 + 2500 - Defn: c5 |--> 1/80*c^5 + 79/40*c, + Defn: c6 |--> 1/80*c^5 + 79/40*c, None), (Number Field in c7 with defining polynomial x^4 - 512, Ring morphism: From 756781e58cdc0b8d64b1c4c129100dfe544261fd Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 20:07:16 +0100 Subject: [PATCH 084/706] Fix tests in homology for different choice of generators --- src/sage/homology/chain_complex.py | 2 +- src/sage/homology/simplicial_complex.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 49aaff1a854..47d14b4b9cb 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -1270,7 +1270,7 @@ def homology(self, deg=None, base_ring=None, generators=False, sage: C_k = ChainComplex({0:d0, 1:d1, 2:d2}, degree=-1) sage: C_k.homology(generators=true, algorithm='no_chomp') {0: [(Z, Chain(0:(1)))], - 1: [(C2, Chain(1:(1, 0, 0))), (Z, Chain(1:(0, 0, 1)))], + 1: [(C2, Chain(1:(0, 1, -1))), (Z, Chain(1:(0, 1, 0)))], 2: []} From a torus using a field:: diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index 8db479fd549..6c71bce4828 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -2305,7 +2305,7 @@ def _homology_(self, dim=None, base_ring=ZZ, subcomplex=None, sage: simplicial_complexes.Torus().homology(generators=True, algorithm='no_chomp') {0: [], - 1: [(Z, (1, 2) - (1, 6) + (2, 6)), (Z, (3, 4) - (3, 6) + (4, 6))], + 1: [(Z, (2, 4) - (2, 6) + (4, 6)), (Z, (1, 4) - (1, 6) + (4, 6))], 2: [(Z, (0, 1, 2) - (0, 1, 5) + (0, 2, 6) - (0, 3, 4) + (0, 3, 5) - (0, 4, 6) - (1, 2, 4) + (1, 3, 4) - (1, 3, 6) + (1, 5, 6) - (2, 3, 5) + (2, 3, 6) + (2, 4, 5) - (4, 5, 6))]} """ From a9fb51c33d009aefb673dac596c0eb9c1f65d90e Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 20:57:11 +0100 Subject: [PATCH 085/706] Update tests in modules.fg_pid for new Smith normal form --- src/sage/modules/fg_pid/fgp_element.py | 16 ++--- src/sage/modules/fg_pid/fgp_module.py | 88 ++++++++++++------------- src/sage/modules/fg_pid/fgp_morphism.py | 16 ++--- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 53857d31a07..5a948b6152b 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -39,7 +39,7 @@ class FGP_Element(ModuleElement): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W sage: x = Q(V.0-V.1); x #indirect doctest - (0, 3) + (0, 9) sage: isinstance(x, sage.modules.fg_pid.fgp_element.FGP_Element) True sage: type(x) @@ -94,14 +94,14 @@ def lift(self): sage: Q.1 (0, 1) sage: Q.0.lift() - (0, 0, 1) + (0, 6, 1) sage: Q.1.lift() - (0, 2, 0) + (0, -2, 0) sage: x = Q(V.0); x - (0, 4) + (0, 8) sage: x.lift() (1/2, 0, 0) - sage: x == 4*Q.1 + sage: x == 8*Q.1 True sage: x.lift().parent() == V True @@ -158,9 +158,9 @@ def _add_(self, other): We test canonical coercion from V and W. sage: Q.0 + V.0 - (1, 4) + (1, 8) sage: V.0 + Q.0 - (1, 4) + (1, 8) sage: W.0 + Q.0 (1, 0) sage: W.0 + Q.0 == Q.0 @@ -291,7 +291,7 @@ def _repr_(self): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W sage: Q(V.1)._repr_() - '(0, 1)' + '(0, 11)' """ return repr(self.vector()) diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 38b95d40a39..935d0ab5c0d 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -70,17 +70,17 @@ sage: M0.optimized()[0].V() Free module of degree 3 and rank 2 over Integer Ring User basis matrix: - [0 0 1] - [0 2 0] + [ 0 8 1] + [ 0 -2 0] Create elements of M0 either by coercing in elements of V0, getting generators, or coercing in a list or tuple or coercing in 0. Finally, one can express an element as a linear combination of the smith form generators :: sage: M0(V0.0) - (0, 14) + (0, 2) sage: M0(V0.0 + W0.0) # no difference modulo W0 - (0, 14) + (0, 2) sage: M0.linear_combination_of_smith_form_gens([3,20]) (3, 4) sage: 3*M0.0 + 20*M0.1 @@ -93,9 +93,9 @@ sage: x = M0.0 - M0.1; x (1, 15) sage: x.lift() - (0, -2, 1) + (0, 10, 1) sage: M0(vector([1/2,0,0])) - (0, 14) + (0, 2) sage: x.additive_order() 16 @@ -142,9 +142,9 @@ Finitely generated module V/W over Integer Ring with invariants (2, 16) sage: M0(K.0) - (2, 0) + (2, 8) sage: M0(K.1) - (3, 1) + (1, 5) sage: f(M0(K.0)) (0) sage: f(M0(K.1)) @@ -180,7 +180,7 @@ sage: Q.linear_combination_of_smith_form_gens([1,3]) (1, 3) sage: Q(V([1,3,4])) - (0, 11) + (0, 1) sage: Q(W([1,16,0])) (0, 0) sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],QQ) @@ -632,7 +632,7 @@ def _element_constructor_(self, x, check=True): sage: W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W sage: x = Q(V.0-V.1); x # indirect doctest - (0, 3) + (0, 9) sage: type(x) sage: x is Q(x) @@ -931,9 +931,9 @@ def _smith_form(self): sage: Q = V/W sage: Q._smith_form() ( - [ 1 0 0] [1 0 0] [ 1 0 -8] - [ 0 4 0] [0 0 1] [ 0 0 1] - [ 0 0 12], [0 1 0], [ 0 1 0] + [ 1 0 0] [ 1 0 0] [ 1 0 8] + [ 0 4 0] [ 0 1 1] [ 0 0 -1] + [ 0 0 12], [ 0 -1 0], [ 0 1 3] ) """ return self._relative_matrix().smith_form() @@ -1026,7 +1026,7 @@ def smith_form_gens(self): sage: Q.smith_form_gens() ((1, 0), (0, 1)) sage: [x.lift() for x in Q.smith_form_gens()] - [(0, 0, 1), (0, 1, 0)] + [(0, 3, 1), (0, -1, 0)] """ # Get the rightmost transformation in the Smith form _, _, X = self._smith_form() @@ -1069,15 +1069,15 @@ def gens_to_smith(self): sage: D.gens_to_smith() [0 3 0] [0 0 3] - [0 2 0] - [1 0 0] + [0 4 0] + [1 2 0] [0 0 4] sage: T = D.gens_to_smith()*D.smith_to_gens() sage: T - [ 3 0 15 0 0] + [ 3 0 3 0 0] [ 0 33 0 0 3] - [ 2 0 10 0 0] - [ 0 0 0 1 0] + [ 4 0 4 0 0] + [ 2 0 3 1 0] [ 0 44 0 0 4] The matrix `T` now satisfies a certain congruence:: @@ -1120,13 +1120,13 @@ def smith_to_gens(self): [ 0 0 0 1/3 0] [ 0 0 0 0 2/3] sage: D.smith_to_gens() - [ 0 0 0 1 0] - [ 1 0 5 0 0] + [ 0 0 1 1 0] + [ 1 0 1 0 0] [ 0 11 0 0 1] sage: T = D.smith_to_gens()*D.gens_to_smith() sage: T - [ 1 0 0] - [ 0 13 0] + [ 1 6 0] + [ 0 7 0] [ 0 0 37] This matrix satisfies the congruence:: @@ -1148,7 +1148,7 @@ def smith_to_gens(self): of the user defined generators that is x:: sage: x.vector() * D.smith_to_gens() - (2, 33, 10, 1, 3) + (2, 33, 3, 1, 3) """ if self.base_ring() != ZZ: # it is not @@ -1196,7 +1196,7 @@ def gens_vector(self, x, reduce=False): sage: gens = [V(g) for g in gens] sage: D = FGP_with_gens(V, W, gens) sage: D.gens() - ((0, 3, 0), (0, 0, 3), (0, 2, 0), (1, 0, 0), (0, 0, 8)) + ((0, 3, 0), (0, 0, 3), (0, 4, 0), (1, 2, 0), (0, 0, 8)) We create some element of D:: @@ -1209,12 +1209,12 @@ def gens_vector(self, x, reduce=False): sage: v = D.gens_vector(x) sage: v - (2, 9, 10, 1, 33) + (2, 9, 3, 1, 33) The output can be further reduced:: sage: D.gens_vector(x, reduce=True) - (0, 1, 1, 1, 0) + (0, 1, 0, 1, 0) Let us check:: @@ -1262,9 +1262,9 @@ def coordinate_vector(self, x, reduce=False): If x is not in self, it is coerced in:: sage: Q.coordinate_vector(V.0) - (1, 0, -3) + (1, -3, 0) sage: Q.coordinate_vector(Q(V.0)) - (1, 0, -3) + (1, -3, 0) TESTS:: @@ -1278,28 +1278,28 @@ def coordinate_vector(self, x, reduce=False): sage: O.V() Free module of degree 3 and rank 2 over Integer Ring User basis matrix: - [0 0 1] - [0 2 0] + [ 0 6 1] + [ 0 -2 0] sage: phi = Q.hom([Q.0, 4*Q.1]) sage: x = Q(V.0); x - (0, 4) + (0, 8) sage: Q.coordinate_vector(x, reduce=True) - (0, 4) + (0, 8) sage: Q.coordinate_vector(-x, reduce=False) # random - (0, -4) - sage: x == 4*Q.1 + (0, -8) + sage: x == 8*Q.1 True sage: x = Q(V.1); x - (0, 1) + (0, 11) sage: Q.coordinate_vector(x) - (0, 1) - sage: x == Q.1 + (0, -1) + sage: x == -Q.1 True sage: x = Q(V.2); x - (1, 0) + (1, 3) sage: Q.coordinate_vector(x) - (1, 0) - sage: x == Q.0 + (1, 3) + sage: x == Q.0 + 3*Q.1 True """ try: @@ -1407,8 +1407,8 @@ def optimized(self): sage: O.V() Free module of degree 3 and rank 2 over Integer Ring User basis matrix: - [0 0 1] - [0 1 0] + [ 0 3 1] + [ 0 -1 0] sage: O.W() Free module of degree 3 and rank 2 over Integer Ring Echelon basis matrix: @@ -1699,7 +1699,7 @@ def random_element(self, *args, **kwds): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W sage: Q.random_element() - (1, 10) + (1, 5) """ return self(self._V.random_element(*args, **kwds)) diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index fbbd479c6e9..ce5df8df525 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -258,20 +258,20 @@ def __call__(self, x): sage: O.V() Free module of degree 3 and rank 2 over Integer Ring User basis matrix: - [0 0 1] - [0 2 0] + [ 0 6 1] + [ 0 -2 0] sage: phi = Q.hom([Q.0, 4*Q.1]) sage: x = Q(V.0); x - (0, 4) - sage: x == 4*Q.1 + (0, 8) + sage: x == 8*Q.1 True sage: x in O.V() False sage: phi(x) - (0, 4) - sage: phi(4*Q.1) - (0, 4) - sage: phi(4*Q.1) == phi(x) + (0, 8) + sage: phi(8*Q.1) + (0, 8) + sage: phi(8*Q.1) == phi(x) True """ from .fgp_module import is_FGP_Module From d7cf52798714865e75537bd080345b62ca9d78b0 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 27 Mar 2021 22:10:12 +0100 Subject: [PATCH 086/706] Fix tests in torsion quadratic modules for new Smith normal form --- .../free_quadratic_module_integer_symmetric.py | 14 +++++++------- src/sage/modules/torsion_quadratic_module.py | 10 +++++----- src/sage/quadratic_forms/genera/genus.py | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/modules/free_quadratic_module_integer_symmetric.py b/src/sage/modules/free_quadratic_module_integer_symmetric.py index 24766d3fdda..d81ceed37e0 100644 --- a/src/sage/modules/free_quadratic_module_integer_symmetric.py +++ b/src/sage/modules/free_quadratic_module_integer_symmetric.py @@ -549,8 +549,8 @@ def IntegralLatticeGluing(Lattices, glue, return_embeddings=False): sage: [L, phi] = IntegralLatticeGluing([L1, L2], [[f1, g1], [f2, 2 * g2]], True) sage: phi[0] Free module morphism defined by the matrix - [ 2 2 -1 -2] - [ 0 2 0 -1] + [ 2 2 -2 -1] + [ 0 2 -1 0] Domain: Lattice of degree 4 and rank 2 over Integer Ring Basis matrix: [1 1 0 0] @@ -563,7 +563,7 @@ def IntegralLatticeGluing(Lattices, glue, return_embeddings=False): Codomain: Lattice of degree 10 and rank 4 over Integer Ring Basis matrix: [ 1/2 0 -1/2 0 0 1/2 0 0 1/2 1/2] - [ 0 1/2 1/2 0 0 0 0 0 1/2 1/2] + [ 0 1/2 1/2 0 0 1/2 0 0 0 0] [ 0 0 0 0 0 1 0 0 0 0] [ 0 0 0 0 0 0 0 0 1 1] Inner product matrix: @@ -785,7 +785,7 @@ def discriminant_group(self, s=0): Finite quadratic module over Integer Ring with invariants (2, 10) Gram matrix of the quadratic form with values in Q/2Z: [ 1 1/2] - [1/2 9/5] + [1/2 1/5] sage: L.discriminant_group(2) Finite quadratic module over Integer Ring with invariants (2, 2) Gram matrix of the quadratic form with values in Q/2Z: @@ -794,7 +794,7 @@ def discriminant_group(self, s=0): sage: L.discriminant_group(5) Finite quadratic module over Integer Ring with invariants (5,) Gram matrix of the quadratic form with values in Q/2Z: - [6/5] + [4/5] TESTS:: @@ -1452,8 +1452,8 @@ def local_modification(M, G, p, check=True): sage: local_modification(M, L.gram_matrix(), 2) Lattice of degree 4 and rank 4 over Integer Ring Basis matrix: - [1/3 0 1/3 2/3] - [ 0 1/3 1/3 2/3] + [1/3 0 2/3 2/3] + [ 0 1/3 0 2/3] [ 0 0 1 0] [ 0 0 0 1] Inner product matrix: diff --git a/src/sage/modules/torsion_quadratic_module.py b/src/sage/modules/torsion_quadratic_module.py index 39e7065ac48..3f7a7f537f5 100644 --- a/src/sage/modules/torsion_quadratic_module.py +++ b/src/sage/modules/torsion_quadratic_module.py @@ -62,7 +62,7 @@ def TorsionQuadraticForm(q): TESTS:: - sage: TorsionQuadraticForm(matrix.diagonal([3/8,3/8,3/4])) + sage: TorsionQuadraticForm(matrix.diagonal([3/4,3/8,3/8])) Finite quadratic module over Integer Ring with invariants (4, 8, 8) Gram matrix of the quadratic form with values in Q/2Z: [3/4 0 0] @@ -1032,10 +1032,10 @@ def normal_form(self, partial=False): sage: T Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12) Gram matrix of the quadratic form with values in Q/(1/3)Z: - [1/18 5/36 0 0] - [5/36 1/18 5/36 5/36] - [ 0 5/36 1/36 1/72] - [ 0 5/36 1/72 1/36] + [ 1/18 1/12 5/36 1/36] + [ 1/12 1/6 1/36 1/9] + [ 5/36 1/36 1/36 11/72] + [ 1/36 1/9 11/72 1/36] sage: T.normal_form() Finite quadratic module over Integer Ring with invariants (6, 6, 12, 12) Gram matrix of the quadratic form with values in Q/(1/3)Z: diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index f0718593ea8..35de7eea8d7 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -2897,18 +2897,18 @@ def discriminant_form(self): sage: GS.discriminant_form() Finite quadratic module over Integer Ring with invariants (2, 2, 4, 24) Gram matrix of the quadratic form with values in Q/2Z: - [ 1/2 0 0 0] - [ 0 3/2 0 0] - [ 0 0 7/4 0] - [ 0 0 0 7/24] + [ 1/2 0 1/2 0] + [ 0 3/2 0 0] + [ 1/2 0 3/4 0] + [ 0 0 0 25/24] sage: A = matrix.diagonal(ZZ, [1, -4, 6, 8]) sage: GS = Genus(A) sage: GS.discriminant_form() Finite quadratic module over Integer Ring with invariants (2, 4, 24) Gram matrix of the quadratic form with values in Q/Z: - [ 1/2 0 0] - [ 0 3/4 0] - [ 0 0 7/24] + [ 1/2 1/2 0] + [ 1/2 3/4 0] + [ 0 0 1/24] """ from sage.modules.torsion_quadratic_module import TorsionQuadraticForm qL = [] From b21951e876b318dcdf3fff1467f37136e9dbc854 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 28 Mar 2021 14:35:56 +0200 Subject: [PATCH 087/706] Backport rnfdisc fix --- build/pkgs/pari/patches/pari-rnfdisc.patch | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 build/pkgs/pari/patches/pari-rnfdisc.patch diff --git a/build/pkgs/pari/patches/pari-rnfdisc.patch b/build/pkgs/pari/patches/pari-rnfdisc.patch new file mode 100644 index 00000000000..3a26e966873 --- /dev/null +++ b/build/pkgs/pari/patches/pari-rnfdisc.patch @@ -0,0 +1,19 @@ +From 3edb98db78dd49bb8b4137b46781a7cd570c2556 Mon Sep 17 00:00:00 2001 +From: Bill Allombert +Date: Sun, 28 Mar 2021 13:27:24 +0200 +Subject: [PATCH] rnfdisc_factored: remove spurious Q_primpart [#2284] + +diff --git a/src/basemath/base2.c b/src/basemath/base2.c +index b2b63ada5..531f5c558 100644 +--- a/src/basemath/base2.c ++++ b/src/basemath/base2.c +@@ -3582,7 +3582,7 @@ rnfdisc_factored(GEN nf, GEN pol, GEN *pd) + + nf = checknf(nf); + pol = rnfdisc_get_T(nf, pol, &lim); +- disc = nf_to_scalar_or_basis(nf, nfX_disc(nf, Q_primpart(pol))); ++ disc = nf_to_scalar_or_basis(nf, nfX_disc(nf, pol)); + pol = nfX_to_monic(nf, pol, NULL); + fa = idealfactor_partial(nf, disc, lim); + P = gel(fa,1); l = lg(P); + From 9d5b3c0fdcf0b7c5a5d61003e11c6cad313945be Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Mon, 29 Mar 2021 17:51:54 +0200 Subject: [PATCH 088/706] Add algorithm enumerating infinite repetitions --- src/doc/en/reference/references/index.rst | 10 + src/sage/combinat/words/finite_word.py | 32 ++ src/sage/combinat/words/morphism.py | 590 +++++++++++++++++++++- 3 files changed, 630 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 86b4fb35b48..afd4c8f234d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3439,6 +3439,11 @@ REFERENCES: .. [Knu2005] Lars R. Knudsen, *SMASH - A Cryptographic Hash Function*; in FSE'05, (2005), pp. 228-242. +.. [KO2000] Yuji Kobayashi and Friedrich Otto, + *Repetitiveness of languages generated by morphisms*. + Theoret. Comp. Sci. 240 (2000) 337--378. + :doi:`10.1016/S0304-3975(99)00238-8` + .. [Kob1993] Neal Koblitz, *Introduction to Elliptic Curves and Modular Forms*. Springer GTM 97, 1993. @@ -3560,6 +3565,11 @@ REFERENCES: algebra of type* `A`. Proc. Amer. Math. Soc. **138** (2010), no. 11, 3877--3889. +.. [KS2015] Karel Klouda and Štěpán Starosta, + *An Algorithm Enumerating All Infinite Repetitions in a D0L System*. + Journal of Discrete Algorithms, 33 (2015), 130-138. + :doi:`10.1016/j.jda.2015.03.006` + .. [KS2019] \J. Kliem and C. Stump. *A face iterator for polyhedra and more general finite locally branched lattices*. diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index aec56d7fbd4..2c714fc3d29 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -7140,6 +7140,38 @@ def is_christoffel(self): else: return False + def minimal_conjugate(self): + r""" + Return the lexicographically minimal conjugate of this word (see + :wikipedia:`Lexicographically_minimal_string_rotation`). + + EXAMPLES:: + + sage: Word('213').minimal_conjugate() + word: 132 + sage: Word('11').minimal_conjugate() + word: 11 + sage: Word('12112').minimal_conjugate() + word: 11212 + sage: Word('211').minimal_conjugate() + word: 112 + sage: Word('211211211').minimal_conjugate() + word: 112112112 + + TESTS:: + + sage: Word().minimal_conjugate() + word: + """ + if not self: + return self + p = self.primitive() + q = self.length() // p.length() + end = 0 + for factor in (p ** 2).lyndon_factorization(): + end += factor.length() + if end >= p.length(): + return factor ** q ####################################################################### diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 1879e4e1833..556d96d93d7 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -89,14 +89,15 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from itertools import chain +from collections import Counter +from itertools import chain, count from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method from sage.misc.lazy_list import lazy_list from sage.sets.set import Set -from sage.rings.all import QQ +from sage.rings.all import ZZ, QQ from sage.rings.infinity import Infinity from sage.rings.integer_ring import IntegerRing from sage.rings.integer import Integer @@ -3184,3 +3185,588 @@ def abelian_rotation_subspace(self): basis.extend((factor[0])(M).right_kernel().basis()) return M._column_ambient_module().change_ring(QQ).subspace(basis) + + def is_injective(self): + """ + Return whether this morphism is injective. + + ALGORITHM: + + Uses a version of :wikipedia:`Sardinas–Patterson_algorithm`. + Time complexity is on average quadratic with regards to the size of the + morphism. + + EXAMPLES:: + + sage: WordMorphism('a->0,b->10,c->110,d->111').is_injective() + True + sage: WordMorphism('a->00,b->01,c->012,d->20001').is_injective() + False + """ + def check(u, v): + if u.is_prefix(v): + tail = v[u.length():] + if tail not in tails: + tails.add(tail) + todo.append(tail) + + images = self._morph.values() + if any(not x for x in images): + return False + tails = set() + todo = [] + + for i, u in enumerate(images): + for j, v in enumerate(images): + if i == j: + continue + check(u, v) + + while todo: + u = todo.pop() + for v in images: + if u == v: + return False + check(u, v) + check(v, u) + + return True + + def is_pushy(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is pushy, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language created by iterating a morphism is pushy if its words + contain an infinite number of factors containing no growing letters. It + turns out that this is equivalent to having at least one infinite + repetition containing no growing letters. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: WordMorphism('a->abca,b->bc,c->').is_pushy() + False + sage: WordMorphism('a->abc,b->,c->bcb').is_pushy() + True + """ + return bool(self.infinite_repetitions_bounded(w)) + + def is_unboundedly_repetitive(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is unboundedly repetitive, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language created by iterating a morphism is unboundedly repetitive if + it has at least one infinite repetition containing at least one growing + letter. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: WordMorphism('a->abca,b->bc,c->').is_unboundedly_repetitive() + True + sage: WordMorphism('a->abc,b->,c->bcb').is_unboundedly_repetitive() + False + """ + return bool(self.infinite_repetitions_growing(w)) + + def is_repetitive(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is repetitive, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language is repetitive if for each positive integer `k` there exists + a word `u` such that `u^k` is a factor of some word of the language. + + It turns that for languages created by iterating a morphism this is + equivalent to having at least one infinite repetition (this property is + also known as strong repetitiveness). + + See :meth:`infinite_repetitions`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES: + + This method can be used to check whether a purely morphic word is NOT + k-power free for all positive integers k. For example, the language + containing just the Thue-Morse word and its prefixes is not repetitive, + since the Thue-Morse word is cube-free:: + + sage: WordMorphism('a->ab,b->ba').is_repetitive('a') + False + + Similarly, the Hanoi word is square-free:: + + sage: WordMorphism('a->aC,A->ac,b->cB,B->cb,c->bA,C->ba').is_repetitive('a') + False + + However, this method solves a more general problem, as it can be called + on any morphism `m` and with any word `w`:: + + sage: WordMorphism('a->c,b->cda,c->a,d->abc').is_repetitive('bd') + True + """ + return self.is_pushy(w) or self.is_unboundedly_repetitive(w) + + def infinite_repetitions(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy) + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A non-empty word `v` is an infinite repetition (also known as an + infinite periodic factor) of a language if for each positive integer + `k` the word `v^k` is a factor of some word from the language. + + If `v` is an infinite repetition, then all its powers are also infinite + repetitions, therefore this method returns only the primitive ones. It + turns out that a language created by iterating a morphism has a finite + number of primitive infinite repetitions. + + Similarly, if `v` is an infinite repetition, then all its conjugates + are also infinite repetitions, therefore this method returns only the + lexicographically minimal one from each conjugacy class. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: inf_reps = m.infinite_repetitions('ac') + sage: sorted(inf_reps) + [word: aab, word: de] + + Incomplete check that these words are indeed infinite repetitions:: + + sage: SL = m._language_naive(10, Word('ac')) + sage: all(x in SL for x in inf_reps) + True + sage: all(x^2 in SL for x in inf_reps) + True + sage: all(x^3 in SL for x in inf_reps) + True + + Larger example:: + + sage: m = WordMorphism('a->1b5,b->fcg,c->dae,d->432,e->678,f->f,g->g,1->2,2->3,3->4,4->1,5->6,6->7,7->8,8->5') + sage: sorted(m.infinite_repetitions('a')) + [word: 1432f2143f3214f4321f, word: 5678g8567g7856g6785g] + """ + return self.infinite_repetitions_bounded(w) | self.infinite_repetitions_growing(w) + + def infinite_repetitions_bounded(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy), + which contain no growing letters, + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: sorted(m.infinite_repetitions_bounded()) + [word: de] + + sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') + sage: sorted(m.infinite_repetitions_bounded()) + [word: c, word: d] + """ + def impl(): + U = {} + for x in unbounded: + hx = g.image(x) + for i, y in enumerate(hx): + if y in unbounded: + break + U[x] = y, hx[:i] + for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): + if all(not U[x][1] for x in cycle): + continue + for cycle in g.domain()(cycle).conjugates_iterator(): + u = g.domain()() + for x in cycle: + u = g(u) + u = u + U[x][1] + history = dict({u: 0}) + for i in count(1): + u = g(u) + if u in history: + s = ZZ(history[u]) + t = ZZ(i - history[u]) + break + history[u] = i + q = len(cycle) + l0 = (s / q).ceil() * q + l1 = l0 + (t.lcm(q) / q) + gq = gb ** q + uql = gq(u, l0) + res = g.domain()() + for _ in range(l0 + 1, l1 + 1): + uql = gq(uql) + res = uql + res + yield k(res.primitive()).primitive() + + if w is None: + w = self._morph + f = self.restrict_domain(self.reach(w)) + g, _, k, _ = f.simplify_injective() + unbounded = set(g.growing_letters()) + gb = g.restrict_domain(set(g._morph) - unbounded) + + result = set() + for x in impl(): + result.add(x.minimal_conjugate()) + g, k = g.reversal(), k.reversal() + for x in impl(): + result.add(self.domain()(reversed(x)).minimal_conjugate()) + + return result + + def infinite_repetitions_growing(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy), + which contain at least one growing letter, + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: sorted(m.infinite_repetitions_growing()) + [word: aab] + + sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') + sage: sorted(m.infinite_repetitions_growing()) + [word: ad, word: bc] + + sage: m = WordMorphism('b->c,c->bcb') + sage: sorted(m.infinite_repetitions_growing()) + [word: bc] + """ + if w is None: + w = self._morph + f = self.restrict_domain(self.reach(w)) + g, _, k, _ = f.simplify_injective() + unbounded = set(g.growing_letters()) + + result = set() + for periodic_orbit in g.periodic_points(): + q = len(periodic_orbit) + gq = g**q + for periodic_point in periodic_orbit: + # Check if this periodic point is a periodic infinite word. + periodic_point = periodic_point[:1] + letter_cnts = Counter(periodic_point) + for _ in g.domain().alphabet(): + previous_length = periodic_point.length() + periodic_point = gq(periodic_point) + letter_cnts.update(periodic_point[previous_length:]) + if any(letter_cnts[letter] >= 2 for letter in unbounded): + break + else: # nobreak + continue + if letter_cnts[periodic_point[0]] < 2: + continue + v = periodic_point[:periodic_point.find(periodic_point[0], start=1)] + vq = gq(v) + m = 0 + while vq[m * v.length() : (m + 1) * v.length()] == v: + m += 1 + if m >= 2 and m * v.length() == vq.length(): + result.add(k(v).primitive().minimal_conjugate()) + + return result + + def reach(self, w): + r""" + Return the set of letters which occur in words of + `\{m^n(w) | n \ge 0\}`, where `m` is this morphism and `w` is a word + (finite iterable is enough) inputted as a parameter. + + Requires this morphism to be an endomorphism. + + EXAMPLES:: + + sage: sorted(WordMorphism('a->ac,b->ce,c->bd,d->d,e->').reach('c')) + ['b', 'c', 'd', 'e'] + """ + if not self.is_endomorphism(): + raise TypeError(f'self ({self}) is not an endomorphism') + + visited = set(w) + todo = list(visited) + while todo: + a = todo.pop() + for b in self.image(a): + if b not in visited: + visited.add(b) + todo.append(b) + return visited + + def simplify(self, Z=None): + r""" + Return morphisms `h` and `k` such that this morphism is simplifiable + with respect to `h` and `k`. + + If this morphism is non-injective, this function always succeeds, but + can fail (raise ``ValueError``) if it is injective, even it if is + simplifiable. + + Let `f: X^* \rightarrow Y^*` be a morphism. Then `f` is simplifiable + with respect to morphisms `h: X^* \rightarrow Z^*` and + `k: Z^* \rightarrow Y^*`, if `f = k \circ h` and `|Z| < |X|`. If also + `Y \subseteq X`, then morphism `g: Z^* \rightarrow Z^* = h \circ k` is + a simplification of `f` (with respect to `h` and `k`). + + Therefore a morphism is simplifiable if it contains "more letters than + is needed". Simplification preserves some properties of the original + morphism (e.g. repetitiveness). + + Time complexity is on average quadratic with regards to the size of the + morphism. + + For more information see Section 3 in [KO2000]_. + + INPUT: + + - ``Z`` -- iterable, whose elements are used as an alphabet for the + simplification, default is ``self.domain().alphabet()`` + + EXAMPLES: + + Example of a simplifiable morphism:: + + sage: f = WordMorphism('a->bca,b->bcaa,c->bcaaa') + sage: h, k = f.simplify('xy') + sage: h + WordMorphism: a->xy, b->xyy, c->xyyy + sage: k + WordMorphism: x->bc, y->a + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: x->xyyxyyy, y->xy + + Example of a non-simplifiable morphism:: + + sage: WordMorphism('a->aa').simplify() + Traceback (most recent call last): + ... + ValueError: failed to simplify a->aa + + Example of a simplifiable morphism that the function fails on:: + + sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') + sage: f.simplify('xyz') + Traceback (most recent call last): + ... + ValueError: failed to simplify a->abcc, b->abcd, c->abdc, d->abdd + + Proof that the above morphism is simplifiable:: + + sage: k = WordMorphism('x->ab,y->c,z->d') + sage: h = WordMorphism('a->xyy,b->xyz,c->xzy,d->xzz') + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: x->xyyxyz, y->xzy, z->xzz + + Example of an erasing morphism:: + + sage: f = WordMorphism('a->abc,b->cc,c->') + sage: h, k = f.simplify(); h, k + (WordMorphism: a->a, b->b, c->, WordMorphism: a->abc, b->cc) + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: a->ab, b-> + + Example of a non-endomorphism:: + + sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') + sage: h, k = f.simplify(ZZ); h, k + (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) + sage: k * h == f + True + sage: len(k.domain().alphabet()) < len(f.domain().alphabet()) + True + """ + X = self.domain().alphabet() + Y = self.codomain().alphabet() + f = self._morph + + if self.is_erasing(): # Trivial case #1. + k = {letter: image for letter, image in f.items() if image} + h = {letter: [letter] if image else [] for letter, image in f.items()} + elif len(Y) < len(X): # Trivial case #2. + k = {x: [y] for x, y in zip(X, Y)} + k_inverse = {y: x for y, x in zip(Y, X)} + h = {x: [k_inverse[y] for y in image] for x, image in f.items()} + else: # Non-trivial case. + k = dict(f) + to_do = set(k) + to_remove = [] + while to_do: + # min() and remove() instead of pop() to have deterministic output. + letter1 = min(to_do) + to_do.remove(letter1) + image1 = k[letter1] + for letter2, image2 in k.items(): + if letter1 == letter2: + continue + if image1 == image2: + to_remove.append(letter2) + to_do.discard(letter2) + elif image1.is_prefix(image2): + k[letter2] = image2[image1.length():] + to_do.add(letter2) + elif image1.is_suffix(image2): + k[letter2] = image2[:-image1.length()] + to_do.add(letter2) + elif image2.is_prefix(image1): + k[letter1] = image1[image2.length():] + to_do.add(letter1) + break + elif image2.is_suffix(image1): + k[letter1] = image1[:-image2.length()] + to_do.add(letter1) + break + for letter in to_remove: + del k[letter] + to_remove = [] + + if len(k) == len(f): + raise ValueError(f'failed to simplify {self}') + + h = {} + for letter1, image1 in f.items(): + image3 = [] + while image1: + for letter2, image2 in k.items(): + if image2.is_prefix(image1): + image1 = image1[image2.length():] + image3.append(letter2) + break + h[letter1] = image3 + + k = type(self)(k, codomain=self.codomain()) + h = type(self)(h, domain=self.domain(), codomain=k.domain()) + + if Z is not None: # Custom alphabet. + old_Z_star = k.domain() + old_Z = old_Z_star.alphabet() + Z = [z for z, _ in zip(Z, old_Z)] + if len(Z) < len(old_Z): + raise ValueError(f'Z should have length at least {len(old_Z)}, is {len(Z)}') + Z_star = FiniteWords(Z) + h_new = {old: [new] for old, new in zip(old_Z, Z)} + k_new = {new: [old] for new, old in zip(Z, old_Z)} + h_new = type(self)(h_new, domain=old_Z_star, codomain=Z_star) + k_new = type(self)(k_new, domain=Z_star, codomain=old_Z_star) + h = h_new * h + k = k * k_new + + return h, k + + def simplify_injective(self): + r""" + Return a quadruplet `(g, h, k, i)`, where `g` is an injective + simplification of this morphism with respect to `h`, `k` and `i`. + + Requires this morphism to be an endomorphism. + + Basically calls :meth:`simplify` until it throws an exception, which + means the input was injective. If already the first call raises an + exception, instead of reraising it a quadruplet `(g, h, k, i)` is still + returned, where `g` and `h` are equal to this morphism, `k` is the + identity morphism and `i` is 0. + + Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then + `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with + respect to morphisms `h: X^* \rightarrow Z^*` and + `k: Z^* \rightarrow Y^*` and a positive integer `i`, if `g` is + injective and `|Z| < |X|` and `g^i = h \circ k` and `f^i = k \circ h`. + + For more information see Section 4 in [KO2000]_. + + EXAMPLES:: + + sage: f = WordMorphism('a->abc,b->a,c->bc') + sage: g, h, k, i = f.simplify_injective(); g, h, k, i + (WordMorphism: a->aa, WordMorphism: a->aa, b->a, c->a, WordMorphism: a->abc, 2) + sage: g.is_injective() + True + sage: g ** i == h * k + True + sage: f ** i == k * h + True + """ + if not self.is_endomorphism(): + raise TypeError(f'self ({self}) is not an endomorphism') + + try: + h, k = self.simplify() + except ValueError: + return self, self, self.domain().identity_morphism(), 0 + g = h * k + + for i in count(start=1): + try: + h_new, k_new = g.simplify() + g, h, k = h_new * k_new, h_new * h, k * k_new + except ValueError: + return g, h, k, i From eaa1e38a0d0d59ca6c8e94daf3fe2bf3681ebd2a Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 30 Mar 2021 19:09:04 +0200 Subject: [PATCH 089/706] A few more test fixes in schemes.toric --- src/sage/schemes/toric/divisor.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index e03b724ae6f..eddbda0d21d 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -113,7 +113,7 @@ in Basis lattice of The toric rational divisor class group of a 2-d CPR-Fano toric variety covered by 6 affine patches sage: Kc.ray(1).lift() - V(y) + V(v) + V(x) + V(w) Given a divisor `D`, we have an associated line bundle (or a reflexive sheaf, if `D` is not Cartier) `\mathcal{O}(D)`. Its sections are:: @@ -1011,10 +1011,10 @@ def move_away_from(self, cone): sage: Cartier 2*V(z0) + 2*V(z1) + V(z2) + V(z3) + V(z4) sage: Cartier.move_away_from(line_cone) - -V(z2) - V(z3) + V(z4) + 3*V(z2) + 3*V(z3) - V(z4) sage: QQ_Weil = X.divisor([1,0,1,1,0]) sage: QQ_Weil.move_away_from(line_cone) - V(z2) + 2*V(z2) + V(z3) - 1/2*V(z4) """ m = self.m(cone) X = self.parent().scheme() @@ -1112,9 +1112,9 @@ def Chow_cycle(self, ring=ZZ): EXAMPLES:: sage: dP6 = toric_varieties.dP6() - sage: cone = dP6.fan(1)[0] + sage: cone = dP6.fan(1)[5] sage: D = dP6.divisor(cone); D - V(x) + V(w) sage: D.Chow_cycle() ( 0 | -1, 0, 1, 1 | 0 ) sage: dP6.Chow_group()(cone) @@ -1959,11 +1959,11 @@ def __init__(self, toric_variety): [1 1 0 0 0] [0 2 1 1 1] sage: Cl._lift_matrix - [1 0] - [0 0] - [0 0] - [0 1] - [0 0] + [ 0 0] + [ 1 0] + [ 0 0] + [-2 1] + [ 0 0] sage: Cl._lift_matrix.base_ring() Integer Ring """ From df8c4d02c2d66ac394e60dd2afeee0607bed24c2 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 30 Mar 2021 19:14:06 +0200 Subject: [PATCH 090/706] One trivial fix in schemes.elliptic_curves --- .../elliptic_curves/ell_number_field.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 29316bebeb8..362ecba8f60 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -2228,29 +2228,29 @@ def torsion_points(self): sage: EK = E.base_extend(K) sage: EK.torsion_points() # long time (1s on sage.math, 2014) [(0 : 1 : 0), - (16 : 60 : 1), - (5 : 5 : 1), - (5 : -6 : 1), - (16 : -61 : 1), (t : 1/11*t^3 + 6/11*t^2 + 19/11*t + 48/11 : 1), - (-3/55*t^3 - 7/55*t^2 - 2/55*t - 133/55 : 6/55*t^3 + 3/55*t^2 + 25/11*t + 156/55 : 1), - (-9/121*t^3 - 21/121*t^2 - 127/121*t - 377/121 : -7/121*t^3 + 24/121*t^2 + 197/121*t + 16/121 : 1), - (5/121*t^3 - 14/121*t^2 - 158/121*t - 453/121 : -49/121*t^3 - 129/121*t^2 - 315/121*t - 207/121 : 1), - (10/121*t^3 + 49/121*t^2 + 168/121*t + 73/121 : 32/121*t^3 + 60/121*t^2 - 261/121*t - 807/121 : 1), (1/11*t^3 - 5/11*t^2 + 19/11*t - 40/11 : -6/11*t^3 - 3/11*t^2 - 26/11*t - 321/11 : 1), - (14/121*t^3 - 15/121*t^2 + 90/121*t + 232/121 : 16/121*t^3 - 69/121*t^2 + 293/121*t - 46/121 : 1), - (3/55*t^3 + 7/55*t^2 + 2/55*t + 78/55 : 7/55*t^3 - 24/55*t^2 + 9/11*t + 17/55 : 1), - (-5/121*t^3 + 36/121*t^2 - 84/121*t + 24/121 : 34/121*t^3 - 27/121*t^2 + 305/121*t + 708/121 : 1), - (-26/121*t^3 + 20/121*t^2 - 219/121*t - 995/121 : 15/121*t^3 + 156/121*t^2 - 232/121*t + 2766/121 : 1), (1/11*t^3 - 5/11*t^2 + 19/11*t - 40/11 : 6/11*t^3 + 3/11*t^2 + 26/11*t + 310/11 : 1), - (-26/121*t^3 + 20/121*t^2 - 219/121*t - 995/121 : -15/121*t^3 - 156/121*t^2 + 232/121*t - 2887/121 : 1), - (-5/121*t^3 + 36/121*t^2 - 84/121*t + 24/121 : -34/121*t^3 + 27/121*t^2 - 305/121*t - 829/121 : 1), - (3/55*t^3 + 7/55*t^2 + 2/55*t + 78/55 : -7/55*t^3 + 24/55*t^2 - 9/11*t - 72/55 : 1), - (14/121*t^3 - 15/121*t^2 + 90/121*t + 232/121 : -16/121*t^3 + 69/121*t^2 - 293/121*t - 75/121 : 1), (t : -1/11*t^3 - 6/11*t^2 - 19/11*t - 59/11 : 1), + (16 : 60 : 1), + (-3/55*t^3 - 7/55*t^2 - 2/55*t - 133/55 : 6/55*t^3 + 3/55*t^2 + 25/11*t + 156/55 : 1), + (14/121*t^3 - 15/121*t^2 + 90/121*t + 232/121 : 16/121*t^3 - 69/121*t^2 + 293/121*t - 46/121 : 1), + (-26/121*t^3 + 20/121*t^2 - 219/121*t - 995/121 : -15/121*t^3 - 156/121*t^2 + 232/121*t - 2887/121 : 1), (10/121*t^3 + 49/121*t^2 + 168/121*t + 73/121 : -32/121*t^3 - 60/121*t^2 + 261/121*t + 686/121 : 1), + (5 : 5 : 1), + (-9/121*t^3 - 21/121*t^2 - 127/121*t - 377/121 : -7/121*t^3 + 24/121*t^2 + 197/121*t + 16/121 : 1), + (3/55*t^3 + 7/55*t^2 + 2/55*t + 78/55 : 7/55*t^3 - 24/55*t^2 + 9/11*t + 17/55 : 1), + (-5/121*t^3 + 36/121*t^2 - 84/121*t + 24/121 : -34/121*t^3 + 27/121*t^2 - 305/121*t - 829/121 : 1), (5/121*t^3 - 14/121*t^2 - 158/121*t - 453/121 : 49/121*t^3 + 129/121*t^2 + 315/121*t + 86/121 : 1), + (5 : -6 : 1), + (5/121*t^3 - 14/121*t^2 - 158/121*t - 453/121 : -49/121*t^3 - 129/121*t^2 - 315/121*t - 207/121 : 1), + (-5/121*t^3 + 36/121*t^2 - 84/121*t + 24/121 : 34/121*t^3 - 27/121*t^2 + 305/121*t + 708/121 : 1), + (3/55*t^3 + 7/55*t^2 + 2/55*t + 78/55 : -7/55*t^3 + 24/55*t^2 - 9/11*t - 72/55 : 1), (-9/121*t^3 - 21/121*t^2 - 127/121*t - 377/121 : 7/121*t^3 - 24/121*t^2 - 197/121*t - 137/121 : 1), + (16 : -61 : 1), + (10/121*t^3 + 49/121*t^2 + 168/121*t + 73/121 : 32/121*t^3 + 60/121*t^2 - 261/121*t - 807/121 : 1), + (-26/121*t^3 + 20/121*t^2 - 219/121*t - 995/121 : 15/121*t^3 + 156/121*t^2 - 232/121*t + 2766/121 : 1), + (14/121*t^3 - 15/121*t^2 + 90/121*t + 232/121 : -16/121*t^3 + 69/121*t^2 - 293/121*t - 75/121 : 1), (-3/55*t^3 - 7/55*t^2 - 2/55*t - 133/55 : -6/55*t^3 - 3/55*t^2 - 25/11*t - 211/55 : 1)] :: From b11ff20d03e21a98e0122f9d0adfe4dd72c0e042 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 30 Mar 2021 19:57:55 +0200 Subject: [PATCH 091/706] Elliptic curve group generators can be different on each run, mark tests as random --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index e477c93c147..76b111b4237 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -786,10 +786,12 @@ def gens(self): 2 sage: E.cardinality() 867361737988403547206134229616487867594472 - sage: E.gens()[0].order() + sage: a = E.gens()[0].order(); a # random 433680868994201773603067114808243933797236 - sage: E.gens()[1].order() + sage: b = E.gens()[1].order(); b # random 30977204928157269543076222486303138128374 + sage: lcm(a,b) + 433680868994201773603067114808243933797236 """ G = self.__pari__().ellgroup(flag=1) return tuple(self.point(list(pt)) for pt in G[2]) From cd6212dd1db8e0042c717e050887346e9c00a164 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 1 Apr 2021 19:47:16 +0200 Subject: [PATCH 092/706] expose memoryallocator as extra package --- build/pkgs/memory_allocator/SPKG.rst | 19 +++++++++++++++++++ build/pkgs/memory_allocator/checksums.ini | 5 +++++ build/pkgs/memory_allocator/dependencies | 5 +++++ .../pkgs/memory_allocator/package-version.txt | 1 + build/pkgs/memory_allocator/spkg-check.in | 1 + build/pkgs/memory_allocator/spkg-install.in | 1 + build/pkgs/memory_allocator/type | 1 + src/sage/ext/memory_allocator.pyx | 2 ++ 8 files changed, 35 insertions(+) create mode 100644 build/pkgs/memory_allocator/SPKG.rst create mode 100644 build/pkgs/memory_allocator/checksums.ini create mode 100644 build/pkgs/memory_allocator/dependencies create mode 100644 build/pkgs/memory_allocator/package-version.txt create mode 100644 build/pkgs/memory_allocator/spkg-check.in create mode 100644 build/pkgs/memory_allocator/spkg-install.in create mode 100644 build/pkgs/memory_allocator/type diff --git a/build/pkgs/memory_allocator/SPKG.rst b/build/pkgs/memory_allocator/SPKG.rst new file mode 100644 index 00000000000..9a4b057f8f3 --- /dev/null +++ b/build/pkgs/memory_allocator/SPKG.rst @@ -0,0 +1,19 @@ +MemoryAllocator: An extension class to allocate memory easily with cython. +========================================================================== + +Description +----------- + +development website: https://github.com/kliem/memory_allocator + +PyPI page: XXX + +License +------- + +GPL-3.0 + +Upstream Contact +---------------- + +https://github.com/kliem/memory_allocator diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini new file mode 100644 index 00000000000..eb282679743 --- /dev/null +++ b/build/pkgs/memory_allocator/checksums.ini @@ -0,0 +1,5 @@ +tarball=pycparser-VERSION.tar.gz +sha1=a016b58e101e9ba9bddc01c40aab3baca048c056 +md5=af75874705418a4d4d7e7333d3318909 +cksum=1342787485 +upstream_url=https://github.com/kliem/memory_allocator/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/memory_allocator/dependencies b/build/pkgs/memory_allocator/dependencies new file mode 100644 index 00000000000..d3dac75e5f2 --- /dev/null +++ b/build/pkgs/memory_allocator/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) cython | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/memory_allocator/package-version.txt b/build/pkgs/memory_allocator/package-version.txt new file mode 100644 index 00000000000..388bb06819f --- /dev/null +++ b/build/pkgs/memory_allocator/package-version.txt @@ -0,0 +1 @@ +0.1.0-alpha diff --git a/build/pkgs/memory_allocator/spkg-check.in b/build/pkgs/memory_allocator/spkg-check.in new file mode 100644 index 00000000000..c9f080a7d0d --- /dev/null +++ b/build/pkgs/memory_allocator/spkg-check.in @@ -0,0 +1 @@ +python3 -c "import memory_allocator.test; import os; os.chdir('src'); exec(open('test.py').read())" diff --git a/build/pkgs/memory_allocator/spkg-install.in b/build/pkgs/memory_allocator/spkg-install.in new file mode 100644 index 00000000000..deba1bb42bb --- /dev/null +++ b/build/pkgs/memory_allocator/spkg-install.in @@ -0,0 +1 @@ +cd src && sdh_pip_install . diff --git a/build/pkgs/memory_allocator/type b/build/pkgs/memory_allocator/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/memory_allocator/type @@ -0,0 +1 @@ +standard diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx index a5609cf40b3..33c9d7a7394 100644 --- a/src/sage/ext/memory_allocator.pyx +++ b/src/sage/ext/memory_allocator.pyx @@ -1,4 +1,5 @@ from cysignals.memory cimport * +from sage.misc.superseded import deprecation cdef class MemoryAllocator: @@ -39,6 +40,7 @@ cdef class MemoryAllocator: 1 16 """ + deprecation(31591, "this class is deprecated; use the class from the python package `memory_allocator`") self.n = 0 self.size = 16 self.pointers = self.static_pointers From b2c75d84bb0de856bf0d6e586e64b20f3466ec23 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 1 Apr 2021 20:10:41 +0200 Subject: [PATCH 093/706] replace by memory_allocator package --- src/sage/calculus/integration.pyx | 6 +++--- src/sage/data_structures/bitset_base.pxd | 4 ++-- src/sage/ext/memory_allocator.pxd | 2 ++ src/sage/ext/memory_allocator.pyx | 4 ++++ .../polyhedron/combinatorial_polyhedron/base.pxd | 2 +- .../combinatorial_polyhedron/combinatorial_face.pxd | 2 +- .../combinatorial_polyhedron/conversions.pyx | 7 +++++-- .../combinatorial_polyhedron/face_data_structure.pxd | 2 +- .../combinatorial_polyhedron/face_iterator.pxd | 2 +- .../combinatorial_polyhedron/list_of_faces.pxd | 2 +- .../polyhedron_face_lattice.pxd | 2 +- src/sage/graphs/asteroidal_triples.pyx | 2 +- src/sage/graphs/base/static_sparse_graph.pyx | 2 +- src/sage/graphs/centrality.pyx | 2 +- src/sage/graphs/chrompoly.pyx | 2 +- src/sage/graphs/connectivity.pxd | 2 +- src/sage/graphs/distances_all_pairs.pyx | 2 +- src/sage/graphs/generic_graph_pyx.pxd | 2 +- src/sage/graphs/generic_graph_pyx.pyx | 2 +- src/sage/graphs/genus.pyx | 2 +- src/sage/graphs/graph_decompositions/bandwidth.pyx | 2 +- .../graph_decompositions/clique_separators.pyx | 8 +++----- src/sage/graphs/hyperbolicity.pyx | 2 +- src/sage/graphs/mcqd.pyx | 2 +- src/sage/graphs/spanning_tree.pyx | 2 +- src/sage/graphs/traversals.pyx | 12 ++++++------ src/sage/graphs/weakly_chordal.pyx | 2 +- src/sage/numerical/backends/glpk_backend.pyx | 10 +++++----- 28 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/sage/calculus/integration.pyx b/src/sage/calculus/integration.pyx index f76a6eeab81..93a3681854d 100644 --- a/src/sage/calculus/integration.pyx +++ b/src/sage/calculus/integration.pyx @@ -27,15 +27,15 @@ AUTHORS: # **************************************************************************** from cysignals.signals cimport sig_on, sig_off -from sage.rings.real_double import RDF +from memory_allocator cimport MemoryAllocator +import inspect +from sage.rings.real_double import RDF from sage.libs.gsl.all cimport * from sage.misc.sageinspect import sage_getargspec from sage.ext.fast_eval cimport FastDoubleFunc from sage.ext.interpreters.wrapper_rdf cimport Wrapper_rdf from sage.ext.fast_callable import fast_callable -from sage.ext.memory_allocator cimport MemoryAllocator -import inspect cdef class PyFunctionWrapper: diff --git a/src/sage/data_structures/bitset_base.pxd b/src/sage/data_structures/bitset_base.pxd index f36a530be57..44baaedaec9 100644 --- a/src/sage/data_structures/bitset_base.pxd +++ b/src/sage/data_structures/bitset_base.pxd @@ -33,13 +33,13 @@ AUTHORS: from libc.string cimport strlen from cysignals.memory cimport check_calloc, check_reallocarray, sig_malloc, sig_free +from memory_allocator cimport MemoryAllocator +from cython.operator import preincrement as preinc from sage.cpython.string cimport char_to_str, str_to_bytes, bytes_to_str from sage.libs.gmp.mpn cimport * from sage.libs.gmp.types cimport * from sage.data_structures.sparse_bitset cimport sparse_bitset_t -from cython.operator import preincrement as preinc -from sage.ext.memory_allocator cimport MemoryAllocator cdef extern from *: diff --git a/src/sage/ext/memory_allocator.pxd b/src/sage/ext/memory_allocator.pxd index 2e1fd267c51..1be5ba69552 100644 --- a/src/sage/ext/memory_allocator.pxd +++ b/src/sage/ext/memory_allocator.pxd @@ -64,6 +64,8 @@ cdef class MemoryAllocator: ....: ptr = mem.aligned_malloc(2**i, 4048) ....: assert ptr == ( ptr) & ~(2**i-1) ....: ''') + doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` + See https://trac.sagemath.org/31591 for details. """ cdef size_t extra = alignment - 1 return align(self.malloc(size + extra), alignment) diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx index 33c9d7a7394..b5ffe70ccc1 100644 --- a/src/sage/ext/memory_allocator.pyx +++ b/src/sage/ext/memory_allocator.pyx @@ -24,6 +24,8 @@ cdef class MemoryAllocator: ....: mem.aligned_calloc(16, n, 16) ....: mem.aligned_allocarray(8, n, 8) ....: ''') + doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` + See https://trac.sagemath.org/31591 for details. """ def __cinit__(self): """ @@ -134,6 +136,8 @@ cdef class MemoryAllocator: ....: mem2.realloc(ptr, 21) ....: ''') sage: test_realloc_good() + doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` + See https://trac.sagemath.org/31591 for details. sage: test_realloc_NULL() sage: test_realloc_bad() Traceback (most recent call last): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 59c77ec0faa..5a2568d1917 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -1,5 +1,5 @@ cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .face_iterator cimport FaceIterator, CombinatorialFace from .list_of_faces cimport ListOfFaces diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..18afc3f397a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -1,5 +1,5 @@ cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .list_of_faces cimport ListOfFaces from .face_data_structure cimport face_t diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index ff7cfd5ef31..eb1666b99f6 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -67,17 +67,20 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** +from memory_allocator cimport MemoryAllocator + from sage.structure.element import is_Matrix +from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from .list_of_faces cimport ListOfFaces -from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -from sage.ext.memory_allocator cimport MemoryAllocator from .face_data_structure cimport face_next_atom, face_add_atom_safe, facet_set_coatom, face_clear from .face_list_data_structure cimport face_list_t + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython + def _Vrep_list_to_bit_rep_wrapper(tup): r""" A function to allow doctesting of :func:`Vrep_list_to_bit_rep`. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index 2b509aaec01..4270885d515 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -11,7 +11,7 @@ Cython data structure for combinatorial faces. # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * ctypedef int simple diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 63ed7858024..8564090855d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -1,5 +1,5 @@ cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.structure.sage_object cimport SageObject from .list_of_faces cimport ListOfFaces from .face_data_structure cimport face_t diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd index a571b8c1c84..8182b0ef704 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd @@ -1,5 +1,5 @@ cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from .face_list_data_structure cimport face_list_t @cython.final diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 951fd46db41..f495ac4433b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -1,5 +1,5 @@ cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from .list_of_faces cimport ListOfFaces from .face_data_structure cimport face_t from .face_list_data_structure cimport face_list_t diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 8a86d969f4c..d9836672922 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -62,10 +62,10 @@ Functions from libc.stdint cimport uint32_t from cysignals.signals cimport sig_on, sig_off +from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph -from sage.ext.memory_allocator cimport MemoryAllocator def is_asteroidal_triple_free(G, certificate=False): """ diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 11782242daa..690cd85454c 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -189,12 +189,12 @@ from libc.math cimport sqrt from libcpp.vector cimport vector from cysignals.memory cimport check_allocarray, check_calloc, sig_free from cysignals.signals cimport sig_on, sig_off +from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * from sage.graphs.base.c_graph cimport CGraph from .static_sparse_backend cimport StaticSparseCGraph from .static_sparse_backend cimport StaticSparseBackend -from sage.ext.memory_allocator cimport MemoryAllocator cdef extern from "fenv.h": int FE_TONEAREST diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index 420f264e209..d73787a4b10 100755 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -20,12 +20,12 @@ from libc.string cimport memset from libc.stdint cimport uint32_t from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport sig_check +from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * from sage.graphs.base.static_sparse_graph cimport * from sage.libs.gmp.mpq cimport * from sage.rings.rational cimport Rational -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.boost_graph import shortest_paths as boost_shortest_paths import random diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index 8d7cda13a77..e1b9c80d2a0 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -25,11 +25,11 @@ REFERENCE: #***************************************************************************** from cysignals.signals cimport sig_check +from memory_allocator cimport MemoryAllocator from sage.libs.gmp.mpz cimport * from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer -from sage.ext.memory_allocator cimport MemoryAllocator from sage.misc.all import prod diff --git a/src/sage/graphs/connectivity.pxd b/src/sage/graphs/connectivity.pxd index d28543974fd..36898d75e76 100644 --- a/src/sage/graphs/connectivity.pxd +++ b/src/sage/graphs/connectivity.pxd @@ -1,4 +1,4 @@ -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.graphs.generic_graph_pyx cimport GenericGraph_pyx ctypedef struct _LinkedListNode: diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 92b1d503f6a..5e92e023d13 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -128,10 +128,10 @@ from libc.stdint cimport uint64_t, UINT64_MAX from libc.stdint cimport uint32_t, INT32_MAX, UINT32_MAX from cysignals.memory cimport sig_malloc, sig_calloc, sig_free from cysignals.signals cimport sig_on, sig_off +from memory_allocator cimport MemoryAllocator from sage.graphs.base.c_graph cimport CGraphBackend from sage.graphs.base.c_graph cimport CGraph -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport (short_digraph, init_short_digraph, diff --git a/src/sage/graphs/generic_graph_pyx.pxd b/src/sage/graphs/generic_graph_pyx.pxd index b106a9c708b..23f45e38080 100644 --- a/src/sage/graphs/generic_graph_pyx.pxd +++ b/src/sage/graphs/generic_graph_pyx.pxd @@ -1,6 +1,6 @@ from sage.structure.sage_object cimport SageObject from sage.graphs.base.dense_graph cimport DenseGraph -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator ctypedef int * D_TWO ctypedef char * D_THREE diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 2f7d83f2db0..d9643cef474 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -28,11 +28,11 @@ import cython from sage.data_structures.binary_matrix cimport * from libc.math cimport sqrt, fabs from libc.string cimport memset +from memory_allocator cimport MemoryAllocator from sage.cpython.string cimport char_to_str from sage.libs.gmp.mpz cimport * from sage.misc.prandom import random -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph diff --git a/src/sage/graphs/genus.pyx b/src/sage/graphs/genus.pyx index db971c877d8..a4fe3776eaa 100644 --- a/src/sage/graphs/genus.pyx +++ b/src/sage/graphs/genus.pyx @@ -38,7 +38,7 @@ described throughout the file. #***************************************************************************** from libc.string cimport memcpy -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from cysignals.signals cimport sig_on, sig_off cimport sage.combinat.permutation_cython diff --git a/src/sage/graphs/graph_decompositions/bandwidth.pyx b/src/sage/graphs/graph_decompositions/bandwidth.pyx index 96a07a355c7..91ffeeab130 100644 --- a/src/sage/graphs/graph_decompositions/bandwidth.pyx +++ b/src/sage/graphs/graph_decompositions/bandwidth.pyx @@ -113,10 +113,10 @@ Functions from libc.stdint cimport uint16_t from cysignals.signals cimport sig_check +from memory_allocator cimport MemoryAllocator from sage.graphs.distances_all_pairs cimport all_pairs_shortest_path_BFS from sage.graphs.base.boost_graph import bandwidth_heuristics -from sage.ext.memory_allocator cimport MemoryAllocator ctypedef uint16_t index_t diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 1158ed629f3..dbcbdd5ee79 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -23,18 +23,16 @@ Methods from libcpp.pair cimport pair from libcpp.vector cimport vector +from libc.stdint cimport uint32_t +from cysignals.signals cimport sig_on, sig_off, sig_check +from memory_allocator cimport MemoryAllocator -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from sage.graphs.base.static_sparse_graph cimport has_edge -from libc.stdint cimport uint32_t - -from cysignals.signals cimport sig_on, sig_off, sig_check from sage.sets.set import Set - from sage.graphs.traversals cimport maximum_cardinality_search_M_short_digraph def make_tree(atoms, cliques): diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 479dfd40616..1f479774b7e 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -151,6 +151,7 @@ Methods from libc.string cimport memset from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport sig_on, sig_off +from memory_allocator cimport MemoryAllocator from sage.graphs.graph import Graph from sage.graphs.distances_all_pairs cimport c_distances_all_pairs @@ -159,7 +160,6 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.functions.other import floor from sage.data_structures.bitset import Bitset -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph diff --git a/src/sage/graphs/mcqd.pyx b/src/sage/graphs/mcqd.pyx index 7916e7e3840..8b8e7d98d52 100644 --- a/src/sage/graphs/mcqd.pyx +++ b/src/sage/graphs/mcqd.pyx @@ -2,7 +2,7 @@ # sage_setup: distribution = sage-mcqd from cysignals.signals cimport sig_on, sig_off -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator def mcqd(G): """ diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 922ecc62ecb..da17cb2183e 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -37,7 +37,7 @@ Methods # **************************************************************************** cimport cython -from sage.ext.memory_allocator cimport MemoryAllocator +from memory_allocator cimport MemoryAllocator from sage.sets.disjoint_set cimport DisjointSet_of_hashables diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 76d27a8b5c1..a9f52acf5e4 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -38,16 +38,16 @@ Methods from collections import deque from libc.string cimport memset -from sage.ext.memory_allocator cimport MemoryAllocator -from sage.graphs.base.static_sparse_graph cimport init_short_digraph -from sage.graphs.base.static_sparse_graph cimport free_short_digraph -from sage.graphs.base.static_sparse_graph cimport out_degree, has_edge from libc.stdint cimport uint32_t -from cysignals.signals cimport sig_on, sig_off, sig_check - from libcpp.queue cimport priority_queue from libcpp.pair cimport pair from libcpp.vector cimport vector +from cysignals.signals cimport sig_on, sig_off, sig_check +from memory_allocator cimport MemoryAllocator + +from sage.graphs.base.static_sparse_graph cimport init_short_digraph +from sage.graphs.base.static_sparse_graph cimport free_short_digraph +from sage.graphs.base.static_sparse_graph cimport out_degree, has_edge def _is_valid_lex_BFS_order(G, L): diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index fed5ced007d..3b8b0c6feb3 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -32,8 +32,8 @@ Methods # http://www.gnu.org/licenses/ ############################################################################## +from memory_allocator cimport MemoryAllocator from sage.data_structures.bitset_base cimport * -from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index e4ec46b93ff..f74801b64fc 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -19,17 +19,17 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from libc.float cimport DBL_MAX +from libc.limits cimport INT_MAX from cysignals.memory cimport sig_malloc, sig_free from cysignals.signals cimport sig_on, sig_off +from memory_allocator cimport MemoryAllocator from sage.cpython.string cimport char_to_str, str_to_bytes from sage.cpython.string import FS_ENCODING -from sage.ext.memory_allocator cimport MemoryAllocator from sage.numerical.mip import MIPSolverException from sage.libs.glpk.constants cimport * from sage.libs.glpk.lp cimport * -from libc.float cimport DBL_MAX -from libc.limits cimport INT_MAX cdef class GLPKBackend(GenericBackend): @@ -2797,7 +2797,7 @@ cdef class GLPKBackend(GenericBackend): raise ValueError("The variable's index j must satisfy 0 <= j < number_of_variables") glp_set_col_stat(self.lp, j+1, stat) - + cpdef int warm_up(self): r""" Warm up the basis using current statuses assigned to rows and cols. @@ -2807,7 +2807,7 @@ cdef class GLPKBackend(GenericBackend): - Returns the warming up status * 0 The operation has been successfully performed. - * GLP_EBADB The basis matrix is invalid. + * GLP_EBADB The basis matrix is invalid. * GLP_ESING The basis matrix is singular within the working precision. * GLP_ECOND The basis matrix is ill-conditioned. From 5161d658528e5f61e052a270611cfad2a8da5fe4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Apr 2021 12:24:05 -0700 Subject: [PATCH 094/706] build/pkgs/sage_conf/src/sage_conf.py.in: Move SAGE_ROOT, SAGE_LOCAL to beginning of file; only use substitution @prefix@ once --- build/pkgs/sage_conf/src/sage_conf.py.in | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 6ac91e68018..4cdc829c95c 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -2,6 +2,13 @@ VERSION = "@PACKAGE_VERSION@" +# The following must not be used during build to determine source or installation +# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in +# These variables come first so that other substituted variable values can refer +# to it. +SAGE_LOCAL = "@prefix@" +SAGE_ROOT = "@SAGE_ROOT@" + MAXIMA = "@prefix@/bin/maxima" ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" @@ -11,7 +18,7 @@ NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script # TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. -ECL_CONFIG = "@prefix@/bin/ecl-config" +ECL_CONFIG = "${prefix}/bin/ecl-config".subst('${prefix}', SAGE_LOCAL) SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" @@ -21,13 +28,8 @@ SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" CBLAS_PC_MODULES = "cblas" # Used in sage.repl.ipython_kernel.install -MATHJAX_DIR = "@prefix@/share/mathjax" -THREEJS_DIR = "@prefix@/share/threejs" - -# The following must not be used during build to determine source or installation -# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in -SAGE_LOCAL = "@prefix@" -SAGE_ROOT = "@SAGE_ROOT@" +MATHJAX_DIR = SAGE_LOCAL + "/share/mathjax" +THREEJS_DIR = SAGE_LOCAL + "/share/threejs" # Entry point 'sage-config'. It does not depend on any packages. From 986ca18c4a3b255daede540ee114e9f38cbe65bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Apr 2021 12:35:43 -0700 Subject: [PATCH 095/706] build/pkgs/sage_conf/src/sage_conf.py.in: replace subst by replace --- build/pkgs/sage_conf/src/sage_conf.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 4cdc829c95c..c627bc50943 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -18,7 +18,7 @@ NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script # TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. -ECL_CONFIG = "${prefix}/bin/ecl-config".subst('${prefix}', SAGE_LOCAL) +ECL_CONFIG = "${prefix}/bin/ecl-config".replace('${prefix}', SAGE_LOCAL) SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" From 4f53a36c00471a21fc465fb0d798c9e6f6b3c49e Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 3 Apr 2021 19:09:17 +0200 Subject: [PATCH 096/706] Update sandpiles tests for new Smith form --- src/doc/en/thematic_tutorials/sandpile.rst | 12 ++++++------ src/sage/sandpiles/sandpile.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst index 487d2f4d29a..7dd7091415a 100644 --- a/src/doc/en/thematic_tutorials/sandpile.rst +++ b/src/doc/en/thematic_tutorials/sandpile.rst @@ -1384,12 +1384,12 @@ EXAMPLES:: sage: s = sandpiles.Cycle(5) sage: s.group_gens() - [{1: 1, 2: 1, 3: 1, 4: 0}] + [{1: 0, 2: 1, 3: 1, 4: 1}] sage: s.group_gens()[0].order() 5 sage: s = sandpiles.Complete(5) sage: s.group_gens(False) - [[2, 2, 3, 2], [2, 3, 2, 2], [3, 2, 2, 2]] + [[2, 3, 2, 2], [2, 2, 3, 2], [2, 2, 2, 3]] sage: [i.order() for i in s.group_gens()] [5, 5, 5] sage: s.invariant_factors() @@ -2058,7 +2058,7 @@ single generator for the group of solutions. sage: S = sandpiles.Complete(4) sage: S.points() - [[1, I, -I], [I, 1, -I]] + [[-I, I, 1], [-I, 1, I]] --- @@ -4257,7 +4257,7 @@ EXAMPLES:: sage: D.is_linearly_equivalent([0,1,1]) True sage: D.is_linearly_equivalent([0,1,1],True) - (1, 0, 0) + (0, -1, -1) sage: v = vector(D.is_linearly_equivalent([0,1,1],True)) sage: vector(D.values()) - s.laplacian()*v (0, 1, 1) @@ -4983,8 +4983,8 @@ Other sage: P = matrix([[2,3,-7,-3],[5,2,-5,5],[8,2,5,4],[-5,-9,6,6]]) sage: wilmes_algorithm(P) - [ 1642 -13 -1627 -1] - [ -1 1980 -1582 -397] + [ 3279 -79 -1599 -1600] + [ -1 1539 -136 -1402] [ 0 -1 1650 -1649] [ 0 0 -1658 1658] diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ad1cc562762..ce4e7fdc9bb 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -1494,12 +1494,12 @@ def group_gens(self, verbose=True): sage: s = sandpiles.Cycle(5) sage: s.group_gens() - [{1: 1, 2: 1, 3: 1, 4: 0}] + [{1: 0, 2: 1, 3: 1, 4: 1}] sage: s.group_gens()[0].order() 5 sage: s = sandpiles.Complete(5) sage: s.group_gens(False) - [[2, 2, 3, 2], [2, 3, 2, 2], [3, 2, 2, 2]] + [[2, 3, 2, 2], [2, 2, 3, 2], [2, 2, 2, 3]] sage: [i.order() for i in s.group_gens()] [5, 5, 5] sage: s.invariant_factors() @@ -2817,7 +2817,7 @@ def points(self): sage: S = sandpiles.Complete(4) sage: S.points() - [[1, I, -I], [I, 1, -I]] + [[-I, I, 1], [-I, 1, I]] """ return self._points @@ -5015,7 +5015,7 @@ def is_linearly_equivalent(self, D, with_firing_vector=False): sage: D.is_linearly_equivalent([0,1,1]) True sage: D.is_linearly_equivalent([0,1,1],True) - (1, 0, 0) + (0, -1, -1) sage: v = vector(D.is_linearly_equivalent([0,1,1],True)) sage: vector(D.values()) - s.laplacian()*v (0, 1, 1) @@ -6646,8 +6646,8 @@ def wilmes_algorithm(M): sage: P = matrix([[2,3,-7,-3],[5,2,-5,5],[8,2,5,4],[-5,-9,6,6]]) sage: wilmes_algorithm(P) - [ 1642 -13 -1627 -1] - [ -1 1980 -1582 -397] + [ 3279 -79 -1599 -1600] + [ -1 1539 -136 -1402] [ 0 -1 1650 -1649] [ 0 0 -1658 1658] From 6f05cc2e4c622125dd8e6501254293fdcb9d5996 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Apr 2021 18:16:13 -0700 Subject: [PATCH 097/706] sage_setup.setenv: New, use it in setup.py so that build works outside of sage-env --- build/pkgs/sage_conf/src/sage_conf.py.in | 4 +++ build/pkgs/sagelib/src/setup.py | 3 ++ src/sage/env.py | 4 +++ src/sage_setup/setenv.py | 44 ++++++++++++++++++++++++ src/setup.py | 3 ++ 5 files changed, 58 insertions(+) create mode 100644 src/sage_setup/setenv.py diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index c627bc50943..614d56778dc 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -27,6 +27,10 @@ SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" # always provides cblas.pc, if necessary by creating a facade pc file for a system BLAS. CBLAS_PC_MODULES = "cblas" +# for sage_setup.setenv +SAGE_ARCHFLAGS = "@SAGE_ARCHFLAGS@" +SAGE_PKG_CONFIG_PATH = "@SAGE_PKG_CONFIG_PATH@".replace('$SAGE_LOCAL', SAGE_LOCAL) + # Used in sage.repl.ipython_kernel.install MATHJAX_DIR = SAGE_LOCAL + "/share/mathjax" THREEJS_DIR = SAGE_LOCAL + "/share/threejs" diff --git a/build/pkgs/sagelib/src/setup.py b/build/pkgs/sagelib/src/setup.py index 0d7c29d7463..a1e0baa07a2 100755 --- a/build/pkgs/sagelib/src/setup.py +++ b/build/pkgs/sagelib/src/setup.py @@ -25,6 +25,9 @@ from sage_setup.excepthook import excepthook sys.excepthook = excepthook +from sage_setup.setenv import setenv +setenv() + ######################################################### ### Configuration ######################################################### diff --git a/src/sage/env.py b/src/sage/env.py index 2908f5d04fa..ea69e8499b3 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -189,6 +189,10 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st DOT_SAGE = var("DOT_SAGE", join(os.environ.get("HOME"), ".sage")) SAGE_STARTUP_FILE = var("SAGE_STARTUP_FILE", join(DOT_SAGE, "init.sage")) +# for sage_setup.setenv +SAGE_ARCHFLAGS = var("SAGE_ARCHFLAGS", "unset") +SAGE_PKG_CONFIG_PATH = var("SAGE_PKG_CONFIG_PATH") + # installation directories for various packages CONWAY_POLYNOMIALS_DATA_DIR = var("CONWAY_POLYNOMIALS_DATA_DIR", join(SAGE_SHARE, "conway_polynomials")) GRAPHS_DATA_DIR = var("GRAPHS_DATA_DIR", join(SAGE_SHARE, "graphs")) diff --git a/src/sage_setup/setenv.py b/src/sage_setup/setenv.py new file mode 100644 index 00000000000..c8b9d22e0c3 --- /dev/null +++ b/src/sage_setup/setenv.py @@ -0,0 +1,44 @@ +# Set some environment variables in the running process + +import os +import sage.env +from pathlib import Path + +def _environ_prepend(var, value, separator=':'): + if value: + if var in os.environ: + os.environ[var] = value + separator + os.environ[var] + else: + os.environ[var] = value + +def setenv(): + from sage.env import UNAME, SAGE_LOCAL, SAGE_VENV, SAGE_ARCHFLAGS, SAGE_PKG_CONFIG_PATH + + ## + ## from sage-env: + ## + + # not done: CC, CXX, FC, OBJC, OBJCXX, F77, F90, F95 + if 'ARCHFLAGS' not in os.environ and SAGE_ARCHFLAGS != "unset": + os.environ['ARCHFLAGS'] = SAGE_ARCHFLAGS + _environ_prepend('PKG_CONFIG_PATH', SAGE_PKG_CONFIG_PATH) + if SAGE_LOCAL: + _environ_prepend('PATH', f'{SAGE_LOCAL}/bin') + _environ_prepend('LIBRARY_PATH', f'{SAGE_LOCAL}/lib') + _environ_prepend('CPATH', f'{SAGE_LOCAL}/include') + _environ_prepend('LDFLAGS', f'-L{SAGE_LOCAL}/lib -Wl,-rpath,{SAGE_LOCAL}/lib', + separator=' ') + if UNAME == 'Linux': + _environ_prepend('LDFLAGS', f'-Wl,-rpath-link,{SAGE_LOCAL}/lib', + separator=' ') + if Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve(): + _environ_prepend('PATH', f'{SAGE_VENV}/bin') + # the following two are not done by sage-env + #_environ_prepend('LIBRARY_PATH', f'{SAGE_VENV}/lib') + #_environ_prepend('CPATH', f'{SAGE_VENV}/include') + + # not done: PATH prepend of SAGE_SRC/bin, SAGE_ROOT/build/bin + # not done: MACOSX_DEPLOYMENT_TARGET + # not done: PATH prepend for ccache & CCACHE_BASEDIR + # not done: Cygwin LD_LIBRARY_PATH + # not done: OPENBLAS_NUM_THREADS diff --git a/src/setup.py b/src/setup.py index df8242c71a0..da309077508 100755 --- a/src/setup.py +++ b/src/setup.py @@ -39,6 +39,9 @@ sys.excepthook = excepthook +from sage_setup.setenv import setenv +setenv() + # ######################################################## # ## Configuration # ######################################################## From 6719d9e5d59addccd093d09d04a0f553520f580a Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 6 Apr 2021 15:54:11 +0200 Subject: [PATCH 098/706] Trac 31616: make quaternion_algebra() method of Brandt modules directly call QuaternionAlgebra() --- src/sage/modular/quatalg/brandt.py | 35 ++++++--------------- src/sage/schemes/elliptic_curves/heegner.py | 29 ++++++++++------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 568f28e6649..f88d44a4654 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -356,7 +356,7 @@ def maximal_order(A): sage: A = BrandtModule(17).quaternion_algebra() sage: sage.modular.quatalg.brandt.maximal_order(A) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) sage: A = QuaternionAlgebra(17,names='i,j,k') sage: A.maximal_order() @@ -382,9 +382,9 @@ def basis_for_left_ideal(R, gens): sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens() sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)]) - [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k] + [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)]) - [3/2 + 1/2*j + 2*k, 3/2*i + 3/2*k, j + k, 3*k] + [3/2 + 1/2*i + k, i + 2*k, 3/2*j + 3/2*k, 3*k] """ return basis_for_quaternion_lattice([b * g for b in R.basis() for g in gens]) @@ -409,14 +409,14 @@ def right_order(R, basis): sage: B = BrandtModule(17); basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), B.maximal_order().basis()) sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) sage: basis - [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k] + [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens() sage: basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i*j-j]) sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*i + 1/2*j + 17/2*k, i, j + 8*k, 9*k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) """ # Compute matrix of multiplication by each element of the basis. B = R.basis() @@ -790,26 +790,9 @@ def quaternion_algebra(self): sage: BrandtModule(5).quaternion_algebra() Quaternion Algebra (-2, -5) with base ring Rational Field sage: BrandtModule(17).quaternion_algebra() - Quaternion Algebra (-17, -3) with base ring Rational Field + Quaternion Algebra (-3, -17) with base ring Rational Field """ - p = self.N() - assert p.is_prime(), "we have only implemented the prime case" - if p == 2: - QA = -1 - QB = -1 - elif p % 4 == 3: - QA = -1 - QB = -p - elif p % 8 == 5: - QA = -2 - QB = -p - elif p % 8 == 1: - q = 3 - while q % 4 != 3 or kronecker(p, q) != -1: - q = next_prime(q) - QA = -p - QB = -q - return QuaternionAlgebra(QQ, QA, QB) + return QuaternionAlgebra(self.N()) @cached_method def maximal_order(self): @@ -819,7 +802,7 @@ def maximal_order(self): EXAMPLES:: sage: BrandtModule(17).maximal_order() - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) sage: BrandtModule(17).maximal_order() is BrandtModule(17).maximal_order() True """ diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index e0f6f9c6b91..97bd761d067 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -5226,13 +5226,18 @@ def kolyvagin_cyclic_subideals(self, I, p, alpha_quaternion): sage: N = 37; D = -7; ell = 17; c=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[1] sage: g = H.kolyvagin_generators(f.domain().number_field(), c) sage: alpha_quaternion = f(g[0]); alpha_quaternion - 1 - 5/128*i - 77/192*j + 137/384*k + 1 - 77/192*i - 5/128*j - 137/384*k sage: H.kolyvagin_cyclic_subideals(I, 5, alpha_quaternion) - [(Fractional ideal (2 + 874/3*j + 128356/3*k, 2*i + 932/3*j + 198806/3*k, 2560/3*j + 33280/3*k, 94720*k), 0), (Fractional ideal (2 + 462*j + 82892*k, 2*i + 932/3*j + 141974/3*k, 2560/3*j + 33280/3*k, 94720*k), 1), (Fractional ideal (2 + 2410/3*j + 261988/3*k, 2*i + 652*j + 89650*k, 2560/3*j + 33280/3*k, 94720*k), 2), (Fractional ideal (2 + 2410/3*j + 91492/3*k, 2*i + 1444/3*j + 148630/3*k, 2560/3*j + 33280/3*k, 94720*k), 3), (Fractional ideal (2 + 874/3*j + 71524/3*k, 2*i + 2468/3*j + 275606/3*k, 2560/3*j + 33280/3*k, 94720*k), 4), (Fractional ideal (2 + 462*j + 63948*k, 2*i + 2468/3*j + 218774/3*k, 2560/3*j + 33280/3*k, 94720*k), 5)] + [(Fractional ideal (2 + 2/3*i + 364*j + 231928/3*k, 4/3*i + 946*j + 69338/3*k, 1280*j + 49920*k, 94720*k), 0), + (Fractional ideal (2 + 2/3*i + 108*j + 31480/3*k, 4/3*i + 434*j + 123098/3*k, 1280*j + 49920*k, 94720*k), 1), + (Fractional ideal (2 + 2/3*i + 876*j + 7672/3*k, 4/3*i + 434*j + 236762/3*k, 1280*j + 49920*k, 94720*k), 2), + (Fractional ideal (2 + 2/3*i + 364*j + 61432/3*k, 4/3*i + 178*j + 206810/3*k, 1280*j + 49920*k, 94720*k), 3), + (Fractional ideal (2 + 2/3*i + 876*j + 178168/3*k, 4/3*i + 1202*j + 99290/3*k, 1280*j + 49920*k, 94720*k), 4), + (Fractional ideal (2 + 2/3*i + 1132*j + 208120/3*k, 4/3*i + 946*j + 183002/3*k, 1280*j + 49920*k, 94720*k), 5)] """ X = I.cyclic_right_subideals(p, alpha_quaternion) return [(J, i) for i, J in enumerate(X)] @@ -5257,7 +5262,7 @@ def kolyvagin_generator(self, K, p): sage: N = 37; D = -7; ell = 17; p=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[0] sage: H.kolyvagin_generator(f.domain().number_field(), 5) a + 1 @@ -5308,7 +5313,7 @@ def kolyvagin_generators(self, K, c): sage: N = 37; D = -7; ell = 17; p=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[0] sage: H.kolyvagin_generators(f.domain().number_field(), 5*17) [-34*a + 1, 35*a + 106] @@ -5386,17 +5391,17 @@ def kolyvagin_sigma_operator(self, D, c, r, bound=None): sage: N = 37; D = -7; ell = 17; c = 41; q = 3 sage: H = heegner_points(N).reduce_mod(ell) sage: H.heegner_divisor(D,1).element().nonzero_positions() - [32, 51] - sage: k32 = H.kolyvagin_sigma_operator(D, c, 32); k32 - (17, 12, 33, 33, 49, 108, 3, 0, 0, 33, 37, 49, 33, 33, 59, 54, 21, 30, 0, 0, 29, 12, 41, 38, 33, 15, 0, 0, 4, 0, 7, 0, 0, 0, 0, 34, 26, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + [49, 51] + sage: k49 = H.kolyvagin_sigma_operator(D, c, 49); k49 + (79, 32, 31, 11, 53, 37, 1, 23, 15, 7, 0, 0, 0, 64, 32, 34, 53, 0, 27, 27, 0, 0, 0, 26, 0, 0, 18, 0, 22, 0, 53, 19, 27, 10, 0, 0, 0, 30, 35, 38, 0, 0, 0, 53, 0, 0, 4, 0, 0, 0, 0, 0) sage: k51 = H.kolyvagin_sigma_operator(D, c, 51); k51 - (5, 13, 0, 0, 14, 0, 21, 0, 0, 0, 29, 0, 0, 45, 0, 6, 0, 40, 0, 61, 0, 0, 40, 32, 0, 9, 0, 0, 0, 0, 17, 0, 0, 0, 77, 40, 2, 10, 18, 0, 0, 61, 19, 45, 26, 80, 61, 35, 35, 19, 1, 0) + (20, 12, 57, 0, 0, 0, 0, 52, 23, 15, 0, 7, 0, 0, 19, 4, 0, 73, 11, 0, 104, 31, 0, 38, 31, 0, 0, 31, 5, 47, 0, 27, 35, 0, 57, 32, 24, 10, 0, 8, 0, 31, 41, 0, 0, 0, 16, 0, 0, 0, 0, 0) sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V Vector space of degree 52 and dimension 2 over Ring of integers modulo 3 Basis matrix: 2 x 52 dense matrix over Ring of integers modulo 3 - sage: [b.dot_product(k32.element().change_ring(GF(q))) for b in V.basis()] - [2, 2] + sage: [b.dot_product(k49.element().change_ring(GF(q))) for b in V.basis()] + [1, 1] sage: [b.dot_product(k51.element().change_ring(GF(q))) for b in V.basis()] [1, 1] @@ -5573,7 +5578,7 @@ def kolyvagin_point_on_curve(self, D, c, E, p, bound=10): sage: N = 37; D = -7; ell = 17; c = 41; p = 3 sage: H = heegner_points(N).reduce_mod(ell) sage: H.kolyvagin_point_on_curve(D, c, EllipticCurve('37a'), p) - [2, 2] + [1, 1] """ k = self.rational_kolyvagin_divisor(D, c) V = self.modp_dual_elliptic_curve_factor(E, p, bound) From 6dcef989d9bf6549bb7e3015d087db88689afc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 8 Apr 2021 11:59:06 +0200 Subject: [PATCH 099/706] 18119: moved itertools imports inside methods --- src/sage/combinat/words/morphism.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 556d96d93d7..62db520c71f 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -89,8 +89,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from collections import Counter -from itertools import chain, count +from itertools import chain from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject @@ -3412,6 +3411,8 @@ def infinite_repetitions_bounded(self, w=None): sage: sorted(m.infinite_repetitions_bounded()) [word: c, word: d] """ + from itertools import count + def impl(): U = {} for x in unbounded: @@ -3497,6 +3498,8 @@ def infinite_repetitions_growing(self, w=None): sage: sorted(m.infinite_repetitions_growing()) [word: bc] """ + from collections import Counter + if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) @@ -3764,6 +3767,7 @@ def simplify_injective(self): return self, self, self.domain().identity_morphism(), 0 g = h * k + from itertools import count for i in count(start=1): try: h_new, k_new = g.simplify() From ad37ebdc09502d6925d7820970363ffb9bed9951 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Apr 2021 12:24:05 -0700 Subject: [PATCH 100/706] build/pkgs/sage_conf/src/sage_conf.py.in: Move SAGE_ROOT, SAGE_LOCAL to beginning of file; only use substitution @prefix@ once --- build/pkgs/sage_conf/src/sage_conf.py.in | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 6ac91e68018..4cdc829c95c 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -2,6 +2,13 @@ VERSION = "@PACKAGE_VERSION@" +# The following must not be used during build to determine source or installation +# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in +# These variables come first so that other substituted variable values can refer +# to it. +SAGE_LOCAL = "@prefix@" +SAGE_ROOT = "@SAGE_ROOT@" + MAXIMA = "@prefix@/bin/maxima" ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" @@ -11,7 +18,7 @@ NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script # TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. -ECL_CONFIG = "@prefix@/bin/ecl-config" +ECL_CONFIG = "${prefix}/bin/ecl-config".subst('${prefix}', SAGE_LOCAL) SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" @@ -21,13 +28,8 @@ SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" CBLAS_PC_MODULES = "cblas" # Used in sage.repl.ipython_kernel.install -MATHJAX_DIR = "@prefix@/share/mathjax" -THREEJS_DIR = "@prefix@/share/threejs" - -# The following must not be used during build to determine source or installation -# location of sagelib. See comments in SAGE_ROOT/src/Makefile.in -SAGE_LOCAL = "@prefix@" -SAGE_ROOT = "@SAGE_ROOT@" +MATHJAX_DIR = SAGE_LOCAL + "/share/mathjax" +THREEJS_DIR = SAGE_LOCAL + "/share/threejs" # Entry point 'sage-config'. It does not depend on any packages. From f04a889f21e4de5c9196cbbd509d1f7c407b6ca7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Apr 2021 12:35:43 -0700 Subject: [PATCH 101/706] build/pkgs/sage_conf/src/sage_conf.py.in: replace subst by replace --- build/pkgs/sage_conf/src/sage_conf.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index 4cdc829c95c..c627bc50943 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -18,7 +18,7 @@ NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script # TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. -ECL_CONFIG = "${prefix}/bin/ecl-config".subst('${prefix}', SAGE_LOCAL) +ECL_CONFIG = "${prefix}/bin/ecl-config".replace('${prefix}', SAGE_LOCAL) SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" From 42c6afad5f611c2b9e458a25183530a5cf89bada Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 19 Jan 2021 13:43:29 -0800 Subject: [PATCH 102/706] build/pkgs/ecl: Add more to distros/ --- build/pkgs/ecl/distros/alpine.txt | 1 + build/pkgs/ecl/distros/fedora.txt | 1 + build/pkgs/ecl/distros/gentoo.txt | 1 + build/pkgs/ecl/distros/homebrew.txt | 1 + 4 files changed, 4 insertions(+) create mode 100644 build/pkgs/ecl/distros/alpine.txt create mode 100644 build/pkgs/ecl/distros/fedora.txt create mode 100644 build/pkgs/ecl/distros/gentoo.txt create mode 100644 build/pkgs/ecl/distros/homebrew.txt diff --git a/build/pkgs/ecl/distros/alpine.txt b/build/pkgs/ecl/distros/alpine.txt new file mode 100644 index 00000000000..b58d87094ea --- /dev/null +++ b/build/pkgs/ecl/distros/alpine.txt @@ -0,0 +1 @@ +ecl-dev diff --git a/build/pkgs/ecl/distros/fedora.txt b/build/pkgs/ecl/distros/fedora.txt new file mode 100644 index 00000000000..100aa2efb32 --- /dev/null +++ b/build/pkgs/ecl/distros/fedora.txt @@ -0,0 +1 @@ +ecl diff --git a/build/pkgs/ecl/distros/gentoo.txt b/build/pkgs/ecl/distros/gentoo.txt new file mode 100644 index 00000000000..416fa20614c --- /dev/null +++ b/build/pkgs/ecl/distros/gentoo.txt @@ -0,0 +1 @@ +dev-lisp/ecls diff --git a/build/pkgs/ecl/distros/homebrew.txt b/build/pkgs/ecl/distros/homebrew.txt new file mode 100644 index 00000000000..100aa2efb32 --- /dev/null +++ b/build/pkgs/ecl/distros/homebrew.txt @@ -0,0 +1 @@ +ecl From 70e34d5b422b8aa287ff2aea001fbf9065e284bc Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 9 Sep 2020 11:16:03 +0100 Subject: [PATCH 103/706] adding spkg-configure for ecl rebased over 9.2.rc0 --- build/pkgs/ecl/spkg-configure.m4 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 build/pkgs/ecl/spkg-configure.m4 diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 new file mode 100644 index 00000000000..32bf1bd7408 --- /dev/null +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -0,0 +1,8 @@ +SAGE_SPKG_CONFIGURE([ecl], [ + SAGE_SPKG_DEPCHECK([gc gmp mpir], [ + AC_PATH_PROG([ECL], [ecl]) + AS_IF([test x$ECL = x], [ + AC_MSG_NOTICE([ecl not found. Installing ecl]) + sage_spkg_install_ecl=yes]) + ]) +]) From 27727acc92443e3efc922ea382f225e86cacac9e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 19 Jan 2021 13:50:48 -0800 Subject: [PATCH 104/706] build/pkgs/ecl/distros/arch.txt: New --- build/pkgs/ecl/distros/arch.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/ecl/distros/arch.txt diff --git a/build/pkgs/ecl/distros/arch.txt b/build/pkgs/ecl/distros/arch.txt new file mode 100644 index 00000000000..100aa2efb32 --- /dev/null +++ b/build/pkgs/ecl/distros/arch.txt @@ -0,0 +1 @@ +ecl From 38cc46d7f4759c826ed7c0e7353e378b359cb6dd Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 16 Mar 2021 14:21:27 -0400 Subject: [PATCH 105/706] Trac #29617: check for ecl-config instead of ecl. The plan for ecl's ./configure check is to build a test program using a header that defines its version, and to check that version against the required one. If those headers are installed in an uncommon location, the ecl-config program exists to provide the appropriate include path(s). Since we will need those include path(s) anyway, we might as well check for ecl-config instead of ecl itself. --- build/pkgs/ecl/spkg-configure.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 index 32bf1bd7408..8a46bcf4ea7 100644 --- a/build/pkgs/ecl/spkg-configure.m4 +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -1,8 +1,8 @@ SAGE_SPKG_CONFIGURE([ecl], [ SAGE_SPKG_DEPCHECK([gc gmp mpir], [ - AC_PATH_PROG([ECL], [ecl]) - AS_IF([test x$ECL = x], [ - AC_MSG_NOTICE([ecl not found. Installing ecl]) + AC_PATH_PROG([ECL_CONFIG], [ecl-config]) + AS_IF([test x$ECL_CONFIG = x], [ + AC_MSG_NOTICE([ecl-config not found. Installing ecl]) sage_spkg_install_ecl=yes]) ]) ]) From 18cbcee9a37d645f12f78c1067257ac27d559130 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 16 Mar 2021 14:40:01 -0400 Subject: [PATCH 106/706] Trac #29617: compile a check for ECL_VERSION_NUMBER. With the new version supported in ticket 31336, we are finally able to support unpatched system installations of ecl. However, we can only accept the *latest* version of ecl, because older ones are missing critical patches. This commit adds an AC_RUN_IFELSE() test to the ./configure script that includes ecl/config.h and then returns whether or not ECL_VERSION_NUMBER < 210201 If the system version is too old, that will return "1" and the check will fail. Otherwise, the entire program succeeds by default. --- build/pkgs/ecl/spkg-configure.m4 | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 index 8a46bcf4ea7..a63ec88b09c 100644 --- a/build/pkgs/ecl/spkg-configure.m4 +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -1,8 +1,25 @@ SAGE_SPKG_CONFIGURE([ecl], [ - SAGE_SPKG_DEPCHECK([gc gmp mpir], [ - AC_PATH_PROG([ECL_CONFIG], [ecl-config]) - AS_IF([test x$ECL_CONFIG = x], [ - AC_MSG_NOTICE([ecl-config not found. Installing ecl]) - sage_spkg_install_ecl=yes]) - ]) + SAGE_SPKG_DEPCHECK([gc gmp mpir], [ + AC_PATH_PROG([ECL_CONFIG], [ecl-config]) + AS_IF([test x$ECL_CONFIG = x], [ + AC_MSG_NOTICE([ecl-config not found. Installing ecl]) + sage_spkg_install_ecl=yes]) + + # "CPPFLAGS" is not a typo, the --cflags output from + # ecl-config typically contains -D and -I flags. + CPPFLAGS="${CPPFLAGS} $($ECL_CONFIG --cflags)" + + AC_LANG_PUSH([C]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([[ + #include + ]],[[ + if (ECL_VERSION_NUMBER < 210201) { return 1; } + ]])], + [], # if it works + [ + AC_MSG_NOTICE([ecl found but too old]) + sage_spkg_install_ecl=yes + ]) + AC_LANG_POP([C]) + ]) ]) From 444d828201fd6559d914dabf0afea9c0aad8e381 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 17 Mar 2021 08:40:07 -0400 Subject: [PATCH 107/706] Trac #29617: substitute ecl-config path into sage_conf. The path to ecl-config used to be hard-coded into sage_conf.py.in; now it isn't. If the spkg is used, we use the $SAGE_LOCAL path. Otherwise, the absolute path found by AC_PATH_PROG on the system is used. --- build/pkgs/ecl/spkg-configure.m4 | 26 +++++++++++++++++------- build/pkgs/sage_conf/src/sage_conf.py.in | 3 +-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 index a63ec88b09c..379ea38649b 100644 --- a/build/pkgs/ecl/spkg-configure.m4 +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -1,25 +1,37 @@ SAGE_SPKG_CONFIGURE([ecl], [ + + # Default to installing the SPKG + sage_spkg_install_ecl=yes + SAGE_SPKG_DEPCHECK([gc gmp mpir], [ AC_PATH_PROG([ECL_CONFIG], [ecl-config]) - AS_IF([test x$ECL_CONFIG = x], [ - AC_MSG_NOTICE([ecl-config not found. Installing ecl]) - sage_spkg_install_ecl=yes]) - + AS_IF([test x$ECL_CONFIG != x], [ # "CPPFLAGS" is not a typo, the --cflags output from # ecl-config typically contains -D and -I flags. + saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} $($ECL_CONFIG --cflags)" AC_LANG_PUSH([C]) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ - if (ECL_VERSION_NUMBER < 210201) { return 1; } + if (ECL_VERSION_NUMBER < 210201) { return 1; } ]])], - [], # if it works [ + sage_spkg_install_ecl=no + ], + [ + CPPFLAGS="${saved_CPPFLAGS}" AC_MSG_NOTICE([ecl found but too old]) - sage_spkg_install_ecl=yes ]) AC_LANG_POP([C]) + ]) ]) +],[],[],[ + # post-check + if test x$sage_spkg_install_ecl = xyes; then + AC_SUBST(SAGE_ECL_CONFIG, ['${prefix}'/bin/ecl-config]) + else + AC_SUBST(SAGE_ECL_CONFIG, [$ECL_CONFIG]) + fi ]) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index c627bc50943..a15c043d0ff 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -17,8 +17,7 @@ NTL_INCDIR = "@NTL_INCDIR@" NTL_LIBDIR = "@NTL_LIBDIR@" # Path to the ecl-config script -# TODO: At the moment this is hard-coded, needs to be set during the configure phase if we want to support system-installed ecl. -ECL_CONFIG = "${prefix}/bin/ecl-config".replace('${prefix}', SAGE_LOCAL) +ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace('${prefix}', SAGE_LOCAL) SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@" From 4061f54a88c234ec03f44df255cbf28eea67213c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 17 Mar 2021 08:41:15 -0400 Subject: [PATCH 108/706] Trac #29617: remove failing ECL test. There is a test in sage/interfaces/tests.py that checks whether or not the "ecl" executable handles stdout and stderr being "full" at the same time. This fails without a custom patch, and therefore with everyone's system copy of ecl. This commit removes the test: running "ecl" on the command-line is not a special sage interface to ecl. This is an upstream ecl bug that will someday get fixed and be added to the ecl test suite. In the meantime, it has no ill effects in sage besides the failing test. --- src/sage/interfaces/tests.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/interfaces/tests.py b/src/sage/interfaces/tests.py index 7d1ca00182d..7e1fa9d68db 100644 --- a/src/sage/interfaces/tests.py +++ b/src/sage/interfaces/tests.py @@ -23,7 +23,7 @@ Singular Test that write errors to stderr are handled gracefully by GAP -(see :trac:`13211`) and ECL (see :trac:`14426`) and other interfaces:: +(see :trac:`13211`) and other interfaces:: sage: import subprocess sage: try: @@ -31,8 +31,6 @@ ....: except IOError: ....: f = open('/dev/null', 'w') sage: kwds = dict(shell=True, stdout=f, stderr=f) - sage: subprocess.call("echo syntax error | ecl", **kwds) in (0, 255) - True sage: subprocess.call("echo syntax error | gap", **kwds) in (0, 1) True sage: subprocess.call("echo syntax error | gp", **kwds) From edd246f03f4b64a31702b3b6c92635ee0dfe506c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 21 Mar 2021 10:34:28 -0400 Subject: [PATCH 109/706] Trac #29617: don't set ECLDIR in sage-env. I'm not sure if this is still doing anything useful in modern-day Sage, but the value that it gets set to is certainly wrong when the system copy of ECL is used, so probably not. --- src/bin/sage-env | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bin/sage-env b/src/bin/sage-env index 4c617d8cd44..a3e6aa42ee7 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -609,12 +609,6 @@ if [ "$UNAME" = "CYGWIN" -a -n "$SAGE_LOCAL" ]; then LD_LIBRARY_PATH="$SAGE_LOCAL/bin:$LD_LIBRARY_PATH" && export LD_LIBRARY_PATH fi -# See trac 7186 -- this is needed if ecl is moved -if [ -n "$SAGE_LOCAL" ]; then - ECLDIR="$SAGE_LOCAL/lib/ecl/" && export ECLDIR -fi - - # Handle parallel building/testing/... case "$SAGE_NUM_THREADS,$SAGE_NUM_THREADS_PARALLEL" in [1-9][0-9]*,[1-9][0-9]*) From 133aab7f38cde61fc5ac916b90eb5745be772760 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 1 Apr 2021 21:46:09 -0400 Subject: [PATCH 110/706] Trac #29617: move MAXIMA_FAS to sage_conf. Now that ECL can come from the system, we need to be able to pass it the value of MAXIMA_FAS even when the maxima SPKG is used; otherwise the system ECL doesn't know where to find maxima.fas under $SAGE_LOCAL. This commit adds MAXIMA_FAS to sage_conf, and defines it to the appropriate $SAGE_LOCAL path in ECL's spkg-configure.m4. Maxima's spkg-install routine has also been updated to use a fixed path rather than asking ECL where its library directory lives, as the latter will be incorrect when ECL comes from the system. --- build/pkgs/ecl/spkg-configure.m4 | 4 ++++ build/pkgs/maxima/spkg-install.in | 6 ++++-- build/pkgs/sage_conf/src/sage_conf.py.in | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 index 379ea38649b..52d13fb4f9b 100644 --- a/build/pkgs/ecl/spkg-configure.m4 +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -34,4 +34,8 @@ SAGE_SPKG_CONFIGURE([ecl], [ else AC_SUBST(SAGE_ECL_CONFIG, [$ECL_CONFIG]) fi + + # Maxima cannot yet be provided by the system, so we always use + # the SAGE_LOCAL path for now. + AC_SUBST(SAGE_MAXIMA_FAS, ['${prefix}'/lib/ecl/maxima.fas]) ]) diff --git a/build/pkgs/maxima/spkg-install.in b/build/pkgs/maxima/spkg-install.in index 41da6c16692..d72b0c37da3 100644 --- a/build/pkgs/maxima/spkg-install.in +++ b/build/pkgs/maxima/spkg-install.in @@ -57,10 +57,12 @@ sdh_make_install # Install Maxima into ECL's library directory: -ecl -eval '(WITH-OPEN-FILE (f "ecllib" :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE) (FORMAT f "~A~%" (SI:GET-LIBRARY-PATHNAME)))' -eval '(QUIT)' -ECLLIB=$(cat ecllib) +# Ensure that the $ECLLIB directory exists in +# case we're using ECL from the system. +ECLLIB="${SAGE_LOCAL}/lib/ecl" echo echo "Now installing the Maxima library as '$ECLLIB/maxima.fas'..." +mkdir -p "${ECLLIB}" cp -f "src/binary-ecl/maxima.fas" "$ECLLIB/maxima.fas" \ || sdh_die "Failed to install 'src/binary-ecl/maxima.fas' as '$ECLLIB/maxima.fas'." diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index a15c043d0ff..a86db8c09de 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -11,6 +11,9 @@ SAGE_ROOT = "@SAGE_ROOT@" MAXIMA = "@prefix@/bin/maxima" +# Delete this line if your ECL can load maxima without further prodding. +MAXIMA_FAS = "@SAGE_MAXIMA_FAS@".replace('${prefix}', SAGE_LOCAL) + ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" NTL_INCDIR = "@NTL_INCDIR@" From 9b595c0c54d6e3591ab6d11462a60f0f03c71fce Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 2 Apr 2021 08:30:43 -0400 Subject: [PATCH 111/706] Trac #29617: modify Kenzo interface to support system ECL. We repeat all of the recent changes made to the maxima interface for kenzo, which also needs to support system installations of ECL. --- build/pkgs/ecl/spkg-configure.m4 | 3 +++ build/pkgs/kenzo/spkg-install.in | 8 +++++--- build/pkgs/sage_conf/src/sage_conf.py.in | 3 +++ src/sage/env.py | 1 + src/sage/features/kenzo.py | 7 ++++++- src/sage/interfaces/kenzo.py | 8 ++++++-- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/build/pkgs/ecl/spkg-configure.m4 b/build/pkgs/ecl/spkg-configure.m4 index 52d13fb4f9b..bcb3c6c1493 100644 --- a/build/pkgs/ecl/spkg-configure.m4 +++ b/build/pkgs/ecl/spkg-configure.m4 @@ -38,4 +38,7 @@ SAGE_SPKG_CONFIGURE([ecl], [ # Maxima cannot yet be provided by the system, so we always use # the SAGE_LOCAL path for now. AC_SUBST(SAGE_MAXIMA_FAS, ['${prefix}'/lib/ecl/maxima.fas]) + + # Likewise for the optional Kenzo SPKG + AC_SUBST(SAGE_KENZO_FAS, ['${prefix}'/lib/ecl/kenzo.fas]) ]) diff --git a/build/pkgs/kenzo/spkg-install.in b/build/pkgs/kenzo/spkg-install.in index 3c8e5946547..7bf5982be3a 100644 --- a/build/pkgs/kenzo/spkg-install.in +++ b/build/pkgs/kenzo/spkg-install.in @@ -5,9 +5,11 @@ cd src ecl < compile.lisp -# Install Kenzo into ECL's library directory (installation procedure -# copied from Maxima's spkg-install.in file): -ECLLIB=`ecl -eval "(princ (SI:GET-LIBRARY-PATHNAME))" -eval "(quit)"` +# Install Kenzo into ECL's library directory. +# Ensure that the $ECLLIB directory exists in +# case we're using ECL from the system. +ECLLIB="${SAGE_LOCAL}/lib/ecl" +mkdir -p "${ECLLIB}" echo echo "Now installing Kenzo as '$ECLLIB/kenzo.fas'..." cp -f kenzo--all-systems.fasb "$ECLLIB/kenzo.fas" \ diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index a86db8c09de..f3407bd5b19 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -14,6 +14,9 @@ MAXIMA = "@prefix@/bin/maxima" # Delete this line if your ECL can load maxima without further prodding. MAXIMA_FAS = "@SAGE_MAXIMA_FAS@".replace('${prefix}', SAGE_LOCAL) +# Delete this line if your ECL can load Kenzo without further prodding. +KENZO_FAS = "@SAGE_KENZO_FAS@".replace('${prefix}', SAGE_LOCAL) + ARB_LIBRARY = "@SAGE_ARB_LIBRARY@" NTL_INCDIR = "@NTL_INCDIR@" diff --git a/src/sage/env.py b/src/sage/env.py index 2908f5d04fa..cc9971c6784 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -207,6 +207,7 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st PPLPY_DOCS = var("PPLPY_DOCS", join(SAGE_SHARE, "doc", "pplpy")) MAXIMA = var("MAXIMA", "maxima") MAXIMA_FAS = var("MAXIMA_FAS") +KENZO_FAS = var("KENZO_FAS") SAGE_NAUTY_BINS_PREFIX = var("SAGE_NAUTY_BINS_PREFIX", "") ARB_LIBRARY = var("ARB_LIBRARY", "arb") CBLAS_PC_MODULES = var("CBLAS_PC_MODULES", "cblas:openblas:blas") diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py index c3c68ede71b..ba36288eadd 100644 --- a/src/sage/features/kenzo.py +++ b/src/sage/features/kenzo.py @@ -46,7 +46,12 @@ def _is_present(self): ecl_eval("(setf *standard-output* *dev-null*)") try: - ecl_eval("(require :kenzo)") + from sage.env import KENZO_FAS + if KENZO_FAS is not None: + ecl_eval("(require :kenzo \"{}\")".format(KENZO_FAS)) + else: + ecl_eval("(require :kenzo)") + except RuntimeError: return FeatureTestResult(self, False, reason="Unable to make ECL require kenzo") return FeatureTestResult(self, True) diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index b501749cb89..b0a8874132c 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -40,7 +40,6 @@ from sage.libs.ecl import EclObject, ecl_eval, EclListIterator from sage.features.kenzo import Kenzo - # defining the auxiliary functions as wrappers over the kenzo ones kenzo_names = ['add', 'array-dimensions', @@ -107,7 +106,12 @@ # example __sphere__ is defined as EclObject("sphere"). Hyphens # are replaced with underscores to get valid Python identifiers. if Kenzo().is_present(): - ecl_eval("(require :kenzo)") + from sage.env import KENZO_FAS + if KENZO_FAS is not None: + ecl_eval("(require :kenzo \"{}\")".format(KENZO_FAS)) + else: + ecl_eval("(require :kenzo)") + ecl_eval("(in-package :cat)") ecl_eval("(setf *HOMOLOGY-VERBOSE* nil)") for s in kenzo_names: From b4ae35ed66488bdc2017af489b6672810473535c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 2 Apr 2021 15:13:10 -0400 Subject: [PATCH 112/706] Trac #29617: treat empty MAXIMA_FAS and KENZO_FAS as None. When these variables are defined to be the empty string, we now assume that ECL knows where to find the corresponding files. --- src/sage/features/kenzo.py | 2 +- src/sage/interfaces/kenzo.py | 2 +- src/sage/interfaces/maxima_lib.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py index ba36288eadd..b6c068eac83 100644 --- a/src/sage/features/kenzo.py +++ b/src/sage/features/kenzo.py @@ -47,7 +47,7 @@ def _is_present(self): try: from sage.env import KENZO_FAS - if KENZO_FAS is not None: + if KENZO_FAS: ecl_eval("(require :kenzo \"{}\")".format(KENZO_FAS)) else: ecl_eval("(require :kenzo)") diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index b0a8874132c..e6a09930a65 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -107,7 +107,7 @@ # are replaced with underscores to get valid Python identifiers. if Kenzo().is_present(): from sage.env import KENZO_FAS - if KENZO_FAS is not None: + if KENZO_FAS: ecl_eval("(require :kenzo \"{}\")".format(KENZO_FAS)) else: ecl_eval("(require :kenzo)") diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 40367c52426..058003470d9 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -99,7 +99,7 @@ ## We begin here by initializing Maxima in library mode ## i.e. loading it into ECL ecl_eval("(setf *load-verbose* NIL)") -if MAXIMA_FAS is not None: +if MAXIMA_FAS: ecl_eval("(require 'maxima \"{}\")".format(MAXIMA_FAS)) else: ecl_eval("(require 'maxima)") From 2d40f36753ed0f4a93c44952730d67e06f19eb38 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 8 Apr 2021 10:58:27 -0400 Subject: [PATCH 113/706] Trac #29617: upgrade kenzo to v1.1.10. This is necessary to support system installations of ECL that have unbundled ASDF, and which therefore do not support the deprecated call to "make-build" in kenzo-1.1.9. --- build/pkgs/kenzo/SPKG.rst | 2 ++ build/pkgs/kenzo/checksums.ini | 10 +++++----- build/pkgs/kenzo/package-version.txt | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/build/pkgs/kenzo/SPKG.rst b/build/pkgs/kenzo/SPKG.rst index 7567b7c4a63..5d139df545e 100644 --- a/build/pkgs/kenzo/SPKG.rst +++ b/build/pkgs/kenzo/SPKG.rst @@ -19,6 +19,8 @@ Upstream Contact - https://github.com/gheber/kenzo +- https://github.com/miguelmarco/kenzo/ + Dependencies ------------ diff --git a/build/pkgs/kenzo/checksums.ini b/build/pkgs/kenzo/checksums.ini index 22ef2a25127..ad2a7c4cc60 100644 --- a/build/pkgs/kenzo/checksums.ini +++ b/build/pkgs/kenzo/checksums.ini @@ -1,5 +1,5 @@ -tarball=kenzo-1.1.9.tar.gz -upstream_url=https://github.com/miguelmarco/kenzo/releases/download/1.1.9/kenzo-1.1.9.tar.gz -sha1=f153b0c172b6c11851a5f248947b61b0bf5be527 -md5=98adc197c6f23716a6ddd115115ab4f6 -cksum=880933361 +tarball=kenzo-1.1.10.tar.gz +upstream_url=https://github.com/miguelmarco/kenzo/releases/download/1.1.10/kenzo-1.1.10.tar.gz +sha1=76115aae9972090d5d51fee18592fc7a79461474 +md5=3a3d5350fb17304f03e614713e585ed4 +cksum=2981306888 diff --git a/build/pkgs/kenzo/package-version.txt b/build/pkgs/kenzo/package-version.txt index 512a1faa680..5ed5faa5f16 100644 --- a/build/pkgs/kenzo/package-version.txt +++ b/build/pkgs/kenzo/package-version.txt @@ -1 +1 @@ -1.1.9 +1.1.10 From ba0845df00dc7c76582403b479ca1b02549a1251 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 8 Apr 2021 20:30:44 +0200 Subject: [PATCH 114/706] 18119: Refactor inf_reps_bounded --- src/sage/combinat/words/morphism.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 62db520c71f..d757adc3093 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3410,25 +3410,30 @@ def infinite_repetitions_bounded(self, w=None): sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') sage: sorted(m.infinite_repetitions_bounded()) [word: c, word: d] + + TESTS:: + + sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') + sage: sorted(m.infinite_repetitions_bounded()) + [word: 1, word: 1519181716, word: 2, word: 2529282726] """ from itertools import count def impl(): U = {} for x in unbounded: - hx = g.image(x) - for i, y in enumerate(hx): + gx = g.image(x) + for i, y in enumerate(reversed(gx)): if y in unbounded: break - U[x] = y, hx[:i] + U[x] = y, gx[gx.length() - i:] for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): if all(not U[x][1] for x in cycle): continue for cycle in g.domain()(cycle).conjugates_iterator(): u = g.domain()() for x in cycle: - u = g(u) - u = u + U[x][1] + u = U[x][1] + g(u) history = dict({u: 0}) for i in count(1): u = g(u) @@ -3438,14 +3443,12 @@ def impl(): break history[u] = i q = len(cycle) - l0 = (s / q).ceil() * q - l1 = l0 + (t.lcm(q) / q) gq = gb ** q - uql = gq(u, l0) + uq = gq(u, (s / q).ceil()) res = g.domain()() - for _ in range(l0 + 1, l1 + 1): - uql = gq(uql) - res = uql + res + for _ in range(t.lcm(q) / q): + uq = gq(uq) + res += uq yield k(res.primitive()).primitive() if w is None: @@ -3456,10 +3459,10 @@ def impl(): gb = g.restrict_domain(set(g._morph) - unbounded) result = set() - for x in impl(): + for x in impl(): # UR. result.add(x.minimal_conjugate()) g, k = g.reversal(), k.reversal() - for x in impl(): + for x in impl(): # UL. result.add(self.domain()(reversed(x)).minimal_conjugate()) return result From f564931ed03e8a1d93dab328f256f30353ded1f6 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 09:45:24 +0100 Subject: [PATCH 115/706] updated eclib package version to 20210408 and checksums --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..de58a9fbb75 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 -md5=9da2bba60ec2920b4524b355aff9333a -cksum=1435512890 +sha1=d1c9add8c95ba502440e7d197da0c1e3329adc7d +md5=47e2c9b59c1d322709a5d99e438d09cd +cksum=2380455615 upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index d52e9bed28f..c12f44c8f0c 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -v20210317 +20210408 From 07c5a20f8cfe7323fc5afb4c9a7a77472ac4d0b8 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 10:45:19 +0100 Subject: [PATCH 116/706] correct a docstring after eclib upgrade --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 3d5d214109f..874589b7ff3 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2525,8 +2525,7 @@ def regulator(self, proof=None, precision=53, **kwds): return reg def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): - """ - Given a list of rational points on E, compute the saturation in + """Given a list of rational points on E, compute the saturation in E(Q) of the subgroup they generate. INPUT: @@ -2552,7 +2551,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): ``min_prime`` both to `p`. One situation where this is useful is after mapping saturated points from another elliptic curve by a `p`-isogeny, since the images may not - be `p`-saturated but with be saturated at all other primes. + be `p`-saturated but will be saturated at all other primes. OUTPUT: @@ -2588,7 +2587,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): The computed index of saturation may be large, in which case saturation may take a long time. For example, the rank 4 curve ``EllipticCurve([0,1,1,-9872,374262])`` has a - saturation index bound of 86682 and takes around 15 minutes + saturation index bound of 11816 and takes around 40 seconds to prove saturation. From 9b1dedb02560d1b3fce444e5f88ac14f42786ce9 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 12:23:21 +0100 Subject: [PATCH 117/706] correct typo and update eclib's m4 script --- build/pkgs/eclib/spkg-configure.m4 | 2 +- src/sage/libs/eclib/interface.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 748e6b8bf3e..8da0723747d 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210318"]) + m4_pushdef([SAGE_ECLIB_VER],["20210408"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 493b5f13470..a163cc31093 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -1102,7 +1102,7 @@ def saturate(self, max_prime=-1, min_prime=2): In versions up to v20190909, ``eclib`` used floating point methods based on elliptic logarithms to divide points, and - did not compute the precision necessary, which could casue + did not compute the precision necessary, which could cause failures. Since v20210310, ``eclib`` uses exact method based on division polynomials, which should mean that such failures does not happen. From 49e0baae95e87d843473b33245f81267e583c2ad Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 9 Apr 2021 21:36:11 +0200 Subject: [PATCH 118/706] 18119: Refactor inf_reps_bounded (2) --- src/sage/combinat/words/morphism.py | 36 +++++++++++------------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index d757adc3093..73aecfbc705 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -96,7 +96,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_list import lazy_list from sage.sets.set import Set -from sage.rings.all import ZZ, QQ +from sage.rings.all import QQ from sage.rings.infinity import Infinity from sage.rings.integer_ring import IntegerRing from sage.rings.integer import Integer @@ -3417,39 +3417,29 @@ def infinite_repetitions_bounded(self, w=None): sage: sorted(m.infinite_repetitions_bounded()) [word: 1, word: 1519181716, word: 2, word: 2529282726] """ - from itertools import count - def impl(): U = {} for x in unbounded: - gx = g.image(x) - for i, y in enumerate(reversed(gx)): + xg = g.image(x) + for i, y in enumerate(reversed(xg)): if y in unbounded: break - U[x] = y, gx[gx.length() - i:] + U[x] = y, xg[xg.length() - i:] for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): if all(not U[x][1] for x in cycle): continue + gq = gb ** len(cycle) for cycle in g.domain()(cycle).conjugates_iterator(): u = g.domain()() for x in cycle: - u = U[x][1] + g(u) - history = dict({u: 0}) - for i in count(1): - u = g(u) - if u in history: - s = ZZ(history[u]) - t = ZZ(i - history[u]) - break - history[u] = i - q = len(cycle) - gq = gb ** q - uq = gq(u, (s / q).ceil()) - res = g.domain()() - for _ in range(t.lcm(q) / q): - uq = gq(uq) - res += uq - yield k(res.primitive()).primitive() + u = U[x][1] + gb(u) + inf_rep = g.domain()() + history = set() + while u not in history: + history.add(u) + inf_rep += u + u = gq(u) + yield k(inf_rep.primitive()).primitive() if w is None: w = self._morph From 0e6e552495e1b53d2e95ee83de9f76a803bef390 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 9 Apr 2021 21:37:35 +0200 Subject: [PATCH 119/706] 18119: Refactor inf_reps_growing --- src/sage/combinat/words/morphism.py | 34 +++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 73aecfbc705..5951cff3e95 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3490,9 +3490,11 @@ def infinite_repetitions_growing(self, w=None): sage: m = WordMorphism('b->c,c->bcb') sage: sorted(m.infinite_repetitions_growing()) [word: bc] - """ - from collections import Counter + sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') + sage: sorted(m.infinite_repetitions_growing()) + [word: ababcd] + """ if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) @@ -3501,29 +3503,33 @@ def infinite_repetitions_growing(self, w=None): result = set() for periodic_orbit in g.periodic_points(): - q = len(periodic_orbit) - gq = g**q + gq = g ** len(periodic_orbit) for periodic_point in periodic_orbit: # Check if this periodic point is a periodic infinite word. periodic_point = periodic_point[:1] - letter_cnts = Counter(periodic_point) + occurred = set(periodic_point) + one_unbounded_twice = False for _ in g.domain().alphabet(): previous_length = periodic_point.length() periodic_point = gq(periodic_point) - letter_cnts.update(periodic_point[previous_length:]) - if any(letter_cnts[letter] >= 2 for letter in unbounded): + for i, letter in enumerate(periodic_point[previous_length:]): + if letter in unbounded: + if letter in occurred: + one_unbounded_twice = True + break + occurred.add(letter) + if one_unbounded_twice: break - else: # nobreak - continue - if letter_cnts[periodic_point[0]] < 2: - continue - v = periodic_point[:periodic_point.find(periodic_point[0], start=1)] + if not one_unbounded_twice or letter != periodic_point[0]: + break + v = periodic_point[:previous_length + i] vq = gq(v) m = 0 while vq[m * v.length() : (m + 1) * v.length()] == v: m += 1 - if m >= 2 and m * v.length() == vq.length(): - result.add(k(v).primitive().minimal_conjugate()) + if m * v.length() != vq.length(): + break + result.add(k(v).primitive().minimal_conjugate()) return result From 617e8eb866608a32f847f252381285343cc155a7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 21 Mar 2021 13:08:08 -0700 Subject: [PATCH 120/706] tox.ini: In -maximal environments, use IGNORE_MISSING_SYSTEM_PACKAGES=yes for all non-current distributions --- tox.ini | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 1d7534148ef..4797b3f7cdf 100644 --- a/tox.ini +++ b/tox.ini @@ -131,7 +131,10 @@ setenv = WITH_SYSTEM_SPKG=yes # Set this to 'yes' instead of 'no' to ignore missing system packages - by installing them one by one # and ignoring errors. We use that to take care of old versions of distributions. - IGNORE_MISSING_SYSTEM_PACKAGES=no + # For -maximal environments, the default is 'yes' but later we override it for the most cutting edge versions + # of distributions, as well as for rolling distributions. + IGNORE_MISSING_SYSTEM_PACKAGES=no + maximal: IGNORE_MISSING_SYSTEM_PACKAGES=yes # What system packages should be installed. Default: All standard packages with spkg-configure. SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard: minimal: SAGE_PACKAGE_LIST_ARGS=_prereq @@ -163,6 +166,7 @@ setenv = ubuntu-focal: BASE_TAG=focal ubuntu-groovy: BASE_TAG=groovy ubuntu-hirsute: BASE_TAG=hirsute + ubuntu-hirsute: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/debian # debian-bullseye does not have libgiac-dev @@ -177,6 +181,7 @@ setenv = debian-bullseye: BASE_TAG=bullseye debian-bullseye: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-sid: BASE_TAG=sid + debian-sid: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/u/linuxmintd # @@ -208,6 +213,7 @@ setenv = fedora-32: BASE_TAG=32 fedora-33: BASE_TAG=33 fedora-34: BASE_TAG=34 + fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/centos # centos-6 only has autoconf 2.63 -- too old for bootstrap; download configure tarball instead. @@ -226,23 +232,27 @@ setenv = gentoo: BASE_IMAGE=sheerluck/sage-on-gentoo-stage4 gentoo-python3.7: BASE_TAG=latest-py37 gentoo-python3.9: BASE_TAG=latest-py39 + gentoo: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/archlinux/ # archlinux: SYSTEM=arch archlinux: BASE_IMAGE=archlinux + archlinux: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/r/vbatts/slackware # slackware: SYSTEM=slackware slackware: BASE_IMAGE=vbatts/slackware slackware-14.2: BASE_TAG=14.2 + slackware: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/r/voidlinux/ # voidlinux: SYSTEM=void voidlinux: BASE_IMAGE=voidlinux/masterdir-x86_64-musl voidlinux: BASE_TAG=20200104 + voidlinux: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/r/continuumio # @@ -250,8 +260,9 @@ setenv = conda: CONDARC=/dev/null conda-forge: BASE_IMAGE=continuumio/miniconda3 conda-forge: CONDARC=condarc.yml + conda-forge: IGNORE_MISSING_SYSTEM_PACKAGES=no conda-anaconda3: BASE_IMAGE=continuumio/anaconda3 - conda-anaconda3: IGNORE_MISSING_SYSTEM_PACKAGES=yes + conda-anaconda3: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/nixos/nix/ # @@ -268,12 +279,14 @@ setenv = # opensuse: SYSTEM=opensuse opensuse: BASE_IMAGE=opensuse/leap + opensuse: IGNORE_MISSING_SYSTEM_PACKAGES=yes opensuse-42: BASE_TAG=42 opensuse-15.0: BASE_TAG=15.0 opensuse-15.1: BASE_TAG=15.1 opensuse-15.2: BASE_TAG=15.2 opensuse-15: BASE_TAG=15 opensuse-tumbleweed: BASE_IMAGE=opensuse/tumbleweed + opensuse-tumbleweed: IGNORE_MISSING_SYSTEM_PACKAGES=no # # Other architectures: # @@ -368,7 +381,8 @@ setenv = local-homebrew: PATH={env:HOMEBREW}/bin:/usr/bin:/bin:/usr/sbin:/sbin local-homebrew-nokegonly: BOOTSTRAP=ACLOCAL_PATH="$HOMEBREW/opt/gettext/share/aclocal:$ACLOCAL_PATH" PATH="$HOMEBREW/opt/gettext/bin/:$PATH" ./bootstrap local-homebrew-!nokegonly: SETENV=. .homebrew-build-env - # + local-homebrew: IGNORE_MISSING_SYSTEM_PACKAGES=no + # conda local-conda: CONDA_PREFIX={envdir}/conda local-conda: PATH={env:CONDA_PREFIX}/bin:/usr/bin:/bin:/usr/sbin:/sbin local-conda: CONDA_PKGS_DIRS={env:SHARED_CACHE_DIR}/conda_pkgs From ab785af0aa359eb059e671371c8c980a5b636eaf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 21 Mar 2021 13:42:07 -0700 Subject: [PATCH 121/706] tox.ini: Use IGNORE_MISSING_SYSTEM_PACKAGES=no for rolling distributions, but IGNORE_MISSING_SYSTEM_PACKAGES=yes for unstable distributions --- tox.ini | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 4797b3f7cdf..7df5c5064c9 100644 --- a/tox.ini +++ b/tox.ini @@ -131,8 +131,8 @@ setenv = WITH_SYSTEM_SPKG=yes # Set this to 'yes' instead of 'no' to ignore missing system packages - by installing them one by one # and ignoring errors. We use that to take care of old versions of distributions. - # For -maximal environments, the default is 'yes' but later we override it for the most cutting edge versions - # of distributions, as well as for rolling distributions. + # For -maximal environments, the default is 'yes' but later we override it for rolling distributions + # (but not for unstable distributions that often have intermittent issues). IGNORE_MISSING_SYSTEM_PACKAGES=no maximal: IGNORE_MISSING_SYSTEM_PACKAGES=yes # What system packages should be installed. Default: All standard packages with spkg-configure. @@ -165,8 +165,8 @@ setenv = ubuntu-bionic: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-focal: BASE_TAG=focal ubuntu-groovy: BASE_TAG=groovy + ubuntu-groovy: IGNORE_MISSING_SYSTEM_PACKAGES=no ubuntu-hirsute: BASE_TAG=hirsute - ubuntu-hirsute: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/debian # debian-bullseye does not have libgiac-dev @@ -181,7 +181,6 @@ setenv = debian-bullseye: BASE_TAG=bullseye debian-bullseye: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-sid: BASE_TAG=sid - debian-sid: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/u/linuxmintd # @@ -212,8 +211,8 @@ setenv = fedora-31: BASE_TAG=31 fedora-32: BASE_TAG=32 fedora-33: BASE_TAG=33 + fedora-33: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-34: BASE_TAG=34 - fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no # # https://hub.docker.com/_/centos # centos-6 only has autoconf 2.63 -- too old for bootstrap; download configure tarball instead. From bc67975e24a71b121cbed5f245125b6abbb2b288 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Apr 2021 14:25:41 -0700 Subject: [PATCH 122/706] build/pkgs/singular/spkg-install.in: Use --with-libparse - just in case we use a tarball without built documentation --- build/pkgs/singular/spkg-install.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/pkgs/singular/spkg-install.in b/build/pkgs/singular/spkg-install.in index baec6f12db6..172bade36bd 100644 --- a/build/pkgs/singular/spkg-install.in +++ b/build/pkgs/singular/spkg-install.in @@ -86,6 +86,7 @@ config() --disable-python_module \ --disable-python-module \ --disable-static \ + --with-libparse \ $SINGULAR_CONFIGURE } @@ -99,7 +100,7 @@ build_singular() sdh_make -j1 sdh_make_install - # Singular tarballs made using "make dist" do not contain built documentation. + # Singular tarballs made using "make dist" (without --enable-doc-build) do not contain built documentation. if [ ! -e doc/doc.tbz2 ]; then (cd doc && make singular.hlp && sdh_install singular.hlp "$SAGE_SHARE/info/") || sdh_die "Building documentation failed" fi From 6f308434fa7c955ca48738c7b038afdcdde9303d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 11 Apr 2021 14:40:10 -0700 Subject: [PATCH 123/706] Polyhedron_base.affine_hull_manifold: New --- src/sage/geometry/polyhedron/base.py | 49 +++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 745930a8051..b3542228cef 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10110,7 +10110,7 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog sage: AA(Pgonal.volume()^2) == (Pnormal.volume()^2)*AA(Adet) True - An other example with ``as_affine_map=True``:: + Another example with ``as_affine_map=True``:: sage: P = polytopes.permutahedron(4) sage: A, b = P.affine_hull_projection(orthonormal=True, as_affine_map=True, extend=True) @@ -10401,6 +10401,53 @@ def affine_hull_projection(self, as_polyhedron=None, as_affine_map=False, orthog affine_hull = deprecated_function_alias(29326, affine_hull_projection) + def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambient_space=None, + names=None, **kwds): + """ + Return the affine hull of ``self`` as a submanifold of the ambient Euclidean space. + + INPUT: + + - ``ambient_space`` -- a :class:`~sage.manifolds.differentiable.examples.euclidean.EuclideanSpace` + of the ambient dimension (default: a new instance of ``EuclideanSpace``) + the ambient space. + + - optional arguments accepted by :meth:`~sage.geometry.polyhedron.base.affine_hull_projection`. + + The default chart is determined by the optional arguments of + :meth:`~sage.geometry.polyhedron.base.affine_hull_projection`. + + EXAMPLES:: + + sage: triangle = Polyhedron([(1,0,0), (0,1,0), (0,0,1)]); triangle + A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices + sage: triangle.affine_hull_manifold() + + + + """ + if ambient_space is None: + from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace + ambient_space = EuclideanSpace(self.ambient_dim(), start_index=start_index) + CE = ambient_space.default_chart() + + from sage.manifolds.manifold import Manifold + if name is None: + name = 'H' + H = Manifold(self.dim(), name, ambient=ambient_space, + latex_name=latex_name, start_index=start_index) + if names is None: + names = tuple(f'x{i}' for i in range(self.dim())) + CH = H.chart(names=names) + + # The inverse + A, b = self.affine_hull_projection(as_affine_map=True, **kwds) + + + #phi = H.continuous_map(ambient_space, {(CH, CE): ....} + #H.set_embedding() + return H + def _polymake_init_(self): """ Return a polymake "Polytope" object corresponding to ``self``. From 05382808adf7af99caa94c70c88548fe59515c83 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Apr 2021 22:55:38 -0700 Subject: [PATCH 124/706] Polyhedron_base.affine_hull_manifold: Finish --- src/sage/geometry/polyhedron/base.py | 39 +++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index b3542228cef..86653ae0688 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10421,9 +10421,25 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien sage: triangle = Polyhedron([(1,0,0), (0,1,0), (0,0,1)]); triangle A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: triangle.affine_hull_manifold() - - + sage: A = triangle.affine_hull_manifold(name='A'); A + 2-dimensional differentiable submanifold A embedded in the Euclidean space E^3 + sage: A.embedding().display() + A --> E^3 + (x0, x1) |--> (x, y, z) = (x0, x1, -x0 - x1 + 1) + sage: A.embedding().inverse().display() + E^3 --> A + (x, y, z) |--> (x0, x1) = (x, y) + + Orthogonal version:: + + sage: A = triangle.affine_hull_manifold(name='A', orthogonal=True); A + 2-dimensional differentiable submanifold A embedded in the Euclidean space E^3 + sage: A.embedding().display() + A --> E^3 + (x0, x1) |--> (x, y, z) = (-1/2*x0 - 1/3*x1 + 1, 1/2*x0 - 1/3*x1, 2/3*x1) + sage: A.embedding().inverse().display() + E^3 --> A + (x, y, z) |--> (x0, x1) = (-x + y + 1, -1/2*x - 1/2*y + z + 1/2) """ if ambient_space is None: @@ -10440,12 +10456,17 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien names = tuple(f'x{i}' for i in range(self.dim())) CH = H.chart(names=names) - # The inverse - A, b = self.affine_hull_projection(as_affine_map=True, **kwds) - - - #phi = H.continuous_map(ambient_space, {(CH, CE): ....} - #H.set_embedding() + data = self.affine_hull_projection(return_all_data=True, **kwds) + projection_linear_map, projection_translation_vector = data['projection_map'] + projection_matrix = projection_linear_map.matrix().transpose() + section_linear_map, section_translation_vector = data['section_map'] + section_matrix = section_linear_map.matrix().transpose() + + phi = H.continuous_map(ambient_space, {(CH, CE): + (section_matrix * vector(CH._xx) + section_translation_vector).list()}) + phi_inv = ambient_space.continuous_map(H, {(CE, CH): + (projection_matrix * vector(CE._xx) + projection_translation_vector).list()}) + H.set_embedding(phi, inverse=phi_inv) return H def _polymake_init_(self): From f2bdbf247d8e271c46c04e080af4c7d452d1779f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Apr 2021 23:17:42 -0700 Subject: [PATCH 125/706] Add example --- src/sage/geometry/polyhedron/base.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 86653ae0688..48d288df133 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10441,6 +10441,16 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien E^3 --> A (x, y, z) |--> (x0, x1) = (-x + y + 1, -1/2*x - 1/2*y + z + 1/2) + Arrangement of affine hull of facets:: + + sage: D = polytopes.dodecahedron() + sage: E3 = EuclideanSpace(3) + sage: submanifolds = [ + ....: F.as_polyhedron().affine_hull_manifold(name=f'F{i}', orthogonal=True, ambient_space=E3) + ....: for i, F in enumerate(D.facets())] + sage: sum(FM.plot({}, srange(-2, 2, 0.1), srange(-2, 2, 0.1), opacity=0.2) + ....: for FM in submanifolds) # not tested + """ if ambient_space is None: from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace From eb88cfeff352600c91ba2c47f57323782e39669b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Apr 2021 23:26:46 -0700 Subject: [PATCH 126/706] also plot the polyhedron --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 48d288df133..aa6f76919ac 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10449,7 +10449,7 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien ....: F.as_polyhedron().affine_hull_manifold(name=f'F{i}', orthogonal=True, ambient_space=E3) ....: for i, F in enumerate(D.facets())] sage: sum(FM.plot({}, srange(-2, 2, 0.1), srange(-2, 2, 0.1), opacity=0.2) - ....: for FM in submanifolds) # not tested + ....: for FM in submanifolds) + D.plot() # not tested """ if ambient_space is None: From 3dca4e6c927053770c5e938f5a59be9047602cde Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 15 Apr 2021 15:20:07 +0100 Subject: [PATCH 127/706] #31443: undated pkg info for 20210415 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 1997cd68c7d..5d12b8a60e0 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=d1c9add8c95ba502440e7d197da0c1e3329adc7d -md5=47e2c9b59c1d322709a5d99e438d09cd -cksum=2380455615 +sha1=2f0ea52fd59352fa0dec8cabf5d513b0f0cf7629 +md5=2a3d4b37ee5f504d46c010e29040d7a4 +cksum=2697309856 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index c12f44c8f0c..e9d3aa7a596 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210408 +20210415 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 8da0723747d..edb759d45f5 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210408"]) + m4_pushdef([SAGE_ECLIB_VER],["20210415"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From 3d5f3c49ffff68707f880a09ad12d11d171fc90c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 09:47:23 -0700 Subject: [PATCH 128/706] Put '# not tested' in the right place --- src/sage/geometry/polyhedron/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index aa6f76919ac..02c80b45116 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10448,8 +10448,9 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien sage: submanifolds = [ ....: F.as_polyhedron().affine_hull_manifold(name=f'F{i}', orthogonal=True, ambient_space=E3) ....: for i, F in enumerate(D.facets())] - sage: sum(FM.plot({}, srange(-2, 2, 0.1), srange(-2, 2, 0.1), opacity=0.2) - ....: for FM in submanifolds) + D.plot() # not tested + sage: sum(FM.plot({}, srange(-2, 2, 0.1), srange(-2, 2, 0.1), opacity=0.2) # not tested + ....: for FM in submanifolds) + D.plot() + Graphics3d Object """ if ambient_space is None: From de50778341abffc546cc048cc547121bc401288b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 10:50:55 -0700 Subject: [PATCH 129/706] Polyhedron_base.affine_hull_manifold: Make it Riemannian --- src/sage/geometry/polyhedron/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 02c80b45116..19b079eb810 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10461,7 +10461,7 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien from sage.manifolds.manifold import Manifold if name is None: name = 'H' - H = Manifold(self.dim(), name, ambient=ambient_space, + H = Manifold(self.dim(), name, ambient=ambient_space, structure="Riemannian", latex_name=latex_name, start_index=start_index) if names is None: names = tuple(f'x{i}' for i in range(self.dim())) @@ -10473,10 +10473,10 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien section_linear_map, section_translation_vector = data['section_map'] section_matrix = section_linear_map.matrix().transpose() - phi = H.continuous_map(ambient_space, {(CH, CE): - (section_matrix * vector(CH._xx) + section_translation_vector).list()}) - phi_inv = ambient_space.continuous_map(H, {(CE, CH): - (projection_matrix * vector(CE._xx) + projection_translation_vector).list()}) + phi = H.diff_map(ambient_space, {(CH, CE): + (section_matrix * vector(CH._xx) + section_translation_vector).list()}) + phi_inv = ambient_space.diff_map(H, {(CE, CH): + (projection_matrix * vector(CE._xx) + projection_translation_vector).list()}) H.set_embedding(phi, inverse=phi_inv) return H From 931b5ca961235d976f2a2eaa132a5794c15afaf8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 15 Apr 2021 22:54:22 -0700 Subject: [PATCH 130/706] Polyhedron_base.affine_hull_manifold: Use a foliation --- src/sage/geometry/polyhedron/base.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 626cefa83f4..dab65fa3ed9 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10494,6 +10494,12 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien sage: A.embedding().inverse().display() E^3 --> A (x, y, z) |--> (x0, x1) = (x, y) + sage: A.adapted_chart() + [Chart (E^3, (x0_E3, x1_E3, t0_E3))] + sage: A.normal().display() + n = 1/3*sqrt(3) e_x + 1/3*sqrt(3) e_y + 1/3*sqrt(3) e_z + sage: A.volume_form() + 2-form eps_gamma on the 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 Orthogonal version:: @@ -10538,11 +10544,26 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien section_linear_map, section_translation_vector = data['section_map'] section_matrix = section_linear_map.matrix().transpose() + from sage.symbolic.ring import SR + # We use the slacks of the (linear independent) equations as the foliation parameters + foliation_parameters = vector(SR.var(f't{i}') for i in range(self.ambient_dim() - self.dim())) + normal_matrix = matrix(equation.A() for equation in self.equation_generator()).transpose() + slack_matrix = normal_matrix.pseudoinverse() + + projection_linear_map.matrix().transpose().right_kernel_matrix().transpose() + phi = H.diff_map(ambient_space, {(CH, CE): - (section_matrix * vector(CH._xx) + section_translation_vector).list()}) + (section_matrix * vector(CH._xx) + section_translation_vector + + normal_matrix * foliation_parameters).list()}) phi_inv = ambient_space.diff_map(H, {(CE, CH): (projection_matrix * vector(CE._xx) + projection_translation_vector).list()}) - H.set_embedding(phi, inverse=phi_inv) + + foliation_scalar_fields = {parameter: + ambient_space.scalar_field({CE: slack_matrix.row(i) * (vector(CE._xx) - section_translation_vector)}) + for i, parameter in enumerate(foliation_parameters)} + + H.set_embedding(phi, inverse=phi_inv, + var=list(foliation_parameters), t_inverse=foliation_scalar_fields) return H def _polymake_init_(self): From 1b5c153e6c973c6fe0b2040dc2af2f12960b8716 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Apr 2021 13:46:14 -0700 Subject: [PATCH 131/706] ManifoldSubset.subset_digraph: New --- src/sage/manifolds/subset.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 6ee2826bca1..b34c6cee4cf 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -653,6 +653,39 @@ def list_of_subsets(self): """ return sorted(self._subsets, key=lambda x: x._name) + def subset_digraph(self, loops=False): + r""" + Return the digraph whose arcs represent subset relations among the subsets of ``self``. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + sage: D = M.subset_digraph(); D + sage: D.plot(layout='acyclic') # not tested + sage: VW = V.union(W) + sage: D = M.subset_digraph(); D + sage: D.plot(layout='acyclic') # not tested + + + """ + from sage.graphs.digraph import DiGraph + D = DiGraph(multiedges=False, loops=loops) + visited = set() + to_visit = [self] + while to_visit: + S = to_visit.pop() + if S not in visited: + visited.add(S) + subsets_without_self = [subset for subset in S._subsets + if subset is not S] + if loops: + D.add_edges((subset, S) for subset in S._subsets) + else: + D.add_edges((subset, S) for subset in subsets_without_self) + to_visit.extend(subsets_without_self) + return D + def get_subset(self, name): r""" Get a subset by its name. From 91bbc3ecb2b08dd3ffbb0ef1f3c2297de6b33816 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Apr 2021 19:07:25 -0700 Subject: [PATCH 132/706] ManifoldSubset.subset_poset: New --- src/sage/manifolds/subset.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index b34c6cee4cf..466a9a7a882 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -686,6 +686,21 @@ def subset_digraph(self, loops=False): to_visit.extend(subsets_without_self) return D + def subset_poset(self): + """ + Return the poset of the subsets of ``self``. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + sage: VW = V.union(W) + sage: P = M.subset_poset(); D + sage: P.plot() # not tested + """ + from sage.combinat.posets import Poset + return Poset(self.subset_digraph()) + def get_subset(self, name): r""" Get a subset by its name. From 858c82de2f280be41c6aa6c3928290c30c97f240 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 17 Apr 2021 22:57:22 -0700 Subject: [PATCH 133/706] Fixup --- src/sage/manifolds/subset.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 466a9a7a882..691bfffcbf5 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -666,8 +666,6 @@ def subset_digraph(self, loops=False): sage: VW = V.union(W) sage: D = M.subset_digraph(); D sage: D.plot(layout='acyclic') # not tested - - """ from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) @@ -695,10 +693,10 @@ def subset_poset(self): sage: M = Manifold(3, 'M') sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') sage: VW = V.union(W) - sage: P = M.subset_poset(); D + sage: P = M.subset_poset(); P sage: P.plot() # not tested """ - from sage.combinat.posets import Poset + from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph()) def get_subset(self, name): From 6988be0f476f1a24111e1c4d437621e2a580f576 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 18 Apr 2021 12:05:31 +0200 Subject: [PATCH 134/706] Trac #31669: homology functor --- src/sage/algebras/commutative_dga.py | 4 + src/sage/categories/all.py | 2 +- src/sage/categories/chain_complexes.py | 114 ++++++++++++++++++++++++- src/sage/homology/chain_complex.py | 2 +- 4 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index d430f3d88df..e5a44933a07 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -2255,6 +2255,8 @@ def cohomology(self, n): """ return self._differential.cohomology(n) + homology = cohomology + def cohomology_generators(self, max_degree): """ Return lifts of algebra generators for cohomology in degrees at @@ -3228,6 +3230,8 @@ def cohomology(self, n, total=False): """ return self._differential.cohomology(n, total) + homology = cohomology + class Element(GCAlgebra_multigraded.Element, DifferentialGCAlgebra.Element): """ Element class of a commutative differential multi-graded algebra. diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index 1d85664c5fe..053fd577d24 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -4,7 +4,7 @@ from .category_types import Elements -from .chain_complexes import ChainComplexes +from .chain_complexes import ChainComplexes, HomologyFunctor from .simplicial_complexes import SimplicialComplexes diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 72a4763b3b8..85b73f19515 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -7,14 +7,17 @@ # 2009 Mike Hansen # 2013 Volker Braun # 2013, 2015 Travis Scrimshaw +# 2021 Michael Jung # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.category_types import Category_module +from .category_types import Category_module +from .modules import Modules +from .functor import Functor +from sage.categories.morphism import SetMorphism -# TODO: make this into a better category ############################################################# # ChainComplex ############################################################# @@ -26,7 +29,6 @@ class ChainComplexes(Category_module): sage: ChainComplexes(RationalField()) Category of chain complexes over Rational Field - sage: ChainComplexes(Integers(9)) Category of chain complexes over Ring of integers modulo 9 @@ -47,3 +49,109 @@ def super_categories(self): if base_ring in Fields(): return [VectorSpaces(base_ring)] return [Modules(base_ring)] + + class ParentMethods: + def homology(self, *args, **kwargs): + r""" + Return the homology of the chain complex. + + INPUT: + + - ``n`` -- degree of homology group + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C.homology(0) + Z x Z + sage: C.homology(1) + Z x C3 + sage: C.homology(2) + 0 + + :: + + sage: A. = GradedCommutativeAlgebra(QQ, degrees=(2, 2, 3)) + sage: C = A.cdg_algebra({z: x*y}) + sage: C.homology(0) + Free module generated by {[1]} over Rational Field + sage: C.homology(1) + Free module generated by {} over Rational Field + sage: C.homology(2) + Free module generated by {[x], [y]} over Rational Field + sage: C.homology(3) + Free module generated by {} over Rational Field + sage: C.homology(4) + Free module generated by {[x^2], [y^2]} over Rational Field + + """ + raise NotImplementedError + + class MorphismMethods: + def induced_homology_morphism(self, *args, **kwargs): + r""" + Return the from the chain map induced morphism on homology level. + """ + raise NotImplementedError + +class HomologyFunctor(Functor): + r""" + Homology functor. + + INPUT: + + - ``domain`` -- must be a category of chain complexes + - ``n`` -- degree of the homology groups + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: H = HomologyFunctor(ChainComplexes(ZZ), 1) + sage: H(C) + Z x C3 + + :: + + sage: A. = GradedCommutativeAlgebra(QQ, degrees=(2, 2, 3)) + sage: C = A.cdg_algebra({z: x*y}) + sage: H = HomologyFunctor(ChainComplexes(QQ), 2) + sage: H(C) + Free module generated by {[x], [y]} over Rational Field + + """ + def __init__(self, domain, n): + r""" + Construct the homology functor. + + TESTS:: + + sage: H = HomologyFunctor(ChainComplexes(QQ), 1); H + Functor from Category of chain complexes over Rational Field to + Category of vector spaces over Rational Field + + """ + if not isinstance(domain, ChainComplexes): + raise TypeError(f'{domain} must be a category of chain complexes') + codomain = Modules(domain.base_ring()) + super().__init__(domain, codomain) + self.__n = n + + def _apply_functor(self, x): + r""" + Apply ``self`` to a chain complex. + + TESTS:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: H = HomologyFunctor(ChainComplexes(ZZ), 1) + sage: H._apply_functor(C) + Z x C3 + + """ + return x.homology(self.__n) + + def _apply_functor_to_morphism(self, f): + r""" + Apply ``self`` to a chain map. + """ + return f.induced_homology_morphism() diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 49aaff1a854..7f1953a174b 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Chain complexes +Bounded chain complexes This module implements bounded chain complexes of free `R`-modules, for any commutative ring `R` (although the interesting things, like From 718d354499400ef4e1686c7f04ea4f2529750e9b Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sun, 18 Apr 2021 12:12:44 +0200 Subject: [PATCH 135/706] Trac #31669: add category of chain complexes to commutative_dga --- src/sage/algebras/commutative_dga.py | 33 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index e5a44933a07..b54d919591e 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -78,6 +78,7 @@ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.functional import is_odd, is_even from sage.misc.misc_c import prod +from sage.categories.chain_complexes import ChainComplexes from sage.categories.algebras import Algebras from sage.categories.morphism import Morphism from sage.categories.modules import Modules @@ -545,6 +546,8 @@ def cohomology(self, n): sorting_key=sorting_keys, monomial_reverse=True) + homology = cohomology + def _is_nonzero(self): """ Return ``True`` iff this morphism is nonzero. @@ -837,6 +840,8 @@ def cohomology(self, n, total=False): sorting_key=sorting_keys, monomial_reverse=True) + homology = cohomology + ########################################################### # Commutative graded algebras @@ -896,7 +901,7 @@ class GCAlgebra(UniqueRepresentation, QuotientRing_nc): """ # TODO: This should be a __classcall_private__? @staticmethod - def __classcall__(cls, base, names=None, degrees=None, R=None, I=None): + def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category=None): r""" Normalize the input for the :meth:`__init__` method and the unique representation. @@ -995,9 +1000,10 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None): side='twosided') return super(GCAlgebra, cls).__classcall__(cls, base=base, names=names, - degrees=degrees, R=R, I=I) + degrees=degrees, R=R, I=I, + category=category) - def __init__(self, base, R=None, I=None, names=None, degrees=None): + def __init__(self, base, R=None, I=None, names=None, degrees=None, category=None): """ Initialize ``self``. @@ -1027,8 +1033,9 @@ def __init__(self, base, R=None, I=None, names=None, degrees=None): sage: TestSuite(A).run() """ self._degrees = tuple(degrees) - cat = Algebras(R.base_ring()).Graded() - QuotientRing_nc.__init__(self, R, I, names, category=cat) + if category is None: + category = Algebras(R.base_ring()).Graded() + QuotientRing_nc.__init__(self, R, I, names, category=category) def _repr_(self): """ @@ -1660,7 +1667,7 @@ class GCAlgebra_multigraded(GCAlgebra): sage: c.degree(total=True) 2 """ - def __init__(self, base, degrees, names=None, R=None, I=None): + def __init__(self, base, degrees, names=None, R=None, I=None, category=None): """ Initialize ``self``. @@ -1674,7 +1681,8 @@ def __init__(self, base, degrees, names=None, R=None, I=None): sage: TestSuite(C).run() """ total_degs = [total_degree(d) for d in degrees] - GCAlgebra.__init__(self, base, R=R, I=I, names=names, degrees=total_degs) + GCAlgebra.__init__(self, base, R=R, I=I, names=names, + degrees=total_degs, category=category) self._degrees_multi = degrees self._grading_rank = len(list(degrees[0])) @@ -2001,10 +2009,10 @@ def __init__(self, A, differential): ... ValueError: The given dictionary does not determine a valid differential """ + cat = Algebras(A.base()).Graded() & ChainComplexes(A.base()) GCAlgebra.__init__(self, A.base(), names=A._names, - degrees=A._degrees, - R=A.cover_ring(), - I=A.defining_ideal()) + degrees=A._degrees, R=A.cover_ring(), + I=A.defining_ideal(), category=cat) self._differential = Differential(self, differential._dic_) self._minimalmodels = {} self._numerical_invariants = {} @@ -3071,10 +3079,11 @@ def __init__(self, A, differential): ... ValueError: The differential does not have a well-defined degree """ + cat = Algebras(A.base()).Graded() & ChainComplexes(A.base()) GCAlgebra_multigraded.__init__(self, A.base(), names=A._names, degrees=A._degrees_multi, - R=A.cover_ring(), - I=A.defining_ideal()) + R=A.cover_ring(), I=A.defining_ideal(), + category=cat) self._differential = Differential_multigraded(self, differential._dic_) def _base_repr(self): From 2e2d91ebc69117b7a49e1f099f83993bfd118db8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 13:39:15 -0700 Subject: [PATCH 136/706] ManifoldSubset.{sub,super}set_{digraph,poset}: More options --- src/sage/manifolds/subset.py | 52 +++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 691bfffcbf5..b33904a0430 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -653,15 +653,21 @@ def list_of_subsets(self): """ return sorted(self._subsets, key=lambda x: x._name) - def subset_digraph(self, loops=False): + def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): r""" Return the digraph whose arcs represent subset relations among the subsets of ``self``. + INPUT: + + - ``lower_bound`` -- (default: ``None``) only include supersets of this + - ``open_covers`` -- (default: ``False``) include vertices for open covers + EXAMPLES:: sage: M = Manifold(3, 'M') sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') sage: D = M.subset_digraph(); D + sage: D.edges() sage: D.plot(layout='acyclic') # not tested sage: VW = V.union(W) sage: D = M.subset_digraph(); D @@ -673,18 +679,30 @@ def subset_digraph(self, loops=False): to_visit = [self] while to_visit: S = to_visit.pop() - if S not in visited: - visited.add(S) - subsets_without_self = [subset for subset in S._subsets - if subset is not S] - if loops: - D.add_edges((subset, S) for subset in S._subsets) - else: - D.add_edges((subset, S) for subset in subsets_without_self) - to_visit.extend(subsets_without_self) + if S in visited: + continue + if lower_bound is not None: + if not lower_bound.is_subset(S): + continue + visited.add(S) + subsets_without_S = [subset for subset in S._subsets + if subset is not S] + if loops: + D.add_edges((subset, S) for subset in S._subsets) + else: + D.add_edges((subset, S) for subset in subsets_without_S) + to_visit.extend(subsets_without_S) + if open_covers: + # Represent an open cover with a node that is a frozenset, + # rather than a list. + open_covers_without_S = [frozenset(open_cover) + for open_cover in S._open_covers + if open_cover != [S]] + D.add_edges((S, open_cover) + for open_cover in open_covers_without_S) return D - def subset_poset(self): + def subset_poset(self, open_covers=False, lower_bound=None): """ Return the poset of the subsets of ``self``. @@ -697,7 +715,17 @@ def subset_poset(self): sage: P.plot() # not tested """ from sage.combinat.posets.posets import Poset - return Poset(self.subset_digraph()) + return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) + + def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): + if upper_bound is None: + upper_bound = self._manifold + return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, lower_bound=self) + + def superset_poset(self, open_covers=False, upper_bound=None): + if upper_bound is None: + upper_bound = self._manifold + return upper_bound.subset_poset(lower_bound=self) def get_subset(self, name): r""" From 02a82f90ccf15540fa8518e3591485092845d4d3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 15:34:12 -0700 Subject: [PATCH 137/706] Fix up handling of {lower,upper}_bound, add examples --- src/sage/manifolds/subset.py | 87 ++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index b33904a0430..3b149c185e2 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -659,39 +659,59 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): INPUT: + - ``loops`` -- (default: ``False``) whether to include the trivial containment + of each subset in itself as loops of the digraph - ``lower_bound`` -- (default: ``None``) only include supersets of this - - ``open_covers`` -- (default: ``False``) include vertices for open covers + - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers EXAMPLES:: sage: M = Manifold(3, 'M') sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') sage: D = M.subset_digraph(); D - sage: D.edges() + Digraph on 4 vertices + sage: D.edges(key=lambda e: (e[0]._name, e[1]._name)) + [(Open subset U of the 3-dimensional differentiable manifold M, + 3-dimensional differentiable manifold M, + None), + (Open subset V of the 3-dimensional differentiable manifold M, + 3-dimensional differentiable manifold M, + None), + (Open subset W of the 3-dimensional differentiable manifold M, + 3-dimensional differentiable manifold M, + None)] sage: D.plot(layout='acyclic') # not tested sage: VW = V.union(W) sage: D = M.subset_digraph(); D + Digraph on 5 vertices sage: D.plot(layout='acyclic') # not tested """ from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) + if lower_bound is not None: + if not lower_bound.is_subset(self): + return D visited = set() to_visit = [self] while to_visit: S = to_visit.pop() if S in visited: continue - if lower_bound is not None: - if not lower_bound.is_subset(S): - continue visited.add(S) - subsets_without_S = [subset for subset in S._subsets + + if lower_bound is None: + subsets = S._subsets + else: + subsets = [subset for subset in S._subsets + if lower_bound.is_subset(subset)] + subsets_without_S = [subset for subset in subsets if subset is not S] if loops: - D.add_edges((subset, S) for subset in S._subsets) + D.add_edges((subset, S) for subset in subsets) else: D.add_edges((subset, S) for subset in subsets_without_S) to_visit.extend(subsets_without_S) + if open_covers: # Represent an open cover with a node that is a frozenset, # rather than a list. @@ -703,29 +723,78 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): return D def subset_poset(self, open_covers=False, lower_bound=None): - """ + r""" Return the poset of the subsets of ``self``. + INPUT: + + - ``lower_bound`` -- (default: ``None``) only include supersets of this + - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + EXAMPLES:: sage: M = Manifold(3, 'M') sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') sage: VW = V.union(W) sage: P = M.subset_poset(); P + Finite poset containing 5 elements + sage: P.maximal_elements() + [3-dimensional differentiable manifold M] + sage: P.minimal_elements() + [Open subset U of the 3-dimensional differentiable manifold M, + Open subset W of the 3-dimensional differentiable manifold M, + Open subset V of the 3-dimensional differentiable manifold M] + sage: P.lower_covers(M) + [Open subset U of the 3-dimensional differentiable manifold M, + Open subset V_union_W of the 3-dimensional differentiable manifold M] sage: P.plot() # not tested """ from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): + """ + Return the digraph whose arcs represent subset relations among the supersets of ``self``. + + INPUT: + + - ``loops`` -- (default: ``False``) whether to include the trivial containment + of each subset in itself as loops of the digraph + - ``upper_bound`` -- (default: ``None``) only include subsets of this + - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + sage: VW = V.union(W) + sage: P = V.superset_digraph(loops=False, upper_bound=VW); P + Digraph on 2 vertices + """ if upper_bound is None: upper_bound = self._manifold return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, lower_bound=self) def superset_poset(self, open_covers=False, upper_bound=None): + r""" + Return the poset of the supersets of ``self``. + + INPUT: + + - ``upper_bound`` -- (default: ``None``) only include subsets of this + - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + sage: VW = V.union(W) + sage: P = V.superset_poset(); P + Finite poset containing 3 elements + """ if upper_bound is None: upper_bound = self._manifold - return upper_bound.subset_poset(lower_bound=self) + return upper_bound.subset_poset(open_covers=open_covers, lower_bound=self) def get_subset(self, name): r""" From 44c8d8c6f7cd8ed6b4055b464e82364e88cc9665 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 15:55:30 -0700 Subject: [PATCH 138/706] Include example for open_covers=True --- src/sage/manifolds/subset.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 3b149c185e2..fb90c973378 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -740,14 +740,24 @@ def subset_poset(self, open_covers=False, lower_bound=None): Finite poset containing 5 elements sage: P.maximal_elements() [3-dimensional differentiable manifold M] - sage: P.minimal_elements() + sage: sorted(P.minimal_elements(), key=lambda v: v._name) [Open subset U of the 3-dimensional differentiable manifold M, - Open subset W of the 3-dimensional differentiable manifold M, - Open subset V of the 3-dimensional differentiable manifold M] - sage: P.lower_covers(M) + Open subset V of the 3-dimensional differentiable manifold M, + Open subset W of the 3-dimensional differentiable manifold M] + sage: sorted(P.lower_covers(M), key=str) [Open subset U of the 3-dimensional differentiable manifold M, Open subset V_union_W of the 3-dimensional differentiable manifold M] sage: P.plot() # not tested + + If ``open_covers`` is ``True``, the poset includes a special vertex for + each nontrivial open cover of a subset. + + sage: P = M.subset_poset(open_covers=True); P + Finite poset containing 6 elements + sage: P.upper_covers(VW) + [3-dimensional differentiable manifold M, + frozenset({Open subset V of the 3-dimensional differentiable manifold M, + Open subset W of the 3-dimensional differentiable manifold M})] """ from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) From 643392cdb38003a090b5d6adaaa7e95852d1855b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 20:04:46 -0700 Subject: [PATCH 139/706] Use new format of affine hull data --- src/sage/geometry/polyhedron/base.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d19a83160f9..6e0e03a25bb 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10576,10 +10576,10 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien CH = H.chart(names=names) data = self.affine_hull_projection(return_all_data=True, **kwds) - projection_linear_map, projection_translation_vector = data['projection_map'] - projection_matrix = projection_linear_map.matrix().transpose() - section_linear_map, section_translation_vector = data['section_map'] - section_matrix = section_linear_map.matrix().transpose() + projection_matrix = data.projection_linear_map.matrix().transpose() + projection_translation_vector = data.projection_translation + section_matrix = data.section_linear_map.matrix().transpose() + section_translation_vector = data.section_translation from sage.symbolic.ring import SR # We use the slacks of the (linear independent) equations as the foliation parameters @@ -10587,8 +10587,6 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien normal_matrix = matrix(equation.A() for equation in self.equation_generator()).transpose() slack_matrix = normal_matrix.pseudoinverse() - projection_linear_map.matrix().transpose().right_kernel_matrix().transpose() - phi = H.diff_map(ambient_space, {(CH, CE): (section_matrix * vector(CH._xx) + section_translation_vector + normal_matrix * foliation_parameters).list()}) From 57dce72795fea7cad909c678482c90973e872358 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 20:20:28 -0700 Subject: [PATCH 140/706] Polyhedron.affine_hull_manifold: Update doctests --- src/sage/geometry/polyhedron/base.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6e0e03a25bb..5aa377ad612 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10524,10 +10524,10 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien sage: triangle = Polyhedron([(1,0,0), (0,1,0), (0,0,1)]); triangle A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices sage: A = triangle.affine_hull_manifold(name='A'); A - 2-dimensional differentiable submanifold A embedded in the Euclidean space E^3 + 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 sage: A.embedding().display() A --> E^3 - (x0, x1) |--> (x, y, z) = (x0, x1, -x0 - x1 + 1) + (x0, x1) |--> (x, y, z) = (t0 + x0, t0 + x1, t0 - x0 - x1 + 1) sage: A.embedding().inverse().display() E^3 --> A (x, y, z) |--> (x0, x1) = (x, y) @@ -10535,16 +10535,16 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien [Chart (E^3, (x0_E3, x1_E3, t0_E3))] sage: A.normal().display() n = 1/3*sqrt(3) e_x + 1/3*sqrt(3) e_y + 1/3*sqrt(3) e_z - sage: A.volume_form() + sage: A.volume_form() # known bug 2-form eps_gamma on the 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 Orthogonal version:: sage: A = triangle.affine_hull_manifold(name='A', orthogonal=True); A - 2-dimensional differentiable submanifold A embedded in the Euclidean space E^3 + 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 sage: A.embedding().display() A --> E^3 - (x0, x1) |--> (x, y, z) = (-1/2*x0 - 1/3*x1 + 1, 1/2*x0 - 1/3*x1, 2/3*x1) + (x0, x1) |--> (x, y, z) = (t0 - 1/2*x0 - 1/3*x1 + 1, t0 + 1/2*x0 - 1/3*x1, t0 + 2/3*x1) sage: A.embedding().inverse().display() E^3 --> A (x, y, z) |--> (x0, x1) = (-x + y + 1, -1/2*x - 1/2*y + z + 1/2) From a47ea805c6bdf547bd96b1ee1f9db3406bf2d1ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 21:50:09 -0700 Subject: [PATCH 141/706] Update doctest --- src/sage/geometry/polyhedron/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 5aa377ad612..486cdc427b9 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10535,7 +10535,9 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien [Chart (E^3, (x0_E3, x1_E3, t0_E3))] sage: A.normal().display() n = 1/3*sqrt(3) e_x + 1/3*sqrt(3) e_y + 1/3*sqrt(3) e_z - sage: A.volume_form() # known bug + sage: A.induced_metric() # Need to call this before volume_form + Riemannian metric gamma on the 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 + sage: A.volume_form() 2-form eps_gamma on the 2-dimensional Riemannian submanifold A embedded in the Euclidean space E^3 Orthogonal version:: From 98e3ed671206d17e9dbe4377a37fd7648abfc3ce Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Mon, 19 Apr 2021 10:54:10 +0200 Subject: [PATCH 142/706] Trac #31669: add differentials to category --- src/sage/categories/chain_complexes.py | 24 ++++++++++++++++++++++++ src/sage/homology/chain_complex.py | 1 + 2 files changed, 25 insertions(+) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 85b73f19515..7acef57ff97 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -87,6 +87,30 @@ def homology(self, *args, **kwargs): """ raise NotImplementedError + def differential(self, *args, **kwargs): + r""" + Return the differentials (or boundary maps) of the chain complex. + + EXAMPLES:: + + sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}) + sage: C.differential(0) + [3 0 0] + [0 0 0] + + :: + + sage: A. = GradedCommutativeAlgebra(QQ, degrees=(2, 2, 3)) + sage: C = A.cdg_algebra({z: x*y}) + sage: C.differential() + Differential of Commutative Differential Graded Algebra with generators ('x', 'y', 'z') in degrees (2, 2, 3) over Rational Field + Defn: x --> 0 + y --> 0 + z --> x*y + + """ + raise NotImplementedError + class MorphismMethods: def induced_homology_morphism(self, *args, **kwargs): r""" diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 7f1953a174b..0563be63b59 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -957,6 +957,7 @@ def differential(self, dim=None): except KeyError: pass # all differentials that are not 0x0 are in self._diff + # TODO: turn differentials into morphisms between free modules? return matrix(self.base_ring(), 0, 0) def dual(self): From 57338c1cc0a835b9ac7fb027ffa99bb5f77a45fc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 19 Apr 2021 12:44:31 -0700 Subject: [PATCH 143/706] Update numpy to 1.20.2, scipy to 1.6.2, networkx to 2.5.1 --- build/pkgs/networkx/checksums.ini | 6 +- build/pkgs/networkx/package-version.txt | 2 +- build/pkgs/numpy/checksums.ini | 6 +- build/pkgs/numpy/package-version.txt | 2 +- ...for-np.isnan-np.infinite-np.isinf-an.patch | 356 ------------------ ...G-Update-compiler-check-for-AVX-512F.patch | 43 --- build/pkgs/numpy/patches/gh-18016.patch | 22 -- build/pkgs/scipy/checksums.ini | 6 +- build/pkgs/scipy/package-version.txt | 2 +- 9 files changed, 12 insertions(+), 433 deletions(-) delete mode 100644 build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch delete mode 100644 build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch delete mode 100644 build/pkgs/numpy/patches/gh-18016.patch diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index 6df69c9aca9..100611f9fa2 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,5 +1,5 @@ tarball=networkx-VERSION.tar.gz -sha1=439af030cbfdc2c4cbdef5dc423d5ea8e527d02a -md5=21f25be1f4373e19153a9beca63346e7 -cksum=1531245694 +sha1=67bf3fb6634d20d56afff123a0a3f03f0567316c +md5=bded9f095b2757f6ce11531a6f874d9d +cksum=3353048305 upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index 95e3ba81920..73462a5a134 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -2.5 +2.5.1 diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 4906cab96a2..bf10ae97dc2 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,5 +1,5 @@ tarball=numpy-VERSION.zip -sha1=61f0b3dad58ce97b14da9dccbee0722d36f26937 -md5=f6a1b48717c552bbc18f1adc3cc1fe0e -cksum=1120756655 +sha1=12b7a872c4d2ddcb22b5796f19ba4349e4fe0038 +md5=5e1b381630af4d18db0fedd56b6d8da2 +cksum=3027595668 upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.zip diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 83d5e73f00e..769e37e159d 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.19.5 +1.20.2 diff --git a/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch b/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch deleted file mode 100644 index 791be1864ec..00000000000 --- a/build/pkgs/numpy/patches/0001-ENH-Use-AVX-512-for-np.isnan-np.infinite-np.isinf-an.patch +++ /dev/null @@ -1,356 +0,0 @@ -From 7bde5a589359a096be253738ed7c53330f7ff4ad Mon Sep 17 00:00:00 2001 -From: Raghuveer Devulapalli -Date: Sun, 31 May 2020 09:16:33 -0700 -Subject: [PATCH 1/2] ENH: Use AVX-512 for np.isnan, np.infinite, np.isinf and - np.signbit (#16334) - -* ENH: Use AVX-512 for np.isnan, np.infinite, np.isinf and np.signbit - -* TST: Add tests to validate isnan, isfinite, signbit and isinf ufuncs - -* BENCH: Adding benchmarks for isnan, isinf, isfinite and signbit ---- - benchmarks/benchmarks/bench_avx.py | 6 +- - numpy/core/code_generators/generate_umath.py | 8 +- - numpy/core/include/numpy/npy_common.h | 7 ++ - numpy/core/setup_common.py | 11 ++ - numpy/core/src/umath/loops.c.src | 10 +- - numpy/core/src/umath/loops.h.src | 7 +- - numpy/core/src/umath/simd.inc.src | 116 ++++++++++++++++++- - numpy/core/tests/test_umath.py | 18 +++ - 8 files changed, 173 insertions(+), 10 deletions(-) - -diff --git a/benchmarks/benchmarks/bench_avx.py b/benchmarks/benchmarks/bench_avx.py -index 2a128b3ff..4f915f82a 100644 ---- a/benchmarks/benchmarks/bench_avx.py -+++ b/benchmarks/benchmarks/bench_avx.py -@@ -13,7 +13,11 @@ avx_ufuncs = ['sin', - 'rint', - 'floor', - 'ceil' , -- 'trunc'] -+ 'trunc', -+ 'isnan', -+ 'isfinite', -+ 'isinf', -+ 'signbit'] - stride = [1, 2, 4] - dtype = ['f', 'd'] - -diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py -index 202912c5f..992bdc862 100644 ---- a/numpy/core/code_generators/generate_umath.py -+++ b/numpy/core/code_generators/generate_umath.py -@@ -843,7 +843,7 @@ defdict = { - Ufunc(1, 1, None, - docstrings.get('numpy.core.umath.isnan'), - 'PyUFunc_IsFiniteTypeResolver', -- TD(noobj, out='?'), -+ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), - ), - 'isnat': - Ufunc(1, 1, None, -@@ -855,19 +855,19 @@ defdict = { - Ufunc(1, 1, None, - docstrings.get('numpy.core.umath.isinf'), - 'PyUFunc_IsFiniteTypeResolver', -- TD(noobj, out='?'), -+ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), - ), - 'isfinite': - Ufunc(1, 1, None, - docstrings.get('numpy.core.umath.isfinite'), - 'PyUFunc_IsFiniteTypeResolver', -- TD(noobj, out='?'), -+ TD(noobj, simd=[('avx512_skx', 'fd')], out='?'), - ), - 'signbit': - Ufunc(1, 1, None, - docstrings.get('numpy.core.umath.signbit'), - None, -- TD(flts, out='?'), -+ TD(flts, simd=[('avx512_skx', 'fd')], out='?'), - ), - 'copysign': - Ufunc(2, 1, None, -diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h -index c2e755958..3cec0c6ff 100644 ---- a/numpy/core/include/numpy/npy_common.h -+++ b/numpy/core/include/numpy/npy_common.h -@@ -64,6 +64,13 @@ - #define NPY_GCC_TARGET_AVX512F - #endif - -+#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX && defined HAVE_LINK_AVX512_SKX -+#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) -+#elif defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS -+#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd"))) -+#else -+#define NPY_GCC_TARGET_AVX512_SKX -+#endif - /* - * mark an argument (starting from 1) that must not be NULL and is not checked - * DO NOT USE IF FUNCTION CHECKS FOR NULL!! the compiler will remove the check -diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py -index 63c4a76a9..a37514eec 100644 ---- a/numpy/core/setup_common.py -+++ b/numpy/core/setup_common.py -@@ -146,6 +146,10 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'), - "stdio.h", "LINK_AVX2"), - ("__asm__ volatile", '"vpaddd %zmm1, %zmm2, %zmm3"', - "stdio.h", "LINK_AVX512F"), -+ ("__asm__ volatile", '"vfpclasspd $0x40, %zmm15, %k6\\n"\ -+ "vmovdqu8 %xmm0, %xmm1\\n"\ -+ "vpbroadcastmb2q %k0, %xmm0\\n"', -+ "stdio.h", "LINK_AVX512_SKX"), - ("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"), - ] - -@@ -164,6 +168,8 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))', - 'attribute_target_avx2'), - ('__attribute__((target ("avx512f")))', - 'attribute_target_avx512f'), -+ ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', -+ 'attribute_target_avx512_skx'), - ] - - # function attributes with intrinsics -@@ -180,6 +186,11 @@ OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fm - 'attribute_target_avx512f_with_intrinsics', - '__m512 temp = _mm512_set1_ps(1.0)', - 'immintrin.h'), -+ ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', -+ 'attribute_target_avx512_skx_with_intrinsics', -+ '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\ -+ _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))', -+ 'immintrin.h'), - ] - - # variable attributes tested via "int %s a" % attribute -diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src -index a59a9acf5..dbb8dd48e 100644 ---- a/numpy/core/src/umath/loops.c.src -+++ b/numpy/core/src/umath/loops.c.src -@@ -1863,10 +1863,15 @@ NPY_NO_EXPORT void - * #kind = isnan, isinf, isfinite, signbit# - * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit# - **/ -+ -+/**begin repeat2 -+ * #ISA = , _avx512_skx# -+ * #isa = simd, avx512_skx# -+ **/ - NPY_NO_EXPORT void --@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) -+@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)) - { -- if (!run_@kind@_simd_@TYPE@(args, dimensions, steps)) { -+ if (!run_@kind@_@isa@_@TYPE@(args, dimensions, steps)) { - UNARY_LOOP { - const @type@ in1 = *(@type@ *)ip1; - *((npy_bool *)op1) = @func@(in1) != 0; -@@ -1874,6 +1879,7 @@ NPY_NO_EXPORT void - } - npy_clear_floatstatus_barrier((char*)dimensions); - } -+/**end repeat2**/ - /**end repeat1**/ - - NPY_NO_EXPORT void -diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src -index 50a7ccfee..b63d442ef 100644 ---- a/numpy/core/src/umath/loops.h.src -+++ b/numpy/core/src/umath/loops.h.src -@@ -274,8 +274,13 @@ NPY_NO_EXPORT void - * #kind = isnan, isinf, isfinite, signbit, copysign, nextafter, spacing# - * #func = npy_isnan, npy_isinf, npy_isfinite, npy_signbit, npy_copysign, nextafter, spacing# - **/ -+ -+/**begin repeat2 -+ * #ISA = , _avx512_skx# -+ **/ - NPY_NO_EXPORT void --@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); -+@TYPE@_@kind@@ISA@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)); -+/**end repeat2**/ - /**end repeat1**/ - - /**begin repeat1 -diff --git a/numpy/core/src/umath/simd.inc.src b/numpy/core/src/umath/simd.inc.src -index b28c63930..a5308d83a 100644 ---- a/numpy/core/src/umath/simd.inc.src -+++ b/numpy/core/src/umath/simd.inc.src -@@ -1,4 +1,4 @@ --/* -*- c -*- */ -+ - - /* - * This file is for the definitions of simd vectorized operations. -@@ -295,6 +295,40 @@ run_binary_avx512f_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_in - } - - -+/**end repeat1**/ -+/**end repeat**/ -+ -+/**begin repeat -+ * #type = npy_float, npy_double, npy_longdouble# -+ * #TYPE = FLOAT, DOUBLE, LONGDOUBLE# -+ * #EXISTS = 1, 1, 0# -+ */ -+ -+/**begin repeat1 -+ * #func = isnan, isfinite, isinf, signbit# -+ */ -+ -+#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ -+static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void -+AVX512_SKX_@func@_@TYPE@(npy_bool*, @type@*, const npy_intp n, const npy_intp stride); -+#endif -+ -+static NPY_INLINE int -+run_@func@_avx512_skx_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *steps) -+{ -+#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS && @EXISTS@ -+ if (IS_OUTPUT_BLOCKABLE_UNARY(sizeof(npy_bool), 64)) { -+ AVX512_SKX_@func@_@TYPE@((npy_bool*)args[1], (@type@*)args[0], dimensions[0], steps[0]); -+ return 1; -+ } -+ else { -+ return 0; -+ } -+#endif -+ return 0; -+} -+ -+ - /**end repeat1**/ - /**end repeat**/ - -@@ -1973,6 +2007,84 @@ static NPY_INLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_@ISA@ @vtype@d - #endif - /**end repeat**/ - -+/**begin repeat -+ * #type = npy_float, npy_double# -+ * #TYPE = FLOAT, DOUBLE# -+ * #num_lanes = 16, 8# -+ * #vsuffix = ps, pd# -+ * #mask = __mmask16, __mmask8# -+ * #vtype = __m512, __m512d# -+ * #scale = 4, 8# -+ * #vindextype = __m512i, __m256i# -+ * #vindexload = _mm512_loadu_si512, _mm256_loadu_si256# -+ * #episize = epi32, epi64# -+ */ -+ -+/**begin repeat1 -+ * #func = isnan, isfinite, isinf, signbit# -+ * #IMM8 = 0x81, 0x99, 0x18, 0x04# -+ * #is_finite = 0, 1, 0, 0# -+ * #is_signbit = 0, 0, 0, 1# -+ */ -+#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS && defined NPY_HAVE_SSE2_INTRINSICS -+static NPY_INLINE NPY_GCC_TARGET_AVX512_SKX void -+AVX512_SKX_@func@_@TYPE@(npy_bool* op, @type@* ip, const npy_intp array_size, const npy_intp steps) -+{ -+ const npy_intp stride_ip = steps/(npy_intp)sizeof(@type@); -+ npy_intp num_remaining_elements = array_size; -+ -+ @mask@ load_mask = avx512_get_full_load_mask_@vsuffix@(); -+#if @is_signbit@ -+ @vtype@ signbit = _mm512_set1_@vsuffix@(-0.0); -+#endif -+ -+ /* -+ * Note: while generally indices are npy_intp, we ensure that our maximum -+ * index will fit in an int32 as a precondition for this function via -+ * IS_OUTPUT_BLOCKABLE_UNARY -+ */ -+ -+ npy_int32 index_ip[@num_lanes@]; -+ for (npy_int32 ii = 0; ii < @num_lanes@; ii++) { -+ index_ip[ii] = ii*stride_ip; -+ } -+ @vindextype@ vindex_ip = @vindexload@((@vindextype@*)&index_ip[0]); -+ @vtype@ zeros_f = _mm512_setzero_@vsuffix@(); -+ __m512i ones = _mm512_set1_@episize@(1); -+ -+ while (num_remaining_elements > 0) { -+ if (num_remaining_elements < @num_lanes@) { -+ load_mask = avx512_get_partial_load_mask_@vsuffix@( -+ num_remaining_elements, @num_lanes@); -+ } -+ @vtype@ x1; -+ if (stride_ip == 1) { -+ x1 = avx512_masked_load_@vsuffix@(load_mask, ip); -+ } -+ else { -+ x1 = avx512_masked_gather_@vsuffix@(zeros_f, ip, vindex_ip, load_mask); -+ } -+#if @is_signbit@ -+ x1 = _mm512_and_@vsuffix@(x1,signbit); -+#endif -+ -+ @mask@ fpclassmask = _mm512_fpclass_@vsuffix@_mask(x1, @IMM8@); -+#if @is_finite@ -+ fpclassmask = _mm512_knot(fpclassmask); -+#endif -+ -+ __m128i out =_mm512_maskz_cvts@episize@_epi8(fpclassmask, ones); -+ _mm_mask_storeu_epi8(op, load_mask, out); -+ -+ ip += @num_lanes@*stride_ip; -+ op += @num_lanes@; -+ num_remaining_elements -= @num_lanes@; -+ } -+} -+#endif -+/**end repeat1**/ -+/**end repeat**/ -+ - /**begin repeat - * #type = npy_float, npy_double# - * #TYPE = FLOAT, DOUBLE# -@@ -2066,8 +2178,8 @@ AVX512F_@func@_@TYPE@(char **args, npy_intp const *dimensions, npy_intp const *s - } - } - #endif --/**end repeat**/ - /**end repeat1**/ -+/**end repeat**/ - - /**begin repeat - * #ISA = FMA, AVX512F# -diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py -index 7caa4c7f9..f208d375e 100644 ---- a/numpy/core/tests/test_umath.py -+++ b/numpy/core/tests/test_umath.py -@@ -771,6 +771,24 @@ class TestSpecialFloats: - for dt in ['f', 'd', 'g']: - assert_raises(FloatingPointError, np.reciprocal, np.array(-0.0, dtype=dt)) - -+class TestFPClass: -+ @pytest.mark.parametrize("stride", [-4,-2,-1,1,2,4]) -+ def test_fpclass(self, stride): -+ arr_f64 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 2.2251e-308, -2.2251e-308], dtype='d') -+ arr_f32 = np.array([np.nan, -np.nan, np.inf, -np.inf, -1.0, 1.0, -0.0, 0.0, 1.4013e-045, -1.4013e-045], dtype='f') -+ nan = np.array([True, True, False, False, False, False, False, False, False, False]) -+ inf = np.array([False, False, True, True, False, False, False, False, False, False]) -+ sign = np.array([False, True, False, True, True, False, True, False, False, True]) -+ finite = np.array([False, False, False, False, True, True, True, True, True, True]) -+ assert_equal(np.isnan(arr_f32[::stride]), nan[::stride]) -+ assert_equal(np.isnan(arr_f64[::stride]), nan[::stride]) -+ assert_equal(np.isinf(arr_f32[::stride]), inf[::stride]) -+ assert_equal(np.isinf(arr_f64[::stride]), inf[::stride]) -+ assert_equal(np.signbit(arr_f32[::stride]), sign[::stride]) -+ assert_equal(np.signbit(arr_f64[::stride]), sign[::stride]) -+ assert_equal(np.isfinite(arr_f32[::stride]), finite[::stride]) -+ assert_equal(np.isfinite(arr_f64[::stride]), finite[::stride]) -+ - # func : [maxulperror, low, high] - avx_ufuncs = {'sqrt' :[1, 0., 100.], - 'absolute' :[0, -100., 100.], --- -2.26.2 - diff --git a/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch b/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch deleted file mode 100644 index 08dd874fe47..00000000000 --- a/build/pkgs/numpy/patches/0002-BUG-Update-compiler-check-for-AVX-512F.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 27d2da99c2390fec567feba3cdb0afa20c810863 Mon Sep 17 00:00:00 2001 -From: Raghuveer Devulapalli -Date: Tue, 14 Jul 2020 13:34:55 -0700 -Subject: [PATCH 2/2] BUG: Update compiler check for AVX-512F - -gcc-4.9 is missing a few AVX-512F intrisics, see -https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878. We use some of these -missing intrinsics to check for compiler support of AVX-512F. ---- - numpy/core/setup_common.py | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py -index a37514eec..7901770e4 100644 ---- a/numpy/core/setup_common.py -+++ b/numpy/core/setup_common.py -@@ -177,6 +177,9 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))', - # gcc 4.8.4 support attributes but not with intrisics - # tested via "#include<%s> int %s %s(void *){code; return 0;};" % (header, attribute, name, code) - # function name will be converted to HAVE_ preprocessor macro -+# The _mm512_castps_si512 instruction is specific check for AVX-512F support -+# in gcc-4.9 which is missing a subset of intrinsics. See -+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878 - OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fma")))', - 'attribute_target_avx2_with_intrinsics', - '__m256 temp = _mm256_set1_ps(1.0); temp = \ -@@ -184,11 +187,12 @@ OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS = [('__attribute__((target("avx2,fm - 'immintrin.h'), - ('__attribute__((target("avx512f")))', - 'attribute_target_avx512f_with_intrinsics', -- '__m512 temp = _mm512_set1_ps(1.0)', -+ '__m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0))', - 'immintrin.h'), - ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))', - 'attribute_target_avx512_skx_with_intrinsics', - '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\ -+ __m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0));\ - _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))', - 'immintrin.h'), - ] --- -2.26.2 - diff --git a/build/pkgs/numpy/patches/gh-18016.patch b/build/pkgs/numpy/patches/gh-18016.patch deleted file mode 100644 index 1a4637d17fe..00000000000 --- a/build/pkgs/numpy/patches/gh-18016.patch +++ /dev/null @@ -1,22 +0,0 @@ -From a91f53119c53d99a3f832dc41e96ccd712d705a3 Mon Sep 17 00:00:00 2001 -From: FX Coudert -Date: Thu, 3 Dec 2020 16:44:16 +0100 -Subject: [PATCH] Update gnu.py - ---- - numpy/distutils/fcompiler/gnu.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py -index 0d9d769c2a8..68d1501eee6 100644 ---- a/numpy/distutils/fcompiler/gnu.py -+++ b/numpy/distutils/fcompiler/gnu.py -@@ -126,7 +126,7 @@ def get_flags_linker_so(self): - target = '10.9' - s = f'Env. variable MACOSX_DEPLOYMENT_TARGET set to {target}' - warnings.warn(s, stacklevel=2) -- os.environ['MACOSX_DEPLOYMENT_TARGET'] = target -+ os.environ['MACOSX_DEPLOYMENT_TARGET'] = str(target) - opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) - else: - opt.append("-shared") diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 54a5f9f216e..dd13a526515 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=ffbc97517d08d8a5b290c7a5dd6cda0c730ed531 -md5=293401ee7ac354a2f2313373b497f40e -cksum=3851762650 +sha1=7ef8a684f9feb4fd24d35e87f9d1f69eb6ec793e +md5=cbcb9b39bd9d877ad3deeccc7c37bb7f +cksum=852682618 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 94fe62c2740..fdd3be6df54 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.5.4 +1.6.2 From 0571aaa5c5b52e0b7623476da8b7b3d10e3690ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 19 Apr 2021 16:16:14 -0700 Subject: [PATCH 144/706] src/sage/manifolds/subset.py: Fix docstring markup --- src/sage/manifolds/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index fb90c973378..ce40c7c991f 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -750,7 +750,7 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P.plot() # not tested If ``open_covers`` is ``True``, the poset includes a special vertex for - each nontrivial open cover of a subset. + each nontrivial open cover of a subset:: sage: P = M.subset_poset(open_covers=True); P Finite poset containing 6 elements From e04d37bd956399e067afa679a11057db6fbc503a Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Tue, 20 Apr 2021 07:11:07 -0400 Subject: [PATCH 145/706] fix bug in Polyhedron_base._richcmp_ --- src/sage/geometry/polyhedron/base.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..f6dcb05f23e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -690,6 +690,16 @@ def _richcmp_(self, other, op): False sage: P == Q False + + Test that we have fixed a problem revealed in :trac:`31701`, + where neither of the two polyhedra contains the other:: + + sage: P = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: Q = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: Q < P + False + sage: P > Q + False """ if self._Vrepresentation is None or other._Vrepresentation is None: raise RuntimeError('some V representation is missing') @@ -702,10 +712,12 @@ def _richcmp_(self, other, op): c1 = other._is_subpolyhedron(self) if c0 and c1: return rich_to_bool(op, 0) - if c0: + elif c0: return rich_to_bool(op, -1) - else: + elif c1: return rich_to_bool(op, 1) + else: + return op == op_NE @coerce_binop def _is_subpolyhedron(self, other): From 2803d29d06cc7c683654cba34a2e1b724198bb11 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Tue, 20 Apr 2021 08:43:27 -0400 Subject: [PATCH 146/706] make PolyhedronRepresentation richcmp compare two representation objects with different types --- src/sage/geometry/polyhedron/representation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 3c73a959ac7..2f0105745d9 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -143,9 +143,8 @@ def __richcmp__(self, other, op): """ if not isinstance(other, PolyhedronRepresentation): return NotImplemented - if type(self) != type(other): - return NotImplemented - return richcmp(self._vector*self._comparison_scalar(), other._vector*other._comparison_scalar(), op) + return richcmp((self.type(), self._vector*self._comparison_scalar()), + (other.type(), other._vector*other._comparison_scalar()), op) def _comparison_scalar(self): r""" From 16a4935c2403d828336cb3b549f7681acb33dedf Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 21 Apr 2021 11:58:39 +1000 Subject: [PATCH 147/706] Refactoring gf2e and gfpn matrices to use common stack() method. --- src/sage/matrix/matrix_gf2e_dense.pyx | 15 +++++++++++++-- src/sage/matrix/matrix_gfpn_dense.pyx | 12 +++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index 63dc9e3ce67..56432891218 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -1229,7 +1229,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): A._entries = mzed_concat(A._entries, self._entries, right._entries) return A - def stack(self, Matrix_gf2e_dense other): + cdef _stack_impl(self, bottom): """ Stack ``self`` on top of ``other``. @@ -1283,7 +1283,18 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: N = Matrix(K, 1, 0, 0) sage: M.stack(N) [] + + Check that we can stack a vector (:trac:`31708`):: + + sage: R. = GF(2^3) + sage: M = matrix(R, [[1,1],[0,a+1]]) + sage: M.stack(vector(R, [a,0])) + [ 1 1] + [ 0 a + 1] + [ a 0] """ + cdef Matrix_gf2e_dense other = bottom + if self._ncols != other._ncols: raise TypeError("Both numbers of columns must match.") @@ -1293,7 +1304,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): return self.__copy__() cdef Matrix_gf2e_dense A - A = self.new_matrix(nrows = self._nrows + other._nrows) + A = self.new_matrix(nrows=self._nrows + other._nrows) if self._ncols == 0: return A A._entries = mzed_stack(A._entries, self._entries, other._entries) diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index 1a2b5d5d235..70b0167ca64 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -1168,7 +1168,7 @@ cdef class Matrix_gfpn_dense(Matrix_dense): raise ValueError("self must be a square matrix") return self._converter.fel_to_field(MatTrace(self.Data)) - def stack(self, Matrix_gfpn_dense other): + cdef _stack_impl(self, bottom): """ Stack two matrices of the same number of columns. @@ -1181,7 +1181,17 @@ cdef class Matrix_gfpn_dense(Matrix_dense): [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2] [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2] + Check that we can stack a vector (:trac:`31708`):: + + sage: R. = GF(3^3) + sage: M = matrix(R, [[1,1],[0,a+1]]) + sage: M.stack(vector(R, [a,0])) + [ 1 1] + [ 0 a + 1] + [ a 0] """ + cdef Matrix_gfpn_dense other = bottom + if self._ncols != other._ncols: raise TypeError("Both numbers of columns must match.") if self._nrows == 0 or self.Data == NULL: From 0aefe529a51100b5870194aeb767e3cc7a2cade5 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Wed, 21 Apr 2021 09:21:21 +0200 Subject: [PATCH 148/706] Remove __nonzero__ from manifolds --- src/sage/manifolds/chart_func.py | 2 -- src/sage/manifolds/differentiable/mixed_form.py | 2 -- src/sage/manifolds/differentiable/tensorfield.py | 2 -- src/sage/manifolds/scalarfield.py | 2 -- src/sage/manifolds/section.py | 2 -- src/sage/tensor/modules/free_module_morphism.py | 2 -- src/sage/tensor/modules/free_module_tensor.py | 2 -- 7 files changed, 14 deletions(-) diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index e821af80367..d38eab686ae 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -816,8 +816,6 @@ def __bool__(self): val = self.expr(curr).is_zero return not val - __nonzero__ = __bool__ # For Python2 compatibility - def is_trivial_zero(self): r""" Check if ``self`` is trivially equal to zero without any diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index e9fdcdb9916..82821068e14 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -540,8 +540,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ # For Python2 compatibility - def _richcmp_(self, other, op): r""" Comparison method for sage objects. diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 52dff82b202..536d50fe1c6 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -544,8 +544,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ # For Python2 compatibility - ##### End of required methods for ModuleElement (beside arithmetic) ##### def _repr_(self): diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index e741e67169a..4714a68f26e 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -1192,8 +1192,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ # For Python2 compatibility - def is_trivial_zero(self): r""" Check if ``self`` is trivially equal to zero without any diff --git a/src/sage/manifolds/section.py b/src/sage/manifolds/section.py index 29ed3a88040..0a769fbf961 100644 --- a/src/sage/manifolds/section.py +++ b/src/sage/manifolds/section.py @@ -298,8 +298,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ # For Python2 compatibility - ##### End of required methods for ModuleElement (beside arithmetic) ##### def _repr_(self): diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py index 558e00c9ff8..3451b2c0554 100644 --- a/src/sage/tensor/modules/free_module_morphism.py +++ b/src/sage/tensor/modules/free_module_morphism.py @@ -488,8 +488,6 @@ def __bool__(self): matrix_rep = next(iter(self._matrices.values())) return not matrix_rep.is_zero() - __nonzero__ = __bool__ - def _add_(self, other): r""" Homomorphism addition. diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 570fabd251d..cfefefe638c 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -376,8 +376,6 @@ def __bool__(self): self._is_zero = True return False - __nonzero__ = __bool__ - ##### End of required methods for ModuleElement (beside arithmetic) ##### def _repr_(self): From 2d57f4173778f02964cb8d74814b51e329a23b59 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 21 Apr 2021 04:33:56 -0400 Subject: [PATCH 149/706] add doctest for 31702 --- src/sage/geometry/polyhedron/representation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 2f0105745d9..7f56aeba23a 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -140,6 +140,14 @@ def __richcmp__(self, other, op): sage: Q = (1/2)*polytopes.cube(backend='field') sage: for p in P.inequalities(): ....: assert p in Q.inequalities() + + Check :trac:`31702`:: + + sage: H = Polyhedron(vertices=[(4,0)], rays=[(1,1)], lines=[(-1,1)]) + sage: sorted([H.lines()[0], H.rays()[0], H.vertices()[0]]) + [A vertex at (4, 0), + A ray in the direction (1, 0), + A line in the direction (1, -1)] """ if not isinstance(other, PolyhedronRepresentation): return NotImplemented From 7af9ebcbd58a9139145fd017fa69d3f1cbe3f996 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 21 Apr 2021 04:36:57 -0400 Subject: [PATCH 150/706] solve relint: commands failed --- .../geometry/polyhedron/representation.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 7f56aeba23a..c7abc0df855 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -287,13 +287,13 @@ def index(self): Return an arbitrary but fixed number according to the internal storage order. - NOTES: + .. NOTE:: - H-representation and V-representation objects are enumerated - independently. That is, amongst all vertices/rays/lines there - will be one with ``index()==0``, and amongst all - inequalities/equations there will be one with ``index()==0``, - unless the polyhedron is empty or spans the whole space. + H-representation and V-representation objects are enumerated + independently. That is, amongst all vertices/rays/lines there + will be one with ``index()==0``, and amongst all + inequalities/equations there will be one with ``index()==0``, + unless the polyhedron is empty or spans the whole space. EXAMPLES:: @@ -608,11 +608,13 @@ def eval(self, Vobj): Evaluates the left hand side `A\vec{x}+b` on the given vertex/ray/line. - NOTES: + .. NOTES: * Evaluating on a vertex returns `A\vec{x}+b` + * Evaluating on a ray returns `A\vec{r}`. Only the sign or whether it is zero is meaningful. + * Evaluating on a line returns `A\vec{l}`. Only whether it is zero or not is meaningful. @@ -1091,9 +1093,9 @@ def interior_contains(self, Vobj): boundary) defined by the inequality contains the given vertex/ray/line. - NOTE: + .. NOTE:: - Return False for any equation. + Return False for any equation. EXAMPLES:: From 2b886a3ad4185761305772694831031b02153b03 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 21 Apr 2021 11:55:15 +0200 Subject: [PATCH 151/706] do not import sage.geometry.polyhedron.base at startup --- src/sage/geometry/all.py | 35 ++++++++++-------------------- src/sage/geometry/hasse_diagram.py | 3 ++- src/sage/schemes/toric/all.py | 13 +++++------ 3 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index f8bb325352d..f143f171600 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -1,29 +1,18 @@ - -from sage.misc.lazy_import import lazy_import - - -from .cone import Cone, random_cone -lazy_import('sage.geometry', 'cone_catalog', 'cones') - -from .fan import Fan, FaceFan, NormalFan, Fan2d - -from .fan_morphism import FanMorphism - from .polyhedron.all import * - -from .lattice_polytope import (LatticePolytope, NefPartition, ReflexivePolytope, - ReflexivePolytopes) - -from . import lattice_polytope - -from .toric_lattice import ToricLattice - -from . import toric_plotter - from .hyperbolic_space.all import * +from sage.misc.lazy_import import lazy_import -from .voronoi_diagram import VoronoiDiagram - +lazy_import('sage.geometry.cone', ['Cone', 'random_cone']) +lazy_import('sage.geometry', 'cone_catalog', 'cones') +lazy_import('sage.geometry.fan', ['Fan', 'FaceFan', 'NormalFan', 'Fan2d']) +lazy_import('sage.geometry.fan_morphism', 'FanMorphism') +lazy_import('sage.geometry.lattice_polytope', + ['LatticePolytope', 'NefPartition', + 'ReflexivePolytope', 'ReflexivePolytopes']) +lazy_import('sage.geometry', 'lattice_polytope') +lazy_import('sage.geometry.toric_lattice', 'ToricLattice') +lazy_import('sage.geometry', 'toric_plotter') +lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram') lazy_import('sage.geometry.ribbon_graph', 'RibbonGraph') lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements') diff --git a/src/sage/geometry/hasse_diagram.py b/src/sage/geometry/hasse_diagram.py index 31884a61161..29c46604e56 100644 --- a/src/sage/geometry/hasse_diagram.py +++ b/src/sage/geometry/hasse_diagram.py @@ -103,7 +103,8 @@ def lattice_from_incidences(atom_to_coatoms, coatom_to_atoms, and we can compute the lattice as :: - sage: L = sage.geometry.cone.lattice_from_incidences( + sage: from sage.geometry.cone import lattice_from_incidences + sage: L = lattice_from_incidences( ....: atom_to_coatoms, coatom_to_atoms) sage: L Finite lattice containing 8 elements with distinguished linear extension diff --git a/src/sage/schemes/toric/all.py b/src/sage/schemes/toric/all.py index 00e93fa50f1..9d7c83954a7 100644 --- a/src/sage/schemes/toric/all.py +++ b/src/sage/schemes/toric/all.py @@ -1,10 +1,7 @@ -# code exports - -from .fano_variety import CPRFanoToricVariety -from .ideal import ToricIdeal -from .library import toric_varieties -from .variety import AffineToricVariety, ToricVariety - - from sage.misc.lazy_import import lazy_import + lazy_import('sage.schemes.toric.weierstrass', 'WeierstrassForm') +lazy_import('sage.schemes.toric.variety', ['AffineToricVariety', 'ToricVariety']) +lazy_import('sage.schemes.toric.library', 'toric_varieties') +lazy_import('sage.schemes.toric.fano_variety', 'CPRFanoToricVariety') +lazy_import('sage.schemes.toric.ideal', 'ToricIdeal') From b027f2d1a4bb09b93e72c6c97f2ab739ddafd70c Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Wed, 21 Apr 2021 17:01:28 +0200 Subject: [PATCH 152/706] Correct pycodestyle error --- src/sage/tensor/modules/free_module_tensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index cfefefe638c..ef6a73e76a8 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -1504,7 +1504,8 @@ def del_other_comp(self, basis=None): [Basis (e_1,e_2,e_3) on the Rank-3 free module M over the Integer Ring] """ - if basis is None: basis = self._fmodule._def_basis + if basis is None: + basis = self._fmodule._def_basis if basis not in self._components: raise ValueError("the components w.r.t. the {}".format(basis) + " have not been defined") From 58b84fd3966c54575df4b0269ff7f99a9bb2aac0 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 17:41:57 +0200 Subject: [PATCH 153/706] fix a call to bnfisprincipal bnfisprincipal is showing warning much more often with PARI 2.13.0 when used with the flag=1. We use the flag=0 to avoid this issue. --- .../rings/number_field/number_field_ideal.py | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index db3e1f0079f..13a7a994c3b 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -780,7 +780,6 @@ def gens_reduced(self, proof=None): sage: P = EllipticCurve(L, '57a1').lift_x(z_x) * 3 sage: ideal = L.fractional_ideal(P[0], P[1]) sage: ideal.is_principal(proof=False) - *** Warning: precision too low for generators, not given. True sage: len(ideal.gens_reduced(proof=False)) 1 @@ -789,7 +788,7 @@ def gens_reduced(self, proof=None): self._is_principal = True self._reduced_generators = self.gens() return self._reduced_generators - self._cache_bnfisprincipal(proof=proof, gens_needed=True) + self._cache_bnfisprincipal(proof=proof, gens=True) return self._reduced_generators def gens_two(self): @@ -1030,7 +1029,7 @@ def pari_prime(self): raise ValueError("%s is not a prime ideal" % self) return self._pari_prime - def _cache_bnfisprincipal(self, proof=None, gens_needed=False): + def _cache_bnfisprincipal(self, proof=None, gens=False): r""" This function is essentially the implementation of :meth:`is_principal`, :meth:`gens_reduced` and @@ -1042,9 +1041,8 @@ def _cache_bnfisprincipal(self, proof=None, gens_needed=False): - ``proof`` -- proof flag. If ``proof=False``, assume GRH. - - ``gens_needed`` -- (default: True) if True, insist on computing - the reduced generators of the ideal. With ``gens=False``, they - may or may not be computed (depending on how big the ideal is). + - ``gens`` -- (default: False) if True, also computes the reduced + generators of the ideal. OUTPUT: @@ -1052,6 +1050,15 @@ def _cache_bnfisprincipal(self, proof=None, gens_needed=False): ``_ideal_class_log`` (see :meth:`ideal_class_log`), ``_is_principal`` (see :meth:`is_principal`) and ``_reduced_generators``. + + TESTS: + + Check that no warnings are triggered from PARI/GP (see :trac:`30801`):: + + sage: K. = NumberField(x^2 - x + 112941801) + sage: I = K.ideal((112941823, a + 49942513)) + sage: I.is_principal() + False """ # Since pari_bnf() is cached, this call to pari_bnf() should not # influence the run-time much. Also, this simplifies the handling @@ -1063,29 +1070,29 @@ def _cache_bnfisprincipal(self, proof=None, gens_needed=False): # If we already have _reduced_generators, no need to compute them again if hasattr(self, "_reduced_generators"): - gens_needed = False + gens = False # Is there something to do? - if hasattr(self, "_ideal_class_log") and not gens_needed: + if hasattr(self, "_ideal_class_log") and not gens: self._is_principal = not any(self._ideal_class_log) return - # Call bnfisprincipal(). - # If gens_needed, use flag=3 which will insist on computing - # the generator. Otherwise, use flag=1, where the generator - # may or may not be computed. - v = bnf.bnfisprincipal(self.pari_hnf(), 3 if gens_needed else 1) - self._ideal_class_log = list(v[0]) - self._is_principal = not any(self._ideal_class_log) - - if self._is_principal: - # Cache reduced generator if it was computed - if v[1]: + if not gens: + v = bnf.bnfisprincipal(self.pari_hnf(), 0) + self._ideal_class_log = list(v) + self._is_principal = not any(self._ideal_class_log) + else: + # TODO: with flag=3 the call to bnfisprincipal is likely to fail, + # consider using the factored form obtained with flag=4 + v = bnf.bnfisprincipal(self.pari_hnf(), 3) + self._ideal_class_log = list(v[0]) + self._is_principal = not any(self._ideal_class_log) + + if self._is_principal: g = self.number_field()(v[1]) self._reduced_generators = (g,) - else: - # Non-principal ideal, compute two generators if asked for - if gens_needed: + elif gens: + # Non-principal ideal self._reduced_generators = self.gens_two() def is_principal(self, proof=None): From 03812531824656bac3aae2b846584f2bc5d43dab Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 18:11:49 +0200 Subject: [PATCH 154/706] tweak bnfisprincipal --- .../rings/number_field/number_field_ideal.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 13a7a994c3b..6e5ae459a15 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -827,10 +827,13 @@ def gens_two(self): try: return self.__two_generators except AttributeError: - if self.is_zero(): - self.__two_generators = (0,0) - return self.__two_generators - K = self.number_field() + pass + + K = self.number_field() + + if self.is_zero(): + self.__two_generators = (K.zero(), K.zero()) + else: HNF = self.pari_hnf() # Check whether the ideal is generated by an integer, i.e. # whether HNF is a multiple of the identity matrix @@ -839,7 +842,8 @@ def gens_two(self): else: a, alpha = K.pari_nf().idealtwoelt(HNF) self.__two_generators = (K(a), K(alpha)) - return self.__two_generators + + return self.__two_generators def integral_basis(self): r""" @@ -1082,14 +1086,18 @@ def _cache_bnfisprincipal(self, proof=None, gens=False): self._ideal_class_log = list(v) self._is_principal = not any(self._ideal_class_log) else: - # TODO: with flag=3 the call to bnfisprincipal is likely to fail, - # consider using the factored form obtained with flag=4 - v = bnf.bnfisprincipal(self.pari_hnf(), 3) - self._ideal_class_log = list(v[0]) + # TODO: this is a bit of a waste. We ask bnfisprincipal to compute the compact form and then + # convert this compact form back into an expanded form. + # (though calling with 3 instead of 3 most likely triggers an error with memory allocation failure) + v = bnf.bnfisprincipal(self.pari_hnf(), 5) + e = v[0] + t = v[1] + t = bnf.nfbasistoalg(bnf.nffactorback(t)) + self._ideal_class_log = list(e) self._is_principal = not any(self._ideal_class_log) if self._is_principal: - g = self.number_field()(v[1]) + g = self.number_field()(t) self._reduced_generators = (g,) elif gens: # Non-principal ideal From 7e98037245a9b3fd1cc8fbb5fd62af33f6637a1c Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 18:13:39 +0200 Subject: [PATCH 155/706] clarify comment --- src/sage/rings/number_field/number_field_ideal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 6e5ae459a15..2478fdb0097 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -1088,7 +1088,7 @@ def _cache_bnfisprincipal(self, proof=None, gens=False): else: # TODO: this is a bit of a waste. We ask bnfisprincipal to compute the compact form and then # convert this compact form back into an expanded form. - # (though calling with 3 instead of 3 most likely triggers an error with memory allocation failure) + # (though calling with 3 instead of 5 most likely triggers an error with memory allocation failure) v = bnf.bnfisprincipal(self.pari_hnf(), 5) e = v[0] t = v[1] From 86980a3b67a7e871e11135d65bc648f7b77a961e Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 18:27:48 +0200 Subject: [PATCH 156/706] fix output of simon_two_descent --- .../elliptic_curves/ell_number_field.py | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 362ecba8f60..33e06505085 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -214,9 +214,9 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, sage: E == loads(dumps(E)) True sage: E.simon_two_descent() - (2, 2, [(0 : 0 : 1), (1/8*a + 5/8 : -3/16*a - 7/16 : 1)]) - sage: E.simon_two_descent(lim1=3, lim3=20, limtriv=5, maxprob=7, limbigprime=10) - (2, 2, [(-1 : 0 : 1), (-1/8*a + 5/8 : -3/16*a - 9/16 : 1)]) + (2, 2, [(0 : 0 : 1)]) + sage: E.simon_two_descent(lim1=5, lim3=5, limtriv=10, maxprob=7, limbigprime=10) + (2, 2, [(-1 : 0 : 1), (-2 : -1/2*a - 1/2 : 1)]) :: @@ -238,35 +238,7 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, K = bnfinit(y^2 + 7); a = Mod(y,K.pol); bnfellrank(K, [0, 0, 0, 1, a], [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]); - elliptic curve: Y^2 = x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7) - A = Mod(0, y^2 + 7) - B = Mod(1, y^2 + 7) - C = Mod(y, y^2 + 7) - - Computing L(S,2) - L(S,2) = [Mod(Mod(-1/2*y + 1/2, y^2 + 7)*x^2 + Mod(-1/2*y - 1/2, y^2 + 7)*x + Mod(-y - 1, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(-x^2 + Mod(-1/2*y - 1/2, y^2 + 7)*x + 1, x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(-1, x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(x^2 + 2, x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(x + Mod(1/2*y + 3/2, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(x + Mod(1/2*y - 3/2, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7))] - - Computing the Selmer group - #LS2gen = 2 - LS2gen = [Mod(Mod(-1/2*y + 1/2, y^2 + 7)*x^2 + Mod(-1/2*y - 1/2, y^2 + 7)*x + Mod(-y - 1, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)), Mod(x^2 + Mod(1/2*y + 1/2, y^2 + 7)*x - 1, x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7))] - Search for trivial points on the curve - Trivial points on the curve = [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)], [1, 1, 0], [Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7), 1]] - zc = Mod(Mod(-1/2*y + 1/2, y^2 + 7)*x^2 + Mod(-1/2*y - 1/2, y^2 + 7)*x + Mod(-y - 1, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)) - Hilbert symbol (Mod(1, y^2 + 7),Mod(-2*y + 2, y^2 + 7)) = - sol of quadratic equation = [1, 1, 0]~ - zc*z1^2 = Mod(4*x + Mod(-2*y + 6, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)) - quartic: (-1)*Y^2 = x^4 + (3*y - 9)*x^2 + (-8*y + 16)*x + (9/2*y - 11/2) - reduced: Y^2 = -x^4 + (-3*y + 9)*x^2 + (-8*y + 16)*x + (-9/2*y + 11/2) - not ELS at [2, [0, 1]~, 1, 1, [1, -2; 1, 0]] - zc = Mod(Mod(1, y^2 + 7)*x^2 + Mod(1/2*y + 1/2, y^2 + 7)*x + Mod(-1, y^2 + 7), x^3 + Mod(1, y^2 + 7)*x + Mod(y, y^2 + 7)) - comes from the trivial point [Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)] - m1 = 1 - m2 = 1 - #S(E/K)[2] = 2 - #E(K)/2E(K) = 2 - #III(E/K)[2] = 1 - rank(E/K) = 1 - listpoints = [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]] + ... v = [1, 1, [[Mod(1/2*y + 3/2, y^2 + 7), Mod(-y - 2, y^2 + 7)]]] sage: v (1, 1, [(1/2*a + 3/2 : -a - 2 : 1)]) From 1d25a05612c6a191fcdf1928c6049fdf6720dab3 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 21 Apr 2021 18:29:58 +0200 Subject: [PATCH 157/706] #31710 fix crash in CBF['x'].zero).roots() --- src/sage/rings/complex_arb.pyx | 9 +++++++++ src/sage/rings/polynomial/polynomial_element.pyx | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 1112255a6d9..224fd1eb165 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -820,6 +820,13 @@ class ComplexBallField(UniqueRepresentation, Field): Traceback (most recent call last): ... ValueError: unable to determine which roots are real + + TESTS:: + + sage: CBF._roots_univariate_polynomial(CBF['x'].zero(), CBF, False, None) + Traceback (most recent call last): + ... + ArithmeticError: taking the roots of the zero polynomial """ if algorithm is not None: raise NotImplementedError @@ -853,6 +860,8 @@ class ComplexBallField(UniqueRepresentation, Field): cdef ComplexBall cb acb_poly_init(rounded_poly) cdef long deg = acb_poly_degree(poly.__poly) + if deg < 0: + raise ArithmeticError("taking the roots of the zero polynomial") cdef acb_ptr roots = _acb_vec_init(deg) try: sig_on() diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 570c148e72a..75c3a7a2a3c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7830,11 +7830,17 @@ cdef class Polynomial(CommutativeAlgebraElement): [(a + O(3^20), 1), (2*a + 2*a*3 + 2*a*3^2 + 2*a*3^3 + 2*a*3^4 + 2*a*3^5 + 2*a*3^6 + 2*a*3^7 + 2*a*3^8 + 2*a*3^9 + 2*a*3^10 + 2*a*3^11 + 2*a*3^12 + 2*a*3^13 + 2*a*3^14 + 2*a*3^15 + 2*a*3^16 + 2*a*3^17 + 2*a*3^18 + 2*a*3^19 + O(3^20), 1)] + + Check that :trac:`31710` is fixed:: + + sage: CBF['x'].zero().roots(multiplicities=False) + Traceback (most recent call last): + ... + ArithmeticError: taking the roots of the zero polynomial """ from sage.rings.finite_rings.finite_field_constructor import GF K = self._parent.base_ring() - # If the base ring has a method _roots_univariate_polynomial, # try to use it. An exception is raised if the method does not # handle the current parameters From 9d35006855c380d96d776e84ea8bab0caa93227f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Apr 2021 10:02:01 -0700 Subject: [PATCH 158/706] Relabel digraphs and posets for plotting --- src/sage/manifolds/subset.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index ce40c7c991f..f8be40709b9 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -681,10 +681,20 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): 3-dimensional differentiable manifold M, None)] sage: D.plot(layout='acyclic') # not tested + sage: def label(element): + ....: try: + ....: return element._name + ....: except AttributeError: + ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' + sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested + sage: VW = V.union(W) sage: D = M.subset_digraph(); D Digraph on 5 vertices - sage: D.plot(layout='acyclic') # not tested + sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested + + sage: D = M.subset_digraph(open_covers=True) + sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested """ from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) @@ -747,7 +757,7 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: sorted(P.lower_covers(M), key=str) [Open subset U of the 3-dimensional differentiable manifold M, Open subset V_union_W of the 3-dimensional differentiable manifold M] - sage: P.plot() # not tested + sage: P.plot(element_labels={element: element._name for element in P}) # not tested If ``open_covers`` is ``True``, the poset includes a special vertex for each nontrivial open cover of a subset:: @@ -758,6 +768,12 @@ def subset_poset(self, open_covers=False, lower_bound=None): [3-dimensional differentiable manifold M, frozenset({Open subset V of the 3-dimensional differentiable manifold M, Open subset W of the 3-dimensional differentiable manifold M})] + sage: def label(element): + ....: try: + ....: return element._name + ....: except AttributeError: + ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' + sage: P.plot(element_labels={element: label(element) for element in P}) # not tested """ from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) @@ -780,6 +796,7 @@ def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): sage: VW = V.union(W) sage: P = V.superset_digraph(loops=False, upper_bound=VW); P Digraph on 2 vertices + """ if upper_bound is None: upper_bound = self._manifold @@ -801,6 +818,8 @@ def superset_poset(self, open_covers=False, upper_bound=None): sage: VW = V.union(W) sage: P = V.superset_poset(); P Finite poset containing 3 elements + sage: P.plot(element_labels={element: element._name for element in P}) # not tested + """ if upper_bound is None: upper_bound = self._manifold From 48a8e8d00dc287f6a50ee3dcbe1c1a57f19591e7 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Wed, 21 Apr 2021 13:30:57 -0400 Subject: [PATCH 159/706] reword the doctest of richcmp --- src/sage/geometry/polyhedron/representation.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index c7abc0df855..c41a13caba9 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -132,6 +132,10 @@ def __richcmp__(self, other, op): sage: ieq != Polyhedron([(0,1,0)]).Vrepresentation(0) True + sage: H = Polyhedron(vertices=[(4,0)], rays=[(1,1)], lines=[(-1,1)]) + sage: H.vertices()[0] < H.rays()[0] < H.lines()[0] + True + TESTS: Check :trac:`30954`:: @@ -140,14 +144,6 @@ def __richcmp__(self, other, op): sage: Q = (1/2)*polytopes.cube(backend='field') sage: for p in P.inequalities(): ....: assert p in Q.inequalities() - - Check :trac:`31702`:: - - sage: H = Polyhedron(vertices=[(4,0)], rays=[(1,1)], lines=[(-1,1)]) - sage: sorted([H.lines()[0], H.rays()[0], H.vertices()[0]]) - [A vertex at (4, 0), - A ray in the direction (1, 0), - A line in the direction (1, -1)] """ if not isinstance(other, PolyhedronRepresentation): return NotImplemented From 860a1ea4f719ebac4141d635b4a9331dd96806ab Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 19:57:22 +0200 Subject: [PATCH 160/706] one long doctest --- src/sage/schemes/elliptic_curves/ell_number_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 33e06505085..ccfa33915a9 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -270,8 +270,8 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, sage: E.simon_two_descent() # long time (4s on sage.math, 2013) (3, 3, - [(0 : 0 : 1), - (-1/2*zeta43_0^2 - 1/2*zeta43_0 + 7 : -3/2*zeta43_0^2 - 5/2*zeta43_0 + 18 : 1)...) + [(5/8*zeta43_0^2 + 17/8*zeta43_0 - 9/4 : -27/16*zeta43_0^2 - 103/16*zeta43_0 + 39/8 : 1), + (0 : 0 : 1)]) """ verbose = int(verbose) if known_points is None: From 558b5e820c169b616477f8a2e0eac47f07e41b91 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 21 Apr 2021 20:12:59 +0200 Subject: [PATCH 161/706] rewrite spkg-configure.m4 --- build/pkgs/pari/spkg-configure.m4 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 1d5e8533693..719b8089eeb 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([pari], [ dnl See gp_version below on how the version is computed from MAJV.MINV.PATCHV - m4_pushdef([SAGE_PARI_MINVER],["133889"]) + m4_pushdef([SAGE_PARI_MINVER],["134401"]) SAGE_SPKG_DEPCHECK([gmp mpir readline], [ AC_PATH_PROG([GP], [gp]) if test x$GP = x; then dnl GP test @@ -99,6 +99,16 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) sage_spkg_install_pari=yes fi + AC_MSG_CHECKING([whether rnfdisc bug of pari 2.13.1 is fixed]) + bug_check=`echo "K = nfinit(y^4-10*y^2+1); disc = rnfdisc(K,x^2-(y^3/2+y^2-5*y/2+1)); idealnorm(K,disc)" | $GP -qf 2 >> config.log` + expected="2304" + if test x"$bug_check" = x"$expected"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) + AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) + AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + fi fi dnl end GP test if test x$sage_spkg_install_pari = xno; then dnl main PARI test From 56b4cfdacf2e2f09c33729bcbb6e600661ded119 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 22 Apr 2021 09:30:56 +1000 Subject: [PATCH 162/706] Trac #31708 reviewer details. --- src/sage/matrix/matrix_gf2e_dense.pyx | 9 +++------ src/sage/matrix/matrix_gfpn_dense.pyx | 10 ++++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index 56432891218..bb6721c50b6 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -1230,12 +1230,12 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): return A cdef _stack_impl(self, bottom): - """ - Stack ``self`` on top of ``other``. + r""" + Stack ``self`` on top of ``bottom``. INPUT: - - ``other`` - a matrix + - ``bottom`` -- a matrix with the same number of columns as ``self`` EXAMPLES:: @@ -1295,9 +1295,6 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): """ cdef Matrix_gf2e_dense other = bottom - if self._ncols != other._ncols: - raise TypeError("Both numbers of columns must match.") - if self._nrows == 0: return other.__copy__() if other._nrows == 0: diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index 70b0167ca64..62fd4af2ee8 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -1169,8 +1169,12 @@ cdef class Matrix_gfpn_dense(Matrix_dense): return self._converter.fel_to_field(MatTrace(self.Data)) cdef _stack_impl(self, bottom): - """ - Stack two matrices of the same number of columns. + r""" + Stack ``self`` on top of ``bottom``. + + INPUT: + + - ``bottom`` -- a matrix with the same number of columns as ``self`` EXAMPLES:: @@ -1192,8 +1196,6 @@ cdef class Matrix_gfpn_dense(Matrix_dense): """ cdef Matrix_gfpn_dense other = bottom - if self._ncols != other._ncols: - raise TypeError("Both numbers of columns must match.") if self._nrows == 0 or self.Data == NULL: return other.__copy__() if other._nrows == 0 or other.Data == NULL: From 4d93fe5a566f71994a901dbbf82ff97e629411f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Apr 2021 20:48:40 -0700 Subject: [PATCH 163/706] src/sage/manifolds/subset_set.py: New --- src/sage/manifolds/subset_set.py | 98 ++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/sage/manifolds/subset_set.py diff --git a/src/sage/manifolds/subset_set.py b/src/sage/manifolds/subset_set.py new file mode 100644 index 00000000000..ca4ff3547f7 --- /dev/null +++ b/src/sage/manifolds/subset_set.py @@ -0,0 +1,98 @@ +r""" +Immutable Sets of Subsets of Topological Manifolds + +The class :class:`ManifoldSubsetSet` is a subclass of the built-in +``frozenset`` class that provides specialized ``__repr__`` and +``_latex_`` methods. + +:class:`ManifoldSubsetSet` instances are totally ordered according +to their lexicographically ordered element (subset) names. + +""" +#***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from functools import total_ordering + +@total_ordering +class ManifoldSubsetSet(frozenset): + + def __new__(cls, *args): + self = super().__new__(cls, *args) + names_and_latex_names = sorted((subset._name, subset._latex_name) + for subset in self) + self._names = tuple(name for name, latex_name in names_and_latex_names) + self._name = '{' + ', '.join(self._names) + '}' + latex_names = (latex_name for name, latex_name in names_and_latex_names) + self._latex_name = r'\{' + ', '.join(latex_names) + r'\}' + try: + subset_iter = iter(self) + self._manifold = next(subset_iter)._manifold + except StopIteration: + pass + else: + if not all(subset._manifold == self._manifold for subset in subset_iter): + raise TypeError('all elements must be subsets of the same manifold') + return self + + def __repr__(self): + r""" + String representation of the object. + + TESTS:: + + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: ManifoldSubsetSet().__repr__() + '{}' + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: ManifoldSubsetSet([A, B]).__repr__() + 'Set {A, B} of subsets of the 2-dimensional topological manifold M' + + """ + if self: + return "Set {} of subsets of the {}".format(self._name, self._manifold) + else: + return "{}" + + def _latex_(self): + r""" + LaTeX representation of ``self``. + + TESTS:: + + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: ManifoldSubsetSet([B, A])._latex_() + '\\{A, B\\}' + """ + return self._latex_name + + def __lt__(self, other): + r""" + Implement the total order on instances of :class:`ManifoldSubsetSet`. + + TESTS:: + + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: sorted([ManifoldSubsetSet([A, B]), ManifoldSubsetSet([]), + ....: ManifoldSubsetSet([B]), ManifoldSubsetSet([A])]) + [{}, + Set {A} of subsets of the 2-dimensional topological manifold M, + Set {A, B} of subsets of the 2-dimensional topological manifold M, + Set {B} of subsets of the 2-dimensional topological manifold M] + """ + return self._names < other._names From 3ae1f9809350bf5d74c65c9020ae1fff4aa793d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Apr 2021 20:50:23 -0700 Subject: [PATCH 164/706] ManifoldSubset.subset_digraph: Make subset nodes (singleton) ManifoldSubsetSet instances, open_cover nodes tuples --- src/sage/manifolds/subset.py | 72 ++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f8be40709b9..5c754db0964 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -76,6 +76,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets +from sage.manifolds.subset_set import ManifoldSubsetSet from sage.manifolds.point import ManifoldPoint class ManifoldSubset(UniqueRepresentation, Parent): @@ -664,6 +665,14 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): - ``lower_bound`` -- (default: ``None``) only include supersets of this - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + OUTPUT: + + A digraph. Each vertex of the digraph is either: + + - a :class:`ManifoldSubsetSet` containing one instance of :class:`ManifoldSubset`. + - (if ``open_covers`` is ``True``) a tuple of :class:`ManifoldSubsetSet` instances, + representing an open cover. + EXAMPLES:: sage: M = Manifold(3, 'M') @@ -671,16 +680,16 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): sage: D = M.subset_digraph(); D Digraph on 4 vertices sage: D.edges(key=lambda e: (e[0]._name, e[1]._name)) - [(Open subset U of the 3-dimensional differentiable manifold M, - 3-dimensional differentiable manifold M, + [(Set {U} of subsets of the 3-dimensional differentiable manifold M, + Set {M} of subsets of the 3-dimensional differentiable manifold M, None), - (Open subset V of the 3-dimensional differentiable manifold M, - 3-dimensional differentiable manifold M, + (Set {V} of subsets of the 3-dimensional differentiable manifold M, + Set {M} of subsets of the 3-dimensional differentiable manifold M, None), - (Open subset W of the 3-dimensional differentiable manifold M, - 3-dimensional differentiable manifold M, + (Set {W} of subsets of the 3-dimensional differentiable manifold M, + Set {M} of subsets of the 3-dimensional differentiable manifold M, None)] - sage: D.plot(layout='acyclic') # not tested + sage: D.plot(layout='acyclic') # not tested sage: def label(element): ....: try: ....: return element._name @@ -698,6 +707,10 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): """ from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) + + def vertex(subset): + return ManifoldSubsetSet([subset]) + if lower_bound is not None: if not lower_bound.is_subset(self): return D @@ -717,19 +730,22 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): subsets_without_S = [subset for subset in subsets if subset is not S] if loops: - D.add_edges((subset, S) for subset in subsets) + D.add_edges((vertex(subset), vertex(S)) for subset in subsets) else: - D.add_edges((subset, S) for subset in subsets_without_S) + D.add_edges((vertex(subset), vertex(S)) for subset in subsets_without_S) + to_visit.extend(subsets_without_S) - if open_covers: - # Represent an open cover with a node that is a frozenset, - # rather than a list. - open_covers_without_S = [frozenset(open_cover) - for open_cover in S._open_covers - if open_cover != [S]] - D.add_edges((S, open_cover) - for open_cover in open_covers_without_S) + if open_covers: + + def open_cover_vertex(open_cover): + return tuple(sorted(ManifoldSubsetSet([subset]) for subset in open_cover)) + + for S in visited: + D.add_edges((vertex(S), open_cover_vertex(open_cover)) + for open_cover in S._open_covers + if open_cover != [S]) + return D def subset_poset(self, open_covers=False, lower_bound=None): @@ -749,14 +765,15 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P = M.subset_poset(); P Finite poset containing 5 elements sage: P.maximal_elements() - [3-dimensional differentiable manifold M] + [Set {M} of subsets of the 3-dimensional differentiable manifold M] sage: sorted(P.minimal_elements(), key=lambda v: v._name) - [Open subset U of the 3-dimensional differentiable manifold M, - Open subset V of the 3-dimensional differentiable manifold M, - Open subset W of the 3-dimensional differentiable manifold M] - sage: sorted(P.lower_covers(M), key=str) - [Open subset U of the 3-dimensional differentiable manifold M, - Open subset V_union_W of the 3-dimensional differentiable manifold M] + [Set {U} of subsets of the 3-dimensional differentiable manifold M, + Set {V} of subsets of the 3-dimensional differentiable manifold M, + Set {W} of subsets of the 3-dimensional differentiable manifold M] + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: sorted(P.lower_covers(ManifoldSubsetSet([M])), key=str) + [Set {U} of subsets of the 3-dimensional differentiable manifold M, + Set {V_union_W} of subsets of the 3-dimensional differentiable manifold M] sage: P.plot(element_labels={element: element._name for element in P}) # not tested If ``open_covers`` is ``True``, the poset includes a special vertex for @@ -764,10 +781,9 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P = M.subset_poset(open_covers=True); P Finite poset containing 6 elements - sage: P.upper_covers(VW) - [3-dimensional differentiable manifold M, - frozenset({Open subset V of the 3-dimensional differentiable manifold M, - Open subset W of the 3-dimensional differentiable manifold M})] + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: P.upper_covers(ManifoldSubsetSet([VW])) + [Set {M} of subsets of the 3-dimensional differentiable manifold M] sage: def label(element): ....: try: ....: return element._name From 6f00253e5702a38a55a232ddc1538bccf6b28f02 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 22 Apr 2021 10:50:36 +0200 Subject: [PATCH 165/706] Trac 31669: lift and reduce to/from homology --- src/sage/categories/chain_complexes.py | 51 +++++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 7acef57ff97..e69660d8853 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -16,7 +16,8 @@ from .category_types import Category_module from .modules import Modules from .functor import Functor -from sage.categories.morphism import SetMorphism +from .morphism import SetMorphism +from sage.misc.abstract_method import abstract_method ############################################################# # ChainComplex @@ -51,6 +52,7 @@ def super_categories(self): return [Modules(base_ring)] class ParentMethods: + @abstract_method(optional=True) def homology(self, *args, **kwargs): r""" Return the homology of the chain complex. @@ -85,8 +87,8 @@ def homology(self, *args, **kwargs): Free module generated by {[x^2], [y^2]} over Rational Field """ - raise NotImplementedError + @abstract_method def differential(self, *args, **kwargs): r""" Return the differentials (or boundary maps) of the chain complex. @@ -109,14 +111,37 @@ def differential(self, *args, **kwargs): z --> x*y """ - raise NotImplementedError - class MorphismMethods: - def induced_homology_morphism(self, *args, **kwargs): + def lift_from_homology(self, x): r""" - Return the from the chain map induced morphism on homology level. + Lift the homology element ``x`` to the corresponding module. + + """ + try: + # is homology already a quotient? + x.lift() + except AttributeError: + # if not, this methods needs to be overwritten by parent + raise NotImplementedError + + def reduce_to_homology(self, x, n=None): + r""" + Reduce a cycle to the corresponding quotient in homology. + + INPUT: + + - ``x`` -- a cycle + + If not overwritten, ``*args`` and ``**kwargs`` (like degree etc.) + are passed to :meth:`homology`. + """ - raise NotImplementedError + try: + # try coercion + self.homology(n)(x) + except TypeError: + # if not, this methods needs to be overwritten by parent + raise NotImplementedError class HomologyFunctor(Functor): r""" @@ -125,7 +150,8 @@ class HomologyFunctor(Functor): INPUT: - ``domain`` -- must be a category of chain complexes - - ``n`` -- degree of the homology groups + - ``n`` -- (default: ``None``) degree of the homology; if none is provided, + the direct sum homology will be used EXAMPLES:: @@ -143,7 +169,7 @@ class HomologyFunctor(Functor): Free module generated by {[x], [y]} over Rational Field """ - def __init__(self, domain, n): + def __init__(self, domain, n=None): r""" Construct the homology functor. @@ -178,4 +204,9 @@ def _apply_functor_to_morphism(self, f): r""" Apply ``self`` to a chain map. """ - return f.induced_homology_morphism() + lift = self.domain.lift_from_homology + reduce = self.codomain.reduce_to_homology + apply_f_star = lambda x: reduce(f(lift(x)), self.__n) + return SetMorphism(self.domain.homology(self.__n), + self.codomain.homology(self.__n), + apply_f_star) From 78475b65f60b604d28c96327f6a23bfa335a8bb9 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 22 Apr 2021 10:55:29 +0200 Subject: [PATCH 166/706] Trac 31669: docstring improved --- src/sage/categories/chain_complexes.py | 28 ++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index e69660d8853..e826c874af2 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -36,6 +36,7 @@ class ChainComplexes(Category_module): TESTS:: sage: TestSuite(ChainComplexes(RationalField())).run() + """ def super_categories(self): @@ -44,6 +45,7 @@ def super_categories(self): sage: ChainComplexes(Integers(9)).super_categories() [Category of modules over Ring of integers modulo 9] + """ from sage.categories.all import Fields, Modules, VectorSpaces base_ring = self.base_ring() @@ -53,13 +55,14 @@ def super_categories(self): class ParentMethods: @abstract_method(optional=True) - def homology(self, *args, **kwargs): + def homology(self, n=None): r""" Return the homology of the chain complex. INPUT: - - ``n`` -- degree of homology group + - ``n`` -- (default: ``None``) degree of the homology; if none is + provided, the direct sum homology will be used EXAMPLES:: @@ -116,6 +119,10 @@ def lift_from_homology(self, x): r""" Lift the homology element ``x`` to the corresponding module. + EXAMPLES:: + + ... + """ try: # is homology already a quotient? @@ -131,9 +138,13 @@ def reduce_to_homology(self, x, n=None): INPUT: - ``x`` -- a cycle + - ``n`` -- (default: ``None``) degree of the homology; if none is + provided, the direct sum homology will be used + - If not overwritten, ``*args`` and ``**kwargs`` (like degree etc.) - are passed to :meth:`homology`. + EXAMPLES:: + + ... """ try: @@ -168,6 +179,10 @@ class HomologyFunctor(Functor): sage: H(C) Free module generated by {[x], [y]} over Rational Field + Applying a chain map:: + + ... + """ def __init__(self, domain, n=None): r""" @@ -203,6 +218,11 @@ def _apply_functor(self, x): def _apply_functor_to_morphism(self, f): r""" Apply ``self`` to a chain map. + + TESTS: + + ... + """ lift = self.domain.lift_from_homology reduce = self.codomain.reduce_to_homology From 41461f748100803f5951945560f49815c430740a Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 22 Apr 2021 10:58:03 +0200 Subject: [PATCH 167/706] Trac 31669: minor docstring improved --- src/sage/categories/chain_complexes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index e826c874af2..36081037701 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -179,7 +179,7 @@ class HomologyFunctor(Functor): sage: H(C) Free module generated by {[x], [y]} over Rational Field - Applying a chain map:: + Applying to a chain map:: ... From ab5c0c9bc2d3504693c6fd82805baf71426d39ec Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:32:04 +0200 Subject: [PATCH 168/706] 18119: Refactor is_injective --- src/sage/combinat/words/morphism.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 5951cff3e95..bf02e9be0b4 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3209,18 +3209,18 @@ def check(u, v): tails.add(tail) todo.append(tail) - images = self._morph.values() + images = self.images() if any(not x for x in images): return False tails = set() todo = [] - for i, u in enumerate(images): - for j, v in enumerate(images): - if i == j: - continue - check(u, v) - + for i in range(len(images)): + for j in range(i + 1, len(images)): + if images[i] == images[j]: + return False + check(images[i], images[j]) + check(images[j], images[i]) while todo: u = todo.pop() for v in images: From 7a230ace15c4ee2d3df6e03d1700a13d75678061 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:32:18 +0200 Subject: [PATCH 169/706] 18119: Refactor simplify --- src/sage/combinat/words/morphism.py | 165 +++++++++++++--------------- 1 file changed, 78 insertions(+), 87 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index bf02e9be0b4..c39cc1d23f0 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3561,25 +3561,22 @@ def reach(self, w): def simplify(self, Z=None): r""" - Return morphisms `h` and `k` such that this morphism is simplifiable - with respect to `h` and `k`. + If this morphism is simplifiable, return morphisms `h` and `k` such that + this morphism is simplifiable with respect to `h` and `k`, otherwise + raise ``ValueError``. - If this morphism is non-injective, this function always succeeds, but - can fail (raise ``ValueError``) if it is injective, even it if is - simplifiable. + This method is quite fast if this morphism is non-injective, but very + slow if it is injective. Let `f: X^* \rightarrow Y^*` be a morphism. Then `f` is simplifiable with respect to morphisms `h: X^* \rightarrow Z^*` and `k: Z^* \rightarrow Y^*`, if `f = k \circ h` and `|Z| < |X|`. If also - `Y \subseteq X`, then morphism `g: Z^* \rightarrow Z^* = h \circ k` is - a simplification of `f` (with respect to `h` and `k`). + `Y \subseteq X`, then the morphism `g: Z^* \rightarrow Z^* = h \circ k` + is a simplification of `f` (with respect to `h` and `k`). - Therefore a morphism is simplifiable if it contains "more letters than - is needed". Simplification preserves some properties of the original - morphism (e.g. repetitiveness). - - Time complexity is on average quadratic with regards to the size of the - morphism. + Loosely speaking a morphism is simplifiable if it contains "more letters + than is needed". Non-injectivity implies simplifiability. Simplification + preserves some properties of the original morphism (e.g. repetitiveness). For more information see Section 3 in [KO2000]_. @@ -3590,43 +3587,33 @@ def simplify(self, Z=None): EXAMPLES: - Example of a simplifiable morphism:: + Example of a simplifiable (non-injective) morphism:: - sage: f = WordMorphism('a->bca,b->bcaa,c->bcaaa') - sage: h, k = f.simplify('xy') - sage: h - WordMorphism: a->xy, b->xyy, c->xyyy - sage: k - WordMorphism: x->bc, y->a + sage: f = WordMorphism('a->aca,b->badc,c->acab,d->adc') + sage: h, k = f.simplify('xyz'); h, k + (WordMorphism: a->x, b->zy, c->xz, d->y, WordMorphism: x->aca, y->adc, z->b) sage: k * h == f True sage: g = h * k; g - WordMorphism: x->xyyxyyy, y->xy + WordMorphism: x->xxzx, y->xyxz, z->zy - Example of a non-simplifiable morphism:: - - sage: WordMorphism('a->aa').simplify() - Traceback (most recent call last): - ... - ValueError: failed to simplify a->aa - - Example of a simplifiable morphism that the function fails on:: + Example of a simplifiable (injective) morphism:: sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') - sage: f.simplify('xyz') - Traceback (most recent call last): - ... - ValueError: failed to simplify a->abcc, b->abcd, c->abdc, d->abdd - - Proof that the above morphism is simplifiable:: - - sage: k = WordMorphism('x->ab,y->c,z->d') - sage: h = WordMorphism('a->xyy,b->xyz,c->xzy,d->xzz') + sage: h, k = f.simplify('xyz'); h, k + (WordMorphism: a->xyy, b->xyz, c->xzy, d->xzz, WordMorphism: x->ab, y->c, z->d) sage: k * h == f True sage: g = h * k; g WordMorphism: x->xyyxyz, y->xzy, z->xzz + Example of a non-simplifiable morphism:: + + sage: WordMorphism('a->aa').simplify() + Traceback (most recent call last): + ... + ValueError: self (a->aa) is not simplifiable + Example of an erasing morphism:: sage: f = WordMorphism('a->abc,b->cc,c->') @@ -3637,16 +3624,31 @@ def simplify(self, Z=None): sage: g = h * k; g WordMorphism: a->ab, b-> - Example of a non-endomorphism:: + Example of a morphism, that is not an endomorphism:: sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') - sage: h, k = f.simplify(ZZ); h, k + sage: h, k = f.simplify(NN); h, k (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) sage: k * h == f True sage: len(k.domain().alphabet()) < len(f.domain().alphabet()) True """ + def try_create_h(f, k): + h = {} + for letter1, image1 in f.items(): + image3 = [] + while image1: + for letter2, image2 in k.items(): + if image2.is_prefix(image1): + image1 = image1[image2.length():] + image3.append(letter2) + break + else: # nobreak + return None + h[letter1] = image3 + return h + X = self.domain().alphabet() Y = self.codomain().alphabet() f = self._morph @@ -3658,11 +3660,11 @@ def simplify(self, Z=None): k = {x: [y] for x, y in zip(X, Y)} k_inverse = {y: x for y, x in zip(Y, X)} h = {x: [k_inverse[y] for y in image] for x, image in f.items()} - else: # Non-trivial case. + elif not self.is_injective(): # Non-trivial but a fast case. k = dict(f) to_do = set(k) - to_remove = [] while to_do: + to_remove = [] # min() and remove() instead of pop() to have deterministic output. letter1 = min(to_do) to_do.remove(letter1) @@ -3676,37 +3678,32 @@ def simplify(self, Z=None): elif image1.is_prefix(image2): k[letter2] = image2[image1.length():] to_do.add(letter2) - elif image1.is_suffix(image2): - k[letter2] = image2[:-image1.length()] - to_do.add(letter2) elif image2.is_prefix(image1): k[letter1] = image1[image2.length():] to_do.add(letter1) break - elif image2.is_suffix(image1): - k[letter1] = image1[:-image2.length()] - to_do.add(letter1) - break for letter in to_remove: del k[letter] - to_remove = [] - - if len(k) == len(f): - raise ValueError(f'failed to simplify {self}') - - h = {} - for letter1, image1 in f.items(): - image3 = [] - while image1: - for letter2, image2 in k.items(): - if image2.is_prefix(image1): - image1 = image1[image2.length():] - image3.append(letter2) - break - h[letter1] = image3 + h = try_create_h(f, k) + else: # Non-trivial and a slow case. + factors = set() + for image in f.values(): + factors.update(x.primitive() for x in image.factor_iterator()) + factors.remove(self.codomain()()) + factors = sorted(factors) # For deterministic output. + from itertools import combinations + for comb in combinations(factors, len(X) - 1): + if any(x.is_proper_prefix(y) for x in comb for y in comb): + continue + k = {x: image for x, image in zip(X, comb)} + h = try_create_h(f, k) + if h: + break + else: # nobreak + raise ValueError(f'self ({self}) is not simplifiable') - k = type(self)(k, codomain=self.codomain()) - h = type(self)(h, domain=self.domain(), codomain=k.domain()) + k = WordMorphism(k, codomain=self.codomain()) + h = WordMorphism(h, domain=self.domain(), codomain=k.domain()) if Z is not None: # Custom alphabet. old_Z_star = k.domain() @@ -3717,8 +3714,8 @@ def simplify(self, Z=None): Z_star = FiniteWords(Z) h_new = {old: [new] for old, new in zip(old_Z, Z)} k_new = {new: [old] for new, old in zip(Z, old_Z)} - h_new = type(self)(h_new, domain=old_Z_star, codomain=Z_star) - k_new = type(self)(k_new, domain=Z_star, codomain=old_Z_star) + h_new = WordMorphism(h_new, domain=old_Z_star, codomain=Z_star) + k_new = WordMorphism(k_new, domain=Z_star, codomain=old_Z_star) h = h_new * h k = k * k_new @@ -3731,17 +3728,16 @@ def simplify_injective(self): Requires this morphism to be an endomorphism. - Basically calls :meth:`simplify` until it throws an exception, which - means the input was injective. If already the first call raises an - exception, instead of reraising it a quadruplet `(g, h, k, i)` is still - returned, where `g` and `h` are equal to this morphism, `k` is the - identity morphism and `i` is 0. + This methods basically calls :meth:`simplify` until the returned + simplification is injective. If this morphism is already injective, a + quadruplet `(g, h, k, i)` is still returned, where `g` is this morphism, + `h` and `k` are the identity morphisms and `i` is 0. Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with respect to morphisms `h: X^* \rightarrow Z^*` and `k: Z^* \rightarrow Y^*` and a positive integer `i`, if `g` is - injective and `|Z| < |X|` and `g^i = h \circ k` and `f^i = k \circ h`. + injective, `|Z| < |X|`, `g^i = h \circ k` and `f^i = k \circ h`. For more information see Section 4 in [KO2000]_. @@ -3760,16 +3756,11 @@ def simplify_injective(self): if not self.is_endomorphism(): raise TypeError(f'self ({self}) is not an endomorphism') - try: - h, k = self.simplify() - except ValueError: - return self, self, self.domain().identity_morphism(), 0 - g = h * k - - from itertools import count - for i in count(start=1): - try: - h_new, k_new = g.simplify() - g, h, k = h_new * k_new, h_new * h, k * k_new - except ValueError: - return g, h, k, i + g = self + h = self.domain().identity_morphism() + k = self.codomain().identity_morphism() + i = 0 + while not g.is_injective(): + h_new, k_new = g.simplify() + g, h, k, i = h_new * k_new, h_new * h, k * k_new, i + 1 + return g, h, k, i From e47204073b6b041c15430e215f267348229c3c99 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:38:46 +0200 Subject: [PATCH 170/706] 18119: Refactor is_injective (2) --- src/sage/combinat/words/morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index c39cc1d23f0..124c9972cf2 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3209,9 +3209,9 @@ def check(u, v): tails.add(tail) todo.append(tail) - images = self.images() - if any(not x for x in images): + if self.is_erasing(): return False + images = self.images() tails = set() todo = [] From e2a22cbca9b77611dbd37070fc5a28dc4ad6ab07 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 22 Apr 2021 15:53:00 +0200 Subject: [PATCH 171/706] 31716: more scalar conversions --- src/sage/rings/fraction_field_element.pyx | 103 +++++++++---- .../rings/polynomial/multi_polynomial.pyx | 140 ++++++++---------- .../rings/polynomial/polynomial_element.pyx | 5 + 3 files changed, 145 insertions(+), 103 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 3f32d2e1c52..4206b1dd4d8 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -753,34 +753,97 @@ cdef class FractionFieldElement(FieldElement): else: raise TypeError("denominator must equal 1") - def _integer_(self, Z=ZZ): + def __float__(self): """ EXAMPLES:: + sage: K. = Frac(ZZ['x,y']) + sage: float(x/x + y/y) + 2.0 + """ + return float(self.__numerator) / float(self.__denominator) + + def __complex__(self): + """ + EXAMPLES:: + + sage: K. = Frac(I.parent()['x,y']) + sage: complex(x/(I*x) + (I*y)/y) + 0j + """ + return complex(self.__numerator) / complex(self.__denominator) + + def _rational_(self): + r""" + TESTS:: + + sage: K = Frac(ZZ['x']) + sage: QQ(K(x) / K(2*x)) + 1/2 + """ + return self._conversion(QQ) + + def _conversion(self, R): + r""" + Generic conversion + + TESTS:: + sage: K = Frac(ZZ['x']) - sage: K(5)._integer_() + sage: ZZ(K(5)) # indirect doctest 5 + sage: ZZ(K(1) / K(2)) + Traceback (most recent call last): + ... + ArithmeticError: inverse does not exist + sage: RDF(K(1) / K(2)) + 0.5 + sage: K. = Frac(RR['x']) sage: ZZ(2*x/x) 2 - """ - if self.__denominator != 1: - self.reduce() - if self.__denominator == 1: - return Z(self.__numerator) - raise TypeError("no way to coerce to an integer.") - - def _rational_(self, Q=QQ): - """ - EXAMPLES:: + sage: RDF(x) + Traceback (most recent call last): + ... + TypeError: cannot convert nonconstant polynomial sage: K. = Frac(QQ['x']) - sage: K(1/2)._rational_() + sage: QQ(K(1/2)) 1/2 - sage: K(1/2 + x/x)._rational_() + sage: QQ(K(1/2 + x/x)) 3/2 + + sage: x = polygen(QQ) + sage: A. = NumberField(x^3 - 2) + sage: A((x+3) / (2*x - 1)) + 14/15*u^2 + 7/15*u + 11/15 + + sage: B = A['y'].fraction_field() + sage: A(B(u)) + u + sage: C = A['x,y'].fraction_field() + sage: A(C(u)) + u """ - return Q(self.__numerator) / Q(self.__denominator) + if self.__denominator.is_one(): + return R(self.__numerator) + else: + self.reduce() + num = R(self.__numerator) + inv_den = R(self.__denominator).inverse_of_unit() + return num * inv_den + + _real_double_ = _conversion + _complex_double_ = _conversion + _mpfr_ = _conversion + _complex_mpfr_ = _conversion + _real_mpfi_ = _conversion + _complex_mpfi_ = _conversion + _arb_ = _conversion + _acb_ = _conversion + _integer_ = _conversion + _algebraic_ = _conversion + _number_field_ = _conversion def __pow__(self, right, dummy): r""" @@ -871,16 +934,6 @@ cdef class FractionFieldElement(FieldElement): return self.__class__(self._parent, self.__denominator, self.__numerator, coerce=False, reduce=False) - def __float__(self): - """ - EXAMPLES:: - - sage: K. = Frac(ZZ['x,y']) - sage: float(x/x + y/y) - 2.0 - """ - return float(self.__numerator) / float(self.__denominator) - cpdef _richcmp_(self, other, int op): """ EXAMPLES:: diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 611a355ab11..40733397412 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -35,45 +35,8 @@ cdef class MPolynomial(CommutativeRingElement): #################### # Some standard conversions #################### - def __int__(self): - """ - TESTS:: - - sage: type(RR['x,y']) - - sage: type(RR['x, y'](0)) - - - sage: int(RR['x,y'](0)) # indirect doctest - 0 - sage: int(RR['x,y'](10)) - 10 - sage: int(ZZ['x,y'].gen(0)) - Traceback (most recent call last): - ... - TypeError: unable to convert non-constant polynomial x to an integer - """ - if self.degree() <= 0: - return int(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to an integer") - - def __float__(self): - """ - TESTS:: - - sage: float(RR['x,y'](0)) # indirect doctest - 0.0 - sage: float(ZZ['x,y'].gen(0)) - Traceback (most recent call last): - ... - TypeError: unable to convert non-constant polynomial x to a float - """ - if self.degree() <= 0: - return float(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a float") - - def _mpfr_(self, R): - """ + def _scalar_conversion(self, R): + r""" TESTS:: sage: RR(RR['x,y'](0)) # indirect doctest @@ -82,14 +45,6 @@ cdef class MPolynomial(CommutativeRingElement): Traceback (most recent call last): ... TypeError: unable to convert non-constant polynomial x to a real number - """ - if self.degree() <= 0: - return R(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a real number") - - def _complex_mpfr_field_(self, R): - """ - TESTS:: sage: CC(RR['x,y'](0)) # indirect doctest 0.000000000000000 @@ -97,14 +52,6 @@ cdef class MPolynomial(CommutativeRingElement): Traceback (most recent call last): ... TypeError: unable to convert non-constant polynomial x to a complex number - """ - if self.degree() <= 0: - return R(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a complex number") - - def _complex_double_(self, R): - """ - TESTS:: sage: CDF(RR['x,y'](0)) # indirect doctest 0.0 @@ -112,14 +59,6 @@ cdef class MPolynomial(CommutativeRingElement): Traceback (most recent call last): ... TypeError: unable to convert non-constant polynomial x to a complex number - """ - if self.degree() <= 0: - return R(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a complex number") - - def _real_double_(self, R): - """ - TESTS:: sage: RDF(RR['x,y'](0)) 0.0 @@ -127,30 +66,45 @@ cdef class MPolynomial(CommutativeRingElement): Traceback (most recent call last): ... TypeError: unable to convert non-constant polynomial x to a real number + + sage: x = polygen(QQ) + sage: A. = NumberField(x^3 - 2) + sage: A(A['x,y'](u)) + u """ if self.degree() <= 0: return R(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a real number") + raise TypeError(f"unable to convert non-constant polynomial {self} to {R}") + + _real_double_ = _scalar_conversion + _complex_double_ = _scalar_conversion + _mpfr_ = _scalar_conversion + _complex_mpfr_ = _scalar_conversion + _real_mpfi_ = _scalar_conversion + _complex_mpfi_ = _scalar_conversion + _arb_ = _scalar_conversion + _acb_ = _scalar_conversion + _integer_ = _scalar_conversion + _algebraic_ = _scalar_conversion + _number_field_ = _scalar_conversion - def _rational_(self): + def __int__(self): """ TESTS:: - sage: QQ(RR['x,y'](0.5)) # indirect doctest - 1/2 - sage: QQ(RR['x,y'].gen(0)) + sage: type(RR['x,y']) + + sage: type(RR['x, y'](0)) + + + sage: int(RR['x,y'](0)) # indirect doctest + 0 + sage: int(RR['x,y'](10)) + 10 + sage: int(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a rational - """ - if self.degree() <= 0: - from sage.rings.rational import Rational - return Rational(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to a rational") - - def _integer_(self, ZZ=None): - """ - TESTS:: + TypeError: unable to convert non-constant polynomial x to an integer sage: ZZ(RR['x,y'](0)) # indirect doctest 0 @@ -163,6 +117,36 @@ cdef class MPolynomial(CommutativeRingElement): ... TypeError: unable to convert non-constant polynomial x to an integer """ + return self._scalar_conversion(int) + + def __float__(self): + """ + TESTS:: + + sage: float(RR['x,y'](0)) # indirect doctest + 0.0 + sage: float(ZZ['x,y'].gen(0)) + Traceback (most recent call last): + ... + TypeError: unable to convert non-constant polynomial x to a float + """ + return self._scalar_conversion(float) + + def _rational_(self): + """ + TESTS:: + + sage: QQ(RR['x,y'](0.5)) # indirect doctest + 1/2 + sage: QQ(RR['x,y'].gen(0)) + Traceback (most recent call last): + ... + TypeError: unable to convert non-constant polynomial x to a rational + """ + from sage.rings.rational_field import QQ + return self._scalar_conversion(QQ) + + def _integer_(self, ZZ=None): if self.degree() <= 0: from sage.rings.integer import Integer return Integer(self.constant_coefficient()) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 570c148e72a..08f9aebd267 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1347,6 +1347,11 @@ cdef class Polynomial(CommutativeAlgebraElement): Traceback (most recent call last): ... TypeError: cannot convert nonconstant polynomial + + sage: x = polygen(QQ) + sage: A. = NumberField(x^3 - 2) + sage: A(A['x'](u)) + u """ if self.degree() > 0: raise TypeError("cannot convert nonconstant polynomial") From 9dbc024025d5447748abe30371827204739da62b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 22 Apr 2021 16:16:11 +0200 Subject: [PATCH 172/706] 31716: fix doctests --- src/sage/rings/polynomial/multi_polynomial.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 40733397412..3fbced8dc6f 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -44,28 +44,28 @@ cdef class MPolynomial(CommutativeRingElement): sage: RR(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a real number + TypeError: unable to convert non-constant polynomial x to Real Field with 53 bits of precision sage: CC(RR['x,y'](0)) # indirect doctest 0.000000000000000 sage: CC(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a complex number + TypeError: unable to convert non-constant polynomial x to Complex Field with 53 bits of precision sage: CDF(RR['x,y'](0)) # indirect doctest 0.0 sage: CDF(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a complex number + TypeError: unable to convert non-constant polynomial x to Complex Double Field sage: RDF(RR['x,y'](0)) 0.0 sage: RDF(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a real number + TypeError: unable to convert non-constant polynomial x to Real Double Field sage: x = polygen(QQ) sage: A. = NumberField(x^3 - 2) @@ -104,7 +104,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: int(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to an integer + TypeError: unable to convert non-constant polynomial x to sage: ZZ(RR['x,y'](0)) # indirect doctest 0 @@ -115,7 +115,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: ZZ(RR['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to an integer + TypeError: unable to convert non-constant polynomial x to Integer Ring """ return self._scalar_conversion(int) @@ -128,7 +128,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: float(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a float + TypeError: unable to convert non-constant polynomial x to """ return self._scalar_conversion(float) @@ -141,7 +141,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: QQ(RR['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a rational + TypeError: unable to convert non-constant polynomial x to Rational Field """ from sage.rings.rational_field import QQ return self._scalar_conversion(QQ) @@ -1952,7 +1952,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: p.weighted_degree(x,1,1) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to an integer + TypeError: unable to convert non-constant polynomial x to Integer Ring sage: p.weighted_degree(2/1,1,1) 6 From 120fe97420a2ef8ebc914ad7c6c5393d1409cb8c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 22 Apr 2021 10:54:09 -0700 Subject: [PATCH 173/706] src/sage/manifolds/subset_set.py: Add documentation --- src/sage/manifolds/subset_set.py | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/sage/manifolds/subset_set.py b/src/sage/manifolds/subset_set.py index ca4ff3547f7..5be36ebfc70 100644 --- a/src/sage/manifolds/subset_set.py +++ b/src/sage/manifolds/subset_set.py @@ -24,7 +24,55 @@ @total_ordering class ManifoldSubsetSet(frozenset): + r""" + Finite set of subsets of a topological manifold. + + The class :class:`ManifoldSubsetSet` inherits from the built-in + ``frozenset`` class. It provides specialized ``__repr__`` and + ``_latex_`` methods. + + :class:`ManifoldSubsetSet` instances are totally ordered according + to their lexicographically ordered element (subset) names. + + EXAMPLES:: + + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: C = B.subset('C') + sage: ManifoldSubsetSet([A, B, C]) + Set {A, B, C} of subsets of the 2-dimensional topological manifold M + sage: latex(_) + \{A, B, C\} + + All subsets must have the same base manifold:: + + sage: N = Manifold(2, 'N', structure='topological') + sage: ManifoldSubsetSet([M, N]) + Traceback (most recent call last): + ... + TypeError: all elements must be subsets of the same manifold + + """ + def __new__(cls, *args): + r""" + Construct a new instance of ``ManifoldSubsetSet``. + + TESTS: + + Like ``frozenset``, it can be created from any iterable:: + + sage: from sage.manifolds.subset import ManifoldSubsetSet + sage: M = Manifold(2, 'M', structure='topological') + sage: I = M.subset('I') + sage: gen = (subset for subset in (M, I, M, I, M, I)); gen + + sage: ManifoldSubsetSet(gen) + Set {I, M} of subsets of the 2-dimensional topological manifold M + + """ self = super().__new__(cls, *args) names_and_latex_names = sorted((subset._name, subset._latex_name) for subset in self) From 4e245da21ba5a8557995a47bde15a0dac1f386b9 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 22 Apr 2021 21:29:40 +0200 Subject: [PATCH 174/706] 31716: more doctests --- src/sage/rings/quotient_ring_element.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index 09564e4c3eb..eab1154f30c 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -473,11 +473,11 @@ def __int__(self): sage: int(a) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to an integer + TypeError: unable to convert non-constant polynomial x to """ return int(self.lift()) - def _integer_(self, Z=None): + def _integer_(self, Z): """ EXAMPLES:: @@ -488,13 +488,10 @@ def _integer_(self, Z=None): TESTS:: - sage: type(S(-3)._integer_()) - + sage: type(ZZ(S(-3))) + """ - try: - return self.lift()._integer_(Z) - except AttributeError: - raise NotImplementedError + return Z(self.lift()) def _rational_(self): """ @@ -510,10 +507,8 @@ def _rational_(self): sage: type(S(-2/3)._rational_()) """ - try: - return self.lift()._rational_() - except AttributeError: - raise NotImplementedError + from sage.rings.rational_field import QQ + return QQ(self.lift()) def __neg__(self): """ @@ -579,7 +574,7 @@ def __float__(self): sage: float(a) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to a float + TypeError: unable to convert non-constant polynomial x to """ return float(self.lift()) From 97ec61d80b555f6b5686987e9be58c366743a73b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 22 Apr 2021 12:59:27 -0700 Subject: [PATCH 175/706] FiniteManifoldObjectFamily: Rename from ManifoldSubsetSet, subclass FiniteFamily instead of frozenset --- src/sage/manifolds/family.py | 250 +++++++++++++++++++++++++++++++ src/sage/manifolds/subset.py | 32 ++-- src/sage/manifolds/subset_set.py | 146 ------------------ 3 files changed, 267 insertions(+), 161 deletions(-) create mode 100644 src/sage/manifolds/family.py delete mode 100644 src/sage/manifolds/subset_set.py diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py new file mode 100644 index 00000000000..9073e528fd9 --- /dev/null +++ b/src/sage/manifolds/family.py @@ -0,0 +1,250 @@ +r""" +Families of Manifold Objects + +The class :class:`FiniteManifoldObjectFamily` is a subclass of :class`FiniteFamily` +that provides an associative container of manifold objects, indexed by their +``_name`` attributes. It provides specialized ``__repr__`` and +``_latex_`` methods. + +:class:`FiniteManifoldObjectFamily` instances are totally ordered according +to their lexicographically ordered element names. + +The subclass :class:`FiniteManifoldSubsetFamily` customizes the print +representation further. + +""" +#***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from functools import total_ordering +from sage.sets.family import FiniteFamily + +@total_ordering +class FiniteManifoldObjectFamily(FiniteFamily): + + r""" + Finite family of manifold objects, indexed by their names. + + The class :class:`FiniteManifoldObjectFamily` inherits from + `:class:`FiniteFamily`. Therefore it is an associative container. + + It provides specialized ``__repr__`` and ``_latex_`` methods. + + :class:`FiniteManifoldObjectFamily` instances are totally ordered + according to their lexicographically ordered element names. + + EXAMPLES:: + + sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: C = B.subset('C') + sage: F = FiniteManifoldObjectFamily([A, B, C]); F + Set {A, B, C} of objects of the 2-dimensional topological manifold M + sage: latex(F) + \{A, B, C\} + sage: F['B'] + Subset B of the 2-dimensional topological manifold M + + All objects must have the same base manifold:: + + sage: N = Manifold(2, 'N', structure='topological') + sage: FiniteManifoldObjectFamily([M, N]) + Traceback (most recent call last): + ... + TypeError: all objects must have the same manifold + + """ + def __init__(self, objects=(), keys=None): + r""" + Initialize a new instance of :class:`FiniteManifoldObjectFamily`. + + TESTS: + + sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: C = B.subset('C') + sage: F = FiniteManifoldObjectFamily([A, B, C]); F + Set {A, B, C} of objects of the 2-dimensional topological manifold M + sage: TestSuite(F).run(skip='_test_elements') + + Like ``frozenset``, it can be created from any iterable:: + + sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: I = M.subset('I') + sage: gen = (subset for subset in (M, I, M, I, M, I)); gen + + sage: FiniteManifoldSubsetFamily(gen) + Set {I, M} of subsets of the 2-dimensional topological manifold M + + """ + if isinstance(objects, dict): + dictionary = objects + else: + dictionary = {object._name: object for object in objects} + if keys is None: + keys = sorted(dictionary.keys()) + FiniteFamily.__init__(self, dictionary, keys) + names_and_latex_names = sorted((object._name, object._latex_name) + for object in self) + self._name = '{' + ', '.join(keys) + '}' + latex_names = (latex_name for name, latex_name in names_and_latex_names) + self._latex_name = r'\{' + ', '.join(latex_names) + r'\}' + try: + object_iter = iter(self) + self._manifold = next(object_iter)._manifold + except StopIteration: + self._manifold = None + else: + if not all(object._manifold == self._manifold for object in object_iter): + raise TypeError(f'all {self._repr_object_type()} must have the same manifold') + + def __bool__(self): + r""" + True if nonempty. + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: bool(FiniteManifoldObjectFamily()) + False + sage: M = Manifold(2, 'M', structure='topological') + sage: bool(FiniteManifoldObjectFamily([M])) + True + """ + # Arguably, FiniteFamily should have this behavior already, but as of Sage 9.3 it does not - #31717 + return bool(self._dictionary) + + def _repr_object_type(self): + r""" + String that describes the type of the elements (plural). + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: FiniteManifoldObjectFamily([A, B]).__repr__() # indirect doctest + 'Set {A, B} of objects of the 2-dimensional topological manifold M' + + """ + return "objects" + + def __lt__(self, other): + r""" + Implement the total order on instances of :class:`FiniteManifoldObjectFamily`. + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: sorted([FiniteManifoldSubsetFamily([A, B]), FiniteManifoldSubsetFamily([]), + ....: FiniteManifoldSubsetFamily([B]), FiniteManifoldSubsetFamily([A])]) + [{}, + Set {A} of subsets of the 2-dimensional topological manifold M, + Set {A, B} of subsets of the 2-dimensional topological manifold M, + Set {B} of subsets of the 2-dimensional topological manifold M] + """ + if not isinstance(other, FiniteManifoldSubsetFamily): + return NotImplemented + return self.keys() < other.keys() + + def __repr__(self): + r""" + String representation of the object. + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: FiniteManifoldObjectFamily().__repr__() + '{}' + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: FiniteManifoldObjectFamily([A, B]).__repr__() + 'Set {A, B} of objects of the 2-dimensional topological manifold M' + + """ + if self: + return "Set {} of {} of the {}".format(self._name, self._repr_object_type(), self._manifold) + else: + return "{}" + + def _latex_(self): + r""" + LaTeX representation of ``self``. + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: FiniteManifoldSubsetFamily([B, A])._latex_() + '\\{A, B\\}' + """ + return self._latex_name + +class FiniteManifoldSubsetFamily(FiniteManifoldObjectFamily): + + r""" + Finite family of subsets of a topological manifold, indexed by their names. + + The class :class:`FiniteManifoldSubsetFamily` inherits from + :class:`FiniteManifoldObjectFamily`. It provides an associative + container with specialized ``__repr__`` and ``_latex_`` methods. + + :class:`FiniteManifoldSubsetFamily` instances are totally ordered according + to their lexicographically ordered element (subset) names. + + EXAMPLES:: + + sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: C = B.subset('C') + sage: FiniteManifoldSubsetFamily([A, B, C]) + Set {A, B, C} of subsets of the 2-dimensional topological manifold M + sage: latex(_) + \{A, B, C\} + + All subsets must have the same base manifold:: + + sage: N = Manifold(2, 'N', structure='topological') + sage: FiniteManifoldSubsetFamily([M, N]) + Traceback (most recent call last): + ... + TypeError: all subsets must have the same manifold + + """ + + def _repr_object_type(self): + r""" + String that describes the type of the elements (plural). + + TESTS:: + + sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: FiniteManifoldSubsetFamily([A, B]).__repr__() # indirect doctest + 'Set {A, B} of subsets of the 2-dimensional topological manifold M' + + """ + return "subsets" diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 5c754db0964..e46a2b7556c 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -76,7 +76,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets -from sage.manifolds.subset_set import ManifoldSubsetSet +from sage.manifolds.family import FiniteManifoldSubsetFamily from sage.manifolds.point import ManifoldPoint class ManifoldSubset(UniqueRepresentation, Parent): @@ -669,8 +669,8 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): A digraph. Each vertex of the digraph is either: - - a :class:`ManifoldSubsetSet` containing one instance of :class:`ManifoldSubset`. - - (if ``open_covers`` is ``True``) a tuple of :class:`ManifoldSubsetSet` instances, + - a :class:`FiniteManifoldSubsetFamily` containing one instance of :class:`ManifoldSubset`. + - (if ``open_covers`` is ``True``) a tuple of :class:`FiniteManifoldSubsetFamily` instances, representing an open cover. EXAMPLES:: @@ -709,7 +709,7 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): D = DiGraph(multiedges=False, loops=loops) def vertex(subset): - return ManifoldSubsetSet([subset]) + return FiniteManifoldSubsetFamily([subset]) if lower_bound is not None: if not lower_bound.is_subset(self): @@ -739,7 +739,7 @@ def vertex(subset): if open_covers: def open_cover_vertex(open_cover): - return tuple(sorted(ManifoldSubsetSet([subset]) for subset in open_cover)) + return tuple(sorted(FiniteManifoldSubsetFamily([subset]) for subset in open_cover)) for S in visited: D.add_edges((vertex(S), open_cover_vertex(open_cover)) @@ -767,13 +767,13 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P.maximal_elements() [Set {M} of subsets of the 3-dimensional differentiable manifold M] sage: sorted(P.minimal_elements(), key=lambda v: v._name) - [Set {U} of subsets of the 3-dimensional differentiable manifold M, - Set {V} of subsets of the 3-dimensional differentiable manifold M, - Set {W} of subsets of the 3-dimensional differentiable manifold M] - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: sorted(P.lower_covers(ManifoldSubsetSet([M])), key=str) - [Set {U} of subsets of the 3-dimensional differentiable manifold M, - Set {V_union_W} of subsets of the 3-dimensional differentiable manifold M] + [Set {U} of subsets of the 3-dimensional differentiable manifold M, + Set {V} of subsets of the 3-dimensional differentiable manifold M, + Set {W} of subsets of the 3-dimensional differentiable manifold M] + sage: from sage.manifolds.subset import FiniteManifoldSubsetFamily + sage: sorted(P.lower_covers(FiniteManifoldSubsetFamily([M])), key=str) + [Set {U} of subsets of the 3-dimensional differentiable manifold M, + Set {V_union_W} of subsets of the 3-dimensional differentiable manifold M] sage: P.plot(element_labels={element: element._name for element in P}) # not tested If ``open_covers`` is ``True``, the poset includes a special vertex for @@ -781,9 +781,11 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P = M.subset_poset(open_covers=True); P Finite poset containing 6 elements - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: P.upper_covers(ManifoldSubsetSet([VW])) - [Set {M} of subsets of the 3-dimensional differentiable manifold M] + sage: from sage.manifolds.subset import FiniteManifoldSubsetFamily + sage: P.upper_covers(FiniteManifoldSubsetFamily([VW])) + [(Set {V} of subsets of the 3-dimensional differentiable manifold M, + Set {W} of subsets of the 3-dimensional differentiable manifold M), + Set {M} of subsets of the 3-dimensional differentiable manifold M] sage: def label(element): ....: try: ....: return element._name diff --git a/src/sage/manifolds/subset_set.py b/src/sage/manifolds/subset_set.py deleted file mode 100644 index 5be36ebfc70..00000000000 --- a/src/sage/manifolds/subset_set.py +++ /dev/null @@ -1,146 +0,0 @@ -r""" -Immutable Sets of Subsets of Topological Manifolds - -The class :class:`ManifoldSubsetSet` is a subclass of the built-in -``frozenset`` class that provides specialized ``__repr__`` and -``_latex_`` methods. - -:class:`ManifoldSubsetSet` instances are totally ordered according -to their lexicographically ordered element (subset) names. - -""" -#***************************************************************************** -# Copyright (C) 2021 Matthias Koeppe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from functools import total_ordering - -@total_ordering -class ManifoldSubsetSet(frozenset): - - r""" - Finite set of subsets of a topological manifold. - - The class :class:`ManifoldSubsetSet` inherits from the built-in - ``frozenset`` class. It provides specialized ``__repr__`` and - ``_latex_`` methods. - - :class:`ManifoldSubsetSet` instances are totally ordered according - to their lexicographically ordered element (subset) names. - - EXAMPLES:: - - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: M = Manifold(2, 'M', structure='topological') - sage: A = M.subset('A') - sage: B = M.subset('B') - sage: C = B.subset('C') - sage: ManifoldSubsetSet([A, B, C]) - Set {A, B, C} of subsets of the 2-dimensional topological manifold M - sage: latex(_) - \{A, B, C\} - - All subsets must have the same base manifold:: - - sage: N = Manifold(2, 'N', structure='topological') - sage: ManifoldSubsetSet([M, N]) - Traceback (most recent call last): - ... - TypeError: all elements must be subsets of the same manifold - - """ - - def __new__(cls, *args): - r""" - Construct a new instance of ``ManifoldSubsetSet``. - - TESTS: - - Like ``frozenset``, it can be created from any iterable:: - - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: M = Manifold(2, 'M', structure='topological') - sage: I = M.subset('I') - sage: gen = (subset for subset in (M, I, M, I, M, I)); gen - - sage: ManifoldSubsetSet(gen) - Set {I, M} of subsets of the 2-dimensional topological manifold M - - """ - self = super().__new__(cls, *args) - names_and_latex_names = sorted((subset._name, subset._latex_name) - for subset in self) - self._names = tuple(name for name, latex_name in names_and_latex_names) - self._name = '{' + ', '.join(self._names) + '}' - latex_names = (latex_name for name, latex_name in names_and_latex_names) - self._latex_name = r'\{' + ', '.join(latex_names) + r'\}' - try: - subset_iter = iter(self) - self._manifold = next(subset_iter)._manifold - except StopIteration: - pass - else: - if not all(subset._manifold == self._manifold for subset in subset_iter): - raise TypeError('all elements must be subsets of the same manifold') - return self - - def __repr__(self): - r""" - String representation of the object. - - TESTS:: - - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: ManifoldSubsetSet().__repr__() - '{}' - sage: M = Manifold(2, 'M', structure='topological') - sage: A = M.subset('A') - sage: B = M.subset('B') - sage: ManifoldSubsetSet([A, B]).__repr__() - 'Set {A, B} of subsets of the 2-dimensional topological manifold M' - - """ - if self: - return "Set {} of subsets of the {}".format(self._name, self._manifold) - else: - return "{}" - - def _latex_(self): - r""" - LaTeX representation of ``self``. - - TESTS:: - - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: M = Manifold(2, 'M', structure='topological') - sage: A = M.subset('A') - sage: B = M.subset('B') - sage: ManifoldSubsetSet([B, A])._latex_() - '\\{A, B\\}' - """ - return self._latex_name - - def __lt__(self, other): - r""" - Implement the total order on instances of :class:`ManifoldSubsetSet`. - - TESTS:: - - sage: from sage.manifolds.subset import ManifoldSubsetSet - sage: M = Manifold(2, 'M', structure='topological') - sage: A = M.subset('A') - sage: B = M.subset('B') - sage: sorted([ManifoldSubsetSet([A, B]), ManifoldSubsetSet([]), - ....: ManifoldSubsetSet([B]), ManifoldSubsetSet([A])]) - [{}, - Set {A} of subsets of the 2-dimensional topological manifold M, - Set {A, B} of subsets of the 2-dimensional topological manifold M, - Set {B} of subsets of the 2-dimensional topological manifold M] - """ - return self._names < other._names From 0354a41f5504017a2aedf0ad27ee12a27f4a1702 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Apr 2021 10:07:00 +1000 Subject: [PATCH 176/706] Fix deprecation warning for viewing crystals with graphviz. --- src/sage/categories/loop_crystals.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index 784ea2b3b43..06bd9f55a17 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -119,8 +119,11 @@ def digraph(self, subset=None, index_set=None): """ G = Crystals().parent_class.digraph(self, subset, index_set) if have_dot2tex(): - f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) - G.set_latex_options(edge_options=f) + def eopt(u_v_label): + if u_v_label[2] == 0: + return {"dir": "back"} + return {} + G.set_latex_options(edge_options=eopt) return G # TODO: Should we make "regular" an axiom? From cbbef90cc8caacb6bafbe2c0cb32d40bfe5a4f4b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Apr 2021 10:16:58 +1000 Subject: [PATCH 177/706] Adding a __bool__ to trivial family. --- src/sage/sets/family.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index d391cbaa49f..77509151d5f 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -1165,6 +1165,22 @@ def __init__(self, enumeration): Parent.__init__(self, category = FiniteEnumeratedSets()) self._enumeration = tuple(enumeration) + def __bool__(self): + r""" + Return if ``self`` is empty or not. + + EXAMPLES:: + + sage: from sage.sets.family import TrivialFamily + sage: f = TrivialFamily((3,4,7)) + sage: bool(f) + True + sage: g = Family([]) + sage: bool(g) + False + """ + return bool(self._enumeration) + def __eq__(self, other): """ TESTS:: From 42abda7c927e2db4378cdaf4af27315c6669d62b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Apr 2021 12:14:00 +1000 Subject: [PATCH 178/706] Adding __bool__ for other families. --- src/sage/sets/family.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index 77509151d5f..7ad6dbf17e0 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -581,6 +581,25 @@ def __hash__(self): return hash(frozenset(self.keys() + [repr(v) for v in self.values()])) + def __bool__(self): + r""" + Return if ``self`` is empty or not. + + EXAMPLES:: + + sage: from sage.sets.family import TrivialFamily + sage: f = Family(["c", "a", "b"], lambda x: x+x) + sage: bool(f) + True + sage: g = Family({}) + sage: bool(g) + False + sage: h = Family([], lambda x: x+x) + sage: bool(h) + False + """ + return bool(self._dictionary) + def keys(self): """ Returns the index set of this family @@ -909,6 +928,25 @@ def __init__(self, set, function, name=None): self.function = function self.function_name = name + def __bool__(self): + r""" + Return if ``self`` is empty or not. + + EXAMPLES:: + + sage: from sage.sets.family import LazyFamily + sage: f = LazyFamily([3,4,7], lambda i: 2*i) + sage: bool(f) + True + sage: g = LazyFamily([], lambda i: 2*i) + sage: bool(g) + False + sage: h = Family(ZZ, lambda x: x+x) + sage: bool(h) + True + """ + return bool(self.set) + @cached_method def __hash__(self): """ From f0959889a79f531474f8fe7a12ebe749db3c2957 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 22 Apr 2021 21:18:34 -0700 Subject: [PATCH 179/706] FiniteManifoldObjectFamily.__bool__: Remove, inherited from superclass after #31717 --- src/sage/manifolds/family.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index 9073e528fd9..d744c2811a8 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -110,22 +110,6 @@ def __init__(self, objects=(), keys=None): if not all(object._manifold == self._manifold for object in object_iter): raise TypeError(f'all {self._repr_object_type()} must have the same manifold') - def __bool__(self): - r""" - True if nonempty. - - TESTS:: - - sage: from sage.manifolds.family import FiniteManifoldObjectFamily - sage: bool(FiniteManifoldObjectFamily()) - False - sage: M = Manifold(2, 'M', structure='topological') - sage: bool(FiniteManifoldObjectFamily([M])) - True - """ - # Arguably, FiniteFamily should have this behavior already, but as of Sage 9.3 it does not - #31717 - return bool(self._dictionary) - def _repr_object_type(self): r""" String that describes the type of the elements (plural). From 186707ba9fe840040c6675421a65f28fc4f3999e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 22 Apr 2021 21:57:02 -0700 Subject: [PATCH 180/706] ManifoldSubset.subsets: Change to generator --- .../differentiable/examples/sphere.py | 2 +- src/sage/manifolds/manifold.py | 2 +- src/sage/manifolds/subset.py | 25 +++++++------------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index 67b40872c7b..11d2b53f9c6 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -505,7 +505,7 @@ def _init_chart_domains(self): [[2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3], [Open subset S^2-{NP} of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3, Open subset S^2-{SP} of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3]] - sage: S2.subsets() # random + sage: frozenset(S2.subsets()) # random frozenset({Euclidean 2-sphere S^2 of radius 1, Open subset A of the Euclidean 2-sphere S^2 of radius 1, Open subset S^2-{NP,SP} of the Euclidean 2-sphere S^2 of radius 1, diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 41fb5a18234..47eb818e470 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -833,7 +833,7 @@ def open_subset(self, name, latex_name=None, coord_def={}): We have then:: - sage: A.subsets() # random (set output) + sage: frozenset(A.subsets()) # random (set output) {Open subset B of the 2-dimensional topological manifold M, Open subset A of the 2-dimensional topological manifold M} sage: B.is_subset(A) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 6ee2826bca1..ac8aa262669 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -574,17 +574,11 @@ def open_covers(self): def subsets(self): r""" - Return the set of subsets that have been defined on the - current subset. - - OUTPUT: - - - a Python set containing all the subsets that have been defined on - the current subset + Generate the subsets that have been defined on the current subset. .. NOTE:: - To get the subsets as a list, used the method + To get the subsets as a list, use the method :meth:`list_of_subsets` instead. EXAMPLES: @@ -594,17 +588,15 @@ def subsets(self): sage: M = Manifold(2, 'M', structure='topological') sage: U = M.open_subset('U') sage: V = M.subset('V') - sage: M.subsets() # random (set output) + sage: frozenset(M.subsets()) # random (set output) {Subset V of the 2-dimensional topological manifold M, 2-dimensional topological manifold M, Open subset U of the 2-dimensional topological manifold M} - sage: type(M.subsets()) - <... 'frozenset'> sage: U in M.subsets() True The method :meth:`list_of_subsets` returns a list (sorted - alphabetically by the subset names) instead of a set:: + alphabetically by the subset names):: sage: M.list_of_subsets() [2-dimensional topological manifold M, @@ -612,7 +604,7 @@ def subsets(self): Subset V of the 2-dimensional topological manifold M] """ - return frozenset(self._subsets) + yield from self._subsets def list_of_subsets(self): r""" @@ -628,7 +620,7 @@ def list_of_subsets(self): .. NOTE:: - To get the subsets as a Python set, used the method + To get the subsets as a Python set, use the method :meth:`subsets` instead. EXAMPLES: @@ -643,9 +635,10 @@ def list_of_subsets(self): Open subset U of the 2-dimensional topological manifold M, Subset V of the 2-dimensional topological manifold M] - The method :meth:`subsets` returns a set instead of a list:: + The method :meth:`subsets` generates the subsets. To create + a set:: - sage: M.subsets() # random (set output) + sage: frozenset(M.subsets()) # random (set output) {Subset V of the 2-dimensional topological manifold M, 2-dimensional topological manifold M, Open subset U of the 2-dimensional topological manifold M} From 779b1ad6cc8df67c463865c8cad1c4305e8dace9 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 23 Apr 2021 08:52:13 +0200 Subject: [PATCH 181/706] more tests --- .../rings/polynomial/multi_polynomial.pyx | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 3fbced8dc6f..7a7dffcb821 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -39,6 +39,17 @@ cdef class MPolynomial(CommutativeRingElement): r""" TESTS:: + sage: ZZ(RR['x,y'](0)) # indirect doctest + 0 + sage: ZZ(RR['x,y'](0.5)) + Traceback (most recent call last): + ... + TypeError: Attempt to coerce non-integral RealNumber to Integer + sage: ZZ(RR['x,y'].gen(0)) + Traceback (most recent call last): + ... + TypeError: unable to convert non-constant polynomial x to Integer Ring + sage: RR(RR['x,y'](0)) # indirect doctest 0.000000000000000 sage: RR(ZZ['x,y'].gen(0)) @@ -53,6 +64,13 @@ cdef class MPolynomial(CommutativeRingElement): ... TypeError: unable to convert non-constant polynomial x to Complex Field with 53 bits of precision + sage: RDF(RR['x,y'](0)) + 0.0 + sage: RDF(ZZ['x,y'].gen(0)) + Traceback (most recent call last): + ... + TypeError: unable to convert non-constant polynomial x to Real Double Field + sage: CDF(RR['x,y'](0)) # indirect doctest 0.0 sage: CDF(ZZ['x,y'].gen(0)) @@ -60,12 +78,22 @@ cdef class MPolynomial(CommutativeRingElement): ... TypeError: unable to convert non-constant polynomial x to Complex Double Field - sage: RDF(RR['x,y'](0)) - 0.0 - sage: RDF(ZZ['x,y'].gen(0)) + sage: a = RR['x,y'](1) + sage: RBF(a) + 1.000000000000000 + sage: RIF(a) + 1 + sage: CBF(a) + 1.000000000000000 + sage: CIF(a) + 1 + + sage: CBF(RR['x,y'](1)) # indirect doctest + 1.000000000000000 + sage: CBF(ZZ['x,y'].gen(0)) Traceback (most recent call last): ... - TypeError: unable to convert non-constant polynomial x to Real Double Field + TypeError: unable to convert non-constant polynomial x to Complex ball field with 53 bits of precision sage: x = polygen(QQ) sage: A. = NumberField(x^3 - 2) From e40555aff8c29f8dfb771273416cf2c24fb84aa3 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Fri, 23 Apr 2021 08:55:07 +0200 Subject: [PATCH 182/706] 31716: remove MPolynomial._integer_ --- src/sage/rings/polynomial/multi_polynomial.pyx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 7a7dffcb821..0a56cf1b519 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -174,12 +174,6 @@ cdef class MPolynomial(CommutativeRingElement): from sage.rings.rational_field import QQ return self._scalar_conversion(QQ) - def _integer_(self, ZZ=None): - if self.degree() <= 0: - from sage.rings.integer import Integer - return Integer(self.constant_coefficient()) - raise TypeError(f"unable to convert non-constant polynomial {self} to an integer") - def _symbolic_(self, R): """ EXAMPLES:: From 5d87ceef7ea74cd88701eb3a3eb728d6a994bd43 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 06:59:22 -0700 Subject: [PATCH 183/706] Manifold{Object,Subset}FiniteFamily: Rename from FiniteManifold{Object,Subset}Family --- src/sage/manifolds/family.py | 70 ++++++++++++++++++------------------ src/sage/manifolds/subset.py | 18 +++++----- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index d744c2811a8..adbeb40dd1f 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -1,15 +1,15 @@ r""" Families of Manifold Objects -The class :class:`FiniteManifoldObjectFamily` is a subclass of :class`FiniteFamily` +The class :class:`ManifoldObjectFiniteFamily` is a subclass of :class`FiniteFamily` that provides an associative container of manifold objects, indexed by their ``_name`` attributes. It provides specialized ``__repr__`` and ``_latex_`` methods. -:class:`FiniteManifoldObjectFamily` instances are totally ordered according +:class:`ManifoldObjectFiniteFamily` instances are totally ordered according to their lexicographically ordered element names. -The subclass :class:`FiniteManifoldSubsetFamily` customizes the print +The subclass :class:`ManifoldSubsetFiniteFamily` customizes the print representation further. """ @@ -27,27 +27,27 @@ from sage.sets.family import FiniteFamily @total_ordering -class FiniteManifoldObjectFamily(FiniteFamily): +class ManifoldObjectFiniteFamily(FiniteFamily): r""" Finite family of manifold objects, indexed by their names. - The class :class:`FiniteManifoldObjectFamily` inherits from + The class :class:`ManifoldObjectFiniteFamily` inherits from `:class:`FiniteFamily`. Therefore it is an associative container. It provides specialized ``__repr__`` and ``_latex_`` methods. - :class:`FiniteManifoldObjectFamily` instances are totally ordered + :class:`ManifoldObjectFiniteFamily` instances are totally ordered according to their lexicographically ordered element names. EXAMPLES:: - sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: from sage.manifolds.family import ManifoldObjectFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') sage: C = B.subset('C') - sage: F = FiniteManifoldObjectFamily([A, B, C]); F + sage: F = ManifoldObjectFiniteFamily([A, B, C]); F Set {A, B, C} of objects of the 2-dimensional topological manifold M sage: latex(F) \{A, B, C\} @@ -57,7 +57,7 @@ class FiniteManifoldObjectFamily(FiniteFamily): All objects must have the same base manifold:: sage: N = Manifold(2, 'N', structure='topological') - sage: FiniteManifoldObjectFamily([M, N]) + sage: ManifoldObjectFiniteFamily([M, N]) Traceback (most recent call last): ... TypeError: all objects must have the same manifold @@ -65,27 +65,27 @@ class FiniteManifoldObjectFamily(FiniteFamily): """ def __init__(self, objects=(), keys=None): r""" - Initialize a new instance of :class:`FiniteManifoldObjectFamily`. + Initialize a new instance of :class:`ManifoldObjectFiniteFamily`. TESTS: - sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: from sage.manifolds.family import ManifoldObjectFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') sage: C = B.subset('C') - sage: F = FiniteManifoldObjectFamily([A, B, C]); F + sage: F = ManifoldObjectFiniteFamily([A, B, C]); F Set {A, B, C} of objects of the 2-dimensional topological manifold M sage: TestSuite(F).run(skip='_test_elements') Like ``frozenset``, it can be created from any iterable:: - sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: I = M.subset('I') sage: gen = (subset for subset in (M, I, M, I, M, I)); gen - sage: FiniteManifoldSubsetFamily(gen) + sage: ManifoldSubsetFiniteFamily(gen) Set {I, M} of subsets of the 2-dimensional topological manifold M """ @@ -116,11 +116,11 @@ def _repr_object_type(self): TESTS:: - sage: from sage.manifolds.family import FiniteManifoldObjectFamily + sage: from sage.manifolds.family import ManifoldObjectFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') - sage: FiniteManifoldObjectFamily([A, B]).__repr__() # indirect doctest + sage: ManifoldObjectFiniteFamily([A, B]).__repr__() # indirect doctest 'Set {A, B} of objects of the 2-dimensional topological manifold M' """ @@ -128,22 +128,22 @@ def _repr_object_type(self): def __lt__(self, other): r""" - Implement the total order on instances of :class:`FiniteManifoldObjectFamily`. + Implement the total order on instances of :class:`ManifoldObjectFiniteFamily`. TESTS:: - sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') - sage: sorted([FiniteManifoldSubsetFamily([A, B]), FiniteManifoldSubsetFamily([]), - ....: FiniteManifoldSubsetFamily([B]), FiniteManifoldSubsetFamily([A])]) + sage: sorted([ManifoldSubsetFiniteFamily([A, B]), ManifoldSubsetFiniteFamily([]), + ....: ManifoldSubsetFiniteFamily([B]), ManifoldSubsetFiniteFamily([A])]) [{}, Set {A} of subsets of the 2-dimensional topological manifold M, Set {A, B} of subsets of the 2-dimensional topological manifold M, Set {B} of subsets of the 2-dimensional topological manifold M] """ - if not isinstance(other, FiniteManifoldSubsetFamily): + if not isinstance(other, ManifoldSubsetFiniteFamily): return NotImplemented return self.keys() < other.keys() @@ -153,13 +153,13 @@ def __repr__(self): TESTS:: - sage: from sage.manifolds.family import FiniteManifoldObjectFamily - sage: FiniteManifoldObjectFamily().__repr__() + sage: from sage.manifolds.family import ManifoldObjectFiniteFamily + sage: ManifoldObjectFiniteFamily().__repr__() '{}' sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') - sage: FiniteManifoldObjectFamily([A, B]).__repr__() + sage: ManifoldObjectFiniteFamily([A, B]).__repr__() 'Set {A, B} of objects of the 2-dimensional topological manifold M' """ @@ -174,35 +174,35 @@ def _latex_(self): TESTS:: - sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') - sage: FiniteManifoldSubsetFamily([B, A])._latex_() + sage: ManifoldSubsetFiniteFamily([B, A])._latex_() '\\{A, B\\}' """ return self._latex_name -class FiniteManifoldSubsetFamily(FiniteManifoldObjectFamily): +class ManifoldSubsetFiniteFamily(ManifoldObjectFiniteFamily): r""" Finite family of subsets of a topological manifold, indexed by their names. - The class :class:`FiniteManifoldSubsetFamily` inherits from - :class:`FiniteManifoldObjectFamily`. It provides an associative + The class :class:`ManifoldSubsetFiniteFamily` inherits from + :class:`ManifoldObjectFiniteFamily`. It provides an associative container with specialized ``__repr__`` and ``_latex_`` methods. - :class:`FiniteManifoldSubsetFamily` instances are totally ordered according + :class:`ManifoldSubsetFiniteFamily` instances are totally ordered according to their lexicographically ordered element (subset) names. EXAMPLES:: - sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') sage: C = B.subset('C') - sage: FiniteManifoldSubsetFamily([A, B, C]) + sage: ManifoldSubsetFiniteFamily([A, B, C]) Set {A, B, C} of subsets of the 2-dimensional topological manifold M sage: latex(_) \{A, B, C\} @@ -210,7 +210,7 @@ class FiniteManifoldSubsetFamily(FiniteManifoldObjectFamily): All subsets must have the same base manifold:: sage: N = Manifold(2, 'N', structure='topological') - sage: FiniteManifoldSubsetFamily([M, N]) + sage: ManifoldSubsetFiniteFamily([M, N]) Traceback (most recent call last): ... TypeError: all subsets must have the same manifold @@ -223,11 +223,11 @@ def _repr_object_type(self): TESTS:: - sage: from sage.manifolds.family import FiniteManifoldSubsetFamily + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily sage: M = Manifold(2, 'M', structure='topological') sage: A = M.subset('A') sage: B = M.subset('B') - sage: FiniteManifoldSubsetFamily([A, B]).__repr__() # indirect doctest + sage: ManifoldSubsetFiniteFamily([A, B]).__repr__() # indirect doctest 'Set {A, B} of subsets of the 2-dimensional topological manifold M' """ diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index e46a2b7556c..ddb8f835595 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -76,7 +76,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets -from sage.manifolds.family import FiniteManifoldSubsetFamily +from sage.manifolds.family import ManifoldSubsetFiniteFamily from sage.manifolds.point import ManifoldPoint class ManifoldSubset(UniqueRepresentation, Parent): @@ -669,8 +669,8 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): A digraph. Each vertex of the digraph is either: - - a :class:`FiniteManifoldSubsetFamily` containing one instance of :class:`ManifoldSubset`. - - (if ``open_covers`` is ``True``) a tuple of :class:`FiniteManifoldSubsetFamily` instances, + - a :class:`ManifoldSubsetFiniteFamily` containing one instance of :class:`ManifoldSubset`. + - (if ``open_covers`` is ``True``) a tuple of :class:`ManifoldSubsetFiniteFamily` instances, representing an open cover. EXAMPLES:: @@ -709,7 +709,7 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): D = DiGraph(multiedges=False, loops=loops) def vertex(subset): - return FiniteManifoldSubsetFamily([subset]) + return ManifoldSubsetFiniteFamily([subset]) if lower_bound is not None: if not lower_bound.is_subset(self): @@ -739,7 +739,7 @@ def vertex(subset): if open_covers: def open_cover_vertex(open_cover): - return tuple(sorted(FiniteManifoldSubsetFamily([subset]) for subset in open_cover)) + return tuple(sorted(ManifoldSubsetFiniteFamily([subset]) for subset in open_cover)) for S in visited: D.add_edges((vertex(S), open_cover_vertex(open_cover)) @@ -770,8 +770,8 @@ def subset_poset(self, open_covers=False, lower_bound=None): [Set {U} of subsets of the 3-dimensional differentiable manifold M, Set {V} of subsets of the 3-dimensional differentiable manifold M, Set {W} of subsets of the 3-dimensional differentiable manifold M] - sage: from sage.manifolds.subset import FiniteManifoldSubsetFamily - sage: sorted(P.lower_covers(FiniteManifoldSubsetFamily([M])), key=str) + sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily + sage: sorted(P.lower_covers(ManifoldSubsetFiniteFamily([M])), key=str) [Set {U} of subsets of the 3-dimensional differentiable manifold M, Set {V_union_W} of subsets of the 3-dimensional differentiable manifold M] sage: P.plot(element_labels={element: element._name for element in P}) # not tested @@ -781,8 +781,8 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P = M.subset_poset(open_covers=True); P Finite poset containing 6 elements - sage: from sage.manifolds.subset import FiniteManifoldSubsetFamily - sage: P.upper_covers(FiniteManifoldSubsetFamily([VW])) + sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily + sage: P.upper_covers(ManifoldSubsetFiniteFamily([VW])) [(Set {V} of subsets of the 3-dimensional differentiable manifold M, Set {W} of subsets of the 3-dimensional differentiable manifold M), Set {M} of subsets of the 3-dimensional differentiable manifold M] From 2f2ace2e9e9b0580d2b6d93eaa98d14bbc966f77 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 07:00:02 -0700 Subject: [PATCH 184/706] src/doc/en/reference/manifolds/manifold.rst: Add sage.manifolds.family --- src/doc/en/reference/manifolds/manifold.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst index f11d8135500..02afb2460b4 100644 --- a/src/doc/en/reference/manifolds/manifold.rst +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -21,3 +21,5 @@ Topological Manifolds sage/manifolds/topological_submanifold vector_bundle + + sage/manifolds/family From 1aff58a70849386432f336b5bba37e9545eed6eb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 07:04:12 -0700 Subject: [PATCH 185/706] Fix up docstring markup --- src/sage/manifolds/family.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index adbeb40dd1f..5b68aab7d61 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -1,10 +1,9 @@ r""" Families of Manifold Objects -The class :class:`ManifoldObjectFiniteFamily` is a subclass of :class`FiniteFamily` +The class :class:`ManifoldObjectFiniteFamily` is a subclass of :class:`FiniteFamily` that provides an associative container of manifold objects, indexed by their -``_name`` attributes. It provides specialized ``__repr__`` and -``_latex_`` methods. +``_name`` attributes. :class:`ManifoldObjectFiniteFamily` instances are totally ordered according to their lexicographically ordered element names. @@ -33,7 +32,7 @@ class ManifoldObjectFiniteFamily(FiniteFamily): Finite family of manifold objects, indexed by their names. The class :class:`ManifoldObjectFiniteFamily` inherits from - `:class:`FiniteFamily`. Therefore it is an associative container. + :class:`FiniteFamily`. Therefore it is an associative container. It provides specialized ``__repr__`` and ``_latex_`` methods. From b922066d150a4bdc6e6ee9d52924a7d49f039946 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 09:09:10 -0700 Subject: [PATCH 186/706] ManifoldSubsetFiniteFamily: If all subsets are open, include 'open' in repr --- src/sage/manifolds/family.py | 5 ++++- src/sage/manifolds/subset.py | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index 5b68aab7d61..c468fac8b24 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -230,4 +230,7 @@ def _repr_object_type(self): 'Set {A, B} of subsets of the 2-dimensional topological manifold M' """ - return "subsets" + if all(subset.is_open() for subset in self): + return "open subsets" + else: + return "subsets" diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index ddb8f835595..4332b8bca09 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -680,14 +680,14 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): sage: D = M.subset_digraph(); D Digraph on 4 vertices sage: D.edges(key=lambda e: (e[0]._name, e[1]._name)) - [(Set {U} of subsets of the 3-dimensional differentiable manifold M, - Set {M} of subsets of the 3-dimensional differentiable manifold M, + [(Set {U} of open subsets of the 3-dimensional differentiable manifold M, + Set {M} of open subsets of the 3-dimensional differentiable manifold M, None), - (Set {V} of subsets of the 3-dimensional differentiable manifold M, - Set {M} of subsets of the 3-dimensional differentiable manifold M, + (Set {V} of open subsets of the 3-dimensional differentiable manifold M, + Set {M} of open subsets of the 3-dimensional differentiable manifold M, None), - (Set {W} of subsets of the 3-dimensional differentiable manifold M, - Set {M} of subsets of the 3-dimensional differentiable manifold M, + (Set {W} of open subsets of the 3-dimensional differentiable manifold M, + Set {M} of open subsets of the 3-dimensional differentiable manifold M, None)] sage: D.plot(layout='acyclic') # not tested sage: def label(element): @@ -765,15 +765,15 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P = M.subset_poset(); P Finite poset containing 5 elements sage: P.maximal_elements() - [Set {M} of subsets of the 3-dimensional differentiable manifold M] + [Set {M} of open subsets of the 3-dimensional differentiable manifold M] sage: sorted(P.minimal_elements(), key=lambda v: v._name) - [Set {U} of subsets of the 3-dimensional differentiable manifold M, - Set {V} of subsets of the 3-dimensional differentiable manifold M, - Set {W} of subsets of the 3-dimensional differentiable manifold M] + [Set {U} of open subsets of the 3-dimensional differentiable manifold M, + Set {V} of open subsets of the 3-dimensional differentiable manifold M, + Set {W} of open subsets of the 3-dimensional differentiable manifold M] sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily sage: sorted(P.lower_covers(ManifoldSubsetFiniteFamily([M])), key=str) - [Set {U} of subsets of the 3-dimensional differentiable manifold M, - Set {V_union_W} of subsets of the 3-dimensional differentiable manifold M] + [Set {U} of open subsets of the 3-dimensional differentiable manifold M, + Set {V_union_W} of open subsets of the 3-dimensional differentiable manifold M] sage: P.plot(element_labels={element: element._name for element in P}) # not tested If ``open_covers`` is ``True``, the poset includes a special vertex for @@ -783,9 +783,9 @@ def subset_poset(self, open_covers=False, lower_bound=None): Finite poset containing 6 elements sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily sage: P.upper_covers(ManifoldSubsetFiniteFamily([VW])) - [(Set {V} of subsets of the 3-dimensional differentiable manifold M, - Set {W} of subsets of the 3-dimensional differentiable manifold M), - Set {M} of subsets of the 3-dimensional differentiable manifold M] + [(Set {V} of open subsets of the 3-dimensional differentiable manifold M, + Set {W} of open subsets of the 3-dimensional differentiable manifold M), + Set {M} of open subsets of the 3-dimensional differentiable manifold M] sage: def label(element): ....: try: ....: return element._name From 30271af51d78e4796f1be8b221088ceb2a71b2fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 09:55:25 -0700 Subject: [PATCH 187/706] Fixup doctest --- src/sage/manifolds/family.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index c468fac8b24..9637fa0b0a5 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -212,7 +212,7 @@ class ManifoldSubsetFiniteFamily(ManifoldObjectFiniteFamily): sage: ManifoldSubsetFiniteFamily([M, N]) Traceback (most recent call last): ... - TypeError: all subsets must have the same manifold + TypeError: all... subsets must have the same manifold """ From 78cc27a8e439c542c2f03958e8e0888b0551a979 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 10:02:21 -0700 Subject: [PATCH 188/706] ManifoldSubset.open_covers: Change to generator, add optional arg 'trivial'; update uses --- .../differentiable/diff_form_module.py | 8 ++- .../differentiable/examples/sphere.py | 16 +++--- .../differentiable/multivector_module.py | 9 ++-- .../manifolds/differentiable/tensorfield.py | 4 +- .../differentiable/tensorfield_module.py | 7 ++- .../differentiable/vectorfield_module.py | 8 ++- src/sage/manifolds/section.py | 4 +- src/sage/manifolds/section_module.py | 8 ++- src/sage/manifolds/subset.py | 51 +++++++++++-------- 9 files changed, 53 insertions(+), 62 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 31e6a3a450b..309dee3e733 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -384,16 +384,14 @@ def _an_element_(self): """ resu = self.element_class(self._vmodule, self._degree) - # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - if open_covers != []: - oc = open_covers[0] # the first non-trivial open cover is selected + for oc in self._domain.open_covers(trivial=False): + # the first non-trivial open cover is selected for dom in oc: vmodule_dom = dom.vector_field_module( dest_map=self._dest_map.restrict(dom)) dmodule_dom = vmodule_dom.dual_exterior_power(self._degree) resu.set_restriction(dmodule_dom._an_element_()) + return resu return resu def _coerce_map_from_(self, other): diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index 11d2b53f9c6..33064f02b8a 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -275,12 +275,9 @@ class Sphere(PseudoRiemannianSubmanifold): sage: stereoN, stereoS = S2.coordinate_charts('stereographic') sage: stereoN, stereoS (Chart (S^2-{NP}, (y1, y2)), Chart (S^2-{SP}, (yp1, yp2))) - sage: S2.open_covers() - [[2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3], - [Open subset S^2-{NP} of the 2-sphere S^2 of radius 1 smoothly - embedded in the Euclidean space E^3, - Open subset S^2-{SP} of the 2-sphere S^2 of radius 1 smoothly - embedded in the Euclidean space E^3]] + sage: list(S2.open_covers()) + [Set {S^2} of open subsets of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3, + Set {S^2-{NP}, S^2-{SP}} of open subsets of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3] .. NOTE:: @@ -501,10 +498,9 @@ def _init_chart_domains(self): TESTS:: sage: S2 = manifolds.Sphere(2) - sage: S2.open_covers() - [[2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3], - [Open subset S^2-{NP} of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3, - Open subset S^2-{SP} of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3]] + sage: list(S2.open_covers()) + [Set {S^2} of open subsets of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3, + Set {S^2-{NP}, S^2-{SP}} of open subsets of the 2-sphere S^2 of radius 1 smoothly embedded in the Euclidean space E^3] sage: frozenset(S2.subsets()) # random frozenset({Euclidean 2-sphere S^2 of radius 1, Open subset A of the Euclidean 2-sphere S^2 of radius 1, diff --git a/src/sage/manifolds/differentiable/multivector_module.py b/src/sage/manifolds/differentiable/multivector_module.py index c489db19e2b..6ab1972a844 100644 --- a/src/sage/manifolds/differentiable/multivector_module.py +++ b/src/sage/manifolds/differentiable/multivector_module.py @@ -344,17 +344,14 @@ def _an_element_(self): """ resu = self.element_class(self._vmodule, self._degree) - # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - if open_covers != []: - oc = open_covers[0] # the first non-trivial open cover is - # selected + for oc in self._domain.open_covers(trivial=False): + # the first non-trivial open cover is selected for dom in oc: vmodule_dom = dom.vector_field_module( dest_map=self._dest_map.restrict(dom)) dmodule_dom = vmodule_dom.exterior_power(self._degree) resu.set_restriction(dmodule_dom._an_element_()) + return resu return resu def _coerce_map_from_(self, other): diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 52dff82b202..f85fb944a74 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -2276,9 +2276,7 @@ def __eq__(self, other): if other._tensor_type != self._tensor_type: return False # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - for oc in open_covers: + for oc in self._domain.open_covers(trivial=False): resu = True for dom in oc: try: diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index ca22433e208..92c76de177e 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -403,14 +403,13 @@ def _an_element_(self): """ resu = self.element_class(self._vmodule, self._tensor_type) - # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 is trivial - if open_covers != []: - oc = open_covers[0] # the first non-trivial open cover is selected + for oc in self._domain.open_covers(trivial=False): + # the first non-trivial open cover is selected for dom in oc: vmodule_dom = dom.vector_field_module(dest_map=self._dest_map.restrict(dom)) tmodule_dom = vmodule_dom.tensor_module(*(self._tensor_type)) resu.set_restriction(tmodule_dom._an_element_()) + return resu return resu def _coerce_map_from_(self, other): diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 3fe82857b8e..71b157b99c7 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -306,15 +306,13 @@ def _an_element_(self): """ resu = self.element_class(self) - # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - if open_covers != []: - oc = open_covers[0] # the first non-trivial open cover is selected + for oc in self._domain.open_covers(trivial=False): + # the first non-trivial open cover is selected for dom in oc: vmodule_dom = dom.vector_field_module( dest_map=self._dest_map.restrict(dom)) resu.set_restriction(vmodule_dom._an_element_()) + return resu return resu def _coerce_map_from_(self, other): diff --git a/src/sage/manifolds/section.py b/src/sage/manifolds/section.py index 29ed3a88040..33044ab8408 100644 --- a/src/sage/manifolds/section.py +++ b/src/sage/manifolds/section.py @@ -1882,9 +1882,7 @@ def __eq__(self, other): if other._smodule != self._smodule: return False # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - for oc in open_covers: + for oc in self._domain.open_covers(trivial=False): resu = True for dom in oc: try: diff --git a/src/sage/manifolds/section_module.py b/src/sage/manifolds/section_module.py index cb30e5ce406..975113ca2c9 100644 --- a/src/sage/manifolds/section_module.py +++ b/src/sage/manifolds/section_module.py @@ -260,14 +260,12 @@ def _an_element_(self): """ resu = self.element_class(self) - # Non-trivial open covers of the domain: - open_covers = self._domain.open_covers()[1:] # the open cover 0 - # is trivial - if open_covers != []: - oc = open_covers[0] # the first non-trivial open cover is selected + for oc in self._domain.open_covers(trivial=False): + # the first non-trivial open cover is selected for dom in oc: smodule_dom = self._vbundle.section_module(domain=dom) resu.set_restriction(smodule_dom._an_element_()) + return resu return resu def _coerce_map_from_(self, other): diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index ba6b79e4f09..ad079fa4f93 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -526,52 +526,61 @@ def is_open(self): """ return False - def open_covers(self): + def open_covers(self, trivial=True): r""" - Return the list of open covers of the current subset. + Generate the open covers of the current subset. If the current subset, `A` say, is a subset of the manifold `M`, an - *open cover* of `A` is list (indexed set) `(U_i)_{i\in I}` of - open subsets of `M` such that + *open cover* of `A` is a :class:`ManifoldSubsetFiniteFamily` `F` + of open subsets `U \in F` of `M` such that .. MATH:: - A \subset \bigcup_{i \in I} U_i. + A \subset \bigcup_{U \in F} U. If `A` is open, we ask that the above inclusion is actually an identity: .. MATH:: - A = \bigcup_{i \in I} U_i. + A = \bigcup_{U \in F} U. + + INPUT: + + - ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial + open cover of ``self`` by itself EXAMPLES:: sage: M = Manifold(2, 'M', structure='topological') sage: M.open_covers() - [[2-dimensional topological manifold M]] + + sage: list(M.open_covers()) + [Set {M} of open subsets of the 2-dimensional topological manifold M] sage: U = M.open_subset('U') - sage: U.open_covers() - [[Open subset U of the 2-dimensional topological manifold M]] + sage: list(U.open_covers()) + [Set {U} of open subsets of the 2-dimensional topological manifold M] sage: A = U.open_subset('A') sage: B = U.open_subset('B') sage: U.declare_union(A,B) - sage: U.open_covers() - [[Open subset U of the 2-dimensional topological manifold M], - [Open subset A of the 2-dimensional topological manifold M, - Open subset B of the 2-dimensional topological manifold M]] + sage: list(U.open_covers()) + [Set {U} of open subsets of the 2-dimensional topological manifold M, + Set {A, B} of open subsets of the 2-dimensional topological manifold M] + sage: list(U.open_covers(trivial=False)) + [Set {A, B} of open subsets of the 2-dimensional topological manifold M] sage: V = M.open_subset('V') sage: M.declare_union(U,V) - sage: M.open_covers() - [[2-dimensional topological manifold M], - [Open subset U of the 2-dimensional topological manifold M, - Open subset V of the 2-dimensional topological manifold M], - [Open subset A of the 2-dimensional topological manifold M, - Open subset B of the 2-dimensional topological manifold M, - Open subset V of the 2-dimensional topological manifold M]] + sage: list(M.open_covers()) + [Set {M} of open subsets of the 2-dimensional topological manifold M, + Set {U, V} of open subsets of the 2-dimensional topological manifold M, + Set {A, B, V} of open subsets of the 2-dimensional topological manifold M] """ - return list(self._open_covers) + for oc in self._open_covers: + if not trivial: + if len(oc) == 1 and next(iter(oc)) is self: + continue + yield ManifoldSubsetFiniteFamily(oc) def subsets(self): r""" From e026e7a68095577277de1b32df0f8c446b594aa7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Apr 2021 10:33:32 -0700 Subject: [PATCH 189/706] ManifoldSubset.subset_digraph: Use open_covers method --- src/sage/manifolds/subset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index ad079fa4f93..82ed9d77001 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -745,8 +745,7 @@ def open_cover_vertex(open_cover): for S in visited: D.add_edges((vertex(S), open_cover_vertex(open_cover)) - for open_cover in S._open_covers - if open_cover != [S]) + for open_cover in S.open_covers(trivial=False)) return D From 57ee0af5f5f0c52dffe353c281352ccaa0b46649 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Fri, 23 Apr 2021 22:43:48 +0200 Subject: [PATCH 190/706] Trac #31669: correct morphism --- src/sage/categories/chain_complexes.py | 34 +++++++++++++++++++++----- src/sage/homology/chain_complex.py | 2 +- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 36081037701..54c2e93ab14 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -16,7 +16,6 @@ from .category_types import Category_module from .modules import Modules from .functor import Functor -from .morphism import SetMorphism from sage.misc.abstract_method import abstract_method ############################################################# @@ -181,7 +180,23 @@ class HomologyFunctor(Functor): Applying to a chain map:: - ... + sage: S = simplicial_complexes.Sphere(1); S + Minimal triangulation of the 1-sphere + sage: C = S.chain_complex() + sage: C.differential() + {0: [], 1: [-1 -1 0] + [ 1 0 -1] + [ 0 1 1], 2: []} + sage: f = {0:zero_matrix(ZZ,3,3),1:zero_matrix(ZZ,3,3)} + sage: G = Hom(C,C) + sage: x = G(f) + sage: H = HomologyFunctor(ChainComplexes(ZZ), 1) + sage: H(C) + Z + sage: H(x) + Generic morphism: + From: Z + To: Z """ def __init__(self, domain, n=None): @@ -224,9 +239,16 @@ def _apply_functor_to_morphism(self, f): ... """ - lift = self.domain.lift_from_homology - reduce = self.codomain.reduce_to_homology + from .morphism import SetMorphism + from .homset import Hom + from .commutative_additive_groups import CommutativeAdditiveGroups + + domain = f.domain() + codomain = f.codomain() + lift = domain.lift_from_homology + reduce = codomain.reduce_to_homology apply_f_star = lambda x: reduce(f(lift(x)), self.__n) - return SetMorphism(self.domain.homology(self.__n), - self.codomain.homology(self.__n), + return SetMorphism(Hom(domain.homology(self.__n), + codomain.homology(self.__n), + CommutativeAdditiveGroups()), apply_f_star) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 0563be63b59..1d628e55f8f 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Bounded chain complexes +Chain complexes This module implements bounded chain complexes of free `R`-modules, for any commutative ring `R` (although the interesting things, like From a475f2a79b4444b0e83cdb9f40b709af1d91b5be Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Fri, 23 Apr 2021 22:46:20 +0200 Subject: [PATCH 191/706] Trac #31669: make lift_from_homology entirely abstract --- src/sage/categories/chain_complexes.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 54c2e93ab14..5b9883f56f1 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -53,7 +53,7 @@ def super_categories(self): return [Modules(base_ring)] class ParentMethods: - @abstract_method(optional=True) + @abstract_method def homology(self, n=None): r""" Return the homology of the chain complex. @@ -114,6 +114,7 @@ def differential(self, *args, **kwargs): """ + @abstract_method(optional=True) def lift_from_homology(self, x): r""" Lift the homology element ``x`` to the corresponding module. @@ -123,12 +124,6 @@ def lift_from_homology(self, x): ... """ - try: - # is homology already a quotient? - x.lift() - except AttributeError: - # if not, this methods needs to be overwritten by parent - raise NotImplementedError def reduce_to_homology(self, x, n=None): r""" From 73e78e2bdd6c05957dfd808d2d4038e20bf43c00 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 24 Apr 2021 01:22:41 -0400 Subject: [PATCH 192/706] improve docstring of __richcmp__ --- src/sage/geometry/polyhedron/representation.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index c41a13caba9..755ccfb1724 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -103,8 +103,14 @@ def __richcmp__(self, other, op): """ Compare two representation objects - They are equal if and only if they define the same - vertex/ray/line or inequality/equation in the ambient space, + This method defines a linear order on the H/V-representation objects. + The order is first determined by the types of the objects, + such that inequality < equation < vertex < ray < line. + Then, representation objects with the same type are ordered + lexicographically according to their canonical vectors. + + Thus, two representation objects are equal if and only if they define + the same vertex/ray/line or inequality/equation in the ambient space, regardless of the polyhedron that they belong to. INPUT: From 215c3785383a48d545e0815c4f4ec42ba7d5ae4d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 12:37:04 -0700 Subject: [PATCH 193/706] ManifoldSubset.subset_{digraph,poset}: Add plots in documentation, remove # not tested --- src/sage/manifolds/subset.py | 66 ++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 4332b8bca09..f874d4c0e95 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -689,21 +689,47 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): (Set {W} of open subsets of the 3-dimensional differentiable manifold M, Set {M} of open subsets of the 3-dimensional differentiable manifold M, None)] - sage: D.plot(layout='acyclic') # not tested + sage: D.plot(layout='acyclic') + Graphics object consisting of 8 graphics primitives sage: def label(element): ....: try: ....: return element._name ....: except AttributeError: ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' - sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested + sage: D.relabel(label, inplace=False).plot(layout='acyclic') + Graphics object consisting of 8 graphics primitives sage: VW = V.union(W) sage: D = M.subset_digraph(); D Digraph on 5 vertices - sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested + sage: D.relabel(label, inplace=False).plot(layout='acyclic') + Graphics object consisting of 12 graphics primitives + + If ``open_covers`` is ``True``, the digraph includes a special vertex for + each nontrivial open cover of a subset:: sage: D = M.subset_digraph(open_covers=True) - sage: D.relabel(label, inplace=False).plot(layout='acyclic') # not tested + sage: D.relabel(label, inplace=False).plot(layout='acyclic') + Graphics object consisting of 14 graphics primitives + + .. PLOT:: + + def label(element): + try: + return element._name + except AttributeError: + return '[' + ', '.join(sorted(x._name for x in element)) + ']' + M = Manifold(3, 'M') + U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + D = M.subset_digraph() + g1 = D.relabel(label, inplace=False).plot(layout='acyclic') + VW = V.union(W) + D = M.subset_digraph() + g2 = D.relabel(label, inplace=False).plot(layout='acyclic') + D = M.subset_digraph(open_covers=True) + g3 = D.relabel(label, inplace=False).plot(layout='acyclic') + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + """ from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) @@ -761,6 +787,10 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: M = Manifold(3, 'M') sage: U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + sage: P = M.subset_poset(); P + Finite poset containing 4 elements + sage: P.plot(element_labels={element: element._name for element in P}) + Graphics object consisting of 8 graphics primitives sage: VW = V.union(W) sage: P = M.subset_poset(); P Finite poset containing 5 elements @@ -774,7 +804,8 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: sorted(P.lower_covers(ManifoldSubsetFiniteFamily([M])), key=str) [Set {U} of open subsets of the 3-dimensional differentiable manifold M, Set {V_union_W} of open subsets of the 3-dimensional differentiable manifold M] - sage: P.plot(element_labels={element: element._name for element in P}) # not tested + sage: P.plot(element_labels={element: element._name for element in P}) + Graphics object consisting of 10 graphics primitives If ``open_covers`` is ``True``, the poset includes a special vertex for each nontrivial open cover of a subset:: @@ -791,7 +822,27 @@ def subset_poset(self, open_covers=False, lower_bound=None): ....: return element._name ....: except AttributeError: ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' - sage: P.plot(element_labels={element: label(element) for element in P}) # not tested + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 12 graphics primitives + + .. PLOT:: + + def label(element): + try: + return element._name + except AttributeError: + return '[' + ', '.join(sorted(x._name for x in element)) + ']' + M = Manifold(3, 'M') + U = M.open_subset('U'); V = M.open_subset('V'); W = M.open_subset('W') + P = M.subset_poset() + g1 = P.plot(element_labels={element: label(element) for element in P}) + VW = V.union(W) + P = M.subset_poset() + g2 = P.plot(element_labels={element: label(element) for element in P}) + P = M.subset_poset(open_covers=True) + g3 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + """ from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) @@ -836,7 +887,8 @@ def superset_poset(self, open_covers=False, upper_bound=None): sage: VW = V.union(W) sage: P = V.superset_poset(); P Finite poset containing 3 elements - sage: P.plot(element_labels={element: element._name for element in P}) # not tested + sage: P.plot(element_labels={element: element._name for element in P}) + Graphics object consisting of 6 graphics primitives """ if upper_bound is None: From 84896c4d1ec02b22bed05d2f5dd188d71ee0767d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 12:43:51 -0700 Subject: [PATCH 194/706] ManifoldSubsetFiniteFamily: Use full error message in doctest --- src/sage/manifolds/family.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index 9637fa0b0a5..7c20fa4466f 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -212,7 +212,7 @@ class ManifoldSubsetFiniteFamily(ManifoldObjectFiniteFamily): sage: ManifoldSubsetFiniteFamily([M, N]) Traceback (most recent call last): ... - TypeError: all... subsets must have the same manifold + TypeError: all open subsets must have the same manifold """ From 287ec785acd6a520beae4f2f61523cc2e40ce9fa Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sat, 24 Apr 2021 20:41:47 -0400 Subject: [PATCH 195/706] implement common_lower_covers --- src/sage/combinat/posets/hasse_diagram.py | 21 +++++++++++++++++++++ src/sage/combinat/posets/posets.py | 15 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index abf5d226ef2..2a57daa792c 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -2360,6 +2360,27 @@ def common_upper_covers(self, vertices): covers = covers.intersection(self.neighbors_out(v)) return list(covers) + def common_lower_covers(self, vertices): + r""" + Return the list of all common lower covers of ``vertices``. + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: H.common_lower_covers([1, 2]) + [0] + + sage: from sage.combinat.posets.poset_examples import Posets + sage: H = Posets.YoungDiagramPoset(Partition([3, 2, 2]))._hasse_diagram + sage: H.common_lower_covers([4, 5]) + [3] + """ + covers = set(self.neighbors_in(vertices.pop())) + for v in vertices: + covers = covers.intersection(self.neighbors_in(v)) + return list(covers) + def _trivial_nonregular_congruence(self): """ Return a pair of elements giving "trivial" non-regular congruence. diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 7ff9e74c82d..3ecbf585242 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -50,6 +50,8 @@ :meth:`~FinitePoset.lower_covers_iterator` | Return an iterator over elements covered by given element. :meth:`~FinitePoset.upper_covers_iterator` | Return an iterator over elements covering given element. :meth:`~FinitePoset.cover_relations_iterator` | Return an iterator over cover relations of the poset. + :meth:`~FinitePoset.common_upper_covers` | Return the list of all common upper covers of the given elements. + :meth:`~FinitePoset.common_lower_covers` | Return the list of all common lower covers of the given elements. **Properties of the poset** @@ -2294,6 +2296,19 @@ def common_upper_covers(self, elmts): vertices = list(map(self._element_to_vertex, elmts)) return list(map(self._vertex_to_element, self._hasse_diagram.common_upper_covers(vertices))) + def common_lower_covers(self, elmts): + r""" + Return all of the common lower covers of the elements ``elmts``. + + EXAMPLES:: + + sage: P = Poset({0: [1,2], 1: [3], 2: [3], 3: []}) + sage: P.common_lower_covers([1, 2]) + [0] + """ + vertices = list(map(self._element_to_vertex, elmts)) + return list(map(self._vertex_to_element, self._hasse_diagram.common_lower_covers(vertices))) + def is_d_complete(self) -> bool: r""" Return ``True`` if a poset is d-complete and ``False`` otherwise. From a155f416e944ef7bc45e6e2751d2a0aaf07e5a8d Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 05:11:36 -0400 Subject: [PATCH 196/706] change HasseDiagram._meet/_join to allow non meet/join-semilattice --- src/sage/combinat/posets/hasse_diagram.py | 204 +++++++++++++--------- src/sage/combinat/posets/posets.py | 4 +- 2 files changed, 124 insertions(+), 84 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 2a57daa792c..8d869280be2 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -617,7 +617,7 @@ def _alternate_interval(self, x, y): sage: P = posets.BooleanLattice(3) sage: P.interval(1, 7) [1, 3, 5, 7] - sage: P._hasse_diagram._precompute_intervals() + sage: P._hasse_diagram._precompute_intervals() # indirect doctest sage: P.interval(1, 7) # Uses this function [1, 3, 5, 7] """ @@ -1420,38 +1420,43 @@ def add_elements(e): @lazy_attribute def _meet(self): r""" - Return the matrix of meets of ``self``. The ``(x,y)``-entry of - this matrix is the meet of ``x`` and ``y`` in ``self``. - - EXAMPLES:: + Return the matrix of meets of ``self``. - sage: from sage.combinat.posets.hasse_diagram import HasseDiagram - sage: H = HasseDiagram({0:[1,3,2],1:[4],2:[4,5,6],3:[6],4:[7],5:[7],6:[7],7:[]}) - sage: H._meet - [0 0 0 0 0 0 0 0] - [0 1 0 0 1 0 0 1] - [0 0 2 0 2 2 2 2] - [0 0 0 3 0 0 3 3] - [0 1 2 0 4 2 2 4] - [0 0 2 0 2 5 2 5] - [0 0 2 3 2 2 6 6] - [0 1 2 3 4 5 6 7] + The ``(x,y)``-entry of this matrix is the meet of ``x`` and + ``y`` in ``self`` if the meet exists; and `-1` otherwise. - TESTS:: + EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram + sage: H = HasseDiagram({0:[1,3,2],1:[4],2:[4,5,6],3:[6],4:[7],5:[7],6:[7],7:[]}) + sage: H._meet + [0 0 0 0 0 0 0 0] + [0 1 0 0 1 0 0 1] + [0 0 2 0 2 2 2 2] + [0 0 0 3 0 0 3 3] + [0 1 2 0 4 2 2 4] + [0 0 2 0 2 5 2 5] + [0 0 2 3 2 2 6 6] + [0 1 2 3 4 5 6 7] + sage: H = HasseDiagram({0:[2,3],1:[2,3]}) - sage: H.meet_matrix() - Traceback (most recent call last): - ... - ValueError: not a meet-semilattice: no bottom element + sage: H._meet + [ 0 -1 0 0] + [-1 1 1 1] + [ 0 1 2 -1] + [ 0 1 -1 3] sage: H = HasseDiagram({0:[1,2],1:[3,4],2:[3,4]}) - sage: H.meet_matrix() - Traceback (most recent call last): - ... - LatticeError: no meet for ... + sage: H._meet + [ 0 0 0 0 0] + [ 0 1 0 1 1] + [ 0 0 2 2 2] + [ 0 1 2 3 -1] + [ 0 1 2 -1 4] + + TESTS:: + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: L = LatticePoset({0:[1,2,3],1:[4],2:[4],3:[4]}) sage: P = L.dual() sage: P.meet(2,3) @@ -1460,20 +1465,21 @@ def _meet(self): n = self.cardinality() if n == 0: return matrix(0) - if not self.has_bottom(): - raise ValueError("not a meet-semilattice: no bottom element") - meet = [[0 for x in range(n)] for x in range(n)] + meet = [[-1 for x in range(n)] for x in range(n)] lc = [self.neighbors_in(x) for x in range(n)] # Lc = lower covers for x in range(n): meet[x][x] = x for y in range(x): - T = [meet[y][z] for z in lc[x]] - - q = max(T) - for z in T: - if meet[z][q] != z: - raise LatticeError('meet', x, y) + T = [meet[y][z] for z in lc[x] if meet[y][z] != -1] + if not T: + q = -1 + else: + q = max(T) + for z in T: + if meet[z][q] != z: + q = -1 + break meet[x][y] = q meet[y][x] = q @@ -1481,7 +1487,8 @@ def _meet(self): def meet_matrix(self): r""" - Return the matrix of meets of ``self``. + Return the matrix of meets of ``self``, when ``self`` is a + meet-semilattice; raise an error otherwise. The ``(x,y)``-entry of this matrix is the meet of ``x`` and ``y`` in ``self``. @@ -1491,8 +1498,9 @@ def meet_matrix(self): .. NOTE:: - Once the matrix has been computed, it is stored in - :meth:`_meet_matrix`. Delete this attribute if you want to + If ``self`` is a meet-semilattice, then the return of this method + is the same as :meth:`_meet`. Once the matrix has been computed, + it is stored in :meth:`_meet`. Delete this attribute if you want to recompute the matrix. EXAMPLES:: @@ -1532,6 +1540,14 @@ def meet_matrix(self): ... LatticeError: no meet for ... """ + n = self.cardinality() + if (n != 0) and (not self.has_bottom()): + raise ValueError("not a meet-semilattice: no bottom element") + n = self.cardinality() + for x in range(n): + for y in range(x): + if self._meet[x, y] == -1: + raise LatticeError('meet', x, y) return self._meet def is_meet_semilattice(self): @@ -1553,6 +1569,10 @@ def is_meet_semilattice(self): sage: H = HasseDiagram({0:[2,3],1:[2,3]}) sage: H.is_meet_semilattice() False + + sage: H = HasseDiagram({0:[1,2],1:[3,4],2:[3,4]}) + sage: H.is_meet_semilattice() + False """ try: self.meet_matrix() @@ -1565,13 +1585,13 @@ def is_meet_semilattice(self): def _join(self): r""" Computes a matrix whose ``(x,y)``-entry is the join of ``x`` - and ``y`` in ``self`` + and ``y`` in ``self`` if the join exists; and `-1` otherwise. EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[1,3,2],1:[4],2:[4,5,6],3:[6],4:[7],5:[7],6:[7],7:[]}) - sage: H.join_matrix() # indirect doctest + sage: H._join [0 1 2 3 4 5 6 7] [1 1 4 7 4 7 7 7] [2 4 2 6 4 5 6 7] @@ -1581,21 +1601,24 @@ def _join(self): [6 7 6 6 7 7 6 7] [7 7 7 7 7 7 7 7] - TESTS:: - - sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[2,3],1:[2,3]}) - sage: H.join_matrix() - Traceback (most recent call last): - ... - ValueError: not a join-semilattice: no top element + sage: H._join + [ 0 -1 2 3] + [-1 1 2 3] + [ 2 2 2 -1] + [ 3 3 -1 3] sage: H = HasseDiagram({0:[2,3],1:[2,3],2:[4],3:[4]}) - sage: H.join_matrix() - Traceback (most recent call last): - ... - LatticeError: no join for ... + sage: H._join + [ 0 -1 2 3 4] + [-1 1 2 3 4] + [ 2 2 2 4 4] + [ 3 3 4 3 4] + [ 4 4 4 4 4] + + TESTS:: + sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: L = LatticePoset({0:[1,2,3],1:[4],2:[4],3:[4]}) sage: P = L.dual() sage: P.join(2,3) @@ -1604,20 +1627,21 @@ def _join(self): n = self.cardinality() if n == 0: return matrix(0) - if not self.has_top(): - raise ValueError("not a join-semilattice: no top element") - join = [[n for x in range(n)] for x in range(n)] + join = [[-1 for x in range(n)] for x in range(n)] uc = [self.neighbors_out(x) for x in range(n)] # uc = upper covers for x in range(n - 1, -1, -1): join[x][x] = x for y in range(n - 1, x, -1): - T = [join[y][z] for z in uc[x]] - - q = min(T) - for z in T: - if join[z][q] != z: - raise LatticeError('join', x, y) + T = [join[y][z] for z in uc[x] if join[y][z] != -1] + if not T: + q = -1 + else: + q = min(T) + for z in T: + if join[z][q] != z: + q = -1 + break join[x][y] = q join[y][x] = q @@ -1625,16 +1649,20 @@ def _join(self): def join_matrix(self): r""" - Return the matrix of joins of ``self``. The ``(x,y)``-entry - of this matrix is the join of ``x`` and ``y`` in ``self``. + Return the matrix of joins of ``self``, when ``self`` is a + join-semilattice; raise an error otherwise. + + The ``(x,y)``-entry of this matrix is the join of ``x`` and + ``y`` in ``self``. This algorithm is modelled after the algorithm of Freese-Jezek-Nation (p217). It can also be found on page 140 of [Gec81]_. - .. note:: + .. NOTE:: - Once the matrix has been computed, it is stored in - :meth:`_join_matrix`. Delete this attribute if you want + If ``self`` is a join-semilattice, then the return of this method + is the same as :meth:`_join`. Once the matrix has been computed, + it is stored in :meth:`_join`. Delete this attribute if you want to recompute the matrix. EXAMPLES:: @@ -1666,6 +1694,13 @@ def join_matrix(self): ... LatticeError: no join for ... """ + n = self.cardinality() + if (n != 0) and (not self.has_top()): + raise ValueError("not a join-semilattice: no top element") + for x in range(n - 1, -1, -1): + for y in range(n - 1, x, -1): + if self._join[x, y] == -1: + raise LatticeError('join', x, y) return self._join def is_join_semilattice(self): @@ -1723,11 +1758,11 @@ def find_nonsemidistributive_elements(self, meet_or_join): True """ if meet_or_join == 'join': - M1 = self._join - M2 = self._meet + M1 = self.join_matrix() + M2 = self.meet_matrix() elif meet_or_join == 'meet': - M1 = self._meet - M2 = self._join + M1 = self.meet_matrix() + M2 = self.join_matrix() else: raise ValueError("meet_or_join must be 'join' or 'meet'") @@ -1857,11 +1892,12 @@ def pseudocomplement(self, element): True """ e = self.order() - 1 - while self._meet[e, element] != 0: + mt = self.meet_matrix() + while mt[e, element] != 0: e -= 1 e1 = e while e1 > 0: - if self._meet[e1, element] == 0 and not self.is_lequal(e1, e): + if mt[e1, element] == 0 and not self.is_lequal(e1, e): return None e1 -= 1 return e @@ -1986,10 +2022,12 @@ def orthocomplementations_iterator(self): orbit_number[e] = ind comps = [None] * n + mt = self.meet_matrix() + jn = self.join_matrix() for e in range(n): # Fix following after ticket #20727 comps[e] = [x for x in range(n) if - self._meet[e, x] == 0 and self._join[e, x] == n - 1 and + mt[e, x] == 0 and jn[e, x] == n - 1 and x in orbits[orbit_number[dual_isomorphism[e]]]] # Fitting is done by this recursive function: @@ -2455,6 +2493,8 @@ def sublattices_iterator(self, elms, min_e): {0} """ yield elms + mt = self.meet_matrix() + jn = self.join_matrix() for e in range(min_e, self.cardinality()): if e in elms: continue @@ -2467,8 +2507,8 @@ def sublattices_iterator(self, elms, min_e): if g in current_set: continue for x in current_set: - gens.add(self._meet[x, g]) - gens.add(self._join[x, g]) + gens.add(mt[x, g]) + gens.add(jn[x, g]) current_set.add(g) else: yield from self.sublattices_iterator(current_set, e + 1) @@ -2678,7 +2718,7 @@ def skeleton(self): raise ValueError("lattice is not pseudocomplemented") p_atoms.append(p_atom) n = len(p_atoms) - mt = self._meet + mt = self.meet_matrix() pos = [0] * n meets = [self.order() - 1] * n result = [self.order() - 1] @@ -2793,8 +2833,8 @@ def neutral_elements(self): notneutrals = set() all_elements = set(range(n)) - mt = self._meet - jn = self._join + mt = self.meet_matrix() + jn = self.join_matrix() def is_neutral(a): noncomp = all_elements.difference(self.depth_first_search(a)) @@ -3017,8 +3057,8 @@ def congruence(self, parts, start=None, stop_pairs=[]): from copy import copy n = self.order() - mt = self._meet - jn = self._join + mt = self.meet_matrix() + jn = self.join_matrix() def fill_to_interval(S): """ @@ -3068,7 +3108,7 @@ def fill_to_interval(S): for a in block: # Quadrilateral up for c in self.neighbor_out_iterator(a): if c not in block: - d = self._join[c, b] + d = jn[c, b] if cong.find(d) != cong.find(c): break else: @@ -3080,7 +3120,7 @@ def fill_to_interval(S): for b in reversed(block): # ...quadrilateral down for d in self.neighbor_in_iterator(b): if d not in block: - c = self._meet[d, a] + c = mt[d, a] if cong.find(c) != cong.find(d): break else: @@ -3109,10 +3149,10 @@ def fill_to_interval(S): if len(mins) > 1 or len(maxs) > 1: c = n - 1 for m in mins: - c = self._meet[c, m] + c = mt[c, m] d = 0 for m in maxs: - d = self._join[d, m] + d = jn[d, m] # This removes duplicates from todo. todo = set(cong.find(x) for x in todo) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 3ecbf585242..54ddde73913 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -4283,7 +4283,7 @@ def is_meet_semilattice(self, certificate=False): """ from sage.combinat.posets.hasse_diagram import LatticeError try: - self._hasse_diagram._meet + self._hasse_diagram.meet_matrix() except LatticeError as error: if not certificate: return False @@ -4354,7 +4354,7 @@ def is_join_semilattice(self, certificate=False): """ from sage.combinat.posets.hasse_diagram import LatticeError try: - self._hasse_diagram._join + self._hasse_diagram.join_matrix() except LatticeError as error: if not certificate: return False From 04f153f09518ea7ffca7ab55fa68bacf4143b7e1 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 07:16:16 -0400 Subject: [PATCH 197/706] change calls of _meet/_join to meet_matrix()/join_matrix() which include checks --- src/sage/combinat/posets/lattices.py | 24 +++++++++++++++--------- src/sage/combinat/posets/posets.py | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 2ef29209a27..9df3051a20c 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -300,12 +300,13 @@ def meet(self, x, y=None): - Dual function: :meth:`~sage.combinat.posets.lattices.FiniteJoinSemilattice.join` """ + mt = self._hasse_diagram.meet_matrix() if y is not None: # Handle basic case fast i, j = map(self._element_to_vertex, (x, y)) - return self._vertex_to_element(self._hasse_diagram._meet[i, j]) + return self._vertex_to_element(mt[i, j]) m = self.cardinality() - 1 # m = top element for i in (self._element_to_vertex(_) for _ in x): - m = self._hasse_diagram._meet[i, m] + m = mt[i, m] return self._vertex_to_element(m) def atoms(self): @@ -625,12 +626,13 @@ def join(self, x, y=None): - Dual function: :meth:`~sage.combinat.posets.lattices.FiniteMeetSemilattice.meet` """ + jn = self._hasse_diagram.join_matrix() if y is not None: # Handle basic case fast i, j = map(self._element_to_vertex, (x, y)) - return self._vertex_to_element(self._hasse_diagram._join[i, j]) + return self._vertex_to_element(jn[i, j]) j = 0 # j = bottom element for i in (self._element_to_vertex(_) for _ in x): - j = self._hasse_diagram._join[i, j] + j = jn[i, j] return self._vertex_to_element(j) def coatoms(self): @@ -1689,7 +1691,7 @@ def is_cosectionally_complemented(self, certificate=False): return False H = self._hasse_diagram - jn = H._join + jn = H.join_matrix() n = H.order() for e in range(n-2, -1, -1): t = 0 @@ -1879,7 +1881,7 @@ def is_sectionally_complemented(self, certificate=False): return False H = self._hasse_diagram - mt = H._meet + mt = H.meet_matrix() n = H.order()-1 for e in range(2, n+1): t = n @@ -1968,7 +1970,7 @@ def breadth(self, certificate=False): H = self._hasse_diagram # Helper function: Join of elements in the list L. - jn = H._join + jn = H.join_matrix() def join(L): j = 0 @@ -2904,6 +2906,8 @@ def is_supersolvable(self, certificate=False): return True H = self._hasse_diagram + mt = H.meet_matrix() + jn = H.join_matrix() height = self.height() n = H.order() cur = H.maximal_elements()[0] @@ -2913,7 +2917,7 @@ def is_supersolvable(self, certificate=False): @cached_function def is_modular_elt(a): return all(H._rank[a] + H._rank[b] == - H._rank[H._meet[a, b]] + H._rank[H._join[a, b]] + H._rank[mt[a, b]] + H._rank[jn[a, b]] for b in range(n)) if not is_modular_elt(cur): @@ -3402,10 +3406,12 @@ def isomorphic_sublattices_iterator(self, other): if not isinstance(other, FiniteLatticePoset): raise TypeError('the input is not a finite lattice') H = self._hasse_diagram + mt = H.meet_matrix() + jn = H.join_matrix() self_closure = H.transitive_closure() other_closure = other._hasse_diagram.transitive_closure() for g in self_closure.subgraph_search_iterator(other_closure, induced=True): - if all(H._meet[a, b] in g and H._join[a, b] in g for a, b in combinations(g, 2)): + if all(mt[a, b] in g and jn[a, b] in g for a, b in combinations(g, 2)): yield self.sublattice([self._vertex_to_element(v) for v in g]) def maximal_sublattices(self): diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 54ddde73913..6d9ec9494b2 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -52,6 +52,8 @@ :meth:`~FinitePoset.cover_relations_iterator` | Return an iterator over cover relations of the poset. :meth:`~FinitePoset.common_upper_covers` | Return the list of all common upper covers of the given elements. :meth:`~FinitePoset.common_lower_covers` | Return the list of all common lower covers of the given elements. + :meth:`~FinitePoset.meet` | Return the meet of given elements if it exists; ``None`` otherwise. + :meth:`~FinitePoset.join` | Return the join of given elements if it exists; ``None`` otherwise. **Properties of the poset** From 2a32c9dbb59f1bd07fa6b321368fe6339b44cdef Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 07:17:47 -0400 Subject: [PATCH 198/706] implement meet/join methods for posets that are not meet/join-semilattices --- src/sage/combinat/posets/posets.py | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 6d9ec9494b2..912b8cd0173 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -2311,6 +2311,74 @@ def common_lower_covers(self, elmts): vertices = list(map(self._element_to_vertex, elmts)) return list(map(self._vertex_to_element, self._hasse_diagram.common_lower_covers(vertices))) + def meet(self, x, y): + r""" + Return the meet of two elements ``x, y`` in the poset if the meet + exists; and ``None`` otherwise. + + EXAMPLES:: + + sage: D = Poset({1:[2,3], 2:[4], 3:[4]}) + sage: D.meet(2, 3) + 1 + sage: P = Poset({'a':['b', 'c'], 'b':['e', 'f'], 'c':['f', 'g'], + ....: 'd':['f', 'g']}) + sage: P.meet('a', 'b') + 'a' + sage: P.meet('e', 'a') + 'a' + sage: P.meet('c', 'b') + 'a' + sage: P.meet('e', 'f') + 'b' + sage: P.meet('e', 'g') + 'a' + sage: P.meet('c', 'd') is None + True + sage: P.meet('g', 'f') is None + True + """ + i, j = map(self._element_to_vertex, (x, y)) + mt = self._hasse_diagram._meet + if mt[i, j] == -1: + return None + else: + return self._vertex_to_element(mt[i, j]) + + def join(self, x, y): + r""" + Return the join of two elements ``x, y`` in the poset if the join + exists; and ``None`` otherwise. + + EXAMPLES:: + + sage: D = Poset({1:[2,3], 2:[4], 3:[4]}) + sage: D.join(2, 3) + 4 + sage: P = Poset({'e':['b'], 'f':['b', 'c', 'd'], 'g':['c', 'd'], + ....: 'b':['a'], 'c':['a']}) + sage: P.join('a', 'b') + 'a' + sage: P.join('e', 'a') + 'a' + sage: P.join('c', 'b') + 'a' + sage: P.join('e', 'f') + 'b' + sage: P.join('e', 'g') + 'a' + sage: P.join('c', 'd') is None + True + sage: P.join('g', 'f') is None + True + """ + i, j = map(self._element_to_vertex, (x, y)) + jn = self._hasse_diagram._join + if jn[i,j] == -1: + return None + else: + return self._vertex_to_element(jn[i,j]) + def is_d_complete(self) -> bool: r""" Return ``True`` if a poset is d-complete and ``False`` otherwise. From e9a42fd01c05d4e892aa08ceadb26095aaf77f78 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 07:20:24 -0400 Subject: [PATCH 199/706] change doctests regarding LatticePoset.is_sublattice(MeetSemilattice or JoinSemilattice) --- src/sage/combinat/posets/lattices.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 9df3051a20c..38486d359d9 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -3239,14 +3239,10 @@ def is_sublattice(self, other): sage: P = MeetSemilattice({0: [1]}) sage: E.is_sublattice(P) - Traceback (most recent call last): - ... - TypeError: other is not a lattice + True sage: P = JoinSemilattice({0: [1]}) sage: E.is_sublattice(P) - Traceback (most recent call last): - ... - TypeError: other is not a lattice + True """ try: o_meet = other.meet From fa07859f935f00630bdf5890e2e01a482a8e6cd4 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 07:58:19 -0400 Subject: [PATCH 200/706] add doctest allowing LatticePoset.is_sublattice(FinitePoset) --- src/sage/combinat/posets/lattices.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 38486d359d9..024ae3c8644 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -3243,6 +3243,9 @@ def is_sublattice(self, other): sage: P = JoinSemilattice({0: [1]}) sage: E.is_sublattice(P) True + sage: P = Poset({0: [1]}) + sage: E.is_sublattice(P) + True """ try: o_meet = other.meet From 52a93f94de48c0fb7070f95b170cd218d7ddfc15 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Apr 2021 12:03:27 -0700 Subject: [PATCH 201/706] ManifoldSubset.subset_family: New; deprecate .list_of_subsets --- src/sage/manifolds/chart.py | 14 +- src/sage/manifolds/differentiable/chart.py | 14 +- .../differentiable/examples/real_line.py | 10 +- src/sage/manifolds/differentiable/manifold.py | 19 +-- src/sage/manifolds/manifold.py | 14 +- src/sage/manifolds/subset.py | 128 +++++++++++------- 6 files changed, 101 insertions(+), 98 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 5a2b7070867..bcd95f4bf1d 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -932,12 +932,9 @@ def transition_map(self, other, transformations, intersection_name=None, The subset `W`, intersection of `U` and `V`, has been created by ``transition_map()``:: - sage: M.list_of_subsets() - [1-dimensional topological manifold S^1, - Open subset U of the 1-dimensional topological manifold S^1, - Open subset V of the 1-dimensional topological manifold S^1, - Open subset W of the 1-dimensional topological manifold S^1] - sage: W = M.list_of_subsets()[3] + sage: F = M.subset_family(); F + Set {S^1, U, V, W} of open subsets of the 1-dimensional topological manifold S^1 + sage: W = F['W'] sage: W is U.intersection(V) True sage: M.atlas() @@ -960,9 +957,8 @@ def transition_map(self, other, transformations, intersection_name=None, In this case, no new subset has been created since `U \cap M = U`:: - sage: M.list_of_subsets() - [2-dimensional topological manifold R^2, - Open subset U of the 2-dimensional topological manifold R^2] + sage: M.subset_family() + Set {R^2, U} of open subsets of the 2-dimensional topological manifold R^2 but a new chart has been created: `(U, (x, y))`:: diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 396792d3792..9deb59613fe 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -357,12 +357,9 @@ def transition_map(self, other, transformations, intersection_name=None, The subset `W`, intersection of `U` and `V`, has been created by ``transition_map()``:: - sage: M.list_of_subsets() - [1-dimensional differentiable manifold S^1, - Open subset U of the 1-dimensional differentiable manifold S^1, - Open subset V of the 1-dimensional differentiable manifold S^1, - Open subset W of the 1-dimensional differentiable manifold S^1] - sage: W = M.list_of_subsets()[3] + sage: F = M.subset_family(); F + Set {S^1, U, V, W} of open subsets of the 1-dimensional differentiable manifold S^1 + sage: W = F['W'] sage: W is U.intersection(V) True sage: M.atlas() @@ -385,9 +382,8 @@ def transition_map(self, other, transformations, intersection_name=None, In this case, no new subset has been created since `U\cap M = U`:: - sage: M.list_of_subsets() - [2-dimensional differentiable manifold R^2, - Open subset U of the 2-dimensional differentiable manifold R^2] + sage: M.subset_family() + Set {R^2, U} of open subsets of the 2-dimensional differentiable manifold R^2 but a new chart has been created: `(U, (x, y))`:: diff --git a/src/sage/manifolds/differentiable/examples/real_line.py b/src/sage/manifolds/differentiable/examples/real_line.py index 6582fa96f41..2e18868db7c 100644 --- a/src/sage/manifolds/differentiable/examples/real_line.py +++ b/src/sage/manifolds/differentiable/examples/real_line.py @@ -229,11 +229,11 @@ class OpenInterval(DifferentiableManifold): We have:: - sage: I.list_of_subsets() + sage: list(I.subset_family()) [Real interval (0, 1), Real interval (0, pi), Real interval (1/2, 1)] - sage: J.list_of_subsets() + sage: list(J.subset_family()) [Real interval (0, 1), Real interval (1/2, 1)] - sage: K.list_of_subsets() + sage: list(K.subset_family()) [Real interval (1/2, 1)] As any open subset of a manifold, open subintervals are created in a @@ -684,7 +684,7 @@ def open_interval(self, lower, upper, name=None, latex_name=None): Real interval (0, pi) sage: J.is_subset(I) True - sage: I.list_of_subsets() + sage: list(I.subset_family()) [Real interval (-4, 4), Real interval (0, pi)] ``J`` is considered as an open submanifold of ``I``:: @@ -863,7 +863,7 @@ class RealLine(OpenInterval): Real interval (0, 1) sage: I.manifold() Real number line R - sage: R.list_of_subsets() + sage: list(R.subset_family()) [Real interval (0, 1), Real number line R] """ diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 61d94a414f8..517ecd2a8b9 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -125,11 +125,8 @@ At this stage, we have four open subsets on `S^2`:: - sage: M.list_of_subsets() - [2-dimensional differentiable manifold S^2, - Open subset U of the 2-dimensional differentiable manifold S^2, - Open subset V of the 2-dimensional differentiable manifold S^2, - Open subset W of the 2-dimensional differentiable manifold S^2] + sage: M.subset_family() + Set {S^2, U, V, W} of open subsets of the 2-dimensional differentiable manifold S^2 `W` is the open subset that is the complement of the two poles:: @@ -350,11 +347,8 @@ The following subsets and charts have been defined:: - sage: M.list_of_subsets() - [Open subset A of the 1-dimensional complex manifold C*, - 1-dimensional complex manifold C*, - Open subset U of the 1-dimensional complex manifold C*, - Open subset V of the 1-dimensional complex manifold C*] + sage: M.subset_family() + Set {A, C*, U, V} of open subsets of the 1-dimensional complex manifold C* sage: M.atlas() [Chart (U, (z,)), Chart (V, (w,)), Chart (A, (z,)), Chart (A, (w,))] @@ -773,9 +767,8 @@ def open_subset(self, name, latex_name=None, coord_def={}): We have then:: - sage: A.list_of_subsets() - [Open subset A of the 2-dimensional differentiable manifold M, - Open subset B of the 2-dimensional differentiable manifold M] + sage: A.subset_family() + Set {A, B} of open subsets of the 2-dimensional differentiable manifold M sage: B.is_subset(A) True sage: B.is_subset(M) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 47eb818e470..af1ae6b7055 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -117,11 +117,8 @@ At this stage, we have four open subsets on `S^2`:: - sage: M.list_of_subsets() - [2-dimensional topological manifold S^2, - Open subset U of the 2-dimensional topological manifold S^2, - Open subset V of the 2-dimensional topological manifold S^2, - Open subset W of the 2-dimensional topological manifold S^2] + sage: M.subset_family() + Set {S^2, U, V, W} of open subsets of the 2-dimensional topological manifold S^2 `W` is the open subset that is the complement of the two poles:: @@ -269,11 +266,8 @@ The following subsets and charts have been defined:: - sage: M.list_of_subsets() - [Open subset A of the Complex 1-dimensional topological manifold C*, - Complex 1-dimensional topological manifold C*, - Open subset U of the Complex 1-dimensional topological manifold C*, - Open subset V of the Complex 1-dimensional topological manifold C*] + sage: M.subset_family() + Set {A, C*, U, V} of open subsets of the Complex 1-dimensional topological manifold C* sage: M.atlas() [Chart (U, (z,)), Chart (V, (w,)), Chart (A, (z,)), Chart (A, (w,))] diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 82ed9d77001..e0ef44ee542 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -26,10 +26,8 @@ Subset A of the 2-dimensional topological manifold M sage: b = M.subset('B'); b Subset B of the 2-dimensional topological manifold M - sage: M.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M, - 2-dimensional topological manifold M] + sage: M.subset_family() + Set {A, B, M} of subsets of the 2-dimensional topological manifold M The intersection of the two subsets:: @@ -41,24 +39,16 @@ sage: d = a.union(b); d Subset A_union_B of the 2-dimensional topological manifold M -Lists of subsets after the above operations:: - - sage: M.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset A_inter_B of the 2-dimensional topological manifold M, - Subset A_union_B of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M, - 2-dimensional topological manifold M] - sage: a.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset A_inter_B of the 2-dimensional topological manifold M] - sage: c.list_of_subsets() - [Subset A_inter_B of the 2-dimensional topological manifold M] - sage: d.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset A_inter_B of the 2-dimensional topological manifold M, - Subset A_union_B of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M] +Families of subsets after the above operations:: + + sage: M.subset_family() + Set {A, A_inter_B, A_union_B, B, M} of subsets of the 2-dimensional topological manifold M + sage: a.subset_family() + Set {A, A_inter_B} of subsets of the 2-dimensional topological manifold M + sage: c.subset_family() + Set {A_inter_B} of subsets of the 2-dimensional topological manifold M + sage: d.subset_family() + Set {A, A_inter_B, A_union_B, B} of subsets of the 2-dimensional topological manifold M """ #***************************************************************************** @@ -75,6 +65,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation +from sage.misc.superseded import deprecation from sage.categories.sets_cat import Sets from sage.manifolds.family import ManifoldSubsetFiniteFamily from sage.manifolds.point import ManifoldPoint @@ -123,10 +114,8 @@ class :class:`~sage.structure.parent.Parent`. sage: B = M.subset('B', latex_name=r'\mathcal{B}'); B Subset B of the 2-dimensional topological manifold M - sage: M.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M, - 2-dimensional topological manifold M] + sage: M.subset_family() + Set {A, B, M} of subsets of the 2-dimensional topological manifold M The manifold is itself a subset:: @@ -588,8 +577,8 @@ def subsets(self): .. NOTE:: - To get the subsets as a list, use the method - :meth:`list_of_subsets` instead. + To get the subsets as a family, sorted by name, use the method + :meth:`subset_family` instead. EXAMPLES: @@ -605,13 +594,11 @@ def subsets(self): sage: U in M.subsets() True - The method :meth:`list_of_subsets` returns a list (sorted + The method :meth:`subset_family` returns a family (sorted alphabetically by the subset names):: - sage: M.list_of_subsets() - [2-dimensional topological manifold M, - Open subset U of the 2-dimensional topological manifold M, - Subset V of the 2-dimensional topological manifold M] + sage: M.subset_family() + Set {M, U, V} of subsets of the 2-dimensional topological manifold M """ yield from self._subsets @@ -630,23 +617,36 @@ def list_of_subsets(self): .. NOTE:: - To get the subsets as a Python set, use the method - :meth:`subsets` instead. + This method is deprecated. + + To get the subsets as a :class:`ManifoldSubsetFiniteFamily` + instance (which sorts its elements alphabetically by name), + use :meth:`subset_family` instead. + + To loop over the subsets in an arbitrary order, use the + generator method :meth:`subsets` instead. EXAMPLES: - Subsets of a 2-dimensional manifold:: + List of subsets of a 2-dimensional manifold (deprecated):: sage: M = Manifold(2, 'M', structure='topological') sage: U = M.open_subset('U') sage: V = M.subset('V') sage: M.list_of_subsets() + doctest:...: DeprecationWarning: the method list_of_subsets of ManifoldSubset + is deprecated; use subset_family or subsets instead... [2-dimensional topological manifold M, Open subset U of the 2-dimensional topological manifold M, Subset V of the 2-dimensional topological manifold M] - The method :meth:`subsets` generates the subsets. To create - a set:: + Using :meth:`subset_family` instead (recommended when order matters):: + + sage: M.subset_family() + Set {M, U, V} of subsets of the 2-dimensional topological manifold M + + The method :meth:`subsets` generates the subsets in an unspecified order. + To create a set:: sage: frozenset(M.subsets()) # random (set output) {Subset V of the 2-dimensional topological manifold M, @@ -654,8 +654,38 @@ def list_of_subsets(self): Open subset U of the 2-dimensional topological manifold M} """ + deprecation(31727, "the method list_of_subsets of ManifoldSubset is deprecated; use subset_family or subsets instead") return sorted(self._subsets, key=lambda x: x._name) + def subset_family(self): + r""" + Return the family of subsets that have been defined on the current subset. + + The family is sorted by the alphabetical names of the subsets. + + OUTPUT: + + - a :class:`ManifoldSubsetFiniteFamily` instance containing all the + subsets that have been defined on the current subset + + .. NOTE:: + + If you only need to iterate over the subsets in arbitrary order, + you can use the generator method :meth:`subsets` instead. + + EXAMPLES: + + Subsets of a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.subset('V') + sage: M.subset_family() + Set {M, U, V} of subsets of the 2-dimensional topological manifold M + + """ + return ManifoldSubsetFiniteFamily(self.subsets()) + def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): r""" Return the digraph whose arcs represent subset relations among the subsets of ``self``. @@ -869,11 +899,8 @@ def get_subset(self, name): sage: A = M.subset('A') sage: B = A.subset('B') sage: U = M.open_subset('U') - sage: M.list_of_subsets() - [Subset A of the 4-dimensional topological manifold M, - Subset B of the 4-dimensional topological manifold M, - 4-dimensional topological manifold M, - Open subset U of the 4-dimensional topological manifold M] + sage: M.subset_family() + Set {A, B, M, U} of subsets of the 4-dimensional topological manifold M sage: M.get_subset('A') Subset A of the 4-dimensional topological manifold M sage: M.get_subset('A') is A @@ -1098,9 +1125,8 @@ def superset(self, name, latex_name=None, is_open=False): sage: a = M.subset('A') sage: b = a.superset('B'); b Subset B of the 2-dimensional topological manifold M - sage: b.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M] + sage: b.subset_family() + Set {A, B} of subsets of the 2-dimensional topological manifold M sage: a._supersets # random (set output) {Subset B of the 2-dimensional topological manifold M, Subset A of the 2-dimensional topological manifold M, @@ -1162,12 +1188,10 @@ def intersection(self, other, name=None, latex_name=None): sage: b = M.subset('B') sage: c = a.intersection(b); c Subset A_inter_B of the 2-dimensional topological manifold M - sage: a.list_of_subsets() - [Subset A of the 2-dimensional topological manifold M, - Subset A_inter_B of the 2-dimensional topological manifold M] - sage: b.list_of_subsets() - [Subset A_inter_B of the 2-dimensional topological manifold M, - Subset B of the 2-dimensional topological manifold M] + sage: a.subset_family() + Set {A, A_inter_B} of subsets of the 2-dimensional topological manifold M + sage: b.subset_family() + Set {A_inter_B, B} of subsets of the 2-dimensional topological manifold M sage: c._supersets # random (set output) {Subset B of the 2-dimensional topological manifold M, Subset A_inter_B of the 2-dimensional topological manifold M, From a19697ff97092b9bd17bd4e01920a6cfa90b8d95 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Sun, 25 Apr 2021 23:43:10 -0400 Subject: [PATCH 202/706] new attributes HasseDiagram._meet/join_semilattice_failure --- src/sage/combinat/posets/hasse_diagram.py | 30 ++++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 8d869280be2..4b3b0edbb45 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -1462,6 +1462,7 @@ def _meet(self): sage: P.meet(2,3) 4 """ + self._meet_semilattice_failure = () n = self.cardinality() if n == 0: return matrix(0) @@ -1482,7 +1483,8 @@ def _meet(self): break meet[x][y] = q meet[y][x] = q - + if q == -1: + self._meet_semilattice_failure += ((x, y),) return matrix(ZZ, meet) def meet_matrix(self): @@ -1543,12 +1545,12 @@ def meet_matrix(self): n = self.cardinality() if (n != 0) and (not self.has_bottom()): raise ValueError("not a meet-semilattice: no bottom element") - n = self.cardinality() - for x in range(n): - for y in range(x): - if self._meet[x, y] == -1: - raise LatticeError('meet', x, y) - return self._meet + # call the attribute to build the matrix and _meet_semilattice_failure + mt = self._meet + if self._meet_semilattice_failure: + x, y = self._meet_semilattice_failure[0] + raise LatticeError('meet', x, y) + return mt def is_meet_semilattice(self): r""" @@ -1624,6 +1626,7 @@ def _join(self): sage: P.join(2,3) 0 """ + self._join_semilattice_failure = () n = self.cardinality() if n == 0: return matrix(0) @@ -1644,6 +1647,8 @@ def _join(self): break join[x][y] = q join[y][x] = q + if q == -1: + self._join_semilattice_failure += ((x, y),) return matrix(ZZ, join) @@ -1697,11 +1702,12 @@ def join_matrix(self): n = self.cardinality() if (n != 0) and (not self.has_top()): raise ValueError("not a join-semilattice: no top element") - for x in range(n - 1, -1, -1): - for y in range(n - 1, x, -1): - if self._join[x, y] == -1: - raise LatticeError('join', x, y) - return self._join + # call the attribute to build the matrix and _join_semilattice_failure + jn = self._join + if self._join_semilattice_failure: + x, y = self._join_semilattice_failure[0] + raise LatticeError('join', x, y) + return jn def is_join_semilattice(self): r""" From 83a9f47566a1025f02c1c286eb3c03046432f0d8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Apr 2021 12:43:59 -0700 Subject: [PATCH 203/706] ManifoldSubset.open_cover_family: New --- src/sage/manifolds/subset.py | 64 +++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 82ed9d77001..f198448f8af 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -76,7 +76,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets -from sage.manifolds.family import ManifoldSubsetFiniteFamily +from sage.manifolds.family import ManifoldObjectFiniteFamily, ManifoldSubsetFiniteFamily from sage.manifolds.point import ManifoldPoint class ManifoldSubset(UniqueRepresentation, Parent): @@ -545,6 +545,12 @@ def open_covers(self, trivial=True): A = \bigcup_{U \in F} U. + .. NOTE:: + + To get the open covers as a family, sorted lexicographically by the + names of the subsets forming the open covers, use the method + :meth:`open_cover_family` instead. + INPUT: - ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial @@ -582,6 +588,62 @@ def open_covers(self, trivial=True): continue yield ManifoldSubsetFiniteFamily(oc) + def open_cover_family(self, trivial=True): + r""" + Return the family of open covers of the current subset. + + If the current subset, `A` say, is a subset of the manifold `M`, an + *open cover* of `A` is a :class:`ManifoldSubsetFiniteFamily` `F` + of open subsets `U \in F` of `M` such that + + .. MATH:: + + A \subset \bigcup_{U \in F} U. + + If `A` is open, we ask that the above inclusion is actually an + identity: + + .. MATH:: + + A = \bigcup_{U \in F} U. + + The family is sorted lexicographically by the names of the subsets + forming the open covers. + + .. NOTE:: + + If you only need to iterate over the open covers in arbitrary + order, you can use the generator method :meth:`open_covers` + instead. + + INPUT: + + - ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial + open cover of ``self`` by itself + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.open_cover_family() + Set {{M}} of objects of the 2-dimensional topological manifold M + sage: U = M.open_subset('U') + sage: U.open_cover_family() + Set {{U}} of objects of the 2-dimensional topological manifold M + sage: A = U.open_subset('A') + sage: B = U.open_subset('B') + sage: U.declare_union(A,B) + sage: U.open_cover_family() + Set {{A, B}, {U}} of objects of the 2-dimensional topological manifold M + sage: U.open_cover_family(trivial=False) + Set {{A, B}} of objects of the 2-dimensional topological manifold M + sage: V = M.open_subset('V') + sage: M.declare_union(U,V) + sage: M.open_cover_family() + Set {{A, B, V}, {M}, {U, V}} of objects of the 2-dimensional topological manifold M + + """ + return ManifoldObjectFiniteFamily(self.open_covers(trivial=trivial)) + def subsets(self): r""" Generate the subsets that have been defined on the current subset. From a18357584a8f36b9db51060e3a6577a424cc7535 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 12:09:21 -0700 Subject: [PATCH 204/706] ManifoldSubset.{declare_empty,declare_nonempty,is_empty,has_defined_points}: New --- src/sage/manifolds/point.py | 3 ++ src/sage/manifolds/subset.py | 84 ++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index e01fbbb99fa..152703075c5 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -182,7 +182,10 @@ def __init__(self, parent, coords=None, chart=None, name=None, sage: TestSuite(q).run() """ + if parent.is_empty(): + raise TypeError(f'cannot define a point on the {parent} because it has been declared empty') Element.__init__(self, parent) + parent._has_defined_points = True self._manifold = parent.manifold() # a useful shortcut self._coordinates = {} # dictionary of the point coordinates in various # charts, with the charts as keys diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f198448f8af..b8c348f0243 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -208,6 +208,7 @@ def __init__(self, manifold, name, latex_name=None, category=None): self._open_covers = [] # list of open covers of self self._is_open = False # a priori (may be redefined by subclasses) self._manifold = manifold # the ambient manifold + self._has_defined_points = False def _repr_(self): r""" @@ -1031,6 +1032,89 @@ def declare_union(self, dom1, dom2): oc.append(s) self._open_covers.append(oc) + def declare_empty(self): + r""" + Declare that ``self`` is the empty set. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A', is_open=True) + sage: AA = A.subset('AA') + sage: A + Open subset A of the 2-dimensional topological manifold M + sage: A.declare_empty() + sage: A.is_empty() + True + + Empty sets do not allow to define points on them:: + + sage: A.point() + Traceback (most recent call last): + ... + TypeError: cannot define a point on the + Open subset A of the 2-dimensional topological manifold M + because it has been declared empty + + Emptiness transfers to subsets:: + + sage: AA.is_empty() + True + sage: AA.point() + Traceback (most recent call last): + ... + TypeError: cannot define a point on the + Subset AA of the 2-dimensional topological manifold M + because it has been declared empty + sage: AD = A.subset('AD') + sage: AD.is_empty() + True + + If points have already been defined on ``self`` (or its subsets), + it is an error to declare it to be empty:: + + sage: B = M.subset('B') + sage: b = B.point(name='b'); b + Point on the 2-dimensional topological manifold M + sage: B.declare_empty() + Traceback (most recent call last): + ... + TypeError: cannot be empty because it has defined points + + Emptiness is recorded as empty open covers:: + + sage: P = M.subset_poset(open_covers=True) + sage: def label(element): + ....: if isinstance(element, str): + ....: return element + ....: try: + ....: return element._name + ....: except AttributeError: + ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' + sage: P.plot(element_labels={element: label(element) for element in P}) # not tested + """ + if self.has_defined_points(): + raise TypeError('cannot be empty because it has defined points') + for subset in self.subsets(): + if not subset.is_empty(): + subset._open_covers.append([]) + + def is_empty(self): + if self._has_defined_points: + return False + return any(not cover + for cover in self.open_covers(trivial=False)) + + def declare_nonempty(self): + if not self.has_defined_points(): + self._has_defined_points = True + + def has_defined_points(self, subsets=True): + if subsets: + return any(subset._has_defined_points for subset in self.subsets()) + else: + return self._has_defined_points + def point(self, coords=None, chart=None, name=None, latex_name=None): r""" Define a point in ``self``. From 4ad562d8b15f65f45cc45848c3abfa10a088843a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 12:11:20 -0700 Subject: [PATCH 205/706] ManifoldSubset.{sub,super}set_{digraph,poset}: Add option 'points' --- src/sage/manifolds/subset.py | 60 ++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index b8c348f0243..0a7b327e537 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -73,6 +73,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from collections import defaultdict +import itertools from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets @@ -719,7 +721,7 @@ def list_of_subsets(self): """ return sorted(self._subsets, key=lambda x: x._name) - def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): + def subset_digraph(self, loops=False, open_covers=False, points=False, lower_bound=None): r""" Return the digraph whose arcs represent subset relations among the subsets of ``self``. @@ -727,8 +729,10 @@ def subset_digraph(self, loops=False, open_covers=False, lower_bound=None): - ``loops`` -- (default: ``False``) whether to include the trivial containment of each subset in itself as loops of the digraph - - ``lower_bound`` -- (default: ``None``) only include supersets of this - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + - ``points`` -- (default: ``False``) whether to include vertices for declared points; + this can also be an iterable for the points to include + - ``lower_bound`` -- (default: ``None``) only include supersets of this OUTPUT: @@ -810,16 +814,41 @@ def open_cover_vertex(open_cover): D.add_edges((vertex(S), open_cover_vertex(open_cover)) for open_cover in S.open_covers(trivial=False)) + if points is not False: + subset_to_points = defaultdict(list) + if points is not True: + # Manifolds do not keep track of the points defined on them. + # Use the provided iterator. + def point_vertex(point): + return point + + for point in points: + S = point.parent() + subset_to_points[S].append(point) + D.add_edge((point_vertex(point), vertex(S))) + + # Add a placeholder vertex under each subset that has a defined + # point that we do not know about. + def anonymous_point_vertex(S): + return f"p{S._name}" + + D.add_edges((anonymous_point_vertex(S), vertex(S)) + for S in visited + if S.has_defined_points(subsets=False) + and S not in subset_to_points) + return D - def subset_poset(self, open_covers=False, lower_bound=None): + def subset_poset(self, open_covers=False, points=False, lower_bound=None): r""" Return the poset of the subsets of ``self``. INPUT: - - ``lower_bound`` -- (default: ``None``) only include supersets of this - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + - ``points`` -- (default: ``False``) whether to include vertices for declared points; + this can also be an iterable for the points to include + - ``lower_bound`` -- (default: ``None``) only include supersets of this EXAMPLES:: @@ -858,9 +887,10 @@ def subset_poset(self, open_covers=False, lower_bound=None): sage: P.plot(element_labels={element: label(element) for element in P}) # not tested """ from sage.combinat.posets.posets import Poset - return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) + return Poset(self.subset_digraph(open_covers=open_covers, points=points, + lower_bound=lower_bound)) - def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): + def superset_digraph(self, loops=False, open_covers=False, points=False, upper_bound=None): """ Return the digraph whose arcs represent subset relations among the supersets of ``self``. @@ -868,8 +898,10 @@ def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): - ``loops`` -- (default: ``False``) whether to include the trivial containment of each subset in itself as loops of the digraph - - ``upper_bound`` -- (default: ``None``) only include subsets of this - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + - ``points`` -- (default: ``False``) whether to include vertices for declared points; + this can also be an iterable for the points to include + - ``upper_bound`` -- (default: ``None``) only include subsets of this EXAMPLES:: @@ -882,16 +914,19 @@ def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): """ if upper_bound is None: upper_bound = self._manifold - return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, lower_bound=self) + return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, points=points, + lower_bound=self) - def superset_poset(self, open_covers=False, upper_bound=None): + def superset_poset(self, open_covers=False, points=False, upper_bound=None): r""" Return the poset of the supersets of ``self``. INPUT: - - ``upper_bound`` -- (default: ``None``) only include subsets of this - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers + - ``points`` -- (default: ``False``) whether to include vertices for declared points; + this can also be an iterable for the points to include + - ``upper_bound`` -- (default: ``None``) only include subsets of this EXAMPLES:: @@ -905,7 +940,8 @@ def superset_poset(self, open_covers=False, upper_bound=None): """ if upper_bound is None: upper_bound = self._manifold - return upper_bound.subset_poset(open_covers=open_covers, lower_bound=self) + return upper_bound.subset_poset(open_covers=open_covers, points=points, + lower_bound=self) def get_subset(self, name): r""" @@ -1083,7 +1119,7 @@ def declare_empty(self): Emptiness is recorded as empty open covers:: - sage: P = M.subset_poset(open_covers=True) + sage: P = M.subset_poset(open_covers=True, points=[b]) sage: def label(element): ....: if isinstance(element, str): ....: return element From cec7fa23dfdf81f5436b432d46b791f8c820938b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 13:27:36 -0700 Subject: [PATCH 206/706] ManifoldSubset.open_covers: Add option supersets; use it to fix is_empty --- src/sage/manifolds/subset.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 0a7b327e537..7651c02df12 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -529,7 +529,7 @@ def is_open(self): """ return False - def open_covers(self, trivial=True): + def open_covers(self, trivial=True, supersets=False): r""" Generate the open covers of the current subset. @@ -558,6 +558,8 @@ def open_covers(self, trivial=True): - ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial open cover of ``self`` by itself + - ``supersets`` -- (default: ``False``) if ``True``, include open covers of + all the supersets; it can also be an iterable of supersets to include EXAMPLES:: @@ -585,11 +587,16 @@ def open_covers(self, trivial=True): Set {A, B, V} of open subsets of the 2-dimensional topological manifold M] """ - for oc in self._open_covers: - if not trivial: - if len(oc) == 1 and next(iter(oc)) is self: - continue - yield ManifoldSubsetFiniteFamily(oc) + if supersets is False: + supersets = [self] + elif supersets is True: + supersets = self._supersets + for superset in supersets: + for oc in superset._open_covers: + if not trivial: + if any(x in supersets for x in oc): + continue + yield ManifoldSubsetFiniteFamily(oc) def open_cover_family(self, trivial=True): r""" @@ -1139,7 +1146,7 @@ def is_empty(self): if self._has_defined_points: return False return any(not cover - for cover in self.open_covers(trivial=False)) + for cover in self.open_covers(trivial=False, supersets=True)) def declare_nonempty(self): if not self.has_defined_points(): From c40ec033530849699c387ad7def2d9ba7f6ad653 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 12:16:58 -0700 Subject: [PATCH 207/706] ManifoldSubset.open_cover_family: Add option supersets --- src/sage/manifolds/subset.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 7651c02df12..3c0e0cec50c 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -598,7 +598,7 @@ def open_covers(self, trivial=True, supersets=False): continue yield ManifoldSubsetFiniteFamily(oc) - def open_cover_family(self, trivial=True): + def open_cover_family(self, trivial=True, supersets=False): r""" Return the family of open covers of the current subset. @@ -630,6 +630,8 @@ def open_cover_family(self, trivial=True): - ``trivial`` -- (default: ``True``) if ``self`` is open, include the trivial open cover of ``self`` by itself + - ``supersets`` -- (default: ``False``) if ``True``, include open covers of + all the supersets; it can also be an iterable of supersets to include EXAMPLES:: @@ -652,7 +654,8 @@ def open_cover_family(self, trivial=True): Set {{A, B, V}, {M}, {U, V}} of objects of the 2-dimensional topological manifold M """ - return ManifoldObjectFiniteFamily(self.open_covers(trivial=trivial)) + return ManifoldObjectFiniteFamily(self.open_covers( + trivial=trivial, supersets=supersets)) def subsets(self): r""" From a066387b0081ffbb2274cfd80c043491ec8cf1f8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 13:27:52 -0700 Subject: [PATCH 208/706] Fix doctests --- src/sage/manifolds/subset.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 3c0e0cec50c..2efb6b9e83a 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1121,7 +1121,7 @@ def declare_empty(self): sage: B = M.subset('B') sage: b = B.point(name='b'); b - Point on the 2-dimensional topological manifold M + Point b on the 2-dimensional topological manifold M sage: B.declare_empty() Traceback (most recent call last): ... @@ -1137,7 +1137,9 @@ def declare_empty(self): ....: return element._name ....: except AttributeError: ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' - sage: P.plot(element_labels={element: label(element) for element in P}) # not tested + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 14 graphics primitives + """ if self.has_defined_points(): raise TypeError('cannot be empty because it has defined points') From 1b5e5ace5e5cc01820d4e85e08f81d1662e5511d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 13:34:27 -0700 Subject: [PATCH 209/706] ManifoldSubset.declare_empty: Add plot --- src/sage/manifolds/subset.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 2efb6b9e83a..bc8cd2bc8aa 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1140,6 +1140,26 @@ def declare_empty(self): sage: P.plot(element_labels={element: label(element) for element in P}) Graphics object consisting of 14 graphics primitives + .. PLOT:: + + def label(element): + if isinstance(element, str): + return element + try: + return element._name + except AttributeError: + return '[' + ', '.join(sorted(x._name for x in element)) + ']' + M = Manifold(2, 'M', structure='topological') + A = M.subset('A', is_open=True) + AA = A.subset('AA') + A.declare_empty() + AD = A.subset('AD') + B = M.subset('B') + b = B.point(name='b') + P = M.subset_poset(open_covers=True, points=[b]) + g1 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1]), figsize=(8, 3)) + """ if self.has_defined_points(): raise TypeError('cannot be empty because it has defined points') From 48b2700e8e8990dbc3f9b358a6799e3c6b960854 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 16:09:07 -0700 Subject: [PATCH 210/706] src/sage/manifolds/subset.py: Remove unused import, make doctest stable by using 'sorted' --- src/sage/manifolds/subset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index bc8cd2bc8aa..0aefcdf3215 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -74,7 +74,6 @@ #***************************************************************************** from collections import defaultdict -import itertools from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.sets_cat import Sets @@ -885,7 +884,7 @@ def subset_poset(self, open_covers=False, points=False, lower_bound=None): sage: P = M.subset_poset(open_covers=True); P Finite poset containing 6 elements sage: from sage.manifolds.subset import ManifoldSubsetFiniteFamily - sage: P.upper_covers(ManifoldSubsetFiniteFamily([VW])) + sage: sorted(P.upper_covers(ManifoldSubsetFiniteFamily([VW])), key=str) [(Set {V} of open subsets of the 3-dimensional differentiable manifold M, Set {W} of open subsets of the 3-dimensional differentiable manifold M), Set {M} of open subsets of the 3-dimensional differentiable manifold M] From 7a22bb0838eccf995ac6dd9de4cdc44d07fee81f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Apr 2021 12:15:35 -0700 Subject: [PATCH 211/706] ManifoldSubset.superset_family: New, use it in doctests --- src/sage/manifolds/subset.py | 79 ++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index e0ef44ee542..784855a51b9 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -827,6 +827,54 @@ def subset_poset(self, open_covers=False, lower_bound=None): from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, lower_bound=lower_bound)) + def supersets(self): + r""" + Generate the declared supersets of the current subset. + + .. NOTE:: + + To get the supersets as a family, sorted by name, use the method + :meth:`superset_family` instead. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.subset('V') + sage: sorted(V.supersets(), key=lambda v: v._name) + [2-dimensional topological manifold M, + Subset V of the 2-dimensional topological manifold M] + + """ + yield from self._supersets + + def superset_family(self): + r""" + Return the family of declared supersets of the current subset. + + The family is sorted by the alphabetical names of the supersets. + + OUTPUT: + + - a :class:`ManifoldSubsetFiniteFamily` instance containing all the + supersets + + .. NOTE:: + + If you only need to iterate over the supersets in arbitrary order, + you can use the generator method :meth:`supersets` instead. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.subset('V') + sage: V.superset_family() + Set {M, V} of subsets of the 2-dimensional topological manifold M + + """ + return ManifoldSubsetFiniteFamily(self.supersets()) + def superset_digraph(self, loops=False, open_covers=False, upper_bound=None): """ Return the digraph whose arcs represent subset relations among the supersets of ``self``. @@ -1127,10 +1175,8 @@ def superset(self, name, latex_name=None, is_open=False): Subset B of the 2-dimensional topological manifold M sage: b.subset_family() Set {A, B} of subsets of the 2-dimensional topological manifold M - sage: a._supersets # random (set output) - {Subset B of the 2-dimensional topological manifold M, - Subset A of the 2-dimensional topological manifold M, - 2-dimensional topological manifold M} + sage: a.superset_family() + Set {A, B, M} of subsets of the 2-dimensional topological manifold M The superset of the whole manifold is itself:: @@ -1192,11 +1238,8 @@ def intersection(self, other, name=None, latex_name=None): Set {A, A_inter_B} of subsets of the 2-dimensional topological manifold M sage: b.subset_family() Set {A_inter_B, B} of subsets of the 2-dimensional topological manifold M - sage: c._supersets # random (set output) - {Subset B of the 2-dimensional topological manifold M, - Subset A_inter_B of the 2-dimensional topological manifold M, - Subset A of the 2-dimensional topological manifold M, - 2-dimensional topological manifold M} + sage: c.superset_family() + Set {A, A_inter_B, B, M} of subsets of the 2-dimensional topological manifold M Some checks:: @@ -1280,18 +1323,12 @@ def union(self, other, name=None, latex_name=None): sage: b = M.subset('B') sage: c = a.union(b); c Subset A_union_B of the 2-dimensional topological manifold M - sage: a._supersets # random (set output) - set([subset 'A_union_B' of the 2-dimensional manifold 'M', - 2-dimensional manifold 'M', - subset 'A' of the 2-dimensional manifold 'M']) - sage: b._supersets # random (set output) - set([subset 'B' of the 2-dimensional manifold 'M', - 2-dimensional manifold 'M', - subset 'A_union_B' of the 2-dimensional manifold 'M']) - sage: c._subsets # random (set output) - set([subset 'A_union_B' of the 2-dimensional manifold 'M', - subset 'A' of the 2-dimensional manifold 'M', - subset 'B' of the 2-dimensional manifold 'M']) + sage: a.superset_family() + Set {A, A_union_B, M} of subsets of the 2-dimensional topological manifold M + sage: b.superset_family() + Set {A_union_B, B, M} of subsets of the 2-dimensional topological manifold M + sage: c.superset_family() + Set {A_union_B, M} of subsets of the 2-dimensional topological manifold M Some checks:: From 2b47b1b4573d19fc8c9201a379d255cc67631c11 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Apr 2021 12:27:43 -0700 Subject: [PATCH 212/706] ManifoldSubset.open_superset_family: New --- src/sage/manifolds/subset.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 43c44838a30..64b8a9c9c62 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -575,6 +575,11 @@ def open_supersets(self): r""" Generate the open supersets of ``self``. + .. NOTE:: + + To get the open supersets as a family, sorted by name, use the method + :meth:`open_superset_family` instead. + EXAMPLES:: sage: M = Manifold(2, 'M', structure='topological') @@ -590,6 +595,35 @@ def open_supersets(self): if superset.is_open(): yield superset + def open_superset_family(self): + r""" + Return the family of open supersets of ``self``. + + The family is sorted by the alphabetical names of the subsets. + + OUTPUT: + + - a :class:`ManifoldSubsetFiniteFamily` instance containing all the + open supersets that have been defined on the current subset + + .. NOTE:: + + If you only need to iterate over the open supersets in arbitrary + order, you can use the generator method :meth:`open_supersets` + instead. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = U.subset('V') + sage: W = V.subset('W') + sage: W.open_superset_family() + Set {M, U} of open subsets of the 2-dimensional topological manifold M + + """ + return ManifoldSubsetFiniteFamily(self.open_supersets()) + def subsets(self): r""" Generate the subsets that have been defined on the current subset. From b77e098787b1795e4b1373a29ac147b74ab4cd05 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 14:02:39 -0700 Subject: [PATCH 213/706] ManifoldSubsetFiniteFamily.from_subsets_or_families: New constructor --- src/sage/manifolds/family.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index 9637fa0b0a5..41ff3f099cd 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -216,6 +216,31 @@ class ManifoldSubsetFiniteFamily(ManifoldObjectFiniteFamily): """ + @classmethod + def from_subsets_or_families(cls, *subsets_or_families): + r""" + Construct a ManifoldSubsetFiniteFamily from given subsets or iterables of subsets. + + EXAMPLES:: + + sage: from sage.manifolds.family import ManifoldSubsetFiniteFamily + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: Bs = (M.subset(f'B{i}') for i in range(5)) + sage: Cs = ManifoldSubsetFiniteFamily([M.subset('C0'), M.subset('C1')]) + sage: ManifoldSubsetFiniteFamily.from_subsets_or_families(A, Bs, Cs) + Set {A, B0, B1, B2, B3, B4, C0, C1} of subsets of the 2-dimensional topological manifold M + """ + def generate_subsets(): + from sage.manifolds.subset import ManifoldSubset + for arg in subsets_or_families: + if isinstance(arg, ManifoldSubset): + yield arg + else: + # arg must be an iterable of ManifoldSubset instances + yield from arg + return cls(generate_subsets()) + def _repr_object_type(self): r""" String that describes the type of the elements (plural). From 8051a21805720825272adc41d6e24af9ce4422b1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 17:56:41 -0700 Subject: [PATCH 214/706] ManifoldSubset.equal_subsets, equal_subset_family: New --- src/sage/manifolds/subset.py | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 46c9ab1f6c4..3a9d29033b6 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -982,6 +982,57 @@ def subset_poset(self, open_covers=False, points=False, lower_bound=None): return Poset(self.subset_digraph(open_covers=open_covers, points=points, lower_bound=lower_bound)) + def equal_subsets(self): + r""" + Generate the declared manifold subsets that are equal to ``self``. + + .. NOTE:: + + To get the equal subsets as a family, sorted by name, use the method + :meth:`equal_subset_family` instead. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = U.subset('V') + sage: V.declare_equal(M) + sage: sorted(V.equal_subsets(), key=lambda v: v._name) + [2-dimensional topological manifold M, + Open subset U of the 2-dimensional topological manifold M, + Subset V of the 2-dimensional topological manifold M] + + """ + for S in self.supersets(): + if S in self._subsets: + yield S + + def equal_subset_family(self): + r""" + Generate the declared manifold subsets that are equal to ``self``. + + .. NOTE:: + + To get the equal subsets as a family, sorted by name, use the method + :meth:`equal_subset_family` instead. + + .. NOTE:: + + If you only need to iterate over the equal sets in arbitrary order, + you can use the generator method :meth:`equal_subsets` instead. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = U.subset('V') + sage: V.declare_equal(M) + sage: V.equal_subset_family() + Set {M, U, V} of subsets of the 2-dimensional topological manifold M + + """ + return ManifoldSubsetFiniteFamily(self.supersets()) + def supersets(self): r""" Generate the declared supersets of the current subset. From 06aedf8e59da5c443e8e71bfb17b8065f339269e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 17:02:47 -0700 Subject: [PATCH 215/706] ManifoldSubset.{subset,superset}_digraph: New option quotient; use it for {subset,superset}_poset --- src/sage/manifolds/subset.py | 64 ++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 3a9d29033b6..c9f221eeeb4 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -813,7 +813,7 @@ def subset_family(self): """ return ManifoldSubsetFiniteFamily(self.subsets()) - def subset_digraph(self, loops=False, open_covers=False, points=False, lower_bound=None): + def subset_digraph(self, loops=False, quotient=False, open_covers=False, points=False, lower_bound=None): r""" Return the digraph whose arcs represent subset relations among the subsets of ``self``. @@ -821,6 +821,10 @@ def subset_digraph(self, loops=False, open_covers=False, points=False, lower_bou - ``loops`` -- (default: ``False``) whether to include the trivial containment of each subset in itself as loops of the digraph + - ``quotient`` -- (default: ``False``) whether to contract directed cycles in the graph, + replacing equivalence classes of equal subsets by a single vertex. + In this case, each vertex of the digraph is a set of :class:`ManifoldSubset` + instances. - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers - ``points`` -- (default: ``False``) whether to include vertices for declared points; this can also be an iterable for the points to include @@ -869,8 +873,27 @@ def subset_digraph(self, loops=False, open_covers=False, points=False, lower_bou from sage.graphs.digraph import DiGraph D = DiGraph(multiedges=False, loops=loops) - def vertex(subset): - return ManifoldSubsetFiniteFamily([subset]) + if loops: + add_edges = D.add_edges + else: + def add_edges(edges): + for u, v in edges: + if u != v: + D.add_edge((u, v)) + + if quotient: + subset_to_vertex = {} + def vertex(subset): + try: + return subset_to_vertex[subset] + except KeyError: + new_equivalence_class = ManifoldSubsetFiniteFamily(subset.equal_subsets()) + for S in new_equivalence_class: + subset_to_vertex[S] = new_equivalence_class + return new_equivalence_class + else: + def vertex(subset): + return ManifoldSubsetFiniteFamily([subset]) if lower_bound is not None: if not lower_bound.is_subset(self): @@ -888,13 +911,11 @@ def vertex(subset): else: subsets = [subset for subset in S._subsets if lower_bound.is_subset(subset)] + + add_edges((vertex(subset), vertex(S)) for subset in subsets) + subsets_without_S = [subset for subset in subsets if subset is not S] - if loops: - D.add_edges((vertex(subset), vertex(S)) for subset in subsets) - else: - D.add_edges((vertex(subset), vertex(S)) for subset in subsets_without_S) - to_visit.extend(subsets_without_S) if open_covers: @@ -903,8 +924,8 @@ def open_cover_vertex(open_cover): return tuple(sorted(ManifoldSubsetFiniteFamily([subset]) for subset in open_cover)) for S in visited: - D.add_edges((vertex(S), open_cover_vertex(open_cover)) - for open_cover in S.open_covers(trivial=False)) + add_edges((vertex(S), open_cover_vertex(open_cover)) + for open_cover in S.open_covers(trivial=False)) if points is not False: subset_to_points = defaultdict(list) @@ -924,16 +945,19 @@ def point_vertex(point): def anonymous_point_vertex(S): return f"p{S._name}" - D.add_edges((anonymous_point_vertex(S), vertex(S)) - for S in visited - if S.has_defined_points(subsets=False) - and S not in subset_to_points) + add_edges((anonymous_point_vertex(S), vertex(S)) + for S in visited + if S.has_defined_points(subsets=False) + and S not in subset_to_points) return D def subset_poset(self, open_covers=False, points=False, lower_bound=None): r""" - Return the poset of the subsets of ``self``. + Return the poset of equivalence classes of the subsets of ``self``. + + Each element of the poset is a set of :class:`ManifoldSubset` instances, + which are known to be equal. INPUT: @@ -980,7 +1004,7 @@ def subset_poset(self, open_covers=False, points=False, lower_bound=None): """ from sage.combinat.posets.posets import Poset return Poset(self.subset_digraph(open_covers=open_covers, points=points, - lower_bound=lower_bound)) + quotient=True, lower_bound=lower_bound)) def equal_subsets(self): r""" @@ -1081,7 +1105,7 @@ def superset_family(self): """ return ManifoldSubsetFiniteFamily(self.supersets()) - def superset_digraph(self, loops=False, open_covers=False, points=False, upper_bound=None): + def superset_digraph(self, loops=False, quotient=False, open_covers=False, points=False, upper_bound=None): """ Return the digraph whose arcs represent subset relations among the supersets of ``self``. @@ -1089,6 +1113,10 @@ def superset_digraph(self, loops=False, open_covers=False, points=False, upper_b - ``loops`` -- (default: ``False``) whether to include the trivial containment of each subset in itself as loops of the digraph + - ``quotient`` -- (default: ``False``) whether to contract directed cycles in the graph, + replacing equivalence classes of equal subsets by a single vertex. + In this case, each vertex of the digraph is a set of :class:`ManifoldSubset` + instances. - ``open_covers`` -- (default: ``False``) whether to include vertices for open covers - ``points`` -- (default: ``False``) whether to include vertices for declared points; this can also be an iterable for the points to include @@ -1106,7 +1134,7 @@ def superset_digraph(self, loops=False, open_covers=False, points=False, upper_b if upper_bound is None: upper_bound = self._manifold return upper_bound.subset_digraph(loops=loops, open_covers=open_covers, points=points, - lower_bound=self) + quotient=quotient, lower_bound=self) def superset_poset(self, open_covers=False, points=False, upper_bound=None): r""" From c9fd9f748ff870f2dc2fc474214e0f403d9a9872 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Apr 2021 16:58:24 -0700 Subject: [PATCH 216/706] ManifoldSubset.declare_equal: New --- src/sage/manifolds/manifold.py | 9 ++-- src/sage/manifolds/subset.py | 85 ++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 95377f959de..294a9dbe87d 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -899,9 +899,12 @@ def _init_open_subset(self, resu, coord_def): Open subset U of the 2-dimensional topological manifold R^2 """ resu._calculus_method = self._calculus_method - resu._supersets.update(self._supersets) - for sd in self._supersets: - sd._subsets.add(resu) + if self.is_empty(): + self.declare_equal(resu) + else: + resu._supersets.update(self._supersets) + for sd in self._supersets: + sd._subsets.add(resu) self._top_subsets.add(resu) # Charts on the result from the coordinate definition: for chart, restrictions in coord_def.items(): diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index c9f221eeeb4..ef4767389bb 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1284,6 +1284,67 @@ def declare_union(self, dom1, dom2): oc.append(s) self._open_covers.append(oc) + def declare_equal(self, *others): + r""" + Declare that ``self`` and ``others`` are the same sets. + + INPUT: + + - ``others`` -- finitely many subsets or iterables of subsets of the same + manifold as ``self``. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: U = M.open_subset('U') + sage: V = M.open_subset('V') + sage: Vs = [M.open_subset(f'V{i}') for i in range(2)] + sage: UV = U.intersection(V) + sage: W = UV.open_subset('W') + sage: P = M.subset_poset() + sage: def label(element): + ....: return element._name + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 15 graphics primitives + sage: V.declare_equal(Vs) + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 11 graphics primitives + sage: W.declare_equal(U) + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 6 graphics primitives + + .. PLOT:: + + def label(element): + return element._name + M = Manifold(2, 'M') + U = M.open_subset('U') + V = M.open_subset('V') + Vs = [M.open_subset(f'V{i}') for i in range(2)] + UV = U.intersection(V) + W = UV.open_subset('W') + P = M.subset_poset() + g1 = P.plot(element_labels={element: label(element) for element in P}) + V.declare_equal(Vs) + P = M.subset_poset() + g2 = P.plot(element_labels={element: label(element) for element in P}) + W.declare_equal(U) + P = M.subset_poset() + g3 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + + """ + F = ManifoldSubsetFiniteFamily.from_subsets_or_families + equal_sets = F(self, *others) + all_supersets = F(*[S.supersets() for S in equal_sets]) + all_subsets = F(*[S.subsets() for S in equal_sets]) + for superset in all_supersets: + superset._subsets.update(all_subsets) + for subset in all_subsets: + subset._supersets.update(all_supersets) + def declare_empty(self): r""" Declare that ``self`` is the empty set. @@ -1344,7 +1405,7 @@ def declare_empty(self): ....: except AttributeError: ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' sage: P.plot(element_labels={element: label(element) for element in P}) - Graphics object consisting of 14 graphics primitives + Graphics object consisting of 10 graphics primitives .. PLOT:: @@ -1362,16 +1423,19 @@ def label(element): AD = A.subset('AD') B = M.subset('B') b = B.point(name='b') + + D = M.subset_digraph(open_covers=True, points=[b]) + g1 = D.relabel(label, inplace=False).plot(layout='spring') P = M.subset_poset(open_covers=True, points=[b]) - g1 = P.plot(element_labels={element: label(element) for element in P}) - sphinx_plot(graphics_array([g1]), figsize=(8, 3)) + g2 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2]), figsize=(8, 5)) """ if self.has_defined_points(): raise TypeError('cannot be empty because it has defined points') - for subset in self.subsets(): - if not subset.is_empty(): - subset._open_covers.append([]) + if not self.is_empty(): + self._open_covers.append([]) + self.declare_equal(self.subsets()) def is_empty(self): if self._has_defined_points: @@ -1482,9 +1546,12 @@ def subset(self, name, latex_name=None, is_open=False): if is_open: return self.open_subset(name, latex_name=latex_name) res = ManifoldSubset(self._manifold, name, latex_name=latex_name) - res._supersets.update(self._supersets) - for sd in self._supersets: - sd._subsets.add(res) + if self.is_empty(): + self.declare_equal(res) + else: + res._supersets.update(self._supersets) + for sd in self._supersets: + sd._subsets.add(res) self._top_subsets.add(res) return res From fa5057ce25fcc2d7dc6e7d22a9881470f341fe7a Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Fri, 23 Apr 2021 19:39:28 +0200 Subject: [PATCH 217/706] Trac 31721: __pow__ method for parents --- .../coercion_and_categories.rst | 2 -- src/sage/categories/fields.py | 12 ------- src/sage/categories/rings.py | 23 ++++++++++++ src/sage/rings/ring.pyx | 23 ------------ src/sage/structure/parent.pyx | 36 +++++++++++++++++++ 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 24bd3abafd3..4a8d86c0ae9 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -108,8 +108,6 @@ This base class provides a lot more methods than a general parent:: '__ideal_monoid', '__iter__', '__len__', - '__pow__', - '__rpow__', '__rtruediv__', '__rxor__', '__truediv__', diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index d88743cc17f..17324a08b2e 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -500,18 +500,6 @@ def _squarefree_decomposition_univariate_polynomial(self, f): return Factorization(factors, unit=unit, sort=False) - def _pow_int(self, n): - r""" - Returns the vector space of dimension `n` over ``self``. - - EXAMPLES:: - - sage: QQ^4 - Vector space of dimension 4 over Rational Field - """ - from sage.modules.all import FreeModule - return FreeModule(self, n) - def vector_space(self, *args, **kwds): r""" Gives an isomorphism of this field with a vector space over a subfield. diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index ea02c088a8b..cd3ff689314 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -510,6 +510,29 @@ def _mul_(self, x, switch_sides=False): # duck typing failed raise TypeError("Don't know how to transform %s into an ideal of %s"%(x,self)) + def __pow__(self, n): + """ + Return the free module of rank `n` over this ring. If n is a tuple of + two elements, creates a matrix space. + + EXAMPLES:: + + sage: QQ^5 + Vector space of dimension 5 over Rational Field + sage: Integers(20)^1000 + Ambient free module of rank 1000 over Ring of integers modulo 20 + + sage: QQ^(2,3) + Full MatrixSpace of 2 by 3 dense matrices over Rational Field + """ + if isinstance(n, tuple): + m, n = n + from sage.matrix.matrix_space import MatrixSpace + return MatrixSpace(self, m, n) + else: + from sage.modules.free_module import FreeModule + return FreeModule(self, n) + @cached_method def ideal_monoid(self): """ diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 0408a5bcabe..468316358f5 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1365,29 +1365,6 @@ cdef class CommutativeRing(Ring): except (NotImplementedError,TypeError): return coercion_model.division_parent(self) - def __pow__(self, n, _): - """ - Return the free module of rank `n` over this ring. If n is a tuple of - two elements, creates a matrix space. - - EXAMPLES:: - - sage: QQ^5 - Vector space of dimension 5 over Rational Field - sage: Integers(20)^1000 - Ambient free module of rank 1000 over Ring of integers modulo 20 - - sage: QQ^(2,3) - Full MatrixSpace of 2 by 3 dense matrices over Rational Field - """ - if isinstance(n, tuple): - m, n = n - from sage.matrix.matrix_space import MatrixSpace - return MatrixSpace(self, m, n) - else: - import sage.modules.all - return sage.modules.all.FreeModule(self, n) - def is_commutative(self): """ Return ``True``, since this ring is commutative. diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index a5a16bacdd4..569dd47fe45 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -992,6 +992,42 @@ cdef class Parent(sage.structure.category_object.CategoryObject): return _mul_(self, switch_sides=True) return _mul_(x) + def __pow__(self, x, mod): + """ + TESTS:: + + sage: ZZ^3 + Ambient free module of rank 3 over the principal ideal domain + Integer Ring + sage: QQ^3 + Vector space of dimension 3 over Rational Field + sage: QQ[x]^3 + Ambient free module of rank 3 over the principal ideal domain + Univariate Polynomial Ring in x over Rational Field + sage: IntegerModRing(6)^3 + Ambient free module of rank 3 over Ring of integers modulo 6 + + sage: 3^ZZ + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for ^: 'Integer Ring' and '' + sage: Partitions(3)^3 + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for ** or pow(): 'Partitions_n_with_category' and 'int' + """ + if mod is not None or not isinstance(self, Parent): + return NotImplemented + try: + meth = super(Parent, ( self)).__pow__ + except AttributeError: + # needed when self is a Cython object + try: + meth = ( self).getattr_from_category('__pow__') + except AttributeError: + return NotImplemented + return meth(x) + ############################################################################# # Containment testing ############################################################################# From a3aa94a209d319a5d923af84cd28cf26acaced66 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 27 Apr 2021 09:47:27 +0200 Subject: [PATCH 218/706] 31700: dummy extend argument to MatrixSymbolicDense.eigenvalues --- src/sage/matrix/matrix_symbolic_dense.pyx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 3edfbb69c32..2dccb1c8679 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -166,7 +166,7 @@ cdef maxima from sage.calculus.calculus import symbolic_expression_from_maxima_string, maxima cdef class Matrix_symbolic_dense(Matrix_generic_dense): - def eigenvalues(self): + def eigenvalues(self, extend=True): """ Compute the eigenvalues by solving the characteristic polynomial in maxima. @@ -177,6 +177,15 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): sage: a.eigenvalues() [-1/2*sqrt(33) + 5/2, 1/2*sqrt(33) + 5/2] + TESTS: + + Check for :trac:`31700`:: + + sage: m = matrix([[cos(pi/5), sin(pi/5)], [-sin(pi/5), cos(pi/5)]]) + sage: t = linear_transformation(m) + sage: t.eigenvalues() + [1/4*sqrt(5) - 1/4*sqrt(2*sqrt(5) - 10) + 1/4, + 1/4*sqrt(5) + 1/4*sqrt(2*sqrt(5) - 10) + 1/4] """ maxima_evals = self._maxima_(maxima).eigenvalues()._sage_() if not len(maxima_evals): From 27422d323a7db31f3fc2068f6aeb3100b3e4d6ae Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 27 Apr 2021 11:01:40 +0200 Subject: [PATCH 219/706] 31721: more comments --- src/sage/structure/parent.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 569dd47fe45..f509960e1d3 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1019,9 +1019,10 @@ cdef class Parent(sage.structure.category_object.CategoryObject): if mod is not None or not isinstance(self, Parent): return NotImplemented try: + # get __pow__ from category in case the parent is a Python class meth = super(Parent, ( self)).__pow__ except AttributeError: - # needed when self is a Cython object + # get __pow__ from category in case the parent is a Cython class try: meth = ( self).getattr_from_category('__pow__') except AttributeError: From e8d34270e0b74d33776f958360a0cdad60a0b74c Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Tue, 27 Apr 2021 12:48:09 +0200 Subject: [PATCH 220/706] clarify comment and doctest for multiple inheritance --- src/sage/structure/parent.pyx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index f509960e1d3..8ae95c32035 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1015,11 +1015,21 @@ cdef class Parent(sage.structure.category_object.CategoryObject): Traceback (most recent call last): ... TypeError: unsupported operand type(s) for ** or pow(): 'Partitions_n_with_category' and 'int' + + Check multiple inheritance:: + + sage: class A: + ....: def __pow__(self, n): + ....: return 'Apow' + sage: class MyParent(A, Parent): + ....: pass + sage: MyParent()^2 + 'Apow' """ if mod is not None or not isinstance(self, Parent): return NotImplemented try: - # get __pow__ from category in case the parent is a Python class + # get __pow__ from super class meth = super(Parent, ( self)).__pow__ except AttributeError: # get __pow__ from category in case the parent is a Cython class From 1e6a05505661e19ad09df7716fc93f5a1a2dde4d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Apr 2021 11:34:15 -0700 Subject: [PATCH 221/706] is_empty, declare_nonempty, has_defined_points: Add doc, examples --- src/sage/manifolds/subset.py | 80 ++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 0aefcdf3215..8488241eba8 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1167,16 +1167,90 @@ def label(element): subset._open_covers.append([]) def is_empty(self): - if self._has_defined_points: + r""" + Return whether the current subset is empty. + + By default, manifold subsets are considered nonempty: The method :meth:`point` can be + used to define points on it, either with or without coordinates some chart. + + However, using :meth:`declare_empty`, a subset can be declared empty, and emptiness + transfers to all of its subsets. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A', is_open=True) + sage: AA = A.subset('AA') + sage: A.is_empty() + False + sage: A.declare_empty() + sage: A.is_empty() + True + sage: AA.is_empty() + True + + """ + if self.has_defined_points(subsets=False): + # Fast path, do not check subsets return False return any(not cover for cover in self.open_covers(trivial=False, supersets=True)) def declare_nonempty(self): - if not self.has_defined_points(): - self._has_defined_points = True + r""" + Declare that ``self`` is nonempty. + + Once declared nonempty, ``self`` (or any of its supersets) cannot be declared empty. + + This is equivalent to defining a point on ``self`` using :meth:`point` + but is cheaper than actually creating a :class:`~sage.manifolds.point.ManifoldPoint` + instance. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A', is_open=True) + sage: AA = A.subset('AA') + sage: AA.declare_nonempty() + sage: A.has_defined_points() + True + sage: A.declare_empty() + Traceback (most recent call last): + ... + TypeError: cannot be empty because it has defined points + + """ + if self.has_defined_points(subsets=False): + # Fast path, do not check subsets + return + if self.is_empty(): + raise TypeError('cannot be nonempty because it has already been declared empty') + self._has_defined_points = True def has_defined_points(self, subsets=True): + r""" + Return whether any points have been defined on ``self`` or any of its subsets. + + INPUT: + + - ``subsets`` -- (default: ``True``) if ``False``, only consider points that have + been defined directly on ``self``; if ``True``, also consider points on all subsets. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A', is_open=True) + sage: AA = A.subset('AA') + sage: AA.point() + Point on the 2-dimensional topological manifold M + sage: AA.has_defined_points() + True + sage: A.has_defined_points(subsets=False) + False + sage: A.has_defined_points() + True + + """ if subsets: return any(subset._has_defined_points for subset in self.subsets()) else: From 92d631312d405685aefcac0b190effebd79510f0 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 28 Apr 2021 23:11:11 +0200 Subject: [PATCH 222/706] document ignorance --- src/sage/matrix/matrix_symbolic_dense.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 2dccb1c8679..742ee7553b3 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -171,6 +171,9 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): Compute the eigenvalues by solving the characteristic polynomial in maxima. + The argument ``extend`` is ignored but kept for compatibility with + other matrix classes. + EXAMPLES:: sage: a=matrix(SR,[[1,2],[3,4]]) From b873bcbe33571dc020c08b05c0390777148b1b53 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 29 Apr 2021 09:04:36 +0200 Subject: [PATCH 223/706] some doc --- src/sage/structure/parent.pyx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 8ae95c32035..5a46c73c26a 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -994,6 +994,17 @@ cdef class Parent(sage.structure.category_object.CategoryObject): def __pow__(self, x, mod): """ + Power function. + + The default implementation of ``__pow__`` on parent redirects to the + super class (in case of multiple inheritance) or to the category. This + redirection is necessary when the parent is a Cython class (aka + extension class) because in that case the parent class does not inherit + from the ``ParentMethods`` of the category. + + Concrete implementations of parents can freely overwrite this default + method. + TESTS:: sage: ZZ^3 From ca3285a58bede4b4c748aabf54e246360b932115 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 29 Apr 2021 09:19:56 +0200 Subject: [PATCH 224/706] handle case where precision_relative is infinite --- src/sage/matrix/matrix_cdv.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index 2206b48f5d6..cda7c94d9c1 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -52,8 +52,15 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): sage: M = random_matrix(K, 6, 6) sage: M.charpoly()[0] == M.determinant() True + + We check that trac:`31753` is resolved:: + + sage: R. = GF(5)[[]] + sage: M = matrix(3, 3, [ 1, t + O(t^3), t^2, 1 + t + O(t^3), 2 + t^2, 3 + 2*t + O(t^3), t - t^2, 2*t, 1 + t ]) + sage: M.charpoly() + x^3 + (1 + 4*t + 4*t^2 + O(t^3))*x^2 + (t + 2*t^2 + O(t^3))*x + 3 + 2*t^2 + O(t^3) """ - cdef Py_ssize_t n, m, i, j, k + cdef Py_ssize_t n, i, j, k cdef Matrix_generic_dense c cdef RingElement pivot, inv, scalar From e43030c3f9f9f6b5cb7152e1eda6e670d0950763 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 29 Apr 2021 12:46:08 +0200 Subject: [PATCH 225/706] missing colon --- src/sage/matrix/matrix_cdv.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index cda7c94d9c1..0384b29bcb5 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -53,7 +53,7 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): sage: M.charpoly()[0] == M.determinant() True - We check that trac:`31753` is resolved:: + We check that :trac:`31753` is resolved:: sage: R. = GF(5)[[]] sage: M = matrix(3, 3, [ 1, t + O(t^3), t^2, 1 + t + O(t^3), 2 + t^2, 3 + 2*t + O(t^3), t - t^2, 2*t, 1 + t ]) From 41b38807e4cbd883a4d27d490b39952238c70f4d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 29 Apr 2021 19:49:53 +0200 Subject: [PATCH 226/706] better cdef --- src/sage/matrix/matrix_cdv.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_cdv.pyx b/src/sage/matrix/matrix_cdv.pyx index 0384b29bcb5..a964b242c0f 100644 --- a/src/sage/matrix/matrix_cdv.pyx +++ b/src/sage/matrix/matrix_cdv.pyx @@ -61,8 +61,7 @@ cpdef hessenbergize_cdvf(Matrix_generic_dense H): x^3 + (1 + 4*t + 4*t^2 + O(t^3))*x^2 + (t + 2*t^2 + O(t^3))*x + 3 + 2*t^2 + O(t^3) """ cdef Py_ssize_t n, i, j, k - cdef Matrix_generic_dense c - cdef RingElement pivot, inv, scalar + cdef RingElement entry, pivot, inv, scalar n = H.nrows() for j in range(n-1): From 7dfe75cf48bf41b432e189774a7a5144fb735907 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Thu, 29 Apr 2021 20:06:50 +0200 Subject: [PATCH 227/706] 31752: fix off-by-1 error creating random polynomials --- src/sage/rings/polynomial/multi_polynomial_ring_base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 879a7bbb707..b617281ddc5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -873,7 +873,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): total = binomial(n+d-1, d) #Select random monomial of degree d - random_index = ZZ.random_element(0, total-1) + random_index = ZZ.random_element(0, total) #Generate the corresponding monomial return self._to_monomial(random_index, n, d) From f86837fc32a55794a785d14d56cb3dd3aaacadde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Fri, 30 Apr 2021 12:38:05 +0200 Subject: [PATCH 228/706] 11323: Fix complex number power so float^complex works --- src/sage/rings/complex_mpfr.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 695a1048a4e..637e3f3990c 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -1227,6 +1227,13 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): 5 sage: a^5 -38.0000000000000 + 41.0000000000000*I + + TESTS: + + Check that :trac:`11323` is fixed:: + + sage: float(5)^(0.5 + 14.1347251*i) + -1.62414637645790 - 1.53692828324508*I """ self._multiplicative_order = Integer(n) @@ -1690,7 +1697,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): try: return (self.log()*right).exp() - except TypeError: + except (AttributeError, TypeError): pass try: From 8012677b2f39e72c42c6fdcfd95ca038125cad40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 30 Apr 2021 19:46:15 +0200 Subject: [PATCH 229/706] add linestyle option to polygon --- src/sage/plot/polygon.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/sage/plot/polygon.py b/src/sage/plot/polygon.py index 33090764311..82ea3197162 100644 --- a/src/sage/plot/polygon.py +++ b/src/sage/plot/polygon.py @@ -1,7 +1,7 @@ """ Polygons """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 Alex Clemesha , # William Stein , # 2008 Mike Hansen , @@ -16,7 +16,7 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.plot.primitive import GraphicPrimitive_xydata from sage.misc.decorators import options, rename_keyword @@ -61,6 +61,11 @@ class Polygon(GraphicPrimitive_xydata): sage: polygon([(0,0,1), (1,1,1), (2,0,1)]) Graphics3d Object + + :: + + sage: polygon2d([(1, 1), (0, 1), (1, 0)], fill=False, linestyle="dashed") + Graphics object consisting of 1 graphics primitive """ def __init__(self, xdata, ydata, options): """ @@ -88,11 +93,11 @@ def _repr_(self): sage: p=P[0]; p Polygon defined by 3 points """ - return "Polygon defined by %s points"%len(self) + return "Polygon defined by %s points" % len(self) def __getitem__(self, i): """ - Returns `i`th vertex of Polygon primitive, starting count + Return `i`th vertex of Polygon primitive, starting count from 0th vertex. EXAMPLES:: @@ -126,7 +131,7 @@ def __setitem__(self, i, point): def __len__(self): """ - Returns number of vertices of Polygon primitive. + Return number of vertices of Polygon primitive. EXAMPLES:: @@ -153,6 +158,7 @@ def _allowed_options(self): 'fill': 'Whether or not to fill the polygon.', 'legend_label': 'The label for this item in the legend.', 'legend_color': 'The color of the legend text.', + 'linestyle': 'The style of the enclosing line.', 'rgbcolor': 'The color as an RGB tuple.', 'hue': 'The color given as a hue.', 'zorder': 'The layer level in which to draw'} @@ -231,7 +237,7 @@ def plot3d(self, z=0, **kwds): if isinstance(z, list): zdata = z else: - zdata = [z]*len(self.xdata) + zdata = [z] * len(self.xdata) if len(zdata) == len(self.xdata): return IndexFaceSet([list(zip(self.xdata, self.ydata, zdata))], **options) @@ -249,6 +255,8 @@ def _render_on_subplot(self, subplot): p = patches.Polygon([(self.xdata[i], self.ydata[i]) for i in range(len(self.xdata))]) p.set_linewidth(float(options['thickness'])) + if 'linestyle' in options: + p.set_linestyle(options['linestyle']) a = float(options['alpha']) z = int(options.pop('zorder', 1)) p.set_alpha(a) @@ -271,7 +279,7 @@ def _render_on_subplot(self, subplot): def polygon(points, **options): """ - Returns either a 2-dimensional or 3-dimensional polygon depending + Return either a 2-dimensional or 3-dimensional polygon depending on value of points. For information regarding additional arguments, see either @@ -305,13 +313,14 @@ def polygon(points, **options): from sage.plot.plot3d.shapes2 import polygon3d return polygon3d(points, **options) + @rename_keyword(color='rgbcolor') -@options(alpha=1, rgbcolor=(0,0,1), edgecolor=None, thickness=None, +@options(alpha=1, rgbcolor=(0, 0, 1), edgecolor=None, thickness=None, legend_label=None, legend_color=None, aspect_ratio=1.0, fill=True) def polygon2d(points, **options): r""" - Returns a 2-dimensional polygon defined by ``points``. + Return a 2-dimensional polygon defined by ``points``. Type ``polygon2d.options`` for a dictionary of the default options for polygons. You can change this to change the From bbf59a23ae600c8b1e834320d606b5d8c3ded66e Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 1 May 2021 12:44:11 +0200 Subject: [PATCH 230/706] Speedup WordMorphism.growing_letters --- src/sage/combinat/words/morphism.py | 91 ++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 1879e4e1833..464ab692676 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3080,27 +3080,14 @@ def is_growing(self, letter=None): Combinatorics, automata and number theory, 163--247, Encyclopedia Math. Appl., 135, Cambridge Univ. Press, Cambridge, 2010. """ - if self.is_primitive() and len(self._morph) > 1: - return True - if letter is None: - I = range(self.domain().alphabet().cardinality()) + if not letter: + return self.domain().alphabet().cardinality() == len(self.growing_letters()) else: - if letter not in self.domain().alphabet(): - raise TypeError("letter (=%s) is not in the domain of self" % letter) - I = [self.domain().alphabet().rank(letter)] - - last_coef = 0 - coefs = self.incidence_matrix().charpoly().coefficients(sparse=False) - while coefs[last_coef] == 0: - last_coef += 1 - V = self.abelian_rotation_subspace() + (self.incidence_matrix()**last_coef).right_kernel().change_ring(QQ) - basis = V.ambient_vector_space().basis() - - return not any(basis[i] in V for i in I) + return letter in self.growing_letters() def growing_letters(self): r""" - Returns the list of growing letters. + Return the list of growing letters. See :meth:`.is_growing` for more information. @@ -3120,17 +3107,67 @@ def growing_letters(self): sage: WordMorphism('a->a').growing_letters() [] """ - if self.is_primitive() and len(self._morph) > 1: - return self.domain().alphabet().list() - last_coef = 0 - coefs = self.incidence_matrix().charpoly().coefficients(sparse=False) - while coefs[last_coef] == 0: - last_coef += 1 - V = self.abelian_rotation_subspace() + (self.incidence_matrix()**last_coef).right_kernel().change_ring(QQ) - basis = V.ambient_vector_space().basis() - A = self.domain().alphabet() + # Remove letters of type b->a, a->. + immortal = self.immortal_letters() + new_morph = {x : [z for z in y if z in immortal] + for x, y in self._morph.items() if x in immortal} + + # Remove letters of type c->bd, d->ca. + graph_first = {x : y[0] for x, y in new_morph.items()} + loops = set() + for cycle in get_cycles(graph_first.__getitem__, graph_first): + if any(len(new_morph[letter]) > 1 for letter in cycle): + continue + loops.update(cycle) + new_morph = {x : [z for z in y if z not in loops] + for x, y in new_morph.items() if x not in loops} + + # Remove letters of type e->abcd. + self._morph, new_morph = new_morph, self._morph + result = self.immortal_letters() + self._morph = new_morph - return list(A.unrank(i) for i in range(A.cardinality()) if basis[i] not in V) + return sorted(result, key=self.domain().alphabet().rank) + + def immortal_letters(self): + r""" + Return the set of immortal letters. + + A letter `a` is *immortal* for the morphism `s` if the length of the + iterates of `| s^n(a) |` is larger than zero as `n` goes to infinity. + + Requires this morphism to be an endomorphism. + + EXAMPLES:: + + sage: WordMorphism('a->abcd,b->cd,c->dd,d->').immortal_letters() + {'a'} + """ + if not self.is_endomorphism(): + raise TypeError(f'self ({self}) is not an endomorphism') + + forward, backward = {}, {} + for letter in self._morph: + forward[letter], backward[letter] = set(), set() + + stack = [] + for preimage, image in self._morph.items(): + if not image: + stack.append(preimage) + for occurrence in image: + forward[preimage].add(occurrence) + backward[occurrence].add(preimage) + + while stack: + letter = stack.pop() + for preimage in backward[letter]: + forward[preimage].remove(letter) + if not forward[preimage]: + stack.append(preimage) + del forward[letter] + del backward[letter] + + return set(forward) def abelian_rotation_subspace(self): r""" From 7de85c8bafc9c60b6d77df242ece117563122588 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 1 May 2021 13:19:04 +0200 Subject: [PATCH 231/706] Fix WordMorphism.periodic_point --- src/sage/combinat/words/morphism.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 1879e4e1833..4b4b6d5cd2c 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -1949,10 +1949,11 @@ def periodic_point(self, letter): TypeError: self must be prolongable on a """ if not self.is_growing(letter): - w = self(letter) - w2 = self(w) - while w2 != w: - w,w2 = w2, self(w2) + w = self.domain()(letter) + prev = set() + while w not in prev: + prev.add(w) + w = self(w) return w elif self.is_erasing(): From 18097e91dfd36648810d2f01594a2c08aa2fd28b Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 1 May 2021 13:32:59 +0200 Subject: [PATCH 232/706] 31759: Add a regression test --- src/sage/combinat/words/morphism.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 4b4b6d5cd2c..c672b9269e6 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -1947,6 +1947,11 @@ def periodic_point(self, letter): Traceback (most recent call last): ... TypeError: self must be prolongable on a + + Make sure that :trac:`31759` is fixed:: + + sage: WordMorphism('a->b,b->a').periodic_point('a') + word: a """ if not self.is_growing(letter): w = self.domain()(letter) From bd67035c3230c6ae7e6ee9bc641f938075c82199 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sun, 2 May 2021 12:31:43 +0200 Subject: [PATCH 233/706] 31760: Don't modify self --- src/sage/combinat/words/morphism.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 464ab692676..b7eb2bd8a03 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3123,9 +3123,8 @@ def growing_letters(self): for x, y in new_morph.items() if x not in loops} # Remove letters of type e->abcd. - self._morph, new_morph = new_morph, self._morph - result = self.immortal_letters() - self._morph = new_morph + new_morph = WordMorphism(new_morph, domain=self.domain(), codomain=self.codomain()) + result = new_morph.immortal_letters() return sorted(result, key=self.domain().alphabet().rank) From 93b8aa3ed701c5f500f5615f4de8ee0849b5b187 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 12:06:20 -0700 Subject: [PATCH 234/706] ManifoldSubset.equal_subset_family: Fixup --- src/sage/manifolds/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 8e52ff1e23c..dd0dcc92b8c 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1055,7 +1055,7 @@ def equal_subset_family(self): Set {M, U, V} of subsets of the 2-dimensional topological manifold M """ - return ManifoldSubsetFiniteFamily(self.supersets()) + return ManifoldSubsetFiniteFamily(self.equal_subsets()) def supersets(self): r""" From 5c7bb75171a85d8097917d58eb301f7b05ef9100 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 13:12:56 -0700 Subject: [PATCH 235/706] src/sage/manifolds/{family,manifold,subset,differentiable/manifold,**/*_submanifold}.py: Update authors, copyright --- .../differentiable/differentiable_submanifold.py | 6 +++++- src/sage/manifolds/differentiable/manifold.py | 11 ++++++++--- .../differentiable/pseudo_riemannian_submanifold.py | 6 +++++- src/sage/manifolds/family.py | 4 ++++ src/sage/manifolds/manifold.py | 9 +++++++-- src/sage/manifolds/subset.py | 8 +++++--- src/sage/manifolds/topological_submanifold.py | 6 +++++- 7 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/sage/manifolds/differentiable/differentiable_submanifold.py b/src/sage/manifolds/differentiable/differentiable_submanifold.py index 9aa5de8a363..6b0f4cac6ad 100644 --- a/src/sage/manifolds/differentiable/differentiable_submanifold.py +++ b/src/sage/manifolds/differentiable/differentiable_submanifold.py @@ -18,6 +18,8 @@ AUTHORS: - Florentin Jaffredo (2018): initial version +- Eric Gourgoulhon (2018-2019): add documentation +- Matthias Koeppe (2021): open subsets of submanifolds REFERENCES: @@ -26,7 +28,9 @@ """ # ***************************************************************************** -# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018-2019 Eric Gourgoulhon +# Copyright (C) 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 216e22a0a5f..c786e843d49 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -411,6 +411,7 @@ - Eric Gourgoulhon (2015): initial version - Travis Scrimshaw (2016): review tweaks - Michael Jung (2020): tensor bundles and orientability +- Matthias Koeppe (2021): refactoring of subsets code REFERENCES: @@ -424,9 +425,13 @@ """ # **************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon -# Copyright (C) 2015 Michal Bejger -# Copyright (C) 2016 Travis Scrimshaw +# Copyright (C) 2015-2019 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# Copyright (C) 2015-2016 Travis Scrimshaw +# Copyright (C) 2017 Karim Van Aelst +# Copyright (C) 2019 Hans Fotsing Tetsing +# Copyright (C) 2019-2020 Michael Jung +# Copyright (C) 2021 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 5b7ef8b72c2..82426ad8890 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -168,6 +168,8 @@ AUTHORS: - Florentin Jaffredo (2018): initial version +- Eric Gourgoulhon (2018-2019): add documentation +- Matthias Koeppe (2021): open subsets of submanifolds REFERENCES: @@ -177,7 +179,9 @@ """ # ***************************************************************************** -# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018-2019 Eric Gourgoulhon +# Copyright (C) 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/manifolds/family.py b/src/sage/manifolds/family.py index 41ff3f099cd..b6735a0e89b 100644 --- a/src/sage/manifolds/family.py +++ b/src/sage/manifolds/family.py @@ -11,6 +11,10 @@ The subclass :class:`ManifoldSubsetFiniteFamily` customizes the print representation further. +AUTHORS: + +- Matthias Koeppe (2021): initial version + """ #***************************************************************************** # Copyright (C) 2021 Matthias Koeppe diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 294a9dbe87d..36373e6f906 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -310,8 +310,13 @@ """ #***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon -# Copyright (C) 2015 Travis Scrimshaw +# Copyright (C) 2015-2020 Eric Gourgoulhon +# Copyright (C) 2015 Travis Scrimshaw +# Copyright (C) 2016 Andrew Mathas +# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2019 Hans Fotsing Tetsing +# Copyright (C) 2019-2020 Michael Jung +# Copyright (C) 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index dd0dcc92b8c..f52a38da5ba 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -11,6 +11,7 @@ - Eric Gourgoulhon, Michal Bejger (2013-2015): initial version - Travis Scrimshaw (2015): review tweaks; removal of facade parents +- Matthias Koeppe (2021): Families and posets of subsets REFERENCES: @@ -52,9 +53,10 @@ """ #***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon -# Copyright (C) 2015 Michal Bejger -# Copyright (C) 2015 Travis Scrimshaw +# Copyright (C) 2015-2020 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# Copyright (C) 2015-2016 Travis Scrimshaw +# Copyright (C) 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/manifolds/topological_submanifold.py b/src/sage/manifolds/topological_submanifold.py index 4d83e9582fc..4a777be7ff6 100644 --- a/src/sage/manifolds/topological_submanifold.py +++ b/src/sage/manifolds/topological_submanifold.py @@ -24,6 +24,8 @@ AUTHORS: - Florentin Jaffredo (2018): initial version +- Eric Gourgoulhon (2018-2019): add documentation +- Matthias Koeppe (2021): open subsets of submanifolds REFERENCES: @@ -33,7 +35,9 @@ # ***************************************************************************** -# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018 Florentin Jaffredo +# Copyright (C) 2018-2019 Eric Gourgoulhon +# Copyright (C) 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From f244b8770b7cd3eb7d66e04586d39af7992a4588 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 14:57:34 -0700 Subject: [PATCH 236/706] ManifoldSubset.subset_digraph: Do not forget to include isolated vertices --- src/sage/manifolds/subset.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f52a38da5ba..c388c84d27a 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -884,18 +884,20 @@ def add_edges(edges): D.add_edge((u, v)) if quotient: - subset_to_vertex = {} - def vertex(subset): - try: - return subset_to_vertex[subset] - except KeyError: - new_equivalence_class = ManifoldSubsetFiniteFamily(subset.equal_subsets()) - for S in new_equivalence_class: - subset_to_vertex[S] = new_equivalence_class - return new_equivalence_class + def vertex_family(subset): + return ManifoldSubsetFiniteFamily(subset.equal_subsets()) else: - def vertex(subset): + def vertex_family(subset): return ManifoldSubsetFiniteFamily([subset]) + subset_to_vertex = {} + def vertex(subset): + try: + return subset_to_vertex[subset] + except KeyError: + family = vertex_family(subset) + for S in family: + subset_to_vertex[S] = family + return family if lower_bound is not None: if not lower_bound.is_subset(self): @@ -920,6 +922,9 @@ def vertex(subset): if subset is not S] to_visit.extend(subsets_without_S) + # Make sure to include isolated vertices in the graph + D.add_vertices(subset_to_vertex.values()) + if open_covers: def open_cover_vertex(open_cover): From a6a7f025207ce89098a54770d2bfec36c0ef783e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 29 Apr 2021 21:40:13 -0700 Subject: [PATCH 237/706] ManifoldSubset.declare_{sub,super}set: New --- src/sage/manifolds/manifold.py | 4 +- src/sage/manifolds/subset.py | 134 +++++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 9 deletions(-) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 36373e6f906..7c3a77e6628 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -907,9 +907,7 @@ def _init_open_subset(self, resu, coord_def): if self.is_empty(): self.declare_equal(resu) else: - resu._supersets.update(self._supersets) - for sd in self._supersets: - sd._subsets.add(resu) + self.declare_superset(resu) self._top_subsets.add(resu) # Charts on the result from the coordinate definition: for chart, restrictions in coord_def.items(): diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index c388c84d27a..cfd08fa8c6c 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1352,6 +1352,132 @@ def label(element): for subset in all_subsets: subset._supersets.update(all_supersets) + def declare_subset(self, *supersets): + r""" + Declare ``self`` to be a subset of each of the given supersets. + + INPUT: + + - ``supersets`` -- other subsets of the same manifold + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: U1 = M.open_subset('U1') + sage: U2 = M.open_subset('U2') + sage: V = M.open_subset('V') + sage: V.superset_family() + Set {M, V} of open subsets of the 2-dimensional differentiable manifold M + sage: U1.subset_family() + Set {U1} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: def label(element): + ....: return element._name + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 8 graphics primitives + sage: V.declare_subset(U1, U2) + sage: V.superset_family() + Set {M, U1, U2, V} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 9 graphics primitives + sage: M.declare_subset(V) + sage: M.superset_family() + Set {M, U1, U2, V} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 2 graphics primitives + + .. PLOT:: + + def label(element): + return element._name + M = Manifold(2, 'M') + U1 = M.open_subset('U1') + U2 = M.open_subset('U2') + V = M.open_subset('V') + P = M.subset_poset() + g1 = P.plot(element_labels={element: label(element) for element in P}) + V.declare_subset(U1, U2) + P = M.subset_poset() + g2 = P.plot(element_labels={element: label(element) for element in P}) + M.declare_subset(V) + P = M.subset_poset() + g3 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + """ + F = ManifoldSubsetFiniteFamily.from_subsets_or_families + supersets = F(*supersets) + all_supersets = F(*[S.supersets() for S in supersets]) + for superset in all_supersets: + superset._subsets.update(self._subsets) + for subset in self._subsets: + subset._supersets.update(all_supersets) + + def declare_superset(self, *subsets): + r""" + Declare ``self`` to be a subset of each of the given supersets. + + INPUT: + + - ``supersets`` -- other subsets of the same manifold + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: U = M.open_subset('U') + sage: V1 = M.open_subset('V1') + sage: V2 = M.open_subset('V2') + sage: W = V1.intersection(V2) + sage: U.subset_family() + Set {U} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: def label(element): + ....: return element._name + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 11 graphics primitives + sage: U.declare_superset(V1, V2) + sage: U.subset_family() + Set {U, V1, V1_inter_V2, V2} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 11 graphics primitives + sage: W.declare_superset(U) + sage: W.subset_family() + Set {U, V1, V1_inter_V2, V2} of open subsets of the 2-dimensional differentiable manifold M + sage: P = M.subset_poset() + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 4 graphics primitives + + .. PLOT:: + + def label(element): + return element._name + M = Manifold(2, 'M') + U = M.open_subset('U') + V1 = M.open_subset('V1') + V2 = M.open_subset('V2') + W = V1.intersection(V2) + P = M.subset_poset() + def label(element): + return element._name + g1 = P.plot(element_labels={element: label(element) for element in P}) + U.declare_superset(V1, V2) + P = M.subset_poset() + g2 = P.plot(element_labels={element: label(element) for element in P}) + W.declare_superset(U) + P = M.subset_poset() + g3 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + """ + F = ManifoldSubsetFiniteFamily.from_subsets_or_families + subsets = F(*subsets) + all_subsets = F(*[S.subsets() for S in subsets]) + for subset in all_subsets: + subset._supersets.update(self._supersets) + for superset in self._supersets: + superset._subsets.update(all_subsets) + def declare_empty(self): r""" Declare that ``self`` is the empty set. @@ -1630,9 +1756,7 @@ def subset(self, name, latex_name=None, is_open=False): if self.is_empty(): self.declare_equal(res) else: - res._supersets.update(self._supersets) - for sd in self._supersets: - sd._subsets.add(res) + self.declare_superset(res) self._top_subsets.add(res) return res @@ -1796,9 +1920,7 @@ def superset(self, name, latex_name=None, is_open=False): res = self._manifold.open_subset(name, latex_name=latex_name) else: res = ManifoldSubset(self._manifold, name, latex_name=latex_name) - res._subsets.update(self._subsets) - for sd in self._subsets: - sd._supersets.add(res) + res.declare_superset(self) if is_open and self._is_open: res._atlas = list(self._atlas) res._top_charts = list(self._top_charts) From 0cc9eb8c4121fa054c866679e348aaae352c81a4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 15:17:10 -0700 Subject: [PATCH 238/706] src/sage/manifolds/differentiable/examples/real_line.py: Use declare_subset --- src/sage/manifolds/differentiable/examples/real_line.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/examples/real_line.py b/src/sage/manifolds/differentiable/examples/real_line.py index 2e18868db7c..cd9efabee5f 100644 --- a/src/sage/manifolds/differentiable/examples/real_line.py +++ b/src/sage/manifolds/differentiable/examples/real_line.py @@ -376,9 +376,7 @@ def __init__(self, lower, upper, ambient_interval=None, if upper > ambient_interval.upper_bound(): raise ValueError("the upper bound is larger than that of " + "the containing interval") - self._supersets.update(ambient_interval._supersets) - for sd in ambient_interval._supersets: - sd._subsets.add(self) + self.declare_subset(ambient_interval) ambient_interval._top_subsets.add(self) t = ambient_interval.canonical_coordinate() if lower != minus_infinity: From c56cefbb2f2111bb01cc630ca5949f663a1e0c69 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 14:31:53 -0700 Subject: [PATCH 239/706] ManifoldSubset.declare_union: Accept arbitrary number of arguments, Add option 'disjoint' --- src/sage/manifolds/subset.py | 84 +++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index ce6db13bce7..749be47e798 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1294,12 +1294,92 @@ def is_subset(self, other): """ return self in other._subsets - def declare_union(self, dom1, dom2): + def declare_union(self, *subsets_or_families, disjoint=False): r""" Declare that the current subset is the union of two subsets. Suppose `U` is the current subset, then this method declares - that `U` + that `U = \bigcup_{S\in F} S`. + + INPUT: + + - ``subsets_or_families`` -- finitely many subsets or iterables of subsets + - ``disjoint`` -- (default: ``False``) whether to declare the subsets + pairwise disjoint + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: AB = M.subset('AB') + sage: A = AB.subset('A') + sage: B = AB.subset('B') + sage: def label(element): + ....: try: + ....: return element._name + ....: except AttributeError: + ....: return '[' + ', '.join(sorted(x._name for x in element)) + ']' + sage: P = M.subset_poset(open_covers=True); P + Finite poset containing 4 elements + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 8 graphics primitives + + sage: AB.declare_union(A, B) + sage: A.union(B) + Subset AB of the 2-dimensional topological manifold M + sage: P = M.subset_poset(open_covers=True); P + Finite poset containing 4 elements + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 8 graphics primitives + + sage: B1 = B.subset('B1', is_open=True) + sage: B2 = B.subset('B2', is_open=True) + sage: B.declare_union(B1, B2, disjoint=True) + sage: P = M.subset_poset(open_covers=True); P + Finite poset containing 9 elements + sage: P.plot(element_labels={element: label(element) for element in P}) + Graphics object consisting of 19 graphics primitives + + .. PLOT:: + + def label(element): + try: + return element._name + except AttributeError: + return '[' + ', '.join(sorted(x._name for x in element)) + ']' + M = Manifold(2, 'M', structure='topological') + AB = M.subset('AB') + A = AB.subset('A') + B = AB.subset('B') + P = M.subset_poset(open_covers=True); P + g1 = P.plot(element_labels={element: label(element) for element in P}) + AB.declare_union(A, B) + A.union(B) + P = M.subset_poset(open_covers=True); P + g2 = P.plot(element_labels={element: label(element) for element in P}) + B1 = B.subset('B1', is_open=True) + B2 = B.subset('B2', is_open=True) + B.declare_union(B1, B2, disjoint=True) + P = M.subset_poset(open_covers=True); P + g3 = P.plot(element_labels={element: label(element) for element in P}) + sphinx_plot(graphics_array([g1, g2, g3]), figsize=(8, 3)) + + """ + subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(*subsets_or_families) + if disjoint: + for U, V in itertools.combinations(subsets, 2): + U.intersection(V).declare_empty() + if not subsets: + self.declare_empty() + elif len(subsets) == 2: + self._declare_union_2_subsets(*subsets) + else: + raise NotImplementedError + + def _declare_union_2_subsets(self, dom1, dom2): + r""" + Declare that the current subset is the union of two of its subsets. + + Suppose `U` is the current subset, then this method declares that .. MATH:: From b9ad9141b6515ec6051d71936cff84b6e1edbb7f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 17:32:11 -0700 Subject: [PATCH 240/706] ManifoldSubset.declare_equal: New; use it for 1-arg ManifoldSubset.declare_union --- src/sage/manifolds/subset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 749be47e798..6df3fab39a5 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1370,6 +1370,8 @@ def label(element): U.intersection(V).declare_empty() if not subsets: self.declare_empty() + elif len(subsets) == 1: + self.declare_equal(*subsets) elif len(subsets) == 2: self._declare_union_2_subsets(*subsets) else: From c7763665684f77a1d3e9b7ed75599cd65932cd5c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 19:26:40 -0700 Subject: [PATCH 241/706] ManifoldSubset.intersection: Handle arbitrary intersections --- src/sage/manifolds/subset.py | 85 +++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 6df3fab39a5..2f1726e536d 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2062,13 +2062,13 @@ def superset(self, name, latex_name=None, is_open=False): res._def_chart = self._def_chart return res - def intersection(self, other, name=None, latex_name=None): + def intersection(self, *others, name=None, latex_name=None): r""" - Return the intersection of the current subset with another subset. + Return the intersection of the current subset with other subsets. INPUT: - - ``other`` -- another subset of the same manifold + - ``others`` -- other subsets of the same manifold - ``name`` -- (default: ``None``) name given to the intersection in the case the latter has to be created; the default is ``self._name`` inter ``other._name`` @@ -2079,7 +2079,7 @@ def intersection(self, other, name=None, latex_name=None): OUTPUT: - instance of :class:`ManifoldSubset` representing the - subset that is the intersection of the current subset with ``other`` + subset that is the intersection of the current subset with ``others`` EXAMPLES: @@ -2115,42 +2115,65 @@ def intersection(self, other, name=None, latex_name=None): True """ - if other._manifold != self._manifold: - raise ValueError("the two subsets do not belong to the same manifold") - # Particular cases: - if self is self._manifold: - return other - if other is self._manifold: - return self - if self in other._subsets: - return self - if other in self._subsets: - return other - # Generic case: - if other._name in self._intersections: - # the intersection has already been created: - return self._intersections[other._name] - else: - # the intersection must be created: + subsets = set(ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others)) + + # Greedily replace inclusion chains by their minimal element + # and pairs with declared intersections by their intersection + def reduce(): + for U, V in itertools.combinations(subsets, 2): + if U.is_subset(V): + subsets.remove(V) + return True + if V.is_subset(U): + subsets.remove(U) + return True + try: + UV = U._intersections[V._name] + except KeyError: + pass + else: + subsets.difference_update([U, V]) + subsets.add(UV) + return True + return False + + while reduce(): + pass + assert subsets # there must be a survivor + + def intersection_subset(*subsets_or_families, name=None, latex_name=None): + subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(*subsets_or_families) + assert len(subsets) >= 2 + if latex_name is None: if name is None: - latex_name = self._latex_name + r'\cap ' + other._latex_name + latex_name = r'\cap '.join(S._latex_name for S in subsets) else: latex_name = name if name is None: - name = self._name + "_inter_" + other._name - if self._is_open and other._is_open: - res = self.open_subset(name, latex_name=latex_name) + name = "_inter_".join(S._name for S in subsets) + if all(S.is_open() for S in subsets): + res = self.open_subset(name, latex_name=latex_name, supersets=subsets) else: res = self.subset(name, latex_name=latex_name) - res._supersets.update(other._supersets) - for sd in other._supersets: - sd._subsets.add(res) - other._top_subsets.add(res) - self._intersections[other._name] = res - other._intersections[self._name] = res + res._supersets.update(subsets) + for S in subsets: + for sd in S.supersets(): + sd._subsets.add(res) + S._top_subsets.add(res) return res + # intersection_subset is able to build the intersection of several + # subsets directly; but because we cache only pairwise intersections, + # we build the intersection by a sequence of pairwise intersections. + subset_iter = iter(subsets) + res = next(subset_iter) + for other in subset_iter: + old, res = res, intersection_subset(res, other, name=name, latex_name=latex_name) + old._intersections[other._name] = other._intersections[old._name] = res + + return res + def union(self, other, name=None, latex_name=None): r""" Return the union of the current subset with another subset. From fb6e66a44ee3c1096dfcd175699183a6f3268285 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 19:52:41 -0700 Subject: [PATCH 242/706] intersection: Refactor --- src/sage/manifolds/subset.py | 77 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 2f1726e536d..656033d33de 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2115,11 +2115,32 @@ def intersection(self, *others, name=None, latex_name=None): True """ - subsets = set(ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others)) + subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) + subset_iter = iter(self._reduce_intersections_members(subsets)) + # _intersection_subset is able to build the intersection of several + # subsets directly; but because we cache only pairwise intersections, + # we build the intersection by a sequence of pairwise intersections. + res = next(subset_iter) + for other in subset_iter: + old, res = res, res._intersection_subset(other, name=name, latex_name=latex_name) + old._intersections[other._name] = other._intersections[old._name] = res + return res + + @staticmethod + def _reduce_intersections_members(subsets): + r""" + Return a reduced set of subsets with the same intersection as the given subsets. - # Greedily replace inclusion chains by their minimal element - # and pairs with declared intersections by their intersection + It is reduced with respect to two operations: + + - replacing an inclusion chain by its minimal element + + - replacing a pair of subsets with a declared intersection by the intersection + """ + subsets = set(subsets) def reduce(): + # Greedily replace inclusion chains by their minimal element + # and pairs with declared intersections by their intersection for U, V in itertools.combinations(subsets, 2): if U.is_subset(V): subsets.remove(V) @@ -2136,42 +2157,32 @@ def reduce(): subsets.add(UV) return True return False - while reduce(): pass assert subsets # there must be a survivor + return subsets - def intersection_subset(*subsets_or_families, name=None, latex_name=None): - subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(*subsets_or_families) - assert len(subsets) >= 2 - - if latex_name is None: - if name is None: - latex_name = r'\cap '.join(S._latex_name for S in subsets) - else: - latex_name = name + def _intersection_subset(self, *others, name=None, latex_name=None): + r""" + Return a subset that is the intersection of the given subsets. + """ + subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) + if latex_name is None: if name is None: - name = "_inter_".join(S._name for S in subsets) - if all(S.is_open() for S in subsets): - res = self.open_subset(name, latex_name=latex_name, supersets=subsets) + latex_name = r'\cap '.join(S._latex_name for S in subsets) else: - res = self.subset(name, latex_name=latex_name) - res._supersets.update(subsets) - for S in subsets: - for sd in S.supersets(): - sd._subsets.add(res) - S._top_subsets.add(res) - return res - - # intersection_subset is able to build the intersection of several - # subsets directly; but because we cache only pairwise intersections, - # we build the intersection by a sequence of pairwise intersections. - subset_iter = iter(subsets) - res = next(subset_iter) - for other in subset_iter: - old, res = res, intersection_subset(res, other, name=name, latex_name=latex_name) - old._intersections[other._name] = other._intersections[old._name] = res - + latex_name = name + if name is None: + name = "_inter_".join(S._name for S in subsets) + if all(S.is_open() for S in subsets): + res = self.open_subset(name, latex_name=latex_name, supersets=subsets) + else: + res = self.subset(name, latex_name=latex_name) + res._supersets.update(subsets) + for S in subsets: + for sd in S.supersets(): + sd._subsets.add(res) + S._top_subsets.add(res) return res def union(self, other, name=None, latex_name=None): From b1bb1aa233b9aff67d6588203a949747bd0a9f46 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 21:41:13 -0700 Subject: [PATCH 243/706] ManifoldSubset.intersection: Only pass name, latex_name to the final intersection --- src/sage/manifolds/subset.py | 65 +++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 656033d33de..2d458c7b956 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2066,6 +2066,10 @@ def intersection(self, *others, name=None, latex_name=None): r""" Return the intersection of the current subset with other subsets. + This methods may return a previously constructed intersection instead + of creating a new subset. In this case, ``name`` and ``latex_name`` + are not used. + INPUT: - ``others`` -- other subsets of the same manifold @@ -2097,7 +2101,49 @@ def intersection(self, *others, name=None, latex_name=None): sage: c.superset_family() Set {A, A_inter_B, B, M} of subsets of the 2-dimensional topological manifold M - Some checks:: + Intersection of six subsets:: + + sage: T = Manifold(2, 'T', structure='topological') + sage: S = [T.subset(f'S{i}') for i in range(6)] + sage: [S[i].intersection(S[i+3]) for i in range(3)] + [Subset S0_inter_S3 of the 2-dimensional topological manifold T, + Subset S1_inter_S4 of the 2-dimensional topological manifold T, + Subset S2_inter_S5 of the 2-dimensional topological manifold T] + sage: inter_S_i = T.intersection(*S, name='inter_S_i'); inter_S_i + Subset inter_S_i of the 2-dimensional topological manifold T + sage: inter_S_i.superset_family() + Set {S0_inter_S3, S1, S1_inter_S4, S1_inter_S4_inter_S2_inter_S5, + S2_inter_S5, S4, T, inter_S_i} + of subsets of the 2-dimensional topological manifold T + + .. PLOT:: + + def label(element): + if isinstance(element, str): + return element + try: + return element._name.replace('_inter_', '∩') + except AttributeError: + return '[' + ', '.join(sorted(label(x) for x in element)) + ']' + + M = Manifold(2, 'M', structure='topological') + a = M.subset('A') + b = M.subset('B') + c = a.intersection(b); c + P = M.subset_poset(open_covers=True) + g1 = P.plot(element_labels={element: label(element) for element in P}) + + T = Manifold(2, 'T', structure='topological') + from sage.typeset.unicode_art import unicode_subscript + S = [T.subset(f'S{unicode_subscript(i)}') for i in range(6)] + [S[i].intersection(S[i+3]) for i in range(3)] + T.intersection(*S, name=f'⋂ᵢSᵢ') + P = T.subset_poset(open_covers=True) + g2 = P.plot(element_labels={element: label(element) for element in P}) + + sphinx_plot(graphics_array([g1, g2]), figsize=(8, 3)) + + TESTS:: sage: (a.intersection(b)).is_subset(a) True @@ -2116,18 +2162,25 @@ def intersection(self, *others, name=None, latex_name=None): """ subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) - subset_iter = iter(self._reduce_intersections_members(subsets)) + subset_iter = iter(self._reduce_intersection_members(subsets)) # _intersection_subset is able to build the intersection of several # subsets directly; but because we cache only pairwise intersections, # we build the intersection by a sequence of pairwise intersections. res = next(subset_iter) - for other in subset_iter: - old, res = res, res._intersection_subset(other, name=name, latex_name=latex_name) + others = list(subset_iter) + if not others: + return res + for other in others[:-1]: + old, res = res, res._intersection_subset(other) old._intersections[other._name] = other._intersections[old._name] = res + # The last one gets the name + other = others[-1] + old, res = res, res._intersection_subset(other, name=name, latex_name=latex_name) + old._intersections[other._name] = other._intersections[old._name] = res return res @staticmethod - def _reduce_intersections_members(subsets): + def _reduce_intersection_members(subsets): r""" Return a reduced set of subsets with the same intersection as the given subsets. @@ -2165,6 +2218,8 @@ def reduce(): def _intersection_subset(self, *others, name=None, latex_name=None): r""" Return a subset that is the intersection of the given subsets. + + This method does not cache the result. """ subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) if latex_name is None: From 0032bf9f08bc66e13c17358e360b98b1c2674bbc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 22:01:56 -0700 Subject: [PATCH 244/706] ManifoldSubset.intersection: Go through ManifoldSubsetFiniteFamily to make the order of operations canonical --- src/sage/manifolds/subset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 2d458c7b956..6aa7ebfea3e 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2112,8 +2112,8 @@ def intersection(self, *others, name=None, latex_name=None): sage: inter_S_i = T.intersection(*S, name='inter_S_i'); inter_S_i Subset inter_S_i of the 2-dimensional topological manifold T sage: inter_S_i.superset_family() - Set {S0_inter_S3, S1, S1_inter_S4, S1_inter_S4_inter_S2_inter_S5, - S2_inter_S5, S4, T, inter_S_i} + Set {S0, S0_inter_S3, S0_inter_S3_inter_S1_inter_S4, S1_inter_S4, + S2_inter_S5, S3, T, inter_S_i} of subsets of the 2-dimensional topological manifold T .. PLOT:: @@ -2213,7 +2213,7 @@ def reduce(): while reduce(): pass assert subsets # there must be a survivor - return subsets + return ManifoldSubsetFiniteFamily(subsets) def _intersection_subset(self, *others, name=None, latex_name=None): r""" From 282010b434abc9468fd52519049ed54eadb222f0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 22:03:14 -0700 Subject: [PATCH 245/706] ManifoldSubset._union_subset: Factor out from ManifoldSubset.union --- src/sage/manifolds/subset.py | 78 ++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 6aa7ebfea3e..e1c04f0d598 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2317,43 +2317,53 @@ def union(self, other, name=None, latex_name=None): return self._unions[other._name] else: # the union must be created: - if latex_name is None: - if name is None: - latex_name = self._latex_name + r'\cup ' + other._latex_name - else: - latex_name = name - if name is None: - name = self._name + "_union_" + other._name - res_open = self._is_open and other._is_open - res = self.superset(name, latex_name, is_open=res_open) - res._subsets.update(other._subsets) - res._top_subsets.add(self) - res._top_subsets.add(other) - for sd in other._subsets: - sd._supersets.add(res) - for sp in self._supersets: - if sp in other._supersets: - sp._subsets.add(res) - res._supersets.add(sp) - if res._is_open: - for chart in other._atlas: - if chart not in res._atlas: - res._atlas.append(chart) - for chart in other._top_charts: - if chart not in res._top_charts: - res._top_charts.append(chart) - res._coord_changes.update(other._coord_changes) + res = self._union_subset(other, name=name, latex_name=latex_name) self._unions[other._name] = res other._unions[self._name] = res - # Open covers of the union: - for oc1 in self._open_covers: - for oc2 in other._open_covers: - oc = oc1[:] - for s in oc2: - if s not in oc: - oc.append(s) - res._open_covers.append(oc) return res + def _union_subset(self, other, name=None, latex_name=None): + r""" + Return a subset of the manifold that is the union of the given subsets. + + This method does not cache the result. + """ + if latex_name is None: + if name is None: + latex_name = r'\cup '.join(S._latex_name for S in (self, other)) + else: + latex_name = name + if name is None: + name = "_union_".join(S._name for S in (self, other)) + res_open = all(S.is_open() for S in (self, other)) + res = self.superset(name, latex_name, is_open=res_open) + res._subsets.update(other._subsets) + res._top_subsets.add(self) + res._top_subsets.add(other) + for sd in other._subsets: + sd._supersets.add(res) + for sp in self._supersets: + if sp in other._supersets: + sp._subsets.add(res) + res._supersets.add(sp) + if res._is_open: + for chart in other._atlas: + if chart not in res._atlas: + res._atlas.append(chart) + for chart in other._top_charts: + if chart not in res._top_charts: + res._top_charts.append(chart) + res._coord_changes.update(other._coord_changes) + # Open covers of the union: + for oc1 in self._open_covers: + for oc2 in other._open_covers: + oc = oc1[:] + for s in oc2: + if s not in oc: + oc.append(s) + res._open_covers.append(oc) + return res + + #### End of construction of new sets from self From 6772570996c36fd531558422d1709aa38854a503 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 22:25:39 -0700 Subject: [PATCH 246/706] ManifoldSubset.union: Handle arbitrary unions --- src/sage/manifolds/subset.py | 86 +++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index e1c04f0d598..d0c2a828b0e 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2066,7 +2066,7 @@ def intersection(self, *others, name=None, latex_name=None): r""" Return the intersection of the current subset with other subsets. - This methods may return a previously constructed intersection instead + This method may return a previously constructed intersection instead of creating a new subset. In this case, ``name`` and ``latex_name`` are not used. @@ -2240,13 +2240,17 @@ def _intersection_subset(self, *others, name=None, latex_name=None): S._top_subsets.add(res) return res - def union(self, other, name=None, latex_name=None): + def union(self, *others, name=None, latex_name=None): r""" - Return the union of the current subset with another subset. + Return the union of the current subset with other subsets. + + This method may return a previously constructed union instead + of creating a new subset. In this case, ``name`` and ``latex_name`` + are not used. INPUT: - - ``other`` -- another subset of the same manifold + - ``others`` -- other subsets of the same manifold - ``name`` -- (default: ``None``) name given to the union in the case the latter has to be created; the default is ``self._name`` union ``other._name`` @@ -2257,7 +2261,7 @@ def union(self, other, name=None, latex_name=None): OUTPUT: - instance of :class:`ManifoldSubset` representing the - subset that is the union of the current subset with ``other`` + subset that is the union of the current subset with ``others`` EXAMPLES: @@ -2275,7 +2279,7 @@ def union(self, other, name=None, latex_name=None): sage: c.superset_family() Set {A_union_B, M} of subsets of the 2-dimensional topological manifold M - Some checks:: + TESTS:: sage: a.is_subset(a.union(b)) True @@ -2292,8 +2296,6 @@ def union(self, other, name=None, latex_name=None): sage: M.union(a) is M True - TESTS: - Check that :trac:`30401` is fixed:: sage: d = a.subset('D') @@ -2302,25 +2304,57 @@ def union(self, other, name=None, latex_name=None): True """ - if other._manifold != self._manifold: - raise ValueError("the two subsets do not belong to the same manifold") - # Particular cases: - if (self is self._manifold) or (other is self._manifold): - return self._manifold - if self in other._subsets: - return other - if other in self._subsets: - return self - # Generic case: - if other._name in self._unions: - # the union has already been created: - return self._unions[other._name] - else: - # the union must be created: - res = self._union_subset(other, name=name, latex_name=latex_name) - self._unions[other._name] = res - other._unions[self._name] = res + subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) + subsets = self._reduce_union_members(subsets) + assert subsets + subset_iter = iter(subsets) + res = next(subset_iter) + others = list(subset_iter) + if not others: return res + for other in others[:-1]: + old, res = res, res._union_subset(other) + old._unions[other._name] = other._unions[old._name] = res + # The last one gets the name + other = others[-1] + old, res = res, res._union_subset(other, name=name, latex_name=latex_name) + old._unions[other._name] = other._unions[old._name] = res + return res + + @staticmethod + def _reduce_union_members(subsets): + r""" + Return a reduced set of subsets with the same union as the given subsets. + + It is reduced with respect to two operations: + + - replacing an inclusion chain by its maximal element + + - replacing a pair of subsets with a declared union by the union + """ + subsets = set(subsets) + def reduce(): + # Greedily replace inclusion chains by their maximal element + # and pairs with declared unions by their union + for U, V in itertools.combinations(subsets, 2): + if U.is_subset(V): + subsets.remove(U) + return True + if V.is_subset(U): + subsets.remove(V) + return True + try: + UV = U._unions[V._name] + except KeyError: + pass + else: + subsets.difference_update([U, V]) + subsets.add(UV) + return True + return False + while reduce(): + pass + return ManifoldSubsetFiniteFamily(subsets) def _union_subset(self, other, name=None, latex_name=None): r""" From edfea33c61a84addb189436807439b1fc0f7b32c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 22:42:33 -0700 Subject: [PATCH 247/706] ManifoldSubset.union: Add example and plots --- src/sage/manifolds/subset.py | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index d0c2a828b0e..7f88e45b10c 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2137,7 +2137,7 @@ def label(element): from sage.typeset.unicode_art import unicode_subscript S = [T.subset(f'S{unicode_subscript(i)}') for i in range(6)] [S[i].intersection(S[i+3]) for i in range(3)] - T.intersection(*S, name=f'⋂ᵢSᵢ') + T.intersection(*S, name='⋂ᵢSᵢ') P = T.subset_poset(open_covers=True) g2 = P.plot(element_labels={element: label(element) for element in P}) @@ -2279,6 +2279,48 @@ def union(self, *others, name=None, latex_name=None): sage: c.superset_family() Set {A_union_B, M} of subsets of the 2-dimensional topological manifold M + Union of six subsets:: + + sage: T = Manifold(2, 'T', structure='topological') + sage: S = [T.subset(f'S{i}') for i in range(6)] + sage: [S[i].union(S[i+3]) for i in range(3)] + [Subset S0_union_S3 of the 2-dimensional topological manifold T, + Subset S1_union_S4 of the 2-dimensional topological manifold T, + Subset S2_union_S5 of the 2-dimensional topological manifold T] + sage: union_S_i = S[0].union(S[1:], name='union_S_i'); union_S_i + Subset union_S_i of the 2-dimensional topological manifold T + sage: T.subset_family() + Set {S0, S0_union_S3, S0_union_S3_union_S1_union_S4, S1, + S1_union_S4, S2, S2_union_S5, S3, S4, S5, T, union_S_i} + of subsets of the 2-dimensional topological manifold T + + .. PLOT:: + + def label(element): + if isinstance(element, str): + return element + try: + return element._name.replace('_union_', '∪') + except AttributeError: + return '[' + ', '.join(sorted(label(x) for x in element)) + ']' + + M = Manifold(2, 'M', structure='topological') + a = M.subset('A') + b = M.subset('B') + c = a.union(b); c + P = M.subset_poset(open_covers=True) + g1 = P.plot(element_labels={element: label(element) for element in P}) + + T = Manifold(2, 'T', structure='topological') + from sage.typeset.unicode_art import unicode_subscript + S = [T.subset(f'S{unicode_subscript(i)}') for i in range(6)] + [S[i].union(S[i+3]) for i in range(3)] + union_S_i = S[0].union(S[1:], name='⋃ᵢSᵢ'); union_S_i + P = T.subset_poset(open_covers=True) + g2 = P.plot(element_labels={element: label(element) for element in P}) + + sphinx_plot(graphics_array([g1, g2]), figsize=(8, 3)) + TESTS:: sage: a.is_subset(a.union(b)) From fdd0aba9ef75cc0c0fc07757b59c98ab272ca030 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 24 Apr 2021 23:14:43 -0700 Subject: [PATCH 248/706] ManifoldSubset.declare_union: Handle arbitrary unions --- src/sage/manifolds/subset.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 7f88e45b10c..f825a36dadc 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1368,14 +1368,16 @@ def label(element): if disjoint: for U, V in itertools.combinations(subsets, 2): U.intersection(V).declare_empty() + subsets = self._reduce_union_members(subsets) if not subsets: self.declare_empty() elif len(subsets) == 1: self.declare_equal(*subsets) - elif len(subsets) == 2: - self._declare_union_2_subsets(*subsets) else: - raise NotImplementedError + subset_iter = iter(subsets) + first = next(subset_iter) + second = next(subset_iter) + self._declare_union_2_subsets(first, second.union(subset_iter)) def _declare_union_2_subsets(self, dom1, dom2): r""" From c2f484c87cc890540b6b9b882bfd433bf292e730 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 15:59:44 -0700 Subject: [PATCH 249/706] src/sage/manifolds/subset.py: import itertools --- src/sage/manifolds/subset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f825a36dadc..d4961d08e98 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -66,6 +66,7 @@ #***************************************************************************** from collections import defaultdict +import itertools from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.misc.superseded import deprecation From a08356971af378cabec7cd5a51be8e597ea77a67 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 16:26:32 -0700 Subject: [PATCH 250/706] ManifoldSubset._intersection_subset, _union_subset: Use declare_subset, declare_superset; fix doctest --- src/sage/manifolds/subset.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index d4961d08e98..21860498279 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2115,9 +2115,9 @@ def intersection(self, *others, name=None, latex_name=None): sage: inter_S_i = T.intersection(*S, name='inter_S_i'); inter_S_i Subset inter_S_i of the 2-dimensional topological manifold T sage: inter_S_i.superset_family() - Set {S0, S0_inter_S3, S0_inter_S3_inter_S1_inter_S4, S1_inter_S4, - S2_inter_S5, S3, T, inter_S_i} - of subsets of the 2-dimensional topological manifold T + Set {S0, S0_inter_S3, S0_inter_S3_inter_S1_inter_S4, S1, S1_inter_S4, + S2, S2_inter_S5, S3, S4, S5, T, inter_S_i} of + subsets of the 2-dimensional topological manifold T .. PLOT:: @@ -2236,10 +2236,8 @@ def _intersection_subset(self, *others, name=None, latex_name=None): res = self.open_subset(name, latex_name=latex_name, supersets=subsets) else: res = self.subset(name, latex_name=latex_name) - res._supersets.update(subsets) + res.declare_subset(subsets) for S in subsets: - for sd in S.supersets(): - sd._subsets.add(res) S._top_subsets.add(res) return res @@ -2416,11 +2414,9 @@ def _union_subset(self, other, name=None, latex_name=None): name = "_union_".join(S._name for S in (self, other)) res_open = all(S.is_open() for S in (self, other)) res = self.superset(name, latex_name, is_open=res_open) - res._subsets.update(other._subsets) + res.declare_superset(other) res._top_subsets.add(self) res._top_subsets.add(other) - for sd in other._subsets: - sd._supersets.add(res) for sp in self._supersets: if sp in other._supersets: sp._subsets.add(res) From fc64ba2ec7d3337e2799fc57f78dcbb30dfa0fe7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 3 May 2021 11:24:54 +1000 Subject: [PATCH 251/706] Improvements to sign() and comparisons for qqbar elements. --- src/sage/rings/qqbar.py | 97 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 671644effc8..de40261f5a3 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4607,8 +4607,8 @@ def _richcmp_(self, other, op): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-79 - 0.7598602580415435?*I, - 0.?e-79 + 0.7598602580415435?*I, + 0.?e-215 - 0.7598602580415435?*I, + 0.?e-229 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] @@ -4699,6 +4699,9 @@ def _richcmp_(self, other, op): sage: a > r True """ + if self is other: + return rich_to_bool(op, 0) + # note: we can assume that self is not other here sd = self._descr od = other._descr @@ -5068,7 +5071,7 @@ def rational_argument(self): -1/3 sage: QQbar(3+4*I).rational_argument() is None True - sage: (QQbar(2)**(1/5) * QQbar.zeta(7)**2).rational_argument() + sage: (QQbar(2)**(1/5) * QQbar.zeta(7)**2).rational_argument() # long time 2/7 sage: (QQbar.zeta(73)**5).rational_argument() 5/73 @@ -5218,7 +5221,31 @@ def _richcmp_(self, other, op): True sage: AA(7) >= AA(50/7) False + + Check for trivial equality with identical elements:: + + sage: x1 = AA(2^(1/100)) + sage: x2 = AA(2^(1/100)) + sage: y = x1 - x2 + sage: y == y + True + sage: y >= y + True + sage: y < y + False + + sage: z = x1 - x2 + sage: z == 0 + True + + sage: a = x1 - x2 + sage: b = x1 - x2 + sage: a == b + True """ + if self is other: + return rich_to_bool(op, 0) + # note: we can assume that self is not other here sd = self._descr od = other._descr @@ -5238,8 +5265,14 @@ def _richcmp_(self, other, op): type(od) is ANExtensionElement and sd._generator is od._generator): return sd._value == od._value if op == op_EQ else sd._value != od._value - elif self.minpoly() != other.minpoly(): - return op == op_NE + else: + # Only compare the minimal polynomials if they have been computed + # as otherwise it calls exactify(). + try: + if self._minimal_polynomial != other._minimal_polynomial: + return op == op_NE + except AttributeError: + pass # case 0: real parts are clearly distinct if not self._value.overlaps(other._value): @@ -5256,10 +5289,15 @@ def _richcmp_(self, other, op): # case 2: possibly equal values # (this case happen a lot when sorting the roots of a real polynomial) - if self.minpoly() == other.minpoly(): - c = cmp_elements_with_same_minpoly(self, other, self.minpoly()) - if c is not None: - return rich_to_bool(op, c) + # Only compare the minimal polynomials if they have been computed + # as otherwise it calls exactify(). + try: + if self._minimal_polynomial != other._minimal_polynomial: + c = cmp_elements_with_same_minpoly(self, other, self.minpoly()) + if c is not None: + return rich_to_bool(op, c) + except AttributeError: + pass if self._value.prec() < 128: self._more_precision() @@ -5547,6 +5585,12 @@ def sign(self): 1 sage: (a*b - b*a).sign() 0 + + sage: x1 = AA(2^(1/100)) + sage: x2 = AA(2^(1/100)) + sage: y = x1 - x2 + sage: y.sign() + 0 """ if not self._value.contains_zero(): return self._value.unique_sign() @@ -5566,7 +5610,7 @@ def sign(self): ls = sd._left.sign() rs = sd._right.sign() if sd._op is operator.mul or sd._op is operator.truediv: - return sd._left.sign() * sd._right.sign() + return ls * rs elif sd._op is operator.add: if ls == rs: return ls @@ -5579,6 +5623,9 @@ def sign(self): elif not rs: self._set_descr(sd._left._descr) return ls + elif sd._left is sd._right: + self._set_descr(ANRational(QQ.zero())) + return 0 elif type(sd) is ANUnaryExpr: if sd._op == 'abs': c = 1 if bool(sd._arg) else 0 @@ -5596,6 +5643,36 @@ def sign(self): if not self._value.contains_zero(): return self._value.unique_sign() + if type(sd) is ANBinaryExpr: + # We will now exactify both sides and do another sign comparison. + # We try to avoid making ourself exact if possible. + # It will only reach this block if the operation is addition or subtraction. + sd._left.exactify() + sd._right.exactify() + + # Rationals + if type(sd._left._descr) is ANRational and type(sd._right._descr) is ANRational: + ret = sd._op(sd._left._descr._value, sd._right._descr._value) + if ret == 0: + self._set_descr(ANRational(QQ.zero())) + return 0 + return ret.sign() + + if sd._left.minpoly() == sd._right.minpoly(): + c = cmp_elements_with_same_minpoly(sd._left, sd._right, sd._left.minpoly()) + if c == 0: + self._set_descr(ANRational(QQ.zero())) + return 0 + elif c is not None: + return c + + ret = sd._op(sd._left._value, sd._right._value) + if not ret.contains_zero(): + return ret.unique_sign() + if not ret: # Known to be exactly 0 + self._set_descr(ANRational(QQ.zero())) + return 0 + # Sigh... self.exactify() return self.sign() From 0239765c1daa2ac22f92af03b231f6a6315b59c4 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 3 May 2021 20:55:25 +0200 Subject: [PATCH 252/706] 31774: let GAP handle errors in PrimitiveGroups --- src/sage/groups/perm_gps/permgroup_named.py | 50 ++++++++------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 0bc4baa4c36..753613884f8 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -2153,11 +2153,10 @@ class PrimitiveGroup(PermutationGroup_unique): Only primitive groups of "small" degree are available in GAP's database:: - sage: PrimitiveGroup(2500,1) + sage: PrimitiveGroup(2^12,1) Traceback (most recent call last): ... - NotImplementedError: Only the primitive groups of degree less - than 2500 are available in GAP's database + GAPError: Error, Primitive groups of degree 4096 are not known! """ def __init__(self, d, n): @@ -2252,14 +2251,12 @@ def PrimitiveGroups(d=None): sage: PrimitiveGroups() Primitive Groups - The database currently only contains primitive groups up to degree - 2499:: + The database is currently limited:: - sage: PrimitiveGroups(2500).cardinality() + sage: PrimitiveGroups(2^12).cardinality() Traceback (most recent call last): ... - NotImplementedError: Only the primitive groups of degree less - than 2500 are available in GAP's database + GAPError: Error, Primitive groups of degree 4096 are not known! .. TODO:: @@ -2423,10 +2420,7 @@ def __contains__(self, G): sage: 1 in PrimitiveGroups(4) False """ - if isinstance(G,PrimitiveGroup): - return G._d == self._degree - else: - False + return isinstance(G, PrimitiveGroup) and G._d == self._degree def __getitem__(self, n): r""" @@ -2486,35 +2480,29 @@ def cardinality(self): sage: [PrimitiveGroups(i).cardinality() for i in range(11)] [1, 1, 1, 2, 2, 5, 4, 7, 7, 11, 9] - GAP contains all primitive groups up to degree 2499:: - - sage: PrimitiveGroups(2500).cardinality() - Traceback (most recent call last): - ... - NotImplementedError: Only the primitive groups of degree less than - 2500 are available in GAP's database - TESTS:: sage: type(PrimitiveGroups(12).cardinality()) sage: type(PrimitiveGroups(0).cardinality()) + + Check for :trac:`31774`:: + + sage: PrimitiveGroups(2500).cardinality() + 34 + sage: PrimitiveGroups(2^12).cardinality() + Traceback (most recent call last): + ... + GAPError: Error, Primitive groups of degree 4096 are not known! """ - # gap.NrPrimitiveGroups(0) fails, so Sage needs to handle this - # While we are at it, and since Sage also handles the - # primitive group of degree 1, we may as well handle 1 if self._degree <= 1: + # gap.NrPrimitiveGroups(0) fails, so Sage needs to handle this + # While we are at it, and since Sage also handles the + # primitive group of degree 1, we may as well handle 1 return Integer(1) - elif self._degree >= 2500: - raise NotImplementedError("Only the primitive groups of degree less than 2500 are available in GAP's database") else: - try: - return Integer(libgap.NrPrimitiveGroups(self._degree)) - except RuntimeError: - from sage.misc.verbose import verbose - verbose("Error: PrimitiveGroups should be in GAP already.", level=0) - + return Integer(libgap.NrPrimitiveGroups(self._degree)) class PermutationGroup_plg(PermutationGroup_unique): def base_ring(self): From e621dead0fd109bde132c655995e9c1f9e61bdba Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Mon, 3 May 2021 23:31:20 +0200 Subject: [PATCH 253/706] 31775: initial version --- src/sage/interfaces/interface.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 66f57641c00..38d17c8105e 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -332,6 +332,16 @@ def _coerce_from_special_method(self, x): return self(x._interface_init_()) def _coerce_impl(self, x, use_special=True): + r""" + Coerce pur Python types via corresponding Sage objects. + + TESTS: + + Check that python type ``complex`` can be converted (:trac:`31775`):: + + sage: giac(complex(I))**2 # should not return `j^2` + -1 + """ if isinstance(x, bool): return self(self._true_symbol() if x else self._false_symbol()) elif isinstance(x, int): @@ -340,6 +350,9 @@ def _coerce_impl(self, x, use_special=True): elif isinstance(x, float): import sage.rings.all return self(sage.rings.all.RDF(x)) + elif isinstance(x, complex): + import sage.rings.all + return self(sage.rings.all.CDF(x)) if use_special: try: return self._coerce_from_special_method(x) From b069b81e5e5fc26b917f22eef825e5746f5510fb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 4 May 2021 15:36:36 +1000 Subject: [PATCH 254/706] Fixing bug with addition x + (-x), adding doctests, removing redundancy. --- src/sage/rings/qqbar.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index de40261f5a3..62272f935bd 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -5224,8 +5224,8 @@ def _richcmp_(self, other, op): Check for trivial equality with identical elements:: - sage: x1 = AA(2^(1/100)) - sage: x2 = AA(2^(1/100)) + sage: x1 = AA(2^(1/50)) + sage: x2 = AA(2^(1/50)) sage: y = x1 - x2 sage: y == y True @@ -5250,6 +5250,7 @@ def _richcmp_(self, other, op): sd = self._descr od = other._descr + # case 0: rationals if type(sd) is ANRational and type(od) is ANRational: return richcmp(sd._value, od._value, op) @@ -5274,19 +5275,13 @@ def _richcmp_(self, other, op): except AttributeError: pass - # case 0: real parts are clearly distinct + # case 1: real parts are clearly distinct if not self._value.overlaps(other._value): # NOTE: do not call richcmp here as self._value and other._value # might have different precisions. See # https://trac.sagemath.org/ticket/29220 return self._value._richcmp_(other._value, op) - # case 1: rationals - sd = self._descr - od = other._descr - if type(sd) is ANRational and type(od) is ANRational: - return richcmp(sd._value, od._value, op) - # case 2: possibly equal values # (this case happen a lot when sorting the roots of a real polynomial) # Only compare the minimal polynomials if they have been computed @@ -5586,11 +5581,29 @@ def sign(self): sage: (a*b - b*a).sign() 0 - sage: x1 = AA(2^(1/100)) - sage: x2 = AA(2^(1/100)) + sage: a = AA(sqrt(1/2)) + sage: b = AA(-sqrt(1/2)) + sage: (a + b).sign() + 0 + + TESTS: + + We avoid calling :meth:`exactify()` for trivial differences. The + following example will take a long time (more than 5 seconds) + when calling ``y.exactify()``:: + + sage: x1 = AA(2^(1/50)) + sage: x2 = AA(2^(1/50)) sage: y = x1 - x2 sage: y.sign() 0 + + Simplify to rationals for binary operations when computing the sign:: + + sage: a = AA(2^(1/60)) + sage: b = a - (a + 1) + sage: (b + 1).sign() + 0 """ if not self._value.contains_zero(): return self._value.unique_sign() @@ -5659,7 +5672,9 @@ def sign(self): return ret.sign() if sd._left.minpoly() == sd._right.minpoly(): - c = cmp_elements_with_same_minpoly(sd._left, sd._right, sd._left.minpoly()) + # Negating the element does not change the minpoly + right = sd._right if sd._op is operator.sub else -sd._right + c = cmp_elements_with_same_minpoly(sd._left, right, sd._left.minpoly()) if c == 0: self._set_descr(ANRational(QQ.zero())) return 0 From 3b8e217e54446b5cf6d7a3a18770fe4455f4cc94 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 4 May 2021 16:01:14 +1000 Subject: [PATCH 255/706] Removing one other redundancy. --- src/sage/rings/qqbar.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 62272f935bd..0df5488c17b 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -5254,10 +5254,15 @@ def _richcmp_(self, other, op): if type(sd) is ANRational and type(od) is ANRational: return richcmp(sd._value, od._value, op) + # case 1: real parts are clearly distinct + if not self._value.overlaps(other._value): + # NOTE: do not call richcmp here as self._value and other._value + # might have different precisions. See + # https://trac.sagemath.org/ticket/29220 + return self._value._richcmp_(other._value, op) + if op == op_EQ or op == op_NE: # some cheap and quite common tests where we can decide equality or difference - if not self._value.real().overlaps(other._value.real()): - return op == op_NE if type(sd) is ANRational and not sd._value: return bool(other) == (op == op_NE) elif type(od) is ANRational and not od._value: @@ -5275,13 +5280,6 @@ def _richcmp_(self, other, op): except AttributeError: pass - # case 1: real parts are clearly distinct - if not self._value.overlaps(other._value): - # NOTE: do not call richcmp here as self._value and other._value - # might have different precisions. See - # https://trac.sagemath.org/ticket/29220 - return self._value._richcmp_(other._value, op) - # case 2: possibly equal values # (this case happen a lot when sorting the roots of a real polynomial) # Only compare the minimal polynomials if they have been computed From 5da8b2774eb2e024cc7a850789e1ed1a6816d354 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 4 May 2021 16:06:13 +1000 Subject: [PATCH 256/706] Removing same redundant checks for the QQbar elements. --- src/sage/rings/qqbar.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 0df5488c17b..33ab2460861 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4702,18 +4702,25 @@ def _richcmp_(self, other, op): if self is other: return rich_to_bool(op, 0) - # note: we can assume that self is not other here + # case 0: rationals sd = self._descr od = other._descr - if isinstance(sd, ANRational) and isinstance(od, ANRational): return richcmp(sd._value, od._value, op) + # case 1: real parts are clearly distinct + ri1 = self._value.real() + ri2 = other._value.real() + if not ri1.overlaps(ri2): + # NOTE: do not call richcmp here as self._value and other._value + # might have different precisions. See + # https://trac.sagemath.org/ticket/29220 + return ri1._richcmp_(ri2, op) + if op == op_EQ or op == op_NE: # some cheap and quite common tests where we can decide # equality or difference - if not (self._value.real().overlaps(other._value.real()) and - self._value.imag().overlaps(other._value.imag())): + if not self._value.imag().overlaps(other._value.imag()): return op == op_NE if isinstance(sd, ANRational) and not sd._value: return bool(other) == (op == op_NE) @@ -4724,21 +4731,6 @@ def _richcmp_(self, other, op): sd._generator is od._generator): return sd._value == od._value if op == op_EQ else sd._value != od._value - # case 0: real parts are clearly distinct - ri1 = self._value.real() - ri2 = other._value.real() - if not ri1.overlaps(ri2): - # NOTE: do not call richcmp here as self._value and other._value - # might have different precisions. See - # https://trac.sagemath.org/ticket/29220 - return ri1._richcmp_(ri2, op) - - # case 1: rationals - sd = self._descr - od = other._descr - if isinstance(sd, ANRational) and isinstance(od, ANRational): - return richcmp(sd._value, od._value, op) - # case 2: possibly equal or conjugate values # (this case happen a lot when sorting the roots of a real polynomial) ci1 = self._value.imag().abs() From 9c216e05102cfcb161c8f3b832b3af9c65a1371b Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Tue, 4 May 2021 21:37:35 +0200 Subject: [PATCH 257/706] 18119: Work around some codomain issues --- src/sage/combinat/words/morphism.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 124c9972cf2..d9a0db1b2fe 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3416,6 +3416,9 @@ def infinite_repetitions_bounded(self, w=None): sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') sage: sorted(m.infinite_repetitions_bounded()) [word: 1, word: 1519181716, word: 2, word: 2529282726] + + sage: WordMorphism('a->b,b->b', codomain=FiniteWords('ab')).infinite_repetitions() + set() """ def impl(): U = {} @@ -3444,7 +3447,9 @@ def impl(): if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) + f._codomain = f._domain g, _, k, _ = f.simplify_injective() + g._codomain = g._domain unbounded = set(g.growing_letters()) gb = g.restrict_domain(set(g._morph) - unbounded) @@ -3498,7 +3503,9 @@ def infinite_repetitions_growing(self, w=None): if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) + f._codomain = f._domain g, _, k, _ = f.simplify_injective() + g._codomain = g._domain unbounded = set(g.growing_letters()) result = set() From 24f3bf96117bbeed8864977081094a9bfd641d53 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 5 May 2021 15:57:45 +1000 Subject: [PATCH 258/706] Small Python tweaks to immortal_letters(). --- src/sage/combinat/words/morphism.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index b7eb2bd8a03..ce25088b0b7 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3109,8 +3109,8 @@ def growing_letters(self): """ # Remove letters of type b->a, a->. immortal = self.immortal_letters() - new_morph = {x : [z for z in y if z in immortal] - for x, y in self._morph.items() if x in immortal} + new_morph = {x: [z for z in y if z in immortal] + for x, y in self._morph.items() if x in immortal} # Remove letters of type c->bd, d->ca. graph_first = {x : y[0] for x, y in new_morph.items()} @@ -3119,8 +3119,8 @@ def growing_letters(self): if any(len(new_morph[letter]) > 1 for letter in cycle): continue loops.update(cycle) - new_morph = {x : [z for z in y if z not in loops] - for x, y in new_morph.items() if x not in loops} + new_morph = {x: [z for z in y if z not in loops] + for x, y in new_morph.items() if x not in loops} # Remove letters of type e->abcd. new_morph = WordMorphism(new_morph, domain=self.domain(), codomain=self.codomain()) @@ -3145,9 +3145,8 @@ def immortal_letters(self): if not self.is_endomorphism(): raise TypeError(f'self ({self}) is not an endomorphism') - forward, backward = {}, {} - for letter in self._morph: - forward[letter], backward[letter] = set(), set() + forward = {letter: set() for letter in self._morph} + backward = {letter: set() for letter in self._morph} stack = [] for preimage, image in self._morph.items(): From ce00ad67f08e0e6a72957a9e4c578adfe9fa1390 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 5 May 2021 09:26:10 +0200 Subject: [PATCH 259/706] improve growing letters, immortal letters --- src/sage/combinat/words/morphism.py | 89 ++++++++++++++++------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index ce25088b0b7..e297ee8e545 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -108,8 +108,7 @@ def get_cycles(f, domain=None): r""" - Return the cycle of the function ``f`` on the finite set domain. It is - assumed that f is an endomorphism. + Return the cycle of the function ``f`` on the finite set domain. INPUT: @@ -127,6 +126,8 @@ def get_cycles(f, domain=None): [(0,)] sage: get_cycles(lambda i: [1,1,1][i], domain=[0,1,2]) [(1,)] + sage: get_cycles(lambda i: [2,3,0][i], domain=[0,1,2]) + [(0, 2)] """ if domain is None: try: @@ -134,18 +135,17 @@ def get_cycles(f, domain=None): except AttributeError: raise ValueError("you should specify the domain of the function f") cycles = [] - not_seen = dict((letter,True) for letter in domain) - for a in not_seen: - if not_seen[a]: - not_seen[a] = False - cycle = [a] - b = f(a) - while not_seen[b]: - not_seen[b] = False - cycle.append(b) - b = f(b) - if b in cycle: - cycles.append(tuple(cycle[cycle.index(b):])) + not_seen = set(domain) + while not_seen: + a = not_seen.pop() + cycle = [a] + b = f(a) + while b in not_seen: + not_seen.remove(b) + cycle.append(b) + b = f(b) + if b in cycle: + cycles.append(tuple(cycle[cycle.index(b):])) return cycles @@ -3099,6 +3099,10 @@ def growing_letters(self): ['0'] sage: WordMorphism('0->01,1->0,2->1',codomain=Words('012')).growing_letters() ['0', '1', '2'] + sage: WordMorphism('a->b,b->a').growing_letters() + [] + sage: WordMorphism('a->b,b->c,c->d,d->c', codomain=Words('abcd')).growing_letters() + [] TESTS: @@ -3107,26 +3111,21 @@ def growing_letters(self): sage: WordMorphism('a->a').growing_letters() [] """ - # Remove letters of type b->a, a->. + # Remove letters that vanishes, ie sigma^n(letter) is ultimately empty immortal = self.immortal_letters() - new_morph = {x: [z for z in y if z in immortal] - for x, y in self._morph.items() if x in immortal} - - # Remove letters of type c->bd, d->ca. - graph_first = {x : y[0] for x, y in new_morph.items()} - loops = set() - for cycle in get_cycles(graph_first.__getitem__, graph_first): - if any(len(new_morph[letter]) > 1 for letter in cycle): - continue - loops.update(cycle) - new_morph = {x: [z for z in y if z not in loops] - for x, y in new_morph.items() if x not in loops} + new_morph = {x: [z for z in self._morph[x] if z in immortal] for x in immortal} - # Remove letters of type e->abcd. - new_morph = WordMorphism(new_morph, domain=self.domain(), codomain=self.codomain()) - result = new_morph.immortal_letters() + # Remove cycles of letters + graph_one = {x : y[0] for x, y in new_morph.items() if len(y) == 1} + no_loops = set(new_morph) + for cycle in get_cycles(graph_one.__getitem__, graph_one): + no_loops.difference_update(cycle) + new_morph = {x: [z for z in new_morph[x] if z in no_loops] for x in no_loops} - return sorted(result, key=self.domain().alphabet().rank) + # Remove letters ending in a cycle + W = FiniteWords(sorted(new_morph, key=self.domain().alphabet().rank)) + new_morph = WordMorphism(new_morph, domain=W, codomain=W) + return new_morph.immortal_letters() def immortal_letters(self): r""" @@ -3139,22 +3138,32 @@ def immortal_letters(self): EXAMPLES:: + sage: WordMorphism('a->a').immortal_letters() + ['a'] + sage: WordMorphism('a->b,b->a').immortal_letters() + ['a', 'b'] sage: WordMorphism('a->abcd,b->cd,c->dd,d->').immortal_letters() - {'a'} + ['a'] + sage: WordMorphism('a->bc,b->cac,c->de,d->,e->').immortal_letters() + ['a', 'b'] + sage: WordMorphism('a->', domain=Words('a'), codomain=Words('a')).immortal_letters() + [] """ if not self.is_endomorphism(): raise TypeError(f'self ({self}) is not an endomorphism') - forward = {letter: set() for letter in self._morph} + forward = {} backward = {letter: set() for letter in self._morph} - stack = [] - for preimage, image in self._morph.items(): + for letter, image in self._morph.items(): if not image: - stack.append(preimage) - for occurrence in image: - forward[preimage].add(occurrence) - backward[occurrence].add(preimage) + stack.append(letter) + forward[letter] = set() + else: + simage = set(image) + forward[letter] = simage + for occurrence in simage: + backward[occurrence].add(letter) while stack: letter = stack.pop() @@ -3165,7 +3174,7 @@ def immortal_letters(self): del forward[letter] del backward[letter] - return set(forward) + return sorted(forward, key=self.domain().alphabet().rank) def abelian_rotation_subspace(self): r""" From a6fa7c6325413de0e1f8a7dbeb90799378c9e9e4 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 5 May 2021 09:32:30 +0200 Subject: [PATCH 260/706] do not build custom domain/codomain --- src/sage/combinat/words/morphism.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index e297ee8e545..a825b36afef 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3123,8 +3123,11 @@ def growing_letters(self): new_morph = {x: [z for z in new_morph[x] if z in no_loops] for x in no_loops} # Remove letters ending in a cycle - W = FiniteWords(sorted(new_morph, key=self.domain().alphabet().rank)) - new_morph = WordMorphism(new_morph, domain=W, codomain=W) + # NOTE: here we should actually be using the domain made of the + # remaining letters in new_morph. However, building the corresponding + # alphabet and finite words cost much more time than using the same + # domain. + new_morph = WordMorphism(new_morph, domain=self.domain(), codomain=self.codomain()) return new_morph.immortal_letters() def immortal_letters(self): From 9443892b2e86d4edf4f040fa7b1736583ab4c29a Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 5 May 2021 08:59:07 +0100 Subject: [PATCH 261/706] #31443: updated eclib tarball to 20210503 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 5d12b8a60e0..8342f43b6da 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=2f0ea52fd59352fa0dec8cabf5d513b0f0cf7629 -md5=2a3d4b37ee5f504d46c010e29040d7a4 -cksum=2697309856 +sha1=b8e1bd10a28341fcd8146cf12fc38a12c798c555 +md5=d54562480e1ebd73ae3f343b949dfa66 +cksum=2657346485 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index e9d3aa7a596..891293d2df1 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210415 +20210503 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index edb759d45f5..4bced5f44ed 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210415"]) + m4_pushdef([SAGE_ECLIB_VER],["20210503"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From 263890855e11b5bf43435e8bc24b3f0666f2b5fc Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Wed, 5 May 2021 10:18:21 +0200 Subject: [PATCH 262/706] 31760: Add one set() call --- src/sage/combinat/words/morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index a825b36afef..21991fe95bc 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3111,8 +3111,8 @@ def growing_letters(self): sage: WordMorphism('a->a').growing_letters() [] """ - # Remove letters that vanishes, ie sigma^n(letter) is ultimately empty - immortal = self.immortal_letters() + # Remove letters that vanish, ie sigma^n(letter) is ultimately empty + immortal = set(self.immortal_letters()) new_morph = {x: [z for z in self._morph[x] if z in immortal] for x in immortal} # Remove cycles of letters From 5aab984e33a165b1a0e6b5a259c8b0c388bf2372 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Wed, 5 May 2021 11:09:10 +0200 Subject: [PATCH 263/706] 31760: Replace set with list in docs --- src/sage/combinat/words/morphism.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 21991fe95bc..731a55f14fe 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3132,7 +3132,7 @@ def growing_letters(self): def immortal_letters(self): r""" - Return the set of immortal letters. + Return the list of immortal letters. A letter `a` is *immortal* for the morphism `s` if the length of the iterates of `| s^n(a) |` is larger than zero as `n` goes to infinity. From 317ad72b85edfc53d2ac95f1505926aae2fdb640 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 5 May 2021 17:07:56 +0200 Subject: [PATCH 264/706] symmetries of the plane and a classical bijection with Dyck paths --- src/sage/combinat/parallelogram_polyomino.py | 199 +++++++++++++++++-- 1 file changed, 178 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index e24ed79070b..f7b56cd9e23 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -1185,24 +1185,78 @@ def __init__(self, parent, value, check=True): self.check() self._options = None + def reflect(self): + r""" + Return the parallelogram polyomino obtained by switching rows and + columns. + + EXAMPLES:: + + sage: pp = ParallelogramPolyomino([[0,0,0,0,1,1,0,1,0,1], [1,0,1,0,0,1,1,0,0,0]]) + sage: pp.heights(), pp.upper_heights() + ([4, 3, 2, 3], [0, 1, 3, 3]) + sage: pp = pp.reflect() + sage: pp.widths(), pp.lower_widths() + ([4, 3, 2, 3], [0, 1, 3, 3]) + + sage: pp = ParallelogramPolyomino([[0,0,0,1,1], [1,0,0,1,0]]) + sage: ascii_art(pp) + * + * + ** + sage: ascii_art(pp.reflect()) + *** + * + """ + a, b = self + return ParallelogramPolyomino([[1-v for v in b], [1-v for v in a]]) + + def rotate(self): + r""" + Return the parallelogram polyomino obtained by rotation of 180 degrees. + + EXAMPLES:: + + sage: pp = ParallelogramPolyomino([[0,0,0,1,1], [1,0,0,1,0]]) + sage: ascii_art(pp) + * + * + ** + sage: ascii_art(pp.rotate()) + ** + * + * + """ + a, b = self + return ParallelogramPolyomino([b[::-1], a[::-1]]) + + def _to_dyck_delest_viennot(self): r""" Convert to a Dyck word using the Delest-Viennot bijection. - This bijection is described page 179 and page 180 Figure 6 in - the article [DeVi1984]_. + This bijection is described on page 179 and page 180 Figure 6 + in the article [DeVi1984]_, where it is called the classical + bijection `\gamma`. EXAMPLES:: - sage: pp = ParallelogramPolyomino( - ....: [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] - ....: ) + sage: pp = ParallelogramPolyomino([[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]]) sage: pp._to_dyck_delest_viennot() [1, 1, 0, 1, 1, 0, 1, 0, 0, 0] + + TESTS:: + + sage: pp = ParallelogramPolyomino([[1], [1]]) + sage: pp._to_dyck_delest_viennot() + [] + """ from sage.combinat.dyck_word import DyckWord dyck = [] dick_size = self.size()-1 + if not dick_size: + return DyckWord([]) upper_path = self.upper_path() lower_path = self.lower_path() dyck.append(1 - lower_path[0]) @@ -1212,20 +1266,53 @@ def _to_dyck_delest_viennot(self): dyck.append(upper_path[dick_size]) return DyckWord(dyck) + def _to_dyck_delest_viennot_peaks_valleys(self): + r""" + Convert to a Dyck word using the Delest-Viennot bijection `\beta`. + + This bijection is described on page 182 and Figure 8 in the + article [DeVi1984]_. It returns the unique Dyck path whose + peak heights are the column heights and whose valley heights + are the overlaps between adjacent columns. + + + + EXAMPLES: + + This is the example in Figure 8 of [DeVi1984]_:: + + sage: pp = ParallelogramPolyomino([[0,0,0,0,1,1,0,1,0,1], [1,0,1,0,0,1,1,0,0,0]]) + sage: pp._to_dyck_delest_viennot_peaks_valleys() + [1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0] + + TESTS:: + + sage: pp = ParallelogramPolyomino([[1], [1]]) + sage: pp._to_dyck_delest_viennot_peaks_valleys() + [] + + """ + from sage.combinat.dyck_word import DyckWord + a = self.heights() + u = self.upper_heights() + b = [0] + [a[i]-u[i+1]+u[i]-1 for i in range(len(a)-1)] + [0] + dyck = [] + for i in range(len(a)): + dyck.extend([1]*(a[i]-b[i])) + dyck.extend([0]*(a[i]-b[i+1])) + return DyckWord(dyck) + @combinatorial_map(name="To Dyck word") def to_dyck_word(self, bijection=None): r""" Convert to a Dyck word. - This bijection is described page 179 and page 180 Figure 6 in - the article [DeVi1984]_. - INPUT: - ``bijection`` -- string or ``None`` (default:``None``) The name of the bijection. If it is set to ``None`` then the ``'Delest-Viennot'`` bijection is used. - Expected values are ``None`` or ``'Delest-Viennot'``. + Expected values are ``None``, ``'Delest-Viennot'``, or ``'Delest-Viennot-beta'``. OUTPUT: @@ -1233,24 +1320,30 @@ def to_dyck_word(self, bijection=None): EXAMPLES:: - sage: pp = ParallelogramPolyomino( - ....: [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] - ....: ) + sage: pp = ParallelogramPolyomino([[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]]) sage: pp.to_dyck_word() [1, 1, 0, 1, 1, 0, 1, 0, 0, 0] sage: pp.to_dyck_word(bijection='Delest-Viennot') [1, 1, 0, 1, 1, 0, 1, 0, 0, 0] + + sage: pp.to_dyck_word(bijection='Delest-Viennot-beta') + [1, 0, 1, 1, 1, 0, 1, 0, 0, 0] """ if bijection is None or bijection == 'Delest-Viennot': return self._to_dyck_delest_viennot() + if bijection == 'Delest-Viennot-beta': + return self._to_dyck_delest_viennot_peaks_valleys() + raise ValueError("The given bijection is not valid.") @staticmethod def _from_dyck_word_delest_viennot(dyck): r""" - Convert Dyck word to parallelogram polyomino using the Delest Viennot - bijection. + Convert a Dyck word to a parallelogram polyomino using the Delest + Viennot bijection. - This bijection come from the article [DeVi1984]_. + This bijection is described on page 179 and page 180 Figure 6 in + the article [DeVi1984]_, where it is called the classical + bijection `\gamma`. INPUT: @@ -1265,6 +1358,14 @@ def _from_dyck_word_delest_viennot(dyck): sage: dyck = DyckWord([1, 1, 0, 1, 1, 0, 1, 0, 0, 0]) sage: ParallelogramPolyomino._from_dyck_word_delest_viennot(dyck) [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] + + TESTS:: + + sage: gamma = ParallelogramPolyomino._to_dyck_delest_viennot + sage: gamma_inv = ParallelogramPolyomino._from_dyck_word_delest_viennot + sage: all(all(D == gamma(gamma_inv(D)) for D in DyckWords(n)) for n in range(7)) + True + """ l = [1] + list(dyck) + [0] word_up = [] @@ -1274,6 +1375,62 @@ def _from_dyck_word_delest_viennot(dyck): word_down.append(1 - l[i+1]) return ParallelogramPolyomino([word_down, word_up]) + @staticmethod + def _from_dyck_word_delest_viennot_peaks_valleys(dyck): + r""" + Convert a Dyck word to a parallelogram polyomino using the Delest + Viennot bijection `\beta`. + + This bijection is described on page 182 and Figure 8 in + the article [DeVi1984]_. + + INPUT: + + - ``dyck`` -- a Dyck word + + OUTPUT: + + A parallelogram polyomino. + + EXAMPLES:: + + sage: dyck = DyckWord([1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0]) + sage: ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck) + [[0, 0, 0, 0, 1, 1, 0, 1, 0, 1], [1, 0, 1, 0, 0, 1, 1, 0, 0, 0]] + + sage: dyck = DyckWord([1,1,0,1,1,1,1,1,0,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0]) + sage: ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck) + [[0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1], [1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0]] + + TESTS:: + + sage: beta = ParallelogramPolyomino._to_dyck_delest_viennot_peaks_valleys + sage: beta_inv = ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys + sage: all(all(D == beta(beta_inv(D)) for D in DyckWords(n)) for n in range(7)) + True + """ + if not dyck: + return ParallelogramPolyomino([[1], [1]]) + a = [] + b = [0] + h = 0 + for i in range(len(dyck)-1): + if dyck[i] == 1: + h += 1 + if dyck[i+1] == 0: + a.append(h) + else: + if dyck[i+1] == 1: + b.append(h) + h -= 1 + b.append(0) + word_down = [] + word_up = [] + for i in range(len(a)): + word_down.extend([0]*(a[i]-b[i]) + [1]) + word_up.extend([1]+[0]*(a[i]-b[i+1])) + return ParallelogramPolyomino([word_down, word_up]) + @staticmethod def from_dyck_word(dyck, bijection=None): r""" @@ -1293,17 +1450,17 @@ def from_dyck_word(dyck, bijection=None): EXAMPLES:: sage: dyck = DyckWord([1, 1, 0, 1, 1, 0, 1, 0, 0, 0]) - sage: pp = ParallelogramPolyomino.from_dyck_word(dyck) - sage: pp + sage: ParallelogramPolyomino.from_dyck_word(dyck) [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] - sage: pp = ParallelogramPolyomino.from_dyck_word( - ....: dyck, bijection='Delest-Viennot' - ....: ) - sage: pp + sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot') [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] + sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot-beta') """ if bijection is None or bijection == 'Delest-Viennot': return ParallelogramPolyomino._from_dyck_word_delest_viennot(dyck) + if bijection == 'Delest-Viennot-beta': + return ParallelogramPolyomino._from_dyck_word_delest_viennot_peaks_valleys(dyck) + raise ValueError("The given bijection is not valid.") def _to_binary_tree_Aval_Boussicault(self, position=[0, 0]): r""" From 0763059370784e5542bca5860161e53defa14643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 5 May 2021 17:21:03 +0200 Subject: [PATCH 265/706] remove deprecation in free_module (python2 cmp related methods) --- src/sage/combinat/free_module.py | 118 +++++++------------------------ 1 file changed, 27 insertions(+), 91 deletions(-) diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index eb3305f3af1..1dfe7d6cc33 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -29,7 +29,6 @@ import sage.data_structures.blas_dict as blas from sage.typeset.ascii_art import AsciiArt, ascii_art from sage.typeset.unicode_art import UnicodeArt, unicode_art -from sage.misc.superseded import deprecation class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): @@ -413,7 +412,7 @@ def __init__(self, R, basis_keys=None, element_class=None, category=None, ... ValueError: keyy is not a valid print option. """ - #Make sure R is a ring with unit element + # Make sure R is a ring with unit element from sage.categories.all import Rings if R not in Rings(): raise TypeError("Argument R must be a ring.") @@ -529,7 +528,7 @@ def _an_element_(self): pass try: g = iter(self.basis().keys()) - for c in range(1,4): + for c in range(1, 4): x = x + self.term(next(g), R(c)) except Exception: pass @@ -556,7 +555,7 @@ def __contains__(self, x): sage: 5/3 in F False """ - return parent(x) == self # is self? + return parent(x) == self # is self? def _element_constructor_(self, x): """ @@ -661,7 +660,7 @@ def _element_constructor_(self, x): """ R = self.base_ring() - #Coerce ints to Integers + # Coerce ints to Integers if isinstance(x, int): x = Integer(x) @@ -670,12 +669,12 @@ def _element_constructor_(self, x): return self.zero() else: raise TypeError("do not know how to make x (= %s) an element of %s" % (x, self)) - #x is an element of the basis enumerated set; + # x is an element of the basis enumerated set; # This is a very ugly way of testing this elif ((hasattr(self._indices, 'element_class') and isinstance(self._indices.element_class, type) and - isinstance(x, self._indices.element_class)) - or (parent(x) == self._indices)): + isinstance(x, self._indices.element_class)) or + parent(x) == self._indices): return self.monomial(x) elif x in self._indices: return self.monomial(self._indices(x)) @@ -710,9 +709,10 @@ def _convert_map_from_(self, S): if isinstance(S, FormalSums) and K.has_coerce_map_from(S.base_ring()): G = self.basis().keys() return SetMorphism(S.Hom(self, category=self.category() | S.category()), - lambda x: self.sum_of_terms( (G(g), K(c)) for c,g in x )) + lambda x: self.sum_of_terms((G(g), K(c)) + for c, g in x)) - def _an_element_impl(self): # TODO: REMOVE? + def _an_element_impl(self): # TODO: REMOVE? """ Return an element of ``self``, namely the zero element. @@ -848,71 +848,6 @@ def get_order(self): self.set_order(self.basis().keys().list()) return self._order - def get_order_cmp(self): - """ - Return a comparison function on the basis indices that is - compatible with the current term order. - - DEPRECATED by :trac:`24548`. - - EXAMPLES:: - - sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example() - sage: Acmp = A.get_order_cmp() - doctest:warning...: - DeprecationWarning: comparison should use keys - See http://trac.sagemath.org/24548 for details. - - sage: sorted(A.basis().keys(), Acmp) # py2 - ['x', 'y', 'a', 'b'] - sage: A.set_order(list(reversed(A.basis().keys()))) - sage: Acmp = A.get_order_cmp() - sage: sorted(A.basis().keys(), Acmp) # py2 - ['b', 'a', 'y', 'x'] - """ - deprecation(24548, 'comparison should use keys') - self.get_order() - return self._order_cmp - - def _order_cmp(self, x, y): - """ - Compare `x` and `y` w.r.t. the term order. - - DEPRECATED by :trac:`24548`. - - INPUT: - - - ``x``, ``y`` -- indices of the basis of ``self`` - - OUTPUT: - - `-1`, `0`, or `1` depending on whether `xy` - w.r.t. the term order. - - EXAMPLES:: - - sage: A = CombinatorialFreeModule(QQ, ['x','y','a','b']) - sage: A.set_order(['x', 'y', 'a', 'b']) - sage: A._order_cmp('x', 'y') - doctest:warning...: - DeprecationWarning: comparison should use keys - See http://trac.sagemath.org/24548 for details. - -1 - sage: A._order_cmp('y', 'y') - 0 - sage: A._order_cmp('a', 'y') - 1 - """ - deprecation(24548, 'comparison should use keys') - ix = self._rank_basis(x) - iy = self._rank_basis(y) - if ix < iy: - return -1 - elif ix > iy: - return 1 - else: - return 0 - def get_order_key(self): """ Return a comparison key on the basis indices that is @@ -971,7 +906,8 @@ def from_vector(self, vector, order=None): """ if order is None: order = self.get_order() - return self._from_dict({order[index]: coeff for (index,coeff) in vector.items()}) + return self._from_dict({order[index]: coeff + for (index, coeff) in vector.items()}) def sum(self, iter_of_elements): """ @@ -1019,9 +955,9 @@ def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): sage: F.linear_combination( (f,i) for i in range(5) ) 20*B[1] + 20*B[2] """ - return self._from_dict(blas.linear_combination( ((element._monomial_coefficients, coeff) + return self._from_dict(blas.linear_combination(((element._monomial_coefficients, coeff) for element, coeff in iter_of_elements_coeff), - factor_on_left=factor_on_left ), + factor_on_left=factor_on_left), remove_zeros=False) def term(self, index, coeff=None): @@ -1577,19 +1513,18 @@ def _coerce_map_from_(self, R): sage: T(tensor((p,p))) 4*B[2] # B[2] + 4*B[2] # B[4] + 4*B[4] # B[2] + 4*B[4] # B[4] """ - if ((R in ModulesWithBasis(self.base_ring()).TensorProducts() - or R in GradedAlgebrasWithBasis(self.base_ring()).SignedTensorProducts()) + if ((R in ModulesWithBasis(self.base_ring()).TensorProducts() or + R in GradedAlgebrasWithBasis(self.base_ring()).SignedTensorProducts()) and isinstance(R, CombinatorialFreeModule_Tensor) and len(R._sets) == len(self._sets) and all(self._sets[i].has_coerce_map_from(M) - for i,M in enumerate(R._sets))): + for i, M in enumerate(R._sets))): modules = R._sets vector_map = [self._sets[i]._internal_coerce_map_from(M) - for i,M in enumerate(modules)] + for i, M in enumerate(modules)] return R.module_morphism(lambda x: self._tensor_of_elements( - [vector_map[i](M.monomial(x[i])) - for i,M in enumerate(modules)]), - codomain=self) + [vector_map[i](M.monomial(x[i])) + for i, M in enumerate(modules)]), codomain=self) return super(CombinatorialFreeModule_Tensor, self)._coerce_map_from_(R) @@ -1628,7 +1563,8 @@ def __call__(self, *indices): (1, 2, 3, 4, 5, 6, 7, 8) """ - return sum( (i if flatten else (i,) for (i,flatten) in zip(indices, self._flatten) ), ()) + return sum((i if flatten else (i,) + for (i, flatten) in zip(indices, self._flatten)), ()) # TODO: find a way to avoid this hack to allow for cross references @@ -1686,7 +1622,7 @@ def __init__(self, modules, **options): Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 7} over Integer Ring sage: TestSuite(C).run() """ - assert(len(modules) > 0) # TODO: generalize to a family or tuple + assert(len(modules)) # TODO: generalize to a family or tuple R = modules[0].base_ring() assert(all(module in ModulesWithBasis(R)) for module in modules) # should check the base ring @@ -1834,9 +1770,9 @@ def _cartesian_product_of_elements(self, elements): B[(0, 0)] + B[(1, 0)] """ - return self.sum( self.summand_embedding(i)(element_i) - for (i, element_i) in zip(self._sets_keys(), - elements) ) + return self.sum(self.summand_embedding(i)(element_i) + for (i, element_i) in zip(self._sets_keys(), + elements)) def cartesian_factors(self): """ @@ -1853,7 +1789,7 @@ def cartesian_factors(self): """ return self._sets - class Element(CombinatorialFreeModule.Element): # TODO: get rid of this inheritance + class Element(CombinatorialFreeModule.Element): # TODO: get rid of this inheritance pass From 63e43e71a586636685bf898b363e49167f823517 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 5 May 2021 18:12:02 +0200 Subject: [PATCH 266/706] add forgotten output of doctest --- src/sage/combinat/parallelogram_polyomino.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index f7b56cd9e23..c9f69f79311 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -1455,6 +1455,7 @@ def from_dyck_word(dyck, bijection=None): sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot') [[0, 1, 0, 0, 1, 1], [1, 1, 1, 0, 0, 0]] sage: ParallelogramPolyomino.from_dyck_word(dyck, bijection='Delest-Viennot-beta') + [[0, 0, 1, 0, 1, 1], [1, 1, 1, 0, 0, 0]] """ if bijection is None or bijection == 'Delest-Viennot': return ParallelogramPolyomino._from_dyck_word_delest_viennot(dyck) From df24dfb104cf129537f6a93d18ab7f06be30f46e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 5 May 2021 18:24:18 +0200 Subject: [PATCH 267/706] catch border case --- src/sage/combinat/parallelogram_polyomino.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index c9f69f79311..f238c132189 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -1207,7 +1207,15 @@ def reflect(self): sage: ascii_art(pp.reflect()) *** * + + TESTS:: + + sage: pp = ParallelogramPolyomino([[1], [1]]) + sage: pp.reflect() + [[1], [1]] """ + if self.size() == 1: + return self a, b = self return ParallelogramPolyomino([[1-v for v in b], [1-v for v in a]]) From 14001685024278a7917290111cf1ca2a3629ef57 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 6 May 2021 13:42:22 +0200 Subject: [PATCH 268/706] fix ordering in get_cycles --- src/sage/combinat/words/morphism.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 731a55f14fe..e3fa92e494a 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -136,10 +136,12 @@ def get_cycles(f, domain=None): raise ValueError("you should specify the domain of the function f") cycles = [] not_seen = set(domain) - while not_seen: - a = not_seen.pop() + for a in domain: + if a not in not_seen: + continue cycle = [a] b = f(a) + not_seen.remove(a) while b in not_seen: not_seen.remove(b) cycle.append(b) From 35f617b7204db754e702356c7f5901df8797fa69 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 6 May 2021 15:11:46 +0200 Subject: [PATCH 269/706] clarify get_cycles --- src/sage/combinat/words/morphism.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index e3fa92e494a..c9084a3c8ba 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -106,34 +106,31 @@ from sage.combinat.words.words import FiniteWords, FiniteOrInfiniteWords -def get_cycles(f, domain=None): +def get_cycles(f, domain): r""" - Return the cycle of the function ``f`` on the finite set domain. + Return the list of cycles of the function ``f`` contained in ``domain``. INPUT: - ``f`` - function. - - ``domain`` - set (default: None) - the domain of ``f``. If none, then - tries to use ``f.domain()``. + - ``domain`` - iterable, a subdomain of the domain of definition of ``f``. EXAMPLES:: sage: from sage.combinat.words.morphism import get_cycles - sage: get_cycles(lambda i: (i+1)%3, domain=[0,1,2]) + sage: get_cycles(lambda i: (i+1)%3, [0,1,2]) [(0, 1, 2)] - sage: get_cycles(lambda i: [0,0,0][i], domain=[0,1,2]) + sage: get_cycles(lambda i: [0,0,0][i], [0,1,2]) [(0,)] - sage: get_cycles(lambda i: [1,1,1][i], domain=[0,1,2]) + sage: get_cycles(lambda i: [1,1,1][i], [0,1,2]) [(1,)] - sage: get_cycles(lambda i: [2,3,0][i], domain=[0,1,2]) + sage: get_cycles(lambda i: [2,3,0][i], [0,1,2]) [(0, 2)] + sage: d = {'a': 'a', 'b': 'b'} + sage: get_cycles(d.__getitem__, 'ba') + [('b',), ('a',)] """ - if domain is None: - try: - domain = f.domain() - except AttributeError: - raise ValueError("you should specify the domain of the function f") cycles = [] not_seen = set(domain) for a in domain: @@ -2026,7 +2023,7 @@ def periodic_points(self): res = [] parent = self.codomain().shift() - for cycle in get_cycles(CallableDict(d),A): + for cycle in get_cycles(CallableDict(d), A): if cycle[0] in G: P = PeriodicPointIterator(self, cycle) res.append([parent(P._cache[i]) for i in range(len(cycle))]) From bd5031bb8570c073fc2ebe5a0473559f0f670508 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 6 May 2021 16:46:41 +0200 Subject: [PATCH 270/706] Trac 31784: fix repr for destination maps --- src/sage/manifolds/differentiable/mixed_form.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/mixed_form.py b/src/sage/manifolds/differentiable/mixed_form.py index e9fdcdb9916..aa4c89d9e8c 100644 --- a/src/sage/manifolds/differentiable/mixed_form.py +++ b/src/sage/manifolds/differentiable/mixed_form.py @@ -267,15 +267,25 @@ def _repr_(self): 'Mixed differential form F on the 3-dimensional differentiable manifold M' + Check whether :trac:`31784` is fixed:: + + sage: E3 = EuclideanSpace(3) + sage: S2 = E3.sphere() + sage: iota = S2.embedding() + sage: Omega = S2.mixed_form_algebra(dest_map=iota) + sage: Omega(1) + Mixed differential form one along the 2-sphere S^2 of radius 1 + smoothly embedded in the Euclidean space E^3 with values on the + Euclidean space E^3 via the map iota + """ desc = "Mixed differential form " if self._name is not None: desc += self._name + " " if self._dest_map is self._domain.identity_map(): - desc += "on the {}".format(self._domain) + desc += f"on the {self._domain}" else: - desc += "along the {} with values on the {} " - desc += desc.format(self._domain, self._ambient_domain) + desc += f"along the {self._domain} with values on the {self._ambient_domain} " if self._dest_map._name is None: dm_name = "unnamed map" else: From 4cd7c037637491dbfb18b1b3c2dca39b2700221e Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 6 May 2021 20:27:12 +0200 Subject: [PATCH 271/706] Trac #31669: add examples --- src/sage/categories/chain_complexes.py | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 5b9883f56f1..057485fcc24 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -14,7 +14,7 @@ #***************************************************************************** from .category_types import Category_module -from .modules import Modules +from .commutative_additive_groups import CommutativeAdditiveGroups from .functor import Functor from sage.misc.abstract_method import abstract_method @@ -121,7 +121,11 @@ def lift_from_homology(self, x): EXAMPLES:: - ... + sage: E3 = EuclideanSpace(3) + sage: C = E3.de_rham_complex() + sage: one = C.homology().one() + sage: C.lift_from_homology(one) + Mixed differential form one on the Euclidean space E^3 """ @@ -138,12 +142,16 @@ def reduce_to_homology(self, x, n=None): EXAMPLES:: - ... + sage: E3 = EuclideanSpace(3) + sage: C = E3.de_rham_complex() + sage: one = C.one() + sage: C.reduce_to_homology(one) + [one] """ try: # try coercion - self.homology(n)(x) + return self.homology(n)(x) except TypeError: # if not, this methods needs to be overwritten by parent raise NotImplementedError @@ -207,7 +215,7 @@ def __init__(self, domain, n=None): """ if not isinstance(domain, ChainComplexes): raise TypeError(f'{domain} must be a category of chain complexes') - codomain = Modules(domain.base_ring()) + codomain = CommutativeAdditiveGroups() super().__init__(domain, codomain) self.__n = n @@ -231,12 +239,20 @@ def _apply_functor_to_morphism(self, f): TESTS: - ... + sage: E3 = EuclideanSpace(3) + sage: C = E3.de_rham_complex() + sage: id = Hom(C, C).identity() + sage: H = HomologyFunctor(ChainComplexes(SR)) + sage: id_star = H(id); id_star + Generic endomorphism of De Rham cohomology ring on the + Euclidean space E^3 + sage: one = C.one() + sage: id_star(one) + [one] """ from .morphism import SetMorphism from .homset import Hom - from .commutative_additive_groups import CommutativeAdditiveGroups domain = f.domain() codomain = f.codomain() From 030873618ef95119bdc4eaedf71b3171bced826f Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 6 May 2021 20:31:45 +0200 Subject: [PATCH 272/706] Trac #31669: minor doctest improvements --- src/sage/categories/chain_complexes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index 057485fcc24..d02925a57b1 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -210,7 +210,7 @@ def __init__(self, domain, n=None): sage: H = HomologyFunctor(ChainComplexes(QQ), 1); H Functor from Category of chain complexes over Rational Field to - Category of vector spaces over Rational Field + Category of commutative additive groups """ if not isinstance(domain, ChainComplexes): @@ -246,7 +246,7 @@ def _apply_functor_to_morphism(self, f): sage: id_star = H(id); id_star Generic endomorphism of De Rham cohomology ring on the Euclidean space E^3 - sage: one = C.one() + sage: one = H(C).one() sage: id_star(one) [one] From 2f9fbe5a27439b62ac1873feedcf03dc681b621f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 7 May 2021 15:08:11 +0200 Subject: [PATCH 273/706] remove deprecated stuff from 8920 --- src/sage/combinat/words/alphabet.py | 99 +---------------------------- 1 file changed, 2 insertions(+), 97 deletions(-) diff --git a/src/sage/combinat/words/alphabet.py b/src/sage/combinat/words/alphabet.py index 88ae2b66ecb..3286eb8b3b4 100644 --- a/src/sage/combinat/words/alphabet.py +++ b/src/sage/combinat/words/alphabet.py @@ -41,8 +41,6 @@ from sage.rings.infinity import Infinity from sage.sets.non_negative_integers import NonNegativeIntegers -from sage.sets.positive_integers import PositiveIntegers -from sage.misc.persist import register_unpickle_override set_of_letters = { @@ -56,8 +54,7 @@ 'octal' : "01234567", 'decimal' : "0123456789", 'hexadecimal' : "0123456789abcdef", - 'radix64' : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", - } + 'radix64' : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"} def build_alphabet(data=None, names=None, name=None): @@ -267,98 +264,6 @@ def build_alphabet(data=None, names=None, name=None): raise ValueError("unable to construct an alphabet from the given parameters") + # TODO: should it be deprecated as it is no more a class ? Alphabet = build_alphabet - -# NOTE: all of the classes below are here for backward compatibility (pickling). -# More precisely, the ticket #8920 suppress several classes. The following code -# just allows to unpickle old style alphabet saved from previous version of -# Sage. - - -class OrderedAlphabet(object): - r""" - .. WARNING:: - - Not to be used! (backward compatibility) - - Returns a finite or infinite ordered alphabet. - - EXAMPLES:: - - sage: from sage.combinat.words.alphabet import OrderedAlphabet - sage: A = OrderedAlphabet('ab'); A - doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. - See http://trac.sagemath.org/8920 for details. - {'a', 'b'} - sage: type(A) - - """ - def __new__(self, alphabet=None, name=None): - """ - EXAMPLES:: - - sage: from sage.combinat.words.alphabet import OrderedAlphabet - sage: A = OrderedAlphabet('ab'); A # indirect doctest - doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. - See http://trac.sagemath.org/8920 for details. - {'a', 'b'} - """ - from sage.misc.superseded import deprecation - deprecation(8920, 'OrderedAlphabet is deprecated; use Alphabet instead.') - - if alphabet is not None or name is not None: - return build_alphabet(data=alphabet, name=name) - from sage.structure.parent import Parent - return Parent.__new__(OrderedAlphabet_backward_compatibility) - -OrderedAlphabet_Finite = OrderedAlphabet - - -class OrderedAlphabet_backward_compatibility(TotallyOrderedFiniteSet): - r""" - .. WARNING:: - - Not to be used! (backward compatibility) - - Version prior to :trac:`8920` uses classes ``Alphabet`` with an argument - ``._alphabet`` instead of ``._elements`` used in - :class:`TotallyOrderedFiniteSet`. This class is dedicated to handle this - problem which occurs when unpickling ``OrderedAlphabet``. - """ - def __getattr__(self, name): - r""" - If the attribute '_elements' is called then it is set to '_alphabet'. - - EXAMPLES:: - - sage: from sage.combinat.words.alphabet import OrderedAlphabet - sage: O = OrderedAlphabet() - doctest:...: DeprecationWarning: OrderedAlphabet is deprecated; use Alphabet instead. - See http://trac.sagemath.org/8920 for details. - sage: O._alphabet = ['a', 'b'] - sage: O._elements - ('a', 'b') - """ - if name == '_elements': - if not hasattr(self, '_alphabet'): - raise AttributeError("no attribute '_elements'") - self._elements = tuple(self._alphabet) - from sage.structure.parent import Parent - from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets - Parent.__init__(self, category=FiniteEnumeratedSets(), facade=True) - return self._elements - raise AttributeError("no attribute %s" % name) - - -register_unpickle_override( - 'sage.combinat.words.alphabet', - 'OrderedAlphabet_NaturalNumbers', - NonNegativeIntegers, - call_name=('sage.sets.non_negative_integers', 'NonNegativeIntegers')) - -register_unpickle_override( - 'sage.combinat.words.alphabet', - 'OrderedAlphabet_PositiveIntegers', - PositiveIntegers, - call_name=('sage.sets.positive_integers', 'PositiveIntegers')) From 969d9e3c5ed31e86e423b218ea077f4307d632b1 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 8 May 2021 12:23:15 +0200 Subject: [PATCH 274/706] Trac #31669: __n -> _n + differential alias for hochschild homology --- src/sage/categories/chain_complexes.py | 10 +++++----- src/sage/homology/hochschild_complex.py | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py index d02925a57b1..3b7d35ceef3 100644 --- a/src/sage/categories/chain_complexes.py +++ b/src/sage/categories/chain_complexes.py @@ -217,7 +217,7 @@ def __init__(self, domain, n=None): raise TypeError(f'{domain} must be a category of chain complexes') codomain = CommutativeAdditiveGroups() super().__init__(domain, codomain) - self.__n = n + self._n = n def _apply_functor(self, x): r""" @@ -231,7 +231,7 @@ def _apply_functor(self, x): Z x C3 """ - return x.homology(self.__n) + return x.homology(self._n) def _apply_functor_to_morphism(self, f): r""" @@ -258,8 +258,8 @@ def _apply_functor_to_morphism(self, f): codomain = f.codomain() lift = domain.lift_from_homology reduce = codomain.reduce_to_homology - apply_f_star = lambda x: reduce(f(lift(x)), self.__n) - return SetMorphism(Hom(domain.homology(self.__n), - codomain.homology(self.__n), + apply_f_star = lambda x: reduce(f(lift(x)), self._n) + return SetMorphism(Hom(domain.homology(self._n), + codomain.homology(self._n), CommutativeAdditiveGroups()), apply_f_star) diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index 78f38739dc5..478118b4274 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -282,6 +282,8 @@ def on_basis(k): return ret return Fd1.module_morphism(on_basis, codomain=Fd) + differential = boundary + def coboundary(self, d): """ Return the coboundary morphism of degree ``d``. From a5b8b3a18f95a96d2349d2e9a3c416ebcb7e6c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 14:21:12 +0200 Subject: [PATCH 275/706] details in partitions --- src/sage/combinat/partition.py | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 09e87b2e92f..6991d69379b 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -281,6 +281,7 @@ # **************************************************************************** from copy import copy +from itertools import accumulate from sage.libs.all import pari from sage.libs.flint.arith import number_of_partitions as flint_number_of_partitions @@ -1032,7 +1033,7 @@ def pp(self): def __truediv__(self, p): """ - Returns the skew partition ``self / p``. + Return the skew partition ``self / p``. EXAMPLES:: @@ -1704,6 +1705,7 @@ def next_within_bounds(self, min=[], max=None, partition_type=None): min = min + [0] * (len(max) - len(min)) # finally, run the algo to find next_p next_p = copy(p) + def condition(a, b): if partition_type in ('strict', 'strictly decreasing'): return a < b - 1 @@ -2645,10 +2647,9 @@ def initial_tableau(self): sage: Partition([3,2,2]).initial_tableau() [[1, 2, 3], [4, 5], [6, 7]] """ - mu = self._list - # In Python 3, improve this using itertools.accumulate - tab = [list(range(1+sum(mu[:i]), 1+sum(mu[:(i+1)]))) - for i in range(len(mu))] + sigma = list(accumulate([1] + self._list)) + tab = [list(range(sigma[i], sigma[i + 1])) + for i in range(len(sigma) - 1)] return tableau.StandardTableau(tab) def initial_column_tableau(self): @@ -5281,6 +5282,7 @@ def dual_equivalence_graph(self, directed=False, coloring=None): if coloring is None: d = {2: 'red', 3: 'blue', 4: 'green', 5: 'purple', 6: 'brown', 7: 'orange', 8: 'yellow'} + def coloring(i): if i in d: return d[i] @@ -5999,7 +6001,7 @@ def __init__(self): def subset(self, size=None, **kwargs): """ - Returns the subset of partitions of a given size and additional + Return the subset of partitions of a given size and additional keyword arguments. EXAMPLES:: @@ -6060,10 +6062,9 @@ def __reversed__(self): yield self.element_class(self, p) n += 1 - def from_frobenius_coordinates(self, frobenius_coordinates): """ - Returns a partition from a pair of sequences of Frobenius coordinates. + Return a partition from a pair of sequences of Frobenius coordinates. EXAMPLES:: @@ -6131,7 +6132,7 @@ def from_beta_numbers(self, beta): def from_exp(self, exp): """ - Returns a partition from its list of multiplicities. + Return a partition from its list of multiplicities. EXAMPLES:: @@ -6189,7 +6190,7 @@ def from_zero_one(self, seq): def from_core_and_quotient(self, core, quotient): """ - Returns a partition from its core and quotient. + Return a partition from its core and quotient. Algorithm from mupad-combinat. @@ -6355,7 +6356,7 @@ def _repr_(self): def _an_element_(self): """ - Returns a partition in ``self``. + Return a partition in ``self``. EXAMPLES:: @@ -6605,7 +6606,7 @@ def random_element_plancherel(self): def first(self): """ - Returns the lexicographically first partition of a positive integer + Return the lexicographically first partition of a positive integer `n`. This is the partition ``[n]``. EXAMPLES:: @@ -6730,7 +6731,7 @@ def _repr_(self): def _an_element_(self): """ - Returns a partition in ``self``. + Return a partition in ``self``. EXAMPLES:: @@ -7520,11 +7521,12 @@ def __setstate__(self, data): """ n = data['n'] self.__class__ = Partitions_with_constraints - constraints = {'max_slope' : 0, - 'min_part' : 1} + constraints = {'max_slope': 0, + 'min_part': 1} constraints.update(data['constraints']) self.__init__(n, **constraints) + class Partitions_with_constraints(IntegerListsLex): """ Partitions which satisfy a set of constraints. @@ -8026,7 +8028,7 @@ def cardinality(self): def _an_element_(self): """ - Returns a partition in ``self``. + Return a partition in ``self``. EXAMPLES:: @@ -8639,7 +8641,7 @@ def _an_element_(self): def number_of_partitions(n, algorithm='default'): r""" - Returns the number of partitions of `n` with, optionally, at most `k` + Return the number of partitions of `n` with, optionally, at most `k` parts. The options of :meth:`number_of_partitions()` are being deprecated From ffc848a52c53756495bb9011c68079b443e886ea Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Sat, 8 May 2021 15:36:27 +0200 Subject: [PATCH 276/706] Trac #31669: small documentation fix of #31691 --- src/sage/manifolds/differentiable/mixed_form_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/mixed_form_algebra.py b/src/sage/manifolds/differentiable/mixed_form_algebra.py index 145fb64cf96..ec41c7125ff 100644 --- a/src/sage/manifolds/differentiable/mixed_form_algebra.py +++ b/src/sage/manifolds/differentiable/mixed_form_algebra.py @@ -471,7 +471,7 @@ def cohomology(self, *args, **kwargs): r""" Return the de Rham cohomology of the de Rham complex ``self``. - The `n`-th de Rham cohomology is given by + The `k`-th de Rham cohomology is given by .. MATH:: From ed5bf6ab989a39ba9107bd00580e57e4fc1f73f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 15:39:57 +0200 Subject: [PATCH 277/706] refine relint configuration and make it pass on combinat/ --- .github/workflows/lint.yml | 2 +- src/.relint.yml | 12 +- src/sage/combinat/crystals/letters.pyx | 336 +++++++++---------- src/sage/combinat/designs/covering_design.py | 16 +- src/sage/combinat/set_partition_ordered.py | 4 +- 5 files changed, 181 insertions(+), 189 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f778e17ed91..6b08a0fe270 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -30,7 +30,7 @@ jobs: - name: Install relint run: pip install tox relint - name: Lint using relint - run: tox -e relint src/ + run: tox -e relint src/sage/ lint-pyright: name: Static type check with pyright runs-on: ubuntu-latest diff --git a/src/.relint.yml b/src/.relint.yml index cfa6592a651..dd33f57f013 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -10,7 +10,7 @@ Hint: # cmp # apply Hint: # <> # sagenb Hint: # next # six is no longer allowed - pattern: '(import.*ifilter|import.*imap|import.*izip|^\s*raise\s*[A-Za-z]*Error\s*,|[\s,\(]cmp\s*=|[^_a-z]cmp\(|<>|\.next\(\)|__metaclass__|except\s*[A-Za-z]\s*,|[^_a-z]apply\(|sagenb|import six|from six import)' + pattern: '(import.*ifilter|import.*imap|import.*izip|^\s*raise\s*[A-Za-z]*Error\s*,|[\s,\(]cmp\s*=|[^_a-z]cmp\(|\.next\(\)|__metaclass__|except\s*[A-Za-z]\s*,|[^_a-z]apply\(|sagenb|import six|from six import)' filePattern: .*[.](py|pyx|rst) - name: 'foreign_latex: foreign commands in LaTeX' @@ -18,11 +18,6 @@ use equivalent LaTeX commands instead of plain TeX commands such as \over, \choose, etc. pattern: '(\\choose|\\over[^l]|\\atop|\\above|\\overwithdelims|\\atopwithdelims|\\abovewithdelims)' -- name: 'doctest_continuation: old-style doctest continuation (...)' - hint: | - the correct syntax is ....: - pattern: '^\s*\.\.\.[ ][ ]*\S' - - name: 'blocks: wrong syntax for blocks (INPUT, OUTPUT, EXAMPLES, NOTE, etc.)' hint: | # the correct syntax is .. SEEALSO:: @@ -30,16 +25,13 @@ Hint: # no :: after INPUT, OUTPUT, REFERENCE blocks Hint: # no " :" at the end of lines Hint: # no "Returns" at the start of lines - pattern: '(\.\.SEE|SEE ALSO|SEEALSO:($|[^:])|^\s*TEST:|^\s*EXAMPLE:|^\s*NOTES:|^\s*[A-Z]*PUT::|^\s*REFERENCES?::|\s:$)' + pattern: '(\.\.SEE|SEE ALSO|SEEALSO:($|[^:])|^\s*TEST:|^\s*EXAMPLE:|^\s*NOTES:|^\s*[A-Z]*PUT::|^\s*REFERENCES?::$)' - name: 'trac_links: bad trac link' hint: | the correct syntax for trac roles is :trac:`NUMBER`, note the initial colon pattern: '[^:]trac:`[0-9]' -- name: 'trailing_whitespace: trailing whitespace' - pattern: '[ ]$' - - name: 'triple_colon: triple colon (::: or : ::)' pattern: ':[ ]*::' diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index 3e899d3fc93..d9469333238 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -1963,173 +1963,173 @@ cdef class Crystal_of_letters_type_E7_element(LetterTuple): sage: C((-7,6)).e(7) (7,) """ - if self.value == (-7, 6) and i == 7 : + if self.value == (-7, 6) and i == 7: return self._parent._element_constructor_( (7,) ) - if self.value == (-6, 5) and i == 6 : + if self.value == (-6, 5) and i == 6: return self._parent._element_constructor_( (-7, 6) ) - if self.value == (-5, 4) and i == 5 : + if self.value == (-5, 4) and i == 5: return self._parent._element_constructor_( (-6, 5) ) - if self.value == (-4, 2, 3) and i == 4 : + if self.value == (-4, 2, 3) and i == 4: return self._parent._element_constructor_( (-5, 4) ) - if self.value == (-2, 3) and i == 2 : + if self.value == (-2, 3) and i == 2: return self._parent._element_constructor_( (-4, 2, 3) ) - if self.value == (-3, 1, 2) and i == 3 : + if self.value == (-3, 1, 2) and i == 3: return self._parent._element_constructor_( (-4, 2, 3) ) - if self.value == (-3, -2, 1, 4) and i == 3 : + if self.value == (-3, -2, 1, 4) and i == 3: return self._parent._element_constructor_( (-2, 3) ) - if self.value == (-1, 2) and i == 1 : + if self.value == (-1, 2) and i == 1: return self._parent._element_constructor_( (-3, 1, 2) ) - if self.value == (-3, -2, 1, 4) and i == 2 : + if self.value == (-3, -2, 1, 4) and i == 2: return self._parent._element_constructor_( (-3, 1, 2) ) - if self.value == (-1, -2, 4) and i == 1 : + if self.value == (-1, -2, 4) and i == 1: return self._parent._element_constructor_( (-3, -2, 1, 4) ) - if self.value == (-4, 1, 5) and i == 4 : + if self.value == (-4, 1, 5) and i == 4: return self._parent._element_constructor_( (-3, -2, 1, 4) ) - if self.value == (-7, 1) and i == 7 : + if self.value == (-7, 1) and i == 7: return self._parent._element_constructor_( (-6, 7, 1) ) - if self.value == (-1, -6, 3, 7) and i == 1 : + if self.value == (-1, -6, 3, 7) and i == 1: return self._parent._element_constructor_( (-6, 7, 1) ) - if self.value == (-1, -2, 4) and i == 2 : + if self.value == (-1, -2, 4) and i == 2: return self._parent._element_constructor_( (-1, 2) ) - if self.value == (-4, -1, 3, 5) and i == 4 : + if self.value == (-4, -1, 3, 5) and i == 4: return self._parent._element_constructor_( (-1, -2, 4) ) - if self.value == (-4, -1, 3, 5) and i == 1 : + if self.value == (-4, -1, 3, 5) and i == 1: return self._parent._element_constructor_( (-4, 1, 5) ) - if self.value == (-5, 6, 1) and i == 5 : + if self.value == (-5, 6, 1) and i == 5: return self._parent._element_constructor_( (-4, 1, 5) ) - if self.value == (-3, 5) and i == 3 : + if self.value == (-3, 5) and i == 3: return self._parent._element_constructor_( (-4, -1, 3, 5) ) - if self.value == (-5, -1, 3, 6) and i == 5 : + if self.value == (-5, -1, 3, 6) and i == 5: return self._parent._element_constructor_( (-4, -1, 3, 5) ) - if self.value == (-5, -3, 4, 6) and i == 5 : + if self.value == (-5, -3, 4, 6) and i == 5: return self._parent._element_constructor_( (-3, 5) ) - if self.value == (-6, 7, 1) and i == 6 : + if self.value == (-6, 7, 1) and i == 6: return self._parent._element_constructor_( (-5, 6, 1) ) - if self.value == (-5, -1, 3, 6) and i == 1 : + if self.value == (-5, -1, 3, 6) and i == 1: return self._parent._element_constructor_( (-5, 6, 1) ) - if self.value == (-5, -3, 4, 6) and i == 3 : + if self.value == (-5, -3, 4, 6) and i == 3: return self._parent._element_constructor_( (-5, -1, 3, 6) ) - if self.value == (-1, -6, 3, 7) and i == 6 : + if self.value == (-1, -6, 3, 7) and i == 6: return self._parent._element_constructor_( (-5, -1, 3, 6) ) - if self.value == (-4, 2, 6) and i == 4 : + if self.value == (-4, 2, 6) and i == 4: return self._parent._element_constructor_( (-5, -3, 4, 6) ) - if self.value == (-6, -3, 7, 4) and i == 6 : + if self.value == (-6, -3, 7, 4) and i == 6: return self._parent._element_constructor_( (-5, -3, 4, 6) ) - if self.value == (-6, -2, 7, 5) and i == 6 : + if self.value == (-6, -2, 7, 5) and i == 6: return self._parent._element_constructor_( (-2, 6) ) - if self.value == (-6, -3, 7, 4) and i == 3 : + if self.value == (-6, -3, 7, 4) and i == 3: return self._parent._element_constructor_( (-1, -6, 3, 7) ) - if self.value == (-1, -7, 3) and i == 7 : + if self.value == (-1, -7, 3) and i == 7: return self._parent._element_constructor_( (-1, -6, 3, 7) ) - if self.value == (-7, -3, 4) and i == 7 : + if self.value == (-7, -3, 4) and i == 7: return self._parent._element_constructor_( (-6, -3, 7, 4) ) - if self.value == (-6, -4, 2, 7, 5) and i == 4 : + if self.value == (-6, -4, 2, 7, 5) and i == 4: return self._parent._element_constructor_( (-6, -3, 7, 4) ) - if self.value == (-2, 6) and i == 2 : + if self.value == (-2, 6) and i == 2: return self._parent._element_constructor_( (-4, 2, 6) ) - if self.value == (-6, -4, 2, 7, 5) and i == 6 : + if self.value == (-6, -4, 2, 7, 5) and i == 6: return self._parent._element_constructor_( (-4, 2, 6) ) - if self.value == (-6, -2, 7, 5) and i == 2 : + if self.value == (-6, -2, 7, 5) and i == 2: return self._parent._element_constructor_( (-6, -4, 2, 7, 5) ) - if self.value == (-4, -7, 2, 5) and i == 7 : + if self.value == (-4, -7, 2, 5) and i == 7: return self._parent._element_constructor_( (-6, -4, 2, 7, 5) ) - if self.value == (-7, -4, 6, 3) and i == 7 : + if self.value == (-7, -4, 6, 3) and i == 7: return self._parent._element_constructor_( (-4, 7, 3) ) - if self.value == (-3, 1, 7) and i == 3 : + if self.value == (-3, 1, 7) and i == 3: return self._parent._element_constructor_( (-4, 7, 3) ) - if self.value == (-1, 7) and i == 1 : + if self.value == (-1, 7) and i == 1: return self._parent._element_constructor_( (-3, 1, 7) ) - if self.value == (-3, -7, 1, 6) and i == 7 : + if self.value == (-3, -7, 1, 6) and i == 7: return self._parent._element_constructor_( (-3, 1, 7) ) - if self.value == (-1, -7, 3) and i == 1 : + if self.value == (-1, -7, 3) and i == 1: return self._parent._element_constructor_( (-7, 1) ) - if self.value == (-7, -2, 5) and i == 2 : + if self.value == (-7, -2, 5) and i == 2: return self._parent._element_constructor_( (-4, -7, 2, 5) ) - if self.value == (-5, -7, 6, 2) and i == 5 : + if self.value == (-5, -7, 6, 2) and i == 5: return self._parent._element_constructor_( (-4, -7, 2, 5) ) - if self.value == (-5, -2, -7, 4, 6) and i == 5 : + if self.value == (-5, -2, -7, 4, 6) and i == 5: return self._parent._element_constructor_( (-7, -2, 5) ) - if self.value == (-5, -7, 6, 2) and i == 7 : + if self.value == (-5, -7, 6, 2) and i == 7: return self._parent._element_constructor_( (-5, 7, 2) ) - if self.value == (-5, -2, 4, 7) and i == 2 : + if self.value == (-5, -2, 4, 7) and i == 2: return self._parent._element_constructor_( (-5, 7, 2) ) - if self.value == (-7, -3, 4) and i == 3 : + if self.value == (-7, -3, 4) and i == 3: return self._parent._element_constructor_( (-1, -7, 3) ) - if self.value == (-5, 7, 2) and i == 5 : + if self.value == (-5, 7, 2) and i == 5: return self._parent._element_constructor_( (-6, -4, 2, 7, 5) ) - if self.value == (-6, 2) and i == 6 : + if self.value == (-6, 2) and i == 6: return self._parent._element_constructor_( (-5, -7, 6, 2) ) - if self.value == (-5, -2, -7, 4, 6) and i == 2 : + if self.value == (-5, -2, -7, 4, 6) and i == 2: return self._parent._element_constructor_( (-5, -7, 6, 2) ) - if self.value == (-7, -2, 5) and i == 7 : + if self.value == (-7, -2, 5) and i == 7: return self._parent._element_constructor_( (-6, -2, 7, 5) ) - if self.value == (-5, -2, 4, 7) and i == 5 : + if self.value == (-5, -2, 4, 7) and i == 5: return self._parent._element_constructor_( (-6, -2, 7, 5) ) - if self.value == (-4, 7, 3) and i == 4 : + if self.value == (-4, 7, 3) and i == 4: return self._parent._element_constructor_( (-5, -2, 4, 7) ) - if self.value == (-5, -2, -7, 4, 6) and i == 7 : + if self.value == (-5, -2, -7, 4, 6) and i == 7: return self._parent._element_constructor_( (-5, -2, 4, 7) ) - if self.value == (-4, -7, 2, 5) and i == 4 : + if self.value == (-4, -7, 2, 5) and i == 4: return self._parent._element_constructor_( (-7, -3, 4) ) - if self.value == (-7, -4, 6, 3) and i == 4 : + if self.value == (-7, -4, 6, 3) and i == 4: return self._parent._element_constructor_( (-5, -2, -7, 4, 6) ) - if self.value == (-2, -6, 4) and i == 6 : + if self.value == (-2, -6, 4) and i == 6: return self._parent._element_constructor_( (-5, -2, -7, 4, 6) ) - if self.value == (-6, -4, 5, 3) and i == 6 : + if self.value == (-6, -4, 5, 3) and i == 6: return self._parent._element_constructor_( (-7, -4, 6, 3) ) - if self.value == (-3, -7, 1, 6) and i == 3 : + if self.value == (-3, -7, 1, 6) and i == 3: return self._parent._element_constructor_( (-7, -4, 6, 3) ) - if self.value == (-3, -6, 1, 5) and i == 6 : + if self.value == (-3, -6, 1, 5) and i == 6: return self._parent._element_constructor_( (-3, -7, 1, 6) ) - if self.value == (-6, -1, 5) and i == 6 : + if self.value == (-6, -1, 5) and i == 6: return self._parent._element_constructor_( (-7, -1, 6) ) - if self.value == (-2, -6, 4) and i == 2 : + if self.value == (-2, -6, 4) and i == 2: return self._parent._element_constructor_( (-6, 2) ) - if self.value == (-6, -4, 5, 3) and i == 4 : + if self.value == (-6, -4, 5, 3) and i == 4: return self._parent._element_constructor_( (-2, -6, 4) ) - if self.value == (-7, -1, 6) and i == 1 : + if self.value == (-7, -1, 6) and i == 1: return self._parent._element_constructor_( (-3, -7, 1, 6) ) - if self.value == (-5, 3) and i == 5 : + if self.value == (-5, 3) and i == 5: return self._parent._element_constructor_( (-6, -4, 5, 3) ) - if self.value == (-3, -6, 1, 5) and i == 3 : + if self.value == (-3, -6, 1, 5) and i == 3: return self._parent._element_constructor_( (-6, -4, 5, 3) ) - if self.value == (-6, -1, 5) and i == 1 : + if self.value == (-6, -1, 5) and i == 1: return self._parent._element_constructor_( (-3, -6, 1, 5) ) - if self.value == (-3, -5, 4, 1) and i == 5 : + if self.value == (-3, -5, 4, 1) and i == 5: return self._parent._element_constructor_( (-3, -6, 1, 5) ) - if self.value == (-5, -1, 4) and i == 5 : + if self.value == (-5, -1, 4) and i == 5: return self._parent._element_constructor_( (-6, -1, 5) ) - if self.value == (-3, -5, 4, 1) and i == 3 : + if self.value == (-3, -5, 4, 1) and i == 3: return self._parent._element_constructor_( (-5, 3) ) - if self.value == (-4, 1, 2) and i == 4 : + if self.value == (-4, 1, 2) and i == 4: return self._parent._element_constructor_( (-3, -5, 4, 1) ) - if self.value == (-5, -1, 4) and i == 1 : + if self.value == (-5, -1, 4) and i == 1: return self._parent._element_constructor_( (-3, -5, 4, 1) ) - if self.value == (-1, -4, 3, 2) and i == 4 : + if self.value == (-1, -4, 3, 2) and i == 4: return self._parent._element_constructor_( (-5, -1, 4) ) - if self.value == (-1, -4, 3, 2) and i == 1 : + if self.value == (-1, -4, 3, 2) and i == 1: return self._parent._element_constructor_( (-4, 1, 2) ) - if self.value == (-2, 1) and i == 2 : + if self.value == (-2, 1) and i == 2: return self._parent._element_constructor_( (-4, 1, 2) ) - if self.value == (-3, 2) and i == 3 : + if self.value == (-3, 2) and i == 3: return self._parent._element_constructor_( (-1, -4, 3, 2) ) - if self.value == (-2, -1, 3) and i == 2 : + if self.value == (-2, -1, 3) and i == 2: return self._parent._element_constructor_( (-1, -4, 3, 2) ) - if self.value == (-2, -1, 3) and i == 1 : + if self.value == (-2, -1, 3) and i == 1: return self._parent._element_constructor_( (-2, 1) ) - if self.value == (-7, -1, 6) and i == 7 : + if self.value == (-7, -1, 6) and i == 7: return self._parent._element_constructor_( (-1, 7) ) - if self.value == (-2, -3, 4) and i == 3 : + if self.value == (-2, -3, 4) and i == 3: return self._parent._element_constructor_( (-2, -1, 3) ) - if self.value == (-2, -3, 4) and i == 2 : + if self.value == (-2, -3, 4) and i == 2: return self._parent._element_constructor_( (-3, 2) ) - if self.value == (-4, 5) and i == 4 : + if self.value == (-4, 5) and i == 4: return self._parent._element_constructor_( (-2, -3, 4) ) - if self.value == (-5, 6) and i == 5 : + if self.value == (-5, 6) and i == 5: return self._parent._element_constructor_( (-4, 5) ) - if self.value == (-6, 7) and i == 6 : + if self.value == (-6, 7) and i == 6: return self._parent._element_constructor_( (-5, 6) ) - if self.value == (-7,) and i == 7 : + if self.value == (-7,) and i == 7: return self._parent._element_constructor_( (-6, 7) ) else: return None @@ -2145,173 +2145,173 @@ cdef class Crystal_of_letters_type_E7_element(LetterTuple): sage: C((7,)).f(7) (-7, 6) """ - if self.value == (7,) and i == 7 : + if self.value == (7,) and i == 7: return self._parent._element_constructor_( (-7, 6) ) - if self.value == (-7, 6) and i == 6 : + if self.value == (-7, 6) and i == 6: return self._parent._element_constructor_( (-6, 5) ) - if self.value == (-6, 5) and i == 5 : + if self.value == (-6, 5) and i == 5: return self._parent._element_constructor_( (-5, 4) ) - if self.value == (-5, 4) and i == 4 : + if self.value == (-5, 4) and i == 4: return self._parent._element_constructor_( (-4, 2, 3) ) - if self.value == (-4, 2, 3) and i == 2 : + if self.value == (-4, 2, 3) and i == 2: return self._parent._element_constructor_( (-2, 3) ) - if self.value == (-4, 2, 3) and i == 3 : + if self.value == (-4, 2, 3) and i == 3: return self._parent._element_constructor_( (-3, 1, 2) ) - if self.value == (-2, 3) and i == 3 : + if self.value == (-2, 3) and i == 3: return self._parent._element_constructor_( (-3, -2, 1, 4) ) - if self.value == (-3, 1, 2) and i == 1 : + if self.value == (-3, 1, 2) and i == 1: return self._parent._element_constructor_( (-1, 2) ) - if self.value == (-3, 1, 2) and i == 2 : + if self.value == (-3, 1, 2) and i == 2: return self._parent._element_constructor_( (-3, -2, 1, 4) ) - if self.value == (-3, -2, 1, 4) and i == 1 : + if self.value == (-3, -2, 1, 4) and i == 1: return self._parent._element_constructor_( (-1, -2, 4) ) - if self.value == (-3, -2, 1, 4) and i == 4 : + if self.value == (-3, -2, 1, 4) and i == 4: return self._parent._element_constructor_( (-4, 1, 5) ) - if self.value == (-6, 7, 1) and i == 7 : + if self.value == (-6, 7, 1) and i == 7: return self._parent._element_constructor_( (-7, 1) ) - if self.value == (-6, 7, 1) and i == 1 : + if self.value == (-6, 7, 1) and i == 1: return self._parent._element_constructor_( (-1, -6, 3, 7) ) - if self.value == (-1, 2) and i == 2 : + if self.value == (-1, 2) and i == 2: return self._parent._element_constructor_( (-1, -2, 4) ) - if self.value == (-1, -2, 4) and i == 4 : + if self.value == (-1, -2, 4) and i == 4: return self._parent._element_constructor_( (-4, -1, 3, 5) ) - if self.value == (-4, 1, 5) and i == 1 : + if self.value == (-4, 1, 5) and i == 1: return self._parent._element_constructor_( (-4, -1, 3, 5) ) - if self.value == (-4, 1, 5) and i == 5 : + if self.value == (-4, 1, 5) and i == 5: return self._parent._element_constructor_( (-5, 6, 1) ) - if self.value == (-4, -1, 3, 5) and i == 3 : + if self.value == (-4, -1, 3, 5) and i == 3: return self._parent._element_constructor_( (-3, 5) ) - if self.value == (-4, -1, 3, 5) and i == 5 : + if self.value == (-4, -1, 3, 5) and i == 5: return self._parent._element_constructor_( (-5, -1, 3, 6) ) - if self.value == (-3, 5) and i == 5 : + if self.value == (-3, 5) and i == 5: return self._parent._element_constructor_( (-5, -3, 4, 6) ) - if self.value == (-5, 6, 1) and i == 6 : + if self.value == (-5, 6, 1) and i == 6: return self._parent._element_constructor_( (-6, 7, 1) ) - if self.value == (-5, 6, 1) and i == 1 : + if self.value == (-5, 6, 1) and i == 1: return self._parent._element_constructor_( (-5, -1, 3, 6) ) - if self.value == (-5, -1, 3, 6) and i == 3 : + if self.value == (-5, -1, 3, 6) and i == 3: return self._parent._element_constructor_( (-5, -3, 4, 6) ) - if self.value == (-5, -1, 3, 6) and i == 6 : + if self.value == (-5, -1, 3, 6) and i == 6: return self._parent._element_constructor_( (-1, -6, 3, 7) ) - if self.value == (-5, -3, 4, 6) and i == 4 : + if self.value == (-5, -3, 4, 6) and i == 4: return self._parent._element_constructor_( (-4, 2, 6) ) - if self.value == (-5, -3, 4, 6) and i == 6 : + if self.value == (-5, -3, 4, 6) and i == 6: return self._parent._element_constructor_( (-6, -3, 7, 4) ) - if self.value == (-2, 6) and i == 6 : + if self.value == (-2, 6) and i == 6: return self._parent._element_constructor_( (-6, -2, 7, 5) ) - if self.value == (-1, -6, 3, 7) and i == 3 : + if self.value == (-1, -6, 3, 7) and i == 3: return self._parent._element_constructor_( (-6, -3, 7, 4) ) - if self.value == (-1, -6, 3, 7) and i == 7 : + if self.value == (-1, -6, 3, 7) and i == 7: return self._parent._element_constructor_( (-1, -7, 3) ) - if self.value == (-6, -3, 7, 4) and i == 7 : + if self.value == (-6, -3, 7, 4) and i == 7: return self._parent._element_constructor_( (-7, -3, 4) ) - if self.value == (-6, -3, 7, 4) and i == 4 : + if self.value == (-6, -3, 7, 4) and i == 4: return self._parent._element_constructor_( (-6, -4, 2, 7, 5) ) - if self.value == (-4, 2, 6) and i == 2 : + if self.value == (-4, 2, 6) and i == 2: return self._parent._element_constructor_( (-2, 6) ) - if self.value == (-4, 2, 6) and i == 6 : + if self.value == (-4, 2, 6) and i == 6: return self._parent._element_constructor_( (-6, -4, 2, 7, 5) ) - if self.value == (-6, -4, 2, 7, 5) and i == 2 : + if self.value == (-6, -4, 2, 7, 5) and i == 2: return self._parent._element_constructor_( (-6, -2, 7, 5) ) - if self.value == (-6, -4, 2, 7, 5) and i == 7 : + if self.value == (-6, -4, 2, 7, 5) and i == 7: return self._parent._element_constructor_( (-4, -7, 2, 5) ) - if self.value == (-4, 7, 3) and i == 7 : + if self.value == (-4, 7, 3) and i == 7: return self._parent._element_constructor_( (-7, -4, 6, 3) ) - if self.value == (-4, 7, 3) and i == 3 : + if self.value == (-4, 7, 3) and i == 3: return self._parent._element_constructor_( (-3, 1, 7) ) - if self.value == (-3, 1, 7) and i == 1 : + if self.value == (-3, 1, 7) and i == 1: return self._parent._element_constructor_( (-1, 7) ) - if self.value == (-3, 1, 7) and i == 7 : + if self.value == (-3, 1, 7) and i == 7: return self._parent._element_constructor_( (-3, -7, 1, 6) ) - if self.value == (-7, 1) and i == 1 : + if self.value == (-7, 1) and i == 1: return self._parent._element_constructor_( (-1, -7, 3) ) - if self.value == (-4, -7, 2, 5) and i == 2 : + if self.value == (-4, -7, 2, 5) and i == 2: return self._parent._element_constructor_( (-7, -2, 5) ) - if self.value == (-4, -7, 2, 5) and i == 5 : + if self.value == (-4, -7, 2, 5) and i == 5: return self._parent._element_constructor_( (-5, -7, 6, 2) ) - if self.value == (-7, -2, 5) and i == 5 : + if self.value == (-7, -2, 5) and i == 5: return self._parent._element_constructor_( (-5, -2, -7, 4, 6) ) - if self.value == (-5, 7, 2) and i == 7 : + if self.value == (-5, 7, 2) and i == 7: return self._parent._element_constructor_( (-5, -7, 6, 2) ) - if self.value == (-5, 7, 2) and i == 2 : + if self.value == (-5, 7, 2) and i == 2: return self._parent._element_constructor_( (-5, -2, 4, 7) ) - if self.value == (-1, -7, 3) and i == 3 : + if self.value == (-1, -7, 3) and i == 3: return self._parent._element_constructor_( (-7, -3, 4) ) - if self.value == (-6, -4, 2, 7, 5) and i == 5 : + if self.value == (-6, -4, 2, 7, 5) and i == 5: return self._parent._element_constructor_( (-5, 7, 2) ) - if self.value == (-5, -7, 6, 2) and i == 6 : + if self.value == (-5, -7, 6, 2) and i == 6: return self._parent._element_constructor_( (-6, 2) ) - if self.value == (-5, -7, 6, 2) and i == 2 : + if self.value == (-5, -7, 6, 2) and i == 2: return self._parent._element_constructor_( (-5, -2, -7, 4, 6) ) - if self.value == (-6, -2, 7, 5) and i == 7 : + if self.value == (-6, -2, 7, 5) and i == 7: return self._parent._element_constructor_( (-7, -2, 5) ) - if self.value == (-6, -2, 7, 5) and i == 5 : + if self.value == (-6, -2, 7, 5) and i == 5: return self._parent._element_constructor_( (-5, -2, 4, 7) ) - if self.value == (-5, -2, 4, 7) and i == 4 : + if self.value == (-5, -2, 4, 7) and i == 4: return self._parent._element_constructor_( (-4, 7, 3) ) - if self.value == (-5, -2, 4, 7) and i == 7 : + if self.value == (-5, -2, 4, 7) and i == 7: return self._parent._element_constructor_( (-5, -2, -7, 4, 6) ) - if self.value == (-7, -3, 4) and i == 4 : + if self.value == (-7, -3, 4) and i == 4: return self._parent._element_constructor_( (-4, -7, 2, 5) ) - if self.value == (-5, -2, -7, 4, 6) and i == 4 : + if self.value == (-5, -2, -7, 4, 6) and i == 4: return self._parent._element_constructor_( (-7, -4, 6, 3) ) - if self.value == (-5, -2, -7, 4, 6) and i == 6 : + if self.value == (-5, -2, -7, 4, 6) and i == 6: return self._parent._element_constructor_( (-2, -6, 4) ) - if self.value == (-7, -4, 6, 3) and i == 6 : + if self.value == (-7, -4, 6, 3) and i == 6: return self._parent._element_constructor_( (-6, -4, 5, 3) ) - if self.value == (-7, -4, 6, 3) and i == 3 : + if self.value == (-7, -4, 6, 3) and i == 3: return self._parent._element_constructor_( (-3, -7, 1, 6) ) - if self.value == (-3, -7, 1, 6) and i == 6 : + if self.value == (-3, -7, 1, 6) and i == 6: return self._parent._element_constructor_( (-3, -6, 1, 5) ) - if self.value == (-7, -1, 6) and i == 6 : + if self.value == (-7, -1, 6) and i == 6: return self._parent._element_constructor_( (-6, -1, 5) ) - if self.value == (-6, 2) and i == 2 : + if self.value == (-6, 2) and i == 2: return self._parent._element_constructor_( (-2, -6, 4) ) - if self.value == (-2, -6, 4) and i == 4 : + if self.value == (-2, -6, 4) and i == 4: return self._parent._element_constructor_( (-6, -4, 5, 3) ) - if self.value == (-3, -7, 1, 6) and i == 1 : + if self.value == (-3, -7, 1, 6) and i == 1: return self._parent._element_constructor_( (-7, -1, 6) ) - if self.value == (-6, -4, 5, 3) and i == 5 : + if self.value == (-6, -4, 5, 3) and i == 5: return self._parent._element_constructor_( (-5, 3) ) - if self.value == (-6, -4, 5, 3) and i == 3 : + if self.value == (-6, -4, 5, 3) and i == 3: return self._parent._element_constructor_( (-3, -6, 1, 5) ) - if self.value == (-3, -6, 1, 5) and i == 1 : + if self.value == (-3, -6, 1, 5) and i == 1: return self._parent._element_constructor_( (-6, -1, 5) ) - if self.value == (-3, -6, 1, 5) and i == 5 : + if self.value == (-3, -6, 1, 5) and i == 5: return self._parent._element_constructor_( (-3, -5, 4, 1) ) - if self.value == (-6, -1, 5) and i == 5 : + if self.value == (-6, -1, 5) and i == 5: return self._parent._element_constructor_( (-5, -1, 4) ) - if self.value == (-5, 3) and i == 3 : + if self.value == (-5, 3) and i == 3: return self._parent._element_constructor_( (-3, -5, 4, 1) ) - if self.value == (-3, -5, 4, 1) and i == 4 : + if self.value == (-3, -5, 4, 1) and i == 4: return self._parent._element_constructor_( (-4, 1, 2) ) - if self.value == (-3, -5, 4, 1) and i == 1 : + if self.value == (-3, -5, 4, 1) and i == 1: return self._parent._element_constructor_( (-5, -1, 4) ) - if self.value == (-5, -1, 4) and i == 4 : + if self.value == (-5, -1, 4) and i == 4: return self._parent._element_constructor_( (-1, -4, 3, 2) ) - if self.value == (-4, 1, 2) and i == 1 : + if self.value == (-4, 1, 2) and i == 1: return self._parent._element_constructor_( (-1, -4, 3, 2) ) - if self.value == (-4, 1, 2) and i == 2 : + if self.value == (-4, 1, 2) and i == 2: return self._parent._element_constructor_( (-2, 1) ) - if self.value == (-1, -4, 3, 2) and i == 3 : + if self.value == (-1, -4, 3, 2) and i == 3: return self._parent._element_constructor_( (-3, 2) ) - if self.value == (-1, -4, 3, 2) and i == 2 : + if self.value == (-1, -4, 3, 2) and i == 2: return self._parent._element_constructor_( (-2, -1, 3) ) - if self.value == (-2, 1) and i == 1 : + if self.value == (-2, 1) and i == 1: return self._parent._element_constructor_( (-2, -1, 3) ) - if self.value == (-1, 7) and i == 7 : + if self.value == (-1, 7) and i == 7: return self._parent._element_constructor_( (-7, -1, 6) ) - if self.value == (-2, -1, 3) and i == 3 : + if self.value == (-2, -1, 3) and i == 3: return self._parent._element_constructor_( (-2, -3, 4) ) - if self.value == (-3, 2) and i == 2 : + if self.value == (-3, 2) and i == 2: return self._parent._element_constructor_( (-2, -3, 4) ) - if self.value == (-2, -3, 4) and i == 4 : + if self.value == (-2, -3, 4) and i == 4: return self._parent._element_constructor_( (-4, 5) ) - if self.value == (-4, 5) and i == 5 : + if self.value == (-4, 5) and i == 5: return self._parent._element_constructor_( (-5, 6) ) - if self.value == (-5, 6) and i == 6 : + if self.value == (-5, 6) and i == 6: return self._parent._element_constructor_( (-6, 7) ) - if self.value == (-6, 7) and i == 7 : + if self.value == (-6, 7) and i == 7: return self._parent._element_constructor_( (-7,) ) else: return None diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index f6b69cc8180..eeaa04bf1a3 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -128,18 +128,18 @@ def trivial_covering_design(v, k, t): 1 3 4 2 3 4 - NOTES: + .. NOTE:: - Cases are: + Cases are: - * `t=0`: This could be empty, but it's a useful convention to have - one block (which is empty if $k=0$). + * `t=0`: This could be empty, but it's a useful convention to have + one block (which is empty if $k=0$). - * `t=1` : This contains `\lceil v/k \rceil` blocks: - `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around if - `k` does not divide `v`. + * `t=1` : This contains `\lceil v/k \rceil` blocks: + `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around if + `k` does not divide `v`. - * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. + * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. """ if t == 0: # single block [0, ..., k-1] diff --git a/src/sage/combinat/set_partition_ordered.py b/src/sage/combinat/set_partition_ordered.py index 48131aaa65c..4b8512e3274 100644 --- a/src/sage/combinat/set_partition_ordered.py +++ b/src/sage/combinat/set_partition_ordered.py @@ -73,9 +73,9 @@ class OrderedSetPartition(ClonableArray, .. MATH:: - \sum_n {T_n \over n!} x^n = {1 \over 2-e^x}. + \sum_n \frac{T_n}{n!} x^n = \frac{1}{2-e^x}. - (See sequence A000670 in OEIS.) + (See sequence :oeis:`A000670` in OEIS.) INPUT: From f77c08187c1be87a18499059adfa543eb87e0d15 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Wed, 28 Apr 2021 19:30:36 +0200 Subject: [PATCH 278/706] 31795: add method homogeneous_components for polynomials --- .../rings/polynomial/multi_polynomial.pyx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 611a355ab11..93553dd2687 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -821,6 +821,41 @@ cdef class MPolynomial(CommutativeRingElement): else: return True + def homogeneous_components(self): + """ + Return the homogeneous components of this polynomial. + + OUTPUT: + + A dictionary mapping degrees to homogeneous polynomials. + + EXAMPLES:: + + sage: R. = QQ[] + sage: (x^3 + 2*x*y^3 + 4*y^3 + y).homogeneous_components() + {1: y, 3: x^3 + 4*y^3, 4: 2*x*y^3} + sage: R.zero().homogeneous_components() + {} + + In case of weighted term orders, the polynomials are homogeneous with + respect to the weights:: + + sage: S. = PolynomialRing(ZZ, order=TermOrder('wdegrevlex', (1,2,3))) + sage: (a^6 + b^3 + b*c + a^2*c + c + a + 1).homogeneous_components() + {0: 1, 1: a, 3: c, 5: a^2*c + b*c, 6: a^6 + b^3} + """ + parts = {} + for c, m in zip(self.coefficients(), self.monomials()): + d = m.degree() + D = parts.get(d) + if D is None: + D = {} + parts[d] = D + e, = m.exponents() + D[e] = c + r = self.parent() + return {d: r(D) for d, D in parts.items()} + cpdef _mod_(self, other): """ EXAMPLES:: From 5dae2a446fbe3c29ceb50c3e93e48409f2b0f78e Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Wed, 28 Apr 2021 19:38:27 +0200 Subject: [PATCH 279/706] 31795: small speedups for conversion between power series and polynomials --- src/sage/rings/multi_power_series_ring.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 925b8842cd9..2b8639ed2a3 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -1045,9 +1045,7 @@ def _send_to_bg(self,f): f = self._poly_ring(f) except TypeError: raise TypeError("Cannot coerce input to polynomial ring.") - fg_to_bg_dict = dict((v,v*self._bg_ps_ring().gen()) - for v in self._poly_ring().gens()) - return self._bg_ps_ring(f.subs(fg_to_bg_dict)) + return self._bg_ps_ring(f.homogeneous_components()) def _send_to_fg(self,f): """ @@ -1072,7 +1070,7 @@ def _send_to_fg(self,f): sage: fg = M._send_to_fg(bg.add_bigoh(2)); fg 4 + 4*f0 + 4*f2 """ - return self._poly_ring(f.polynomial().subs({self._bg_indeterminate:1})) + return self._poly_ring_.sum(f.polynomial().coefficients()) def unpickle_multi_power_series_ring_v0(base_ring, num_gens, names, order, default_prec, sparse): From 48408eef3382864684345c53ef266d0dad57726b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 16:12:36 +0200 Subject: [PATCH 280/706] remove one more check by relint --- src/.relint.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/.relint.yml b/src/.relint.yml index dd33f57f013..13ef9b3e00b 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -7,10 +7,9 @@ hint: | # ifilter, imap, izip # __metaclass__ Hint: # update raise statements # except Exception, var - Hint: # cmp # apply - Hint: # <> # sagenb + Hint: # cmp # sagenb Hint: # next # six is no longer allowed - pattern: '(import.*ifilter|import.*imap|import.*izip|^\s*raise\s*[A-Za-z]*Error\s*,|[\s,\(]cmp\s*=|[^_a-z]cmp\(|\.next\(\)|__metaclass__|except\s*[A-Za-z]\s*,|[^_a-z]apply\(|sagenb|import six|from six import)' + pattern: '(import.*ifilter|import.*imap|import.*izip|^\s*raise\s*[A-Za-z]*Error\s*,|[\s,\(]cmp\s*=|[^_a-z]cmp\(|\.next\(\)|__metaclass__|except\s*[A-Za-z]\s*,|sagenb|import six|from six import)' filePattern: .*[.](py|pyx|rst) - name: 'foreign_latex: foreign commands in LaTeX' From 31ddbf2c26d4d38c3813c1e6f5574836f3d177fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 16:19:25 +0200 Subject: [PATCH 281/706] remove one check in relint --- src/.relint.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/.relint.yml b/src/.relint.yml index 13ef9b3e00b..4bfc5452114 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -36,12 +36,6 @@ # From various typo tickets -# https://trac.sagemath.org/ticket/30472 -- name: 'capitalization_sage: all-caps "SAGE" detected' - hint: | - Use "Sage" or "SageMath" instead - pattern: '\bSAGE\b' - # https://trac.sagemath.org/ticket/30585 - name: 'typo "homogenous" detected' hint: | From d040d9f7845c2fab95c1ac30dcbe6092f45c1c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 16:36:58 +0200 Subject: [PATCH 282/706] tweak relint config --- src/.relint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/.relint.yml b/src/.relint.yml index 4bfc5452114..e174f30d6dc 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -23,7 +23,6 @@ Hint: # TESTS and EXAMPLES should be plural, NOTE singular Hint: # no :: after INPUT, OUTPUT, REFERENCE blocks Hint: # no " :" at the end of lines - Hint: # no "Returns" at the start of lines pattern: '(\.\.SEE|SEE ALSO|SEEALSO:($|[^:])|^\s*TEST:|^\s*EXAMPLE:|^\s*NOTES:|^\s*[A-Z]*PUT::|^\s*REFERENCES?::$)' - name: 'trac_links: bad trac link' @@ -32,7 +31,7 @@ pattern: '[^:]trac:`[0-9]' - name: 'triple_colon: triple colon (::: or : ::)' - pattern: ':[ ]*::' + pattern: ':[ ]*::$' # From various typo tickets From a3fa15a8e7ded05733ef2e2a33b3d8adeaafb0cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 16:44:12 +0200 Subject: [PATCH 283/706] fix raise in giac.pyx --- src/sage/libs/giac/giac.pyx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/libs/giac/giac.pyx b/src/sage/libs/giac/giac.pyx index 203acd318fe..b2d8a19155a 100644 --- a/src/sage/libs/giac/giac.pyx +++ b/src/sage/libs/giac/giac.pyx @@ -1012,7 +1012,7 @@ cdef class Pygen(GiacMethods_base): except: raise RuntimeError else: - raise IndexError,'list index %s out of range'%(i) + raise IndexError('list index %s out of range'%(i)) else: if isinstance(i,slice): sig_on() @@ -1029,14 +1029,14 @@ cdef class Pygen(GiacMethods_base): else: return self[i[0],i[1]][tuple(i[2:])] else: - raise TypeError,'gen indexes are not yet implemented' + raise TypeError('gen indexes are not yet implemented') # Here we add support to formal variable indexes: else: cmd='%s[%s]'%(self,i) ans=Pygen(cmd).eval() # if the answer is a string, it must be an error message because self is not a list or a string if (ans._type == 12): - raise TypeError, "Error executing code in Giac\nCODE:\n\t%s\nGiac ERROR:\n\t%s"%(cmd, ans) + raise TypeError("Error executing code in Giac\nCODE:\n\t%s\nGiac ERROR:\n\t%s"%(cmd, ans)) return ans @@ -1380,7 +1380,7 @@ cdef class Pygen(GiacMethods_base): else: return self else: - raise TypeError, "self is not a giac List" + raise TypeError("self is not a giac List") # def htmlhelp(self, str lang='en'): @@ -1397,7 +1397,7 @@ cdef class Pygen(GiacMethods_base): # url=decstring23(browser_help(self.gptr[0],l[lang])) #python3 # giacbasedir=decstring23(GIAC_giac_aide_dir()) # python3 # except: - # raise RuntimeError,'giac docs dir not found' + # raise RuntimeError('giac docs dir not found') # print(url) # if os.access(giacbasedir,os.F_OK): # url='file:'+url @@ -1494,7 +1494,7 @@ cdef class Pygen(GiacMethods_base): return result else: - raise TypeError, "Cannot convert non giac integers to Integer" + raise TypeError("Cannot convert non giac integers to Integer") def _rational_(self,Z=None): @@ -1522,9 +1522,9 @@ cdef class Pygen(GiacMethods_base): result=ZZ(self.numer())/ZZ(self.denom()) return result except: - RuntimeError, "Failed to convert to QQ" + RuntimeError("Failed to convert to QQ") else: - raise TypeError, "Cannot convert non giac _FRAC_ to QQ" + raise TypeError("Cannot convert non giac _FRAC_ to QQ") def sage(self): @@ -1711,7 +1711,7 @@ cdef class Pygen(GiacMethods_base): try: n = v._val except: - raise TypeError, "Entry is not a giac vector" + raise TypeError("Entry is not a giac vector") from sage.modules.free_module_element import vector sig_on() entries = [R(self[c]) for c in range(n)] @@ -1816,7 +1816,7 @@ cdef class Pygen(GiacMethods_base): sig_off() return result else: - raise TypeError,"Cannot convert non _INT_ giac gen" + raise TypeError("Cannot convert non _INT_ giac gen") property _double: # immediate double (type _DOUBLE_) @@ -1830,7 +1830,7 @@ cdef class Pygen(GiacMethods_base): sig_off() return result else: - raise TypeError,"Cannot convert non _DOUBLE_ giac gen" + raise TypeError("Cannot convert non _DOUBLE_ giac gen") property help: def __get__(self): @@ -1903,7 +1903,7 @@ cdef inline _wrap_gen(gen g)except +: # if(pyg.gptr !=NULL): # return pyg # else: -# raise MemoryError,"empty gen" +# raise MemoryError("empty gen") @@ -1925,7 +1925,7 @@ cdef vecteur _wrap_pylist(L) except +: sig_off() return V[0] else: - raise TypeError,"argument must be a tuple or a list" + raise TypeError("argument must be a tuple or a list") ################################# @@ -1947,7 +1947,7 @@ cdef vecteur _getgiacslice(Pygen L,slice sl) except +: sig_off() return V[0] else: - raise TypeError,"argument must be a Pygen list and a slice" + raise TypeError("argument must be a Pygen list and a slice") From f7cc8c970f8e0eac6abfae6829b168480f21029d Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Wed, 9 Sep 2020 23:28:17 +0200 Subject: [PATCH 284/706] 30755: implement precision for display of matrices --- src/sage/matrix/constructor.pyx | 38 +++++++++++++++++++++++ src/sage/matrix/matrix0.pyx | 49 +++++++++++++++++++++++++++++- src/sage/repl/display/formatter.py | 39 ++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index c6712f37e3f..2525155b642 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -669,6 +669,30 @@ class options(GlobalOptions): sage: matrix(ZZ, 4, 6) 4 x 6 dense matrix over Integer Ring... sage: matrix.options._reset() + + The precision can also be set via the IPython magic:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%precision 5') + '%.5f' + sage: matrix.options.precision + 5 + sage: A = matrix(RR, [[200/3]]); A + [66.667] + + The number format can be specified as well:: + + sage: matrix.options.format_numeric = '{:.{prec}e}' + sage: A + [6.66667e+1] + sage: matrix.options.format_numeric = '{:.{prec}f}' + sage: A + [66.66667] + sage: matrix.options.format_numeric = '{:+.{prec}g}' + sage: A + [+66.667] + sage: matrix.options._reset() """ NAME = 'Matrix' max_cols = dict(default=49, @@ -677,3 +701,17 @@ class options(GlobalOptions): max_rows = dict(default=19, description='maximum number of rows to display', checker=lambda val: val >= 0) + precision = \ + dict(default=None, + description='number of digits to display for floating point ' + 'entries; if ``None``, the exact representation is ' + 'used instead. This option is also set by the ' + '`IPython magic `_ ' + '``%precision``.', + checker=lambda val: val is None or val >= 0) + format_numeric = \ + dict(default='{:.{prec}}', + description='string used for formatting floating point numbers of' + ' an (optional) precision ``prec``; only supported ' + 'for entry types implementing ``__format__``', + checker=lambda val: isinstance(val.format(3.1415, prec=3), str)) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index e58ee774efb..88d9c2af4d0 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1875,6 +1875,25 @@ cdef class Matrix(sage.structure.element.Matrix): -0.35104242112828943 0.5084492941557279⎟ -0.9541798283979341 -0.8948790563276592⎠ + The number of floating point digits to display is controlled by + :obj:`matrix.options.precision <.constructor.options>` and can also be + set by the `IPython magic + `_ + ``%precision``. This does not affect the internal precision of the + represented data, but only the textual display of matrices:: + + sage: matrix.options.precision = 4 + sage: A = matrix(RR, [[1/3, 200/3], [-3, 1e6]]); A + [ 0.3333 66.67] + [ -3.000 1.000E+6] + sage: unicode_art(A) + ⎛ 0.3333 66.67⎞ + ⎝ -3.000 1.000E+6⎠ + sage: matrix.options.precision = None + sage: A + [ 0.333333333333333 66.6666666666667] + [ -3.00000000000000 1.00000000000000e6] + TESTS: Prior to :trac:`11544` this could take a full minute to run (2011). :: @@ -1898,6 +1917,19 @@ cdef class Matrix(sage.structure.element.Matrix): ⎛─⎞ ⎜1⎟ ⎝─⎠ + + Check that exact number types are not affected by the precision + option:: + + sage: matrix.options.precision = 4 + sage: matrix(ZZ, [[10^10]]) + [10000000000] + sage: matrix(QQ, [[2/3, 10^6]]) + [ 2/3 1000000] + sage: R. = QQ[[]] + sage: matrix(R, [[2/3 - 10^6 * x^3 + 3 * y + O(x, y)^4]]) + [2/3 + 3*y - 1000000*x^3 + O(x, y)^4] + sage: matrix.options._reset() """ cdef Py_ssize_t nr, nc, r, c nr = self._nrows @@ -1988,15 +2020,30 @@ cdef class Matrix(sage.structure.element.Matrix): if minus_one is not None: rep_mapping[-self.base_ring().one()] = minus_one + entries = self.list() + + # only use floating point formatting for inexact types that have + # custom implementation of __format__ + from .constructor import options + prec = options.precision() + if prec is None or callable(rep_mapping) or not entries \ + or type(entries[0]).__format__ is Element.__format__ \ + or self._base_ring.is_exact(): + fmt_numeric = None + else: + fmt_numeric = options.format_numeric() + # compute column widths S = [] - for x in self.list(): + for x in entries: # Override the usual representations with those specified if callable(rep_mapping): rep = rep_mapping(x) # avoid hashing entries, especially algebraic numbers elif rep_mapping and x in rep_mapping: rep = rep_mapping.get(x) + elif fmt_numeric is not None: + rep = fmt_numeric.format(x, prec=prec) else: rep = repr(x) S.append(rep) diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 307da60c8d8..59456cb094a 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -104,6 +104,10 @@ def __init__(self, *args, **kwds): from sage.repl.rich_output.backend_ipython import BackendIPython self.dm.check_backend_class(BackendIPython) + pt_formatter = self.formatters[PLAIN_TEXT] + pt_formatter.observe(self._ipython_float_precision_changed, + names=['float_precision']) + def format(self, obj, include=None, exclude=None): r""" Use the Sage rich output instead of IPython @@ -195,6 +199,41 @@ def format(self, obj, include=None, exclude=None): ipy_metadata[PLAIN_TEXT] = sage_metadata[PLAIN_TEXT] return ipy_format, ipy_metadata + @staticmethod + def _ipython_float_precision_changed(change): + """ + Update the current float precision for the display of matrices in Sage. + + This function is called when the IPython ``%precision`` magic is + invoked. + + TESTS:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%precision 4') + '%.4f' + sage: shell.run_cell('matrix.options.precision') # indirect doctest + 4 + sage: shell.run_cell('%precision') + '%r' + sage: shell.run_cell('matrix.options.precision') # indirect doctest + None + """ + from sage.matrix.constructor import options + s = change.new + if not s: + # unset the precision + options.precision = None + else: + try: + prec = int(s) + if prec >= 0: + options.precision = prec + # otherwise ignore the change + except ValueError: + pass + class SagePlainTextFormatter(PlainTextFormatter): From 459f424e09537bda86b70e2f1380ff28d3eee3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 8 May 2021 16:49:40 +0200 Subject: [PATCH 285/706] use \frac instead of \over --- src/sage/coding/code_bounds.py | 2 +- src/sage/functions/special.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index 6fb82fd4315..89d7948ad2f 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -502,7 +502,7 @@ def hamming_upper_bound(n,q,d): .. MATH:: - M \leq {q^n \over V(n,e)}, + M \leq \frac{q^n}{V(n,e)}, diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index c92e783bbe7..d48208a21d0 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -594,7 +594,7 @@ class EllipticEU(BuiltinFunction): E(u,m)= \int_0^u \mathrm{dn}(x,m)^2\, dx = \int_0^\tau - {\sqrt{1-m x^2}\over\sqrt{1-x^2}}\, dx. + \frac{\sqrt{1-m x^2}}{\sqrt{1-x^2}}\, dx. where `\tau = \mathrm{sn}(u, m)`. From da9165368b08b0115c6e141641a8282f1dcf9f5a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 8 May 2021 12:43:40 -0700 Subject: [PATCH 286/706] ManifoldSubset._union_subset: Fixup --- src/sage/manifolds/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 21860498279..2d852d404ef 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1426,7 +1426,7 @@ def _declare_union_2_subsets(self, dom1, dom2): for s in oc2: if s not in oc: oc.append(s) - self._open_covers.append(oc) + self._open_covers.append(oc) def declare_equal(self, *others): r""" From 94edd68461c01d02e789c1dd60b5e94963dc39f4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 9 May 2021 08:20:13 -0700 Subject: [PATCH 287/706] ManifoldSubset.declare_superset: Fix documentation --- src/sage/manifolds/subset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index cfd08fa8c6c..8597d433a56 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1416,11 +1416,11 @@ def label(element): def declare_superset(self, *subsets): r""" - Declare ``self`` to be a subset of each of the given supersets. + Declare ``self`` to be a superset of each of the given subsets. INPUT: - - ``supersets`` -- other subsets of the same manifold + - ``subsets`` -- other subsets of the same manifold EXAMPLES:: From 41826b46f4b46592b3ed200842b8e5f8d301f081 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 9 May 2021 08:28:14 -0700 Subject: [PATCH 288/706] ManifoldSubset.declare_{sub,super}set: Expand docstring --- src/sage/manifolds/subset.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 8597d433a56..0acc6c907b7 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1381,9 +1381,14 @@ def declare_subset(self, *supersets): sage: P = M.subset_poset() sage: P.plot(element_labels={element: label(element) for element in P}) Graphics object consisting of 9 graphics primitives + + Subsets in a directed cycle of inclusions are equal:: + sage: M.declare_subset(V) sage: M.superset_family() Set {M, U1, U2, V} of open subsets of the 2-dimensional differentiable manifold M + sage: M.equal_subset_family() + Set {M, U1, U2, V} of open subsets of the 2-dimensional differentiable manifold M sage: P = M.subset_poset() sage: P.plot(element_labels={element: label(element) for element in P}) Graphics object consisting of 2 graphics primitives @@ -1442,9 +1447,14 @@ def declare_superset(self, *subsets): sage: P = M.subset_poset() sage: P.plot(element_labels={element: label(element) for element in P}) Graphics object consisting of 11 graphics primitives + + Subsets in a directed cycle of inclusions are equal:: + sage: W.declare_superset(U) sage: W.subset_family() Set {U, V1, V1_inter_V2, V2} of open subsets of the 2-dimensional differentiable manifold M + sage: W.equal_subset_family() + Set {U, V1, V1_inter_V2, V2} of open subsets of the 2-dimensional differentiable manifold M sage: P = M.subset_poset() sage: P.plot(element_labels={element: label(element) for element in P}) Graphics object consisting of 4 graphics primitives From 6370e88a31ff2eba67b753ec1bacb424dc86aa5d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 9 May 2021 08:32:46 -0700 Subject: [PATCH 289/706] ManifoldSubset.difference, complement: New --- src/sage/manifolds/subset.py | 123 +++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 2d852d404ef..da7f6006f95 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2439,6 +2439,129 @@ def _union_subset(self, other, name=None, latex_name=None): res._open_covers.append(oc) return res + def complement(self, superset=None, name=None, latex_name=None, is_open=False): + r""" + Return the complement of ``self`` in the manifold or in ``superset``. + + INPUT: + + - ``superset`` -- (default: ``self.manifold()``) a superset of ``self`` + - ``name`` -- (default: ``None``) name given to the complement in the + case the latter has to be created; the default is + ``superset._name`` minus ``self._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + complement in the case the latter has to be created; the default + is built upon the symbol `\setminus` + + OUTPUT: + + - instance of :class:`ManifoldSubset` representing the + subset that is difference of ``superset`` minus ``self`` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B1 = A.subset('B1') + sage: B2 = A.subset('B2') + sage: B1.complement() + Subset M_minus_B1 of the 2-dimensional topological manifold M + sage: B1.complement(A) + Subset A_minus_B1 of the 2-dimensional topological manifold M + sage: B1.complement(B2) + Traceback (most recent call last): + ... + TypeError: superset must be a superset of self + + """ + if superset is None: + superset = self.manifold() + elif not self.is_subset(superset): + raise TypeError("superset must be a superset of self") + return superset.difference(self, + name=name, latex_name=latex_name, + is_open=is_open) + + def difference(self, other, name=None, latex_name=None, is_open=False): + r""" + Return the set difference of ``self`` minus ``other``. + + INPUT: + + - ``other`` -- another subset of the same manifold + - ``name`` -- (default: ``None``) name given to the difference in the + case the latter has to be created; the default is + ``self._name`` minus ``other._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + difference in the case the latter has to be created; the default + is built upon the symbol `\setminus` + + OUTPUT: + + - instance of :class:`ManifoldSubset` representing the + subset that is difference of ``self`` minus ``other`` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: CA = M.difference(A); CA + Subset M_minus_A of the 2-dimensional topological manifold M + sage: latex(CA) + M\setminus A + sage: A.intersection(CA).is_empty() + True + sage: A.union(CA) + 2-dimensional topological manifold M + + sage: O = M.open_subset('O') + sage: CO = M.difference(O); CO + Subset M_minus_O of the 2-dimensional topological manifold M + sage: M.difference(O) is CO + True + + sage: CO2 = M.difference(O, is_open=True, name='CO2'); CO2 + Open subset CO2 of the 2-dimensional topological manifold M + sage: CO is CO2 + False + sage: CO.is_subset(CO2) and CO2.is_subset(CO) + True + sage: M.difference(O, is_open=True) + Open subset CO2 of the 2-dimensional topological manifold M + + """ + # See if it has been created already + diffs = [] + for diff_name, intersection in other._intersections.items(): + if intersection.is_empty(): + try: + union = other._unions[diff_name] + except KeyError: + pass + else: + if union == self: + diff = self.subset_family()[diff_name] + if not is_open: + return diff + if diff.is_open(): + return diff + # is_open=True but we found a subset that + # is not known to be open - and we cannot + # declare it open. + diffs.append(diff) + + if latex_name is None: + if name is None: + latex_name = r'\setminus '.join(S._latex_name for S in (self, other)) + else: + latex_name = name + if name is None: + name = "_minus_".join(S._name for S in (self, other)) + + diff = self.subset(name=name, latex_name=latex_name, is_open=is_open) + diff.declare_equal(diffs) + self.declare_union(other, diff, disjoint=True) + return diff #### End of construction of new sets from self From 2028258a800e84669fd085245c83dca602b81574 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Apr 2021 19:13:08 -0700 Subject: [PATCH 290/706] sage.manifolds.subsets.closure, TopologicalSubmanifold.closure: New --- src/doc/en/reference/manifolds/manifold.rst | 2 + src/sage/manifolds/subset.py | 21 +++++- src/sage/manifolds/subsets/closure.py | 84 +++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/sage/manifolds/subsets/closure.py diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst index 02afb2460b4..c5dc02c0098 100644 --- a/src/doc/en/reference/manifolds/manifold.rst +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -23,3 +23,5 @@ Topological Manifolds vector_bundle sage/manifolds/family + + sage/manifolds/subsets/closure diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f915b66eaf2..26498522522 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2573,5 +2573,24 @@ def difference(self, other, name=None, latex_name=None, is_open=False): self.declare_union(other, diff, disjoint=True) return diff - #### End of construction of new sets from self + def closure(self, name=None, latex_name=None): + r""" + Return the topological closure of ``self`` as a subset of the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: M.closure() is M + True + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D + Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + """ + if self.manifold().is_subset(self): + return self + from .subsets.closure import ManifoldSubsetClosure + return ManifoldSubsetClosure(self, name=name, latex_name=latex_name) + #### End of construction of new sets from self diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py new file mode 100644 index 00000000000..9954e6578cc --- /dev/null +++ b/src/sage/manifolds/subsets/closure.py @@ -0,0 +1,84 @@ +r""" +Topological Closures of Manifold Subsets + +:class:`ManifoldSubsetClosure` implements the topological closure +of a manifold subset in the topology of the manifold. + +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.manifolds.subset import ManifoldSubset + +class ManifoldSubsetClosure(ManifoldSubset): + + """ + Topological closure of a manifold subset in the topology of the manifold. + + INPUT: + + - ``subset`` -- a :class:`ManifoldSubset` + - ``name`` -- (default: computed from the name of the subset) + string; name (symbol) given to the closure + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the subset; if none is provided, it is set to ``name`` + + """ + + def __init__(self, subset, name=None, latex_name=None): + r""" + Initialize a :class:`ManifoldSubsetClosure` instance. + + TESTS:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + sage: also_cl_D = ManifoldSubsetClosure(D, name='also_cl_D'); also_cl_D + Topological closure also_cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D is also_cl_D + False + sage: cl_D == also_cl_D + False + + """ + self._subset = subset + base_manifold = subset.manifold() + if latex_name is None: + if name is None: + latex_name = r'\mathop{\mathrm{cl}}(' + subset._latex_name + ')' + else: + latex_name = name + if name is None: + name = 'cl_' + subset._name + ManifoldSubset.__init__(self, base_manifold, name, latex_name=latex_name) + self.declare_superset(subset) + + def _repr_(self): + r""" + String representation of the object. + + TESTS:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: D = M.open_subset('D') + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + """ + return "Topological closure {} of the {}".format(self._name, self._subset) From 38c9e96e08560fbf7fa28397bba2e46b16e87e0e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 8 May 2021 15:15:37 -0700 Subject: [PATCH 291/706] ManifoldSubset.is_closed, declare_closed: New --- src/sage/manifolds/subset.py | 102 ++++++++++++++++++++++++-- src/sage/manifolds/subsets/closure.py | 24 ++++++ 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 26498522522..e07d78266b4 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -520,6 +520,54 @@ def is_open(self): """ return False + def is_closed(self): + """ + Return if ``self`` is a closed set. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.is_closed() + True + sage: also_M = M.subset('also_M') + sage: M.declare_subset(also_M) + sage: also_M.is_closed() + True + + sage: A = M.subset('A') + sage: A.is_closed() + False + sage: A.declare_empty() + sage: A.is_closed() + True + + sage: N = M.open_subset('N') + sage: N.is_closed() + False + sage: complement_N = M.subset('complement_N') + sage: M.declare_union(N, complement_N, disjoint=True) + sage: complement_N.is_closed() + True + + """ + if self.manifold().is_subset(self): + return True + if self.is_empty(): + return True + for other_name, intersection in self._intersections.items(): + if intersection.is_empty(): + other = self.manifold().subset_family()[other_name] + if other.is_open(): + try: + union = self._unions[other_name] + except KeyError: + pass + else: + if union.is_open(): + # self is complement of open other in open union + return True + return False + def open_covers(self, trivial=True, supersets=False): r""" Generate the open covers of the current subset. @@ -1854,6 +1902,37 @@ def point(self, coords=None, chart=None, name=None, latex_name=None): return self.element_class(self, coords=coords, chart=chart, name=name, latex_name=latex_name) + def declare_closed(self): + r""" + Declare ``self`` to be a closed subset of the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B1 = A.subset('B1') + sage: B1.is_closed() + False + sage: B1.declare_closed() + sage: B1.is_closed() + True + + sage: B2 = A.subset('B2') + sage: cl_B2 = B2.closure() + sage: A.declare_closed() + sage: cl_B2.is_subset(A) + True + + """ + if self.is_closed(): + return + self.complement(is_open=True) + from .subsets.closure import ManifoldSubsetClosure + for closure in self.manifold().subsets(): + if isinstance(closure, ManifoldSubsetClosure): + if closure._subset.is_subset(self): + closure.declare_subset(self) + #### Construction of new sets from self: def subset(self, name, latex_name=None, is_open=False): @@ -2568,6 +2647,8 @@ def difference(self, other, name=None, latex_name=None, is_open=False): if name is None: name = "_minus_".join(S._name for S in (self, other)) + is_open = is_open or (self.is_open() and other.is_closed()) + diff = self.subset(name=name, latex_name=latex_name, is_open=is_open) diff.declare_equal(diffs) self.declare_union(other, diff, disjoint=True) @@ -2583,12 +2664,23 @@ def closure(self, name=None, latex_name=None): sage: c_cart. = M.chart() # Cartesian coordinates on R^2 sage: M.closure() is M True - sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D - Open subset D of the 2-dimensional topological manifold R^2 - sage: cl_D = D.closure(); cl_D - Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + sage: D2 = M.open_subset('D2', coord_def={c_cart: x^2+y^2<2}); D2 + Open subset D2 of the 2-dimensional topological manifold R^2 + sage: cl_D2 = D2.closure(); cl_D2 + Topological closure cl_D2 of the + Open subset D2 of the 2-dimensional topological manifold R^2 + sage: cl_D2.is_closed() + True + sage: cl_D2 is cl_D2.closure() + True + + sage: D1 = D2.open_subset('D1'); D1 + Open subset D1 of the 2-dimensional topological manifold R^2 + sage: D1.closure().is_subset(D2.closure()) + True + """ - if self.manifold().is_subset(self): + if self.is_closed(): return self from .subsets.closure import ManifoldSubsetClosure return ManifoldSubsetClosure(self, name=name, latex_name=latex_name) diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index 9954e6578cc..f66f5dec589 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -67,6 +67,9 @@ def __init__(self, subset, name=None, latex_name=None): name = 'cl_' + subset._name ManifoldSubset.__init__(self, base_manifold, name, latex_name=latex_name) self.declare_superset(subset) + self.declare_subset(superset + for superset in subset.supersets() + if superset.is_closed()) def _repr_(self): r""" @@ -82,3 +85,24 @@ def _repr_(self): Open subset D of the 2-dimensional topological manifold R^2 """ return "Topological closure {} of the {}".format(self._name, self._subset) + + def is_closed(self): + """ + Return if ``self`` is a closed set. + + This implementation of the method always returns ``True``. + + EXAMPLES:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D.is_closed() + True + + """ + return True From c8272c0bb80ef219597a3968ae1e6182ebc8f943 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:16:51 +0200 Subject: [PATCH 292/706] Sync autodoc with upstream to fix deprecation warnings --- src/sage_docbuild/ext/sage_autodoc.py | 56 +++++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index ac9f8538bc7..c4523f2a531 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -34,13 +34,12 @@ from docutils.statemachine import ViewList import sphinx -from sphinx.ext.autodoc import mock +from sphinx.ext.autodoc import mock, ObjectMember from sphinx.ext.autodoc.importer import import_object, get_object_members, get_module_members from sphinx.locale import _, __ from sphinx.pycode import ModuleAnalyzer from sphinx.errors import PycodeError from sphinx.util import logging -from sphinx.util import rpartition, force_decode from sphinx.util.docstrings import prepare_docstring from sphinx.util.inspect import isdescriptor, \ safe_getattr, object_description, is_builtin_class_method, \ @@ -488,10 +487,7 @@ def get_doc(self, encoding=None, ignore=1): # make sure we have Unicode docstrings, then sanitize and split # into lines if isinstance(docstring, str): - return [prepare_docstring(docstring, ignore)] - elif isinstance(docstring, str): # this will not trigger on Py3 - return [prepare_docstring(force_decode(docstring, encoding), - ignore)] + return [prepare_docstring(docstring)] # ... else it is something strange, let's ignore it return [] @@ -536,7 +532,7 @@ def add_content(self, more_content, no_docstring=False): # add content from docstrings if not no_docstring: - encoding = self.analyzer and self.analyzer._encoding + encoding = self.analyzer docstrings = self.get_doc(encoding) if not docstrings: # append at least a dummy docstring, so that the event @@ -876,13 +872,42 @@ def add_directive_header(self, sig): if self.options.deprecated: self.add_line(u' :deprecated:', sourcename) + def get_module_members(self): + """Get members of target module.""" + if self.analyzer: + attr_docs = self.analyzer.attr_docs + else: + attr_docs = {} + + members = {} # type: Dict[str, ObjectMember] + for name in dir(self.object): + try: + value = safe_getattr(self.object, name, None) + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, value, docstring="\n".join(docstring)) + except AttributeError: + continue + + # annotation only member (ex. attr: int) + try: + for name in inspect.getannotations(self.object): + if name not in members: + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, INSTANCEATTR, + docstring="\n".join(docstring)) + except AttributeError: + pass + + return members + def get_object_members(self, want_all): # type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]] + members = self.get_module_members() if want_all: if not hasattr(self.object, '__all__'): # for implicit module members, check __module__ to avoid # documenting imported objects - return True, get_module_members(self.object) + return True, list(members.values()) else: memberlist = self.object.__all__ # Sometimes __all__ is broken... @@ -893,14 +918,14 @@ def get_object_members(self, want_all): '(in module %s) -- ignoring __all__' % (memberlist, self.fullname)) # fall back to all members - return True, get_module_members(self.object) + return True, list(members.values()) else: memberlist = self.options.members or [] ret = [] for mname in memberlist: - try: - ret.append((mname, safe_getattr(self.object, mname))) - except AttributeError: + if mname in members: + ret.append(members[mname]) + else: logger.warning( 'missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s' % @@ -951,7 +976,7 @@ def resolve_name(self, modname, parents, path, base): # ... if still None, there's no way to know if mod_cls is None: return None, [] - modname, cls = rpartition(mod_cls, '.') # type: ignore + modname, _, cls = mod_cls.rpartition('.') # type: ignore parents = [cls] # if the module name is still missing, get it like above if not modname: @@ -1275,10 +1300,7 @@ def get_doc(self, encoding=None, ignore=1): doc = [] for docstring in docstrings: if isinstance(docstring, str): - doc.append(prepare_docstring(docstring, ignore)) - elif isinstance(docstring, str): # this will not trigger on Py3 - doc.append(prepare_docstring(force_decode(docstring, encoding), - ignore)) + doc.append(prepare_docstring(docstring)) return doc def add_content(self, more_content, no_docstring=False): From 1ed5bd7eab075e0ed3df2333b4ed5dec7bb793f8 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:17:21 +0200 Subject: [PATCH 293/706] Remove duplicate modules, which throw a warning with sphinx 4 --- src/doc/en/reference/combinat/module_list.rst | 1 - src/doc/en/reference/modfrm/index.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index a95069590b4..f8bfe88c719 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -364,7 +364,6 @@ Comprehensive Module list sage/combinat/superpartition sage/combinat/symmetric_group_algebra sage/combinat/symmetric_group_representations - sage/combinat/super_tableau sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple diff --git a/src/doc/en/reference/modfrm/index.rst b/src/doc/en/reference/modfrm/index.rst index e1f8a483a3f..3d5e55dfd63 100644 --- a/src/doc/en/reference/modfrm/index.rst +++ b/src/doc/en/reference/modfrm/index.rst @@ -23,7 +23,6 @@ Module List sage/modular/modform/hecke_operator_on_qexp sage/modular/modform/numerical sage/modular/modform/vm_basis - sage/modular/modform/ambient sage/modular/modform/half_integral sage/modular/modform/find_generators sage/modular/modform/j_invariant From ad51564265ddef74f7feb5b7de32b2590a38308b Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:18:01 +0200 Subject: [PATCH 294/706] Upgrade sphinx to 4.0.0 --- build/pkgs/sphinx/checksums.ini | 6 +++--- build/pkgs/sphinx/package-version.txt | 2 +- build/pkgs/sphinx/patches/environment.patch | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index ce072d1bb1e..567dc6e28bc 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,5 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=9043e0f324d62a5c47a0773f562d423e66163f63 -md5=6e01aa4ab0f1dcbc8d65cc25c736e3ea -cksum=4106849897 +sha1=743988ef5dcf1f2dfa99435b93aa100b3a8d5674 +md5=e38a2e83c6ab6af2bd889da310a233b3 +cksum=3127931243 upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 0be0b439969..3c82c17abed 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -3.1.2.p0 +4.0.0.p0 diff --git a/build/pkgs/sphinx/patches/environment.patch b/build/pkgs/sphinx/patches/environment.patch index 4fbdac0cfe4..12627ec70f3 100644 --- a/build/pkgs/sphinx/patches/environment.patch +++ b/build/pkgs/sphinx/patches/environment.patch @@ -13,4 +13,4 @@ diff -ru a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py + continue if self.config[item.name] != item.value: self.config_status = CONFIG_CHANGED - break + self.config_status_extra = ' (%r)' % (item.name,) From b9e390f8402202ab61b8b313a990e4e4b862edc4 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:21:53 +0200 Subject: [PATCH 295/706] Upgrade sphinx to 4.0.1 --- build/pkgs/sphinx/checksums.ini | 6 +++--- build/pkgs/sphinx/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 567dc6e28bc..b42e84540d3 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,5 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=743988ef5dcf1f2dfa99435b93aa100b3a8d5674 -md5=e38a2e83c6ab6af2bd889da310a233b3 -cksum=3127931243 +sha1=ac493c525509f639a4eeb784a85648b3066f1f40 +md5=4714d870be4e599280e2c06561b44430 +cksum=2654318749 upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 3c82c17abed..f727dc16efb 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -4.0.0.p0 +4.0.1.p0 From 5912839dd8f9c7b4f8a0f3e5e8715947263a7095 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 May 2021 21:36:49 +0200 Subject: [PATCH 296/706] implement combinatorial face as polyhedron --- src/sage/geometry/polyhedron/base.py | 44 +++++ .../combinatorial_polyhedron/base.pyx | 18 ++ .../combinatorial_face.pxd | 1 + .../combinatorial_face.pyx | 74 +++++++++ .../face_data_structure.pxd | 8 + .../face_list_data_structure.pxd | 36 ++++ .../list_of_faces.pxd | 12 +- .../list_of_faces.pyx | 157 +++++++++++++++++- .../polyhedron_face_lattice.pxd | 1 + .../polyhedron_face_lattice.pyx | 1 + 10 files changed, 349 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..56927af263f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6891,6 +6891,50 @@ def facets(self): return () return self.faces(self.dimension()-1) + def _test_combinatorial_face_as_polyhedron(self, tester=None, **options): + """ + Run tests on obtaining the combinatorial face as combinatorial polyhedron. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_combinatorial_face_as_polyhedron() + """ + if not self.is_compact(): + return + if self.dim() > 7 or self.n_vertices() > 100 or self.n_facets() > 100: + # Avoid very long tests. + return + if self.dim() < 1: + # Avoid trivial cases. + return + + from sage.misc.prandom import random + + if tester is None: + tester = self._tester(**options) + + it = self.face_generator() + _ = next(it), next(it) # get rid of non_proper faces + C1 = self.combinatorial_polyhedron() + it1 = C1.face_iter() + C2 = C1.dual() + it2 = C2.face_iter(dual=not it1.dual) + + for f in it: + f1 = next(it1) + f2 = next(it2) + if random() < 0.95: + # Only test a random 5 percent of the faces. + continue + + P = f.as_polyhedron() + D1 = f1.as_polyhedron() + D2 = f2.as_polyhedron(face_figure=True).dual() + D1._test_bitsets(tester, **options) + D2._test_bitsets(tester, **options) + tester.assertTrue(P.combinatorial_polyhedron().vertex_facet_graph().is_isomorphic(D1.vertex_facet_graph())) + tester.assertTrue(P.combinatorial_polyhedron().vertex_facet_graph().is_isomorphic(D2.vertex_facet_graph())) + @cached_method(do_pickle=True) def f_vector(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..3e8eab03f0a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -649,6 +649,24 @@ cdef class CombinatorialPolyhedron(SageObject): return (CombinatorialPolyhedron, (self.facets(), self.Vrepresentation(), self.Hrepresentation())) + def _test_bitsets(self, tester=None, **options): + """ + Test if the bitsets are consistent. + + TESTS:: + + sage: P = polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: C._test_bitsets() + """ + if tester is None: + tester = self._tester(**options) + + cdef ListOfFaces facets = self.bitrep_facets() + cdef ListOfFaces Vrep = self.bitrep_Vrep() + + tester.assertEqual(facets.matrix(), Vrep.matrix().transpose()) + def Vrepresentation(self): r""" Return a list of names of ``[vertices, rays, lines]``. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..a2bbfa4f1e9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -24,6 +24,7 @@ cdef class CombinatorialFace(SageObject): # some copies from ``CombinatorialPolyhedron`` cdef tuple _ambient_Vrep, _ambient_facets, _equalities + cdef bint _ambient_bounded # Atoms and coatoms are the vertices/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 1d790726f23..7a8ce82c4c7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -74,6 +74,7 @@ from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice from .face_data_structure cimport face_len_atoms, face_init, face_copy from .face_list_data_structure cimport bit_rep_to_coatom_rep +from .list_of_faces cimport face_as_combinatorial_polyhedron cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -183,6 +184,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_facets = it._facet_names self._equalities = it._equalities self._hash_index = it.structure._index + self._ambient_bounded = it._bounded self._initialized_from_face_lattice = False @@ -207,6 +209,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names self._equalities = all_faces._equalities + self._ambient_bounded = all_faces._bounded self._initialized_from_face_lattice = True @@ -751,6 +754,77 @@ cdef class CombinatorialFace(SageObject): n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) + def as_polyhedron(self, quotient=False): + r""" + Return ``self`` as combinatorial polyhedron. + + If ``quotient`` is ``True`` return the quotient of the polyhedron by ``F``. + + Let `[\hat{0], \hat{1}]` be the face lattice of the ambient polyhedron + and `F` be ``self`` as element of the face lattice. + `F` as polyhedron corresponds to `[\hat{0}, F]` and + the quotient by `F` corresponds to `[F, \hat{1}]`. + + EXAMPLES:: + + sage: P = polytopes.cyclic_polytope(7,11) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(4) + sage: f = next(it); f + A 4-dimensional face of a 7-dimensional combinatorial polyhedron + sage: C1 = f.as_polyhedron(); C1 + A 4-dimensional combinatorial polyhedron with 5 facets + sage: C1.f_vector() + (1, 5, 10, 10, 5, 1) + sage: C1_alt = polytopes.cyclic_polytope(4,5).combinatorial_polyhedron() + sage: C1_alt.vertex_facet_graph().is_isomorphic(C1.vertex_facet_graph()) + True + + Obtaining the quotient:: + + sage: C2 = f.as_polyhedron(quotient=True); C2 + A 2-dimensional combinatorial polyhedron with 6 facets + sage: C2 + A 2-dimensional combinatorial polyhedron with 6 facets + sage: C2.f_vector() + (1, 6, 6, 1) + + The method is not implemented for unbounded polyhedra:: + + sage: P = Polyhedron(rays=[[0,1]])*polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: f.as_polyhedron() + Traceback (most recent call last): + ... + NotImplementedError: only implemented for bounded polyhedra + + REFERENCES: + + For more information, see Exercise 2.9 of [Zie2007]_. + + .. NOTE:: + + This method is tested in + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base._test_combinatorial_face_as_polyhedron`. + """ + if not self._ambient_bounded: + raise NotImplementedError("only implemented for bounded polyhedra") + + cdef ListOfFaces facets = self.atoms if self._dual else self.coatoms + cdef ListOfFaces Vrep = self.atoms if not self._dual else self.coatoms + + if not quotient: + return CombinatorialPolyhedron(face_as_combinatorial_polyhedron(facets, Vrep, self.face, self._dual)) + else: + # We run ``face_as_combinatorial_polyhedron`` with interchanged arguments to obtain + # the face as polyhedron in the dual setting. + + # We then interchange the output of it, to obtain the quotient. + new_Vrep, new_facets = face_as_combinatorial_polyhedron(Vrep, facets, self.face, not self._dual) + return CombinatorialPolyhedron((new_facets, new_Vrep)) + cdef size_t n_atom_rep(self) except -1: r""" Compute the number of atoms in the current face by counting the diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index 2b509aaec01..e91a4a0bd86 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -151,6 +151,14 @@ cdef inline long face_next_atom(face_t face, mp_bitcnt_t n): """ return bitset_next(face.atoms, n) +cdef inline long face_first_missing_atom(face_t face): + """ + Return the index of the first atom not in ``face``. + + In case there are none, return ``-1``. + """ + return bitset_first_in_complement(face.atoms) + cdef inline long face_len_atoms(face_t face) nogil: """ Calculate the number of atoms in the face. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index fa28c8c5473..cd910d91cbe 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -103,6 +103,42 @@ cdef inline int add_face_deep(face_list_t faces, face_t face) except -1: face_copy(faces.faces[faces.n_faces], face) faces.n_faces += 1 +cdef inline void face_list_delete_faces_by_array(face_list_t faces, bint *delete): + r""" + Remove face ``i`` if and only if ``delete[i]`` decreasing ``faces.n_faces``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``faces.n_faces``. + """ + cdef size_t n_newfaces = 0 + cdef size_t i + for i in range(faces.n_faces): + if not delete[i]: + faces.faces[n_newfaces][0] = faces.faces[i][0] + n_newfaces += 1 + + faces.n_faces = n_newfaces + +cdef inline void face_list_delete_faces_by_face(face_list_t faces, face_t face): + r""" + Remove all faces such that the ``i``-th bit in ``face`` is not set + descreasing ``faces.n_faces``. + + .. WARNING:: + + ``face`` is assumed to contain ``self.n_faces`` atoms. + """ + cdef size_t n_newfaces = 0 + cdef size_t i + for i in range(faces.n_faces): + if face_atom_in(face, i): + faces.faces[n_newfaces][0] = faces.faces[i][0] + n_newfaces += 1 + + faces.n_faces = n_newfaces + + ############################################################################# # Face Comparison ############################################################################# diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd index a571b8c1c84..3f3c05f08ab 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd @@ -1,6 +1,6 @@ cimport cython from sage.ext.memory_allocator cimport MemoryAllocator -from .face_list_data_structure cimport face_list_t +from .face_list_data_structure cimport face_list_t, face_t @cython.final cdef class ListOfFaces: @@ -10,6 +10,8 @@ cdef class ListOfFaces: # It will be of "type" ``uint64_t[n_faces][face_length]`` cdef face_list_t data + cpdef ListOfFaces __copy__(self) + cpdef int compute_dimension(self) except -2 cdef inline size_t n_faces(self): @@ -20,3 +22,11 @@ cdef class ListOfFaces: return self.data.n_coatoms cpdef ListOfFaces pyramid(self) + + cdef ListOfFaces delete_atoms_unsafe(self, bint* delete, face_t face) # not in place + cdef void delete_faces_unsafe(self, bint* delete, face_t face) # in place + + cdef void get_not_inclusion_maximal_unsafe(self, bint *not_inclusion_maximal) + cdef void get_faces_all_set_unsafe(self, bint *all_set) + +cdef tuple face_as_combinatorial_polyhedron(ListOfFaces facets, ListOfFaces Vrep, face_t face, bint dual) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index 8974a80ab3a..98b3d4df828 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -91,7 +91,6 @@ AUTHOR: # **************************************************************************** from sage.structure.element import is_Matrix - from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from .face_list_data_structure cimport * @@ -154,7 +153,7 @@ cdef class ListOfFaces: """ assert face_list_check_alignment(self.data) - def __copy__(self): + cpdef ListOfFaces __copy__(self): r""" Return a copy of self. @@ -357,6 +356,101 @@ cdef class ListOfFaces: return copy + cdef ListOfFaces delete_atoms_unsafe(self, bint *delete, face_t face): + r""" + Return a copy of ``self`` where bits in ``delete`` have been + removed/contracted. + + The the remaining bits will be shifted to the left. + + If ``delete`` is ``NULL``, keep exactly the bits set in ``face``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``self.n_atoms`` or NULL. + ``face`` is assumed to be of length ``self.face_length`` if ``delete`` is not ``NULL``. + """ + + cdef output_n_atoms + cdef size_t i, j + if delete is NULL: + output_n_atoms = face_len_atoms(face) + else: + output_n_atoms = self.n_atoms() + for i in range(self.n_atoms()): + if delete[i]: + output_n_atoms -= 1 + + cdef ListOfFaces output = ListOfFaces(self.n_faces(), output_n_atoms, self.n_coatoms()) + cdef size_t counter = 0 + cdef size_t n_atoms = self.n_atoms() + cdef size_t n_faces = self.n_faces() + for i in range(n_atoms): + if ((delete is NULL and face_atom_in(face, i)) or + (delete is not NULL and not delete[i])): + # The atom will be kept. + for j in range(n_faces): + if face_atom_in(self.data.faces[j], i): + face_add_atom(output.data.faces[j], counter) + counter += 1 + + return output + + cdef void delete_faces_unsafe(self, bint *delete, face_t face): + r""" + Deletes face ``i`` if and only if ``delete[i]``. + + Alternatively, deletes all faces such that the ``i``-th bit in ``face`` is not set. + + This will modify ``self``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``self.n_faces()`` or NULL. + ``face`` is assumed to contain ``self.n_faces()`` atoms if ``delete`` is not ``NULL``. + """ + if delete is not NULL: + face_list_delete_faces_by_array(self.data, delete) + else: + face_list_delete_faces_by_face(self.data, face) + + cdef void get_not_inclusion_maximal_unsafe(self, bint *not_inclusion_maximal): + r""" + Get all faces that are not inclusion maximal. + + Set ``not_inclusion_maximal[i]`` to one if ``self.data[i]`` is not + an inclusion-maximal face, otherwise to zero. + + If there are duplicates, all but the last duplicate will be marked as + not inclusion maximal. + + .. WARNING:: + + ``not_inclusion_maximal`` is assumed to be at least of length ``self.n_atoms`` or NULL. + """ + cdef size_t i + memset(not_inclusion_maximal, 0, sizeof(bint)*self.n_faces()) + for i in range(self.n_faces()): + not_inclusion_maximal[i] = is_not_maximal_fused(self.data, i, 0, not_inclusion_maximal) + + cdef void get_faces_all_set_unsafe(self, bint *all_set): + r""" + Get the faces that have all ``bits`` set. + + Set ``all_set[i]`` to one if ``self.data[i]`` + has all bits set, otherwise to zero. + + .. WARNING:: + + ``all_set`` is assumed to be at least of length ``self.n_atoms`` or NULL. + """ + cdef size_t i + for i in range(self.n_faces()): + if face_first_missing_atom(self.data.faces[i]) == -1: + all_set[i] = 1 + else: + all_set[i] = 0 + def matrix(self): r""" Obtain the matrix of self. @@ -398,3 +492,62 @@ cdef class ListOfFaces: M.set_immutable() return M + +cdef tuple face_as_combinatorial_polyhedron(ListOfFaces facets, ListOfFaces Vrep, face_t face, bint dual): + r""" + Obtain facets and Vrepresentation of ``face`` as new combinatorial polyhedron. + + INPUT: + + - ``facets`` -- facets of the polyhedron + - ``Vrep`` -- Vrepresentation of the polyhedron + - ``face`` -- face in Vrepresentation or ``NULL`` + - ``dual`` -- boolean + + OUTPUT: A tuple of new facets and new Vrepresentation as :class:`ListOfFaces`. + """ + cdef ListOfFaces new_facets, new_Vrep + cdef bint* delete + cdef MemoryAllocator mem = MemoryAllocator() + cdef size_t i + cdef face_t null_face + + # Delete all atoms not in the face. + if not dual: + new_facets = facets.delete_atoms_unsafe(NULL, face) + new_Vrep = Vrep.__copy__() + new_Vrep.delete_faces_unsafe(NULL, face) + + delete = mem.allocarray(new_facets.n_faces(), sizeof(bint)) + else: + delete = mem.allocarray(max(facets.n_faces(), facets.n_atoms()), sizeof(bint)) + + # Set ``delete[i]`` to one if ``i`` is not an vertex of ``face``. + for i in range(Vrep.n_faces()): + if face_issubset(face, Vrep.data.faces[i]): + delete[i] = 0 + else: + delete[i] = 1 + + new_facets = facets.delete_atoms_unsafe(delete, null_face) + new_Vrep = Vrep.__copy__() + new_Vrep.delete_faces_unsafe(delete, null_face) + + # Delete all facets that define the face. + new_facets.get_faces_all_set_unsafe(delete) + new_facets.delete_faces_unsafe(delete, null_face) + new_Vrep = new_Vrep.delete_atoms_unsafe(delete, null_face) + + # Now delete all facets that are not inclusion maximal. + # the last copy of each duplicate will remain. + new_facets.get_not_inclusion_maximal_unsafe(delete) + new_facets.delete_faces_unsafe(delete, null_face) + new_Vrep = new_Vrep.delete_atoms_unsafe(delete, null_face) + + # Finally set coatoms of the output. + for i in range(new_facets.n_faces()): + facet_set_coatom(new_facets.data.faces[i], i) + for i in range(new_Vrep.n_faces()): + facet_set_coatom(new_Vrep.data.faces[i], i) + + return (new_facets, new_Vrep) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 951fd46db41..b9bc4d80c0c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -16,6 +16,7 @@ cdef class PolyhedronFaceLattice: # some copies from CombinatorialPolyhedron cdef tuple _Vrep, _facet_names, _equalities + cdef bint _bounded # Atoms and coatoms are the Vrep/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms Vrepresentatives and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index 6c9f5c678b5..f01b38b94eb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -139,6 +139,7 @@ cdef class PolyhedronFaceLattice: self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() + self._bounded = C.is_bounded() # copy f_vector for later use f_vector = C.f_vector() From 1d58214e420b6424161098cf0be72ebe451587fb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 May 2021 21:41:27 +0200 Subject: [PATCH 297/706] update documentation --- .../polyhedron/combinatorial_polyhedron/combinatorial_face.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 7a8ce82c4c7..7d93b9fc307 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -818,8 +818,7 @@ cdef class CombinatorialFace(SageObject): if not quotient: return CombinatorialPolyhedron(face_as_combinatorial_polyhedron(facets, Vrep, self.face, self._dual)) else: - # We run ``face_as_combinatorial_polyhedron`` with interchanged arguments to obtain - # the face as polyhedron in the dual setting. + # We run ``face_as_combinatorial_polyhedron`` for the dual setting. # We then interchange the output of it, to obtain the quotient. new_Vrep, new_facets = face_as_combinatorial_polyhedron(Vrep, facets, self.face, not self._dual) From 3896c3224b9b41bac9c78ff1c2f173ec466d8ed9 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 10 May 2021 22:58:42 +0200 Subject: [PATCH 298/706] fix naming and fix a doctest --- src/sage/geometry/polyhedron/base.py | 24 +++++++++---------- .../combinatorial_face.pyx | 10 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 56927af263f..11803a76767 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -6891,13 +6891,13 @@ def facets(self): return () return self.faces(self.dimension()-1) - def _test_combinatorial_face_as_polyhedron(self, tester=None, **options): + def _test_combinatorial_face_as_combinatorial_polyhedron(self, tester=None, **options): """ Run tests on obtaining the combinatorial face as combinatorial polyhedron. TESTS:: - sage: polytopes.cross_polytope(3)._test_combinatorial_face_as_polyhedron() + sage: polytopes.cross_polytope(3)._test_combinatorial_face_as_combinatorial_polyhedron() """ if not self.is_compact(): return @@ -6928,8 +6928,8 @@ def _test_combinatorial_face_as_polyhedron(self, tester=None, **options): continue P = f.as_polyhedron() - D1 = f1.as_polyhedron() - D2 = f2.as_polyhedron(face_figure=True).dual() + D1 = f1.as_combinatorial_polyhedron() + D2 = f2.as_combinatorial_polyhedron(quotient=True).dual() D1._test_bitsets(tester, **options) D2._test_bitsets(tester, **options) tester.assertTrue(P.combinatorial_polyhedron().vertex_facet_graph().is_isomorphic(D1.vertex_facet_graph())) @@ -10229,10 +10229,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 7d93b9fc307..62bf03ecf21 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -754,7 +754,7 @@ cdef class CombinatorialFace(SageObject): n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) - def as_polyhedron(self, quotient=False): + def as_combinatorial_polyhedron(self, quotient=False): r""" Return ``self`` as combinatorial polyhedron. @@ -772,7 +772,7 @@ cdef class CombinatorialFace(SageObject): sage: it = C.face_iter(4) sage: f = next(it); f A 4-dimensional face of a 7-dimensional combinatorial polyhedron - sage: C1 = f.as_polyhedron(); C1 + sage: C1 = f.as_combinatorial_polyhedron(); C1 A 4-dimensional combinatorial polyhedron with 5 facets sage: C1.f_vector() (1, 5, 10, 10, 5, 1) @@ -782,7 +782,7 @@ cdef class CombinatorialFace(SageObject): Obtaining the quotient:: - sage: C2 = f.as_polyhedron(quotient=True); C2 + sage: C2 = f.as_combinatorial_polyhedron(quotient=True); C2 A 2-dimensional combinatorial polyhedron with 6 facets sage: C2 A 2-dimensional combinatorial polyhedron with 6 facets @@ -795,7 +795,7 @@ cdef class CombinatorialFace(SageObject): sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) sage: f = next(it) - sage: f.as_polyhedron() + sage: f.as_combinatorial_polyhedron() Traceback (most recent call last): ... NotImplementedError: only implemented for bounded polyhedra @@ -807,7 +807,7 @@ cdef class CombinatorialFace(SageObject): .. NOTE:: This method is tested in - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base._test_combinatorial_face_as_polyhedron`. + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base._test_combinatorial_face_as_combinatorial_polyhedron`. """ if not self._ambient_bounded: raise NotImplementedError("only implemented for bounded polyhedra") From 691e17e1397c2b5d49b12823f0ba5417e50f4a3a Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 11 May 2021 09:34:44 +0900 Subject: [PATCH 299/706] Introduce strict mode to html() --- src/sage/misc/html.py | 15 +++++++++++---- src/sage/repl/rich_output/backend_base.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/html.py b/src/sage/misc/html.py index b9f67c566af..3301be2539f 100644 --- a/src/sage/misc/html.py +++ b/src/sage/misc/html.py @@ -403,18 +403,21 @@ def _repr_(self): """ return 'Create HTML output (see html? for details)' - def __call__(self, obj, concatenate=True): + def __call__(self, obj, concatenate=True, strict=False): r""" Construct a HTML fragment INPUT: - - ``obj`` -- anything. An object for which you want a HTML + - ``obj`` -- anything. An object for which you want an HTML representation. - ``concatenate`` -- if ``True``, combine HTML representations of elements of the container ``obj`` + - ``strict`` -- if ``True``, construct an HTML representation of + ``obj`` even if ``obj`` is a string + OUTPUT: A :class:`HtmlFragment` instance. @@ -427,12 +430,16 @@ def __call__(self, obj, concatenate=True): sage: html(1/2) - + \[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{1}{2}\] sage: html('sagemath') sagemath + + sage: html('sagemath', strict=True) + \[\newcommand{\Bold}[1]{\mathbf{#1}}\verb|sagemath|\] """ - if isinstance(obj, str): + # string obj is interpreted as an HTML in not strict mode + if isinstance(obj, str) and not strict: return HtmlFragment(math_parse(obj)) # prefer dedicated _html_() method diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 152530ab383..c98a90e2a00 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -475,7 +475,7 @@ def latex_formatter(self, obj, **kwds): concatenate = kwds.get('concatenate', False) from sage.misc.html import html from sage.repl.rich_output.output_browser import OutputHtml - return OutputHtml(html(obj, concatenate=concatenate)) + return OutputHtml(html(obj, concatenate=concatenate, strict=True)) def set_underscore_variable(self, obj): """ From fd038896796f00880f0b83de9f45e5798904a74f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 11 May 2021 11:51:46 +1000 Subject: [PATCH 300/706] Speedups to NF element __getitem__ and Guassian real/imag. --- .../number_field/number_field_element.pyx | 17 ++-- .../number_field_element_quadratic.pxd | 2 + .../number_field_element_quadratic.pyx | 81 +++++++++++++++---- 3 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 29932830f3c..10dab491021 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -756,11 +756,11 @@ cdef class NumberFieldElement(FieldElement): return repr(self.__pari__(name=name)) def __getitem__(self, n): - """ - Return the n-th coefficient of this number field element, written + r""" + Return the ``n``-th coefficient of this number field element, written as a polynomial in the generator. - Note that `n` must be between 0 and `d-1`, where + Note that ``n`` must be between `0` and `d-1`, where `d` is the degree of the number field. EXAMPLES:: @@ -780,11 +780,11 @@ cdef class NumberFieldElement(FieldElement): sage: c[-1] Traceback (most recent call last): ... - IndexError: index must be between 0 and degree minus 1. + IndexError: index must be between 0 and degree minus 1 sage: c[4] Traceback (most recent call last): ... - IndexError: index must be between 0 and degree minus 1. + IndexError: index must be between 0 and degree minus 1 The list method implicitly calls ``__getitem__``:: @@ -794,8 +794,11 @@ cdef class NumberFieldElement(FieldElement): True """ if n < 0 or n >= self.number_field().degree(): # make this faster. - raise IndexError("index must be between 0 and degree minus 1.") - return self.polynomial()[n] + raise IndexError("index must be between 0 and degree minus 1") + cdef list coeffs = self._coefficients() + if n >= len(coeffs): + return QQ.zero() + return coeffs[n] cpdef _richcmp_(left, right, int op): r""" diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pxd b/src/sage/rings/number_field/number_field_element_quadratic.pxd index 6344ceec552..dd068999bc4 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pxd +++ b/src/sage/rings/number_field/number_field_element_quadratic.pxd @@ -18,5 +18,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cdef int arb_set_real(self, arb_t x, long prec) except -1 cdef void arb_set_imag(self, arb_t x, long prec) + cpdef tuple parts(self) + cdef class OrderElement_quadratic(NumberFieldElement_quadratic): pass diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 203f7520fe1..fdca08e537a 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -602,7 +602,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): def _real_mpfi_(self, R): r""" - Conversion to a real interval field + Conversion to a real interval field. TESTS:: @@ -661,7 +661,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): def _complex_mpfi_(self, R): r""" - Conversion to a complex interval field + Conversion to a complex interval field. TESTS:: @@ -867,8 +867,44 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): self.arb_set_imag(acb_imagref(res.value), R._prec) return res - def parts(self): + def __getitem__(self, n): """ + Return the ``n``-th coefficient of this number field element, + written as a polynomial in the generator. + + Note that ``n`` must be either ``0`` or ``1``. + + EXAMPLES:: + + sage: K. = NumberField(x^2-13) + sage: elt = a/4 + 1/3 + sage: elt[0] + 1/3 + sage: elt[1] + 1/4 + + sage: K.zero()[0] + 0 + sage: K.zero()[1] + 0 + + sage: K.one()[0] + 1 + sage: K.one()[1] + 0 + + sage: elt[2] + Traceback (most recent call last): + ... + IndexError: index must be either 0 or 1 + """ + try: + return self.parts()[n] + except IndexError: # So we have a better error message + raise IndexError("index must be either 0 or 1") + + cpdef tuple parts(self): + r""" This function returns a pair of rationals `a` and `b` such that self `= a+b\sqrt{D}`. @@ -912,7 +948,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpz_set(mpq_denref(bd.value), self.denom) mpq_canonicalize(bd.value) - return ad, bd + return (ad, bd) cdef bint is_sqrt_disc(self): r""" @@ -1726,9 +1762,9 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: K.gen().is_one() False """ - return mpz_cmp_ui(self.a, 1) == 0 and \ - mpz_cmp_ui(self.b, 0) == 0 and \ - mpz_cmp_ui(self.denom, 1) == 0 + return (mpz_cmp_ui(self.a, 1) == 0 and + mpz_cmp_ui(self.b, 0) == 0 and + mpz_cmp_ui(self.denom, 1) == 0) cpdef bint is_rational(self): r""" @@ -1782,8 +1818,8 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): def real(self): r""" - Returns the real part of self, which is either self (if self lives - it a totally real field) or a rational number. + Return the real part of ``self``, which is either ``self`` (if + ``self`` lives it a totally real field) or a rational number. EXAMPLES:: @@ -1816,7 +1852,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): def imag(self): r""" - Returns the imaginary part of self. + Return the imaginary part of ``self``. EXAMPLES:: @@ -2445,7 +2481,14 @@ cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): sage: (1 + 2*I).real().parent() Rational Field """ - return self[0] + cdef Rational ad = Rational.__new__(Rational) + if mpz_cmp_ui(self.a, 0) == 0: + mpq_set_ui(ad.value, 0, 1) + else: + mpz_set(mpq_numref(ad.value), self.a) + mpz_set(mpq_denref(ad.value), self.denom) + mpq_canonicalize(ad.value) + return ad real = real_part @@ -2464,10 +2507,18 @@ cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): sage: (1 - mi).imag() 1 """ - if self.standard_embedding: - return self[1] - else: - return -self[1] + cdef Rational bd = Rational.__new__(Rational) + if mpz_cmp_ui(self.b, 0) == 0: + # It is 0, so all we need to do is initialize the result + mpq_set_ui(bd.value, 0, 1) + return bd + + mpz_set(mpq_numref(bd.value), self.b) + mpz_set(mpq_denref(bd.value), self.denom) + mpq_canonicalize(bd.value) + if not self.standard_embedding: + mpq_neg(bd.value, bd.value) + return bd imag = imag_part From d5a374a612539fe8ca71ada9d8f82b8290dd4a04 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 11 May 2021 17:28:35 +0200 Subject: [PATCH 301/706] Clear LANGUAGE too Sphinx 4.x uses it to get the language of its messages, so we need to reset it for the warning filter to work properly --- build/bin/sage-site | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/bin/sage-site b/build/bin/sage-site index 400782164b3..5e00ec9a12f 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -153,8 +153,10 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # proper UTF-8 operation in Python 3.6 or older. LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) LANG=$LC_ALL + LANGUAGE=$LC_ALL export LC_ALL export LANG + export LANGUAGE # See #30351: bugs in macOS implementations of openblas/libgopm can cause # docbuild to hang if multiple OpenMP threads are allowed. From 285ea99487b9dcf8fa917df21a235e5275732e77 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 11 May 2021 18:02:37 +0200 Subject: [PATCH 302/706] Update test for different sphinx output --- src/sage/misc/sagedoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 6bad826a882..33f28ada452 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -24,7 +24,7 @@ ....: for line in fobj: ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) - numerical_approx(prec=None, digits=None, algorithm=None)... + numerical_approx(prec=None, digits=None, algorithm=None)... Check that sphinx is not imported at Sage start-up:: From 79a625b0ebf7fbfc9e045662bd36eeedb7614fa7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 15:08:44 -0700 Subject: [PATCH 303/706] ManifoldSubset._reduce_intersection_members: Add examples, raise TypeError if input is empty family --- src/sage/manifolds/subset.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 26fc035e1c1..983c59a7b4b 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2202,8 +2202,34 @@ def _reduce_intersection_members(subsets): - replacing an inclusion chain by its minimal element - replacing a pair of subsets with a declared intersection by the intersection + + INPUT: + + - ``subsets`` -- a non-empty iterable of :class:`ManifoldSubset` instances + of the same manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B1 = A.subset('B1') + sage: B2 = A.subset('B2') + sage: C = B1.intersection(B2) + sage: M._reduce_intersection_members([A, M, A]) + Set {A} of subsets of the 2-dimensional topological manifold M + sage: M._reduce_intersection_members([A, B1]) + Set {B1} of subsets of the 2-dimensional topological manifold M + sage: M._reduce_intersection_members([B1, B2]) + Set {B1_inter_B2} of subsets of the 2-dimensional topological manifold M + sage: M._reduce_intersection_members([]) + Traceback (most recent call last): + ... + TypeError: input set must be nonempty + """ subsets = set(subsets) + if not subsets: + raise TypeError('input set must be nonempty') def reduce(): # Greedily replace inclusion chains by their minimal element # and pairs with declared intersections by their intersection From 8e70023c3d35bd50c632d30dc8961038fd9efd69 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 15:09:30 -0700 Subject: [PATCH 304/706] ManifoldSubset._reduce_union_members: Add examples --- src/sage/manifolds/subset.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 983c59a7b4b..fd6d7180fdc 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2410,6 +2410,26 @@ def _reduce_union_members(subsets): - replacing an inclusion chain by its maximal element - replacing a pair of subsets with a declared union by the union + + INPUT: + + - ``subsets`` -- an iterable of :class:`ManifoldSubset` instances + of the same manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B1 = A.subset('B1') + sage: B2 = A.subset('B2') + sage: B = B1.union(B2) + sage: M._reduce_union_members([]) + {} + sage: M._reduce_union_members([B1, B]) + Set {B1_union_B2} of subsets of the 2-dimensional topological manifold M + sage: M._reduce_union_members([A, B1, B2]) + Set {A} of subsets of the 2-dimensional topological manifold M + """ subsets = set(subsets) def reduce(): From ee4efc9ad51725bd500380a86333eb8a69e63311 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 May 2021 22:49:08 -0700 Subject: [PATCH 305/706] ManifoldSubset._{union,intersection}_subset: Do cache the result here; add examples --- src/sage/manifolds/subset.py | 57 +++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index fd6d7180fdc..261bad4170a 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2185,11 +2185,9 @@ def label(element): return res for other in others[:-1]: old, res = res, res._intersection_subset(other) - old._intersections[other._name] = other._intersections[old._name] = res # The last one gets the name other = others[-1] old, res = res, res._intersection_subset(other, name=name, latex_name=latex_name) - old._intersections[other._name] = other._intersections[old._name] = res return res @staticmethod @@ -2256,9 +2254,32 @@ def reduce(): def _intersection_subset(self, *others, name=None, latex_name=None): r""" - Return a subset that is the intersection of the given subsets. + Return a subset that is the intersection of ``self`` and ``others``. + + The result is always a new subset of the manifold. If the intersection + involves two subsets only, the result is stored in the dictionaries + of known intersections for later reuse by other methods. + + INPUT: + + - ``others`` -- an iterable of :class:`ManifoldSubset` instances + of the same manifold. + - ``name`` -- (default: ``None``) name given to the intersection; the + default is ``self._name`` inter [...] inter ``last_other._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + intersection; the default is built upon the symbol `\cap` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: B1 = M.subset('B1') + sage: B2 = M.subset('B2') + sage: B3 = M.subset('B3') + sage: B1._intersection_subset(B2) + Subset B1_inter_B2 of the 2-dimensional topological manifold M + sage: B1._intersection_subset(B2, B3) + Subset B1_inter_B2_inter_B3 of the 2-dimensional topological manifold M - This method does not cache the result. """ subsets = ManifoldSubsetFiniteFamily.from_subsets_or_families(self, *others) if latex_name is None: @@ -2275,6 +2296,9 @@ def _intersection_subset(self, *others, name=None, latex_name=None): res.declare_subset(subsets) for S in subsets: S._top_subsets.add(res) + if len(subsets) == 2: + S1, S2 = subsets + S1._intersections[S2._name] = S2._intersections[S1._name] = res return res def union(self, *others, name=None, latex_name=None): @@ -2393,11 +2417,9 @@ def label(element): return res for other in others[:-1]: old, res = res, res._union_subset(other) - old._unions[other._name] = other._unions[old._name] = res # The last one gets the name other = others[-1] old, res = res, res._union_subset(other, name=name, latex_name=latex_name) - old._unions[other._name] = other._unions[old._name] = res return res @staticmethod @@ -2457,9 +2479,27 @@ def reduce(): def _union_subset(self, other, name=None, latex_name=None): r""" - Return a subset of the manifold that is the union of the given subsets. + Return a subset of the manifold that is the union of ``self`` and ``other``. + + The result is always a new subset of the manifold and is also + stored in ``self`` and ``other``'s dictionaries of known unions. + + INPUT: + + - ``other`` -- an instance of :class:`ManifoldSubset` + - ``name`` -- (default: ``None``) name given to the union; the default is + ``self._name`` union ``other._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + union; the default is built upon the symbol `\cup` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: B1 = M.subset('B1') + sage: B2 = M.subset('B2') + sage: B1._union_subset(B2) + Subset B1_union_B2 of the 2-dimensional topological manifold M - This method does not cache the result. """ if latex_name is None: if name is None: @@ -2473,6 +2513,7 @@ def _union_subset(self, other, name=None, latex_name=None): res.declare_superset(other) res._top_subsets.add(self) res._top_subsets.add(other) + self._unions[other._name] = other._unions[self._name] = res for sp in self._supersets: if sp in other._supersets: sp._subsets.add(res) From b560ad2518dcd4f2a96bc19975ac695d607d15aa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 20 Apr 2021 09:56:23 -0700 Subject: [PATCH 306/706] build/pkgs/setuptools: Update to 56.2.0 --- build/pkgs/setuptools/checksums.ini | 6 +++--- build/pkgs/setuptools/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 5665466423b..ef3af392c38 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools-VERSION.tar.gz -sha1=77d3f0bd339c3d7778b3b7dacd14f47d09ac56be -md5=e22db062c2429f9dd7239aecdeb64d6f -cksum=3206580377 +sha1=93a2efe727c901cd140fad483b5e05d5f8117ec2 +md5=e8caf0f129f585a887bb3cbb528149d1 +cksum=591865795 upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index c4df1a44834..716e2294624 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -51.1.1.p0 +56.2.0 From 20062123bce97f985c34200fd0406a7433bed7fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 20 Apr 2021 09:56:46 -0700 Subject: [PATCH 307/706] build/pkgs/pkgconfig: Update to 1.5.2 --- build/pkgs/pkgconfig/checksums.ini | 6 +++--- build/pkgs/pkgconfig/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pkgconfig/checksums.ini b/build/pkgs/pkgconfig/checksums.ini index a715bf5b36b..35ac385cfa6 100644 --- a/build/pkgs/pkgconfig/checksums.ini +++ b/build/pkgs/pkgconfig/checksums.ini @@ -1,4 +1,4 @@ tarball=pkgconfig-VERSION.tar.gz -sha1=1e844638e8ba3886b6a28b86500c4414ba910b4e -md5=9f9cdb224ec0a1e59efcc7cac4b91972 -cksum=1169273382 +sha1=9db3e565c5bd85ceee16cbc162ab4f2f5019153c +md5=0d889edf670b644bfeaa3bb9444169cb +cksum=3590715331 diff --git a/build/pkgs/pkgconfig/package-version.txt b/build/pkgs/pkgconfig/package-version.txt index 26ca594609a..4cda8f19edc 100644 --- a/build/pkgs/pkgconfig/package-version.txt +++ b/build/pkgs/pkgconfig/package-version.txt @@ -1 +1 @@ -1.5.1 +1.5.2 From 012774f5f914574764e9d2845f9cbb1a25d141e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 20 Apr 2021 09:57:09 -0700 Subject: [PATCH 308/706] build/pkgs/packaging: Update to 20.9 --- build/pkgs/packaging/checksums.ini | 6 +++--- build/pkgs/packaging/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index f22c8a9fae6..68db6c29e45 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -1,4 +1,4 @@ tarball=packaging-VERSION.tar.gz -sha1=b99fa7af153646722b2d1817bb09906cc5a94bc6 -md5=3208229da731c5d8e29d4d8941e75005 -cksum=1742055469 +sha1=6f8880ab84f05714a2549c1b54314b4f79fee319 +md5=5377308b3ba89f2d78c05e7f485be65d +cksum=1567718319 diff --git a/build/pkgs/packaging/package-version.txt b/build/pkgs/packaging/package-version.txt index d0d4616e59f..48ef2c10bab 100644 --- a/build/pkgs/packaging/package-version.txt +++ b/build/pkgs/packaging/package-version.txt @@ -1 +1 @@ -20.4 +20.9 From fff89bc2ff73d2261dcb04af334d0e392933898d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 20 Apr 2021 09:57:29 -0700 Subject: [PATCH 309/706] build/pkgs/importlib_metadata: Update to 4.0.1 --- build/pkgs/importlib_metadata/checksums.ini | 6 +++--- build/pkgs/importlib_metadata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini index 902313b3d20..92286cd18a8 100644 --- a/build/pkgs/importlib_metadata/checksums.ini +++ b/build/pkgs/importlib_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_metadata-VERSION.tar.gz -sha1=bdb2154fb65f956c30cc2766413215f04120929f -md5=4505ea85600cca1e693a4f8f5dd27ba8 -cksum=608862478 +sha1=9a41be5cdf1fb347fca35640d607cbb27873cd02 +md5=de8753734db41db35d763475b3cf56be +cksum=2900617554 upstream_url=https://pypi.io/packages/source/i/importlib_metadata/importlib_metadata-VERSION.tar.gz diff --git a/build/pkgs/importlib_metadata/package-version.txt b/build/pkgs/importlib_metadata/package-version.txt index bd8bf882d06..1454f6ed4b7 100644 --- a/build/pkgs/importlib_metadata/package-version.txt +++ b/build/pkgs/importlib_metadata/package-version.txt @@ -1 +1 @@ -1.7.0 +4.0.1 From e14a5ede8ae98330caea1b9ffa54e115e0a0acce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 16:03:14 -0700 Subject: [PATCH 310/706] build/pkgs/pip: Update to 21.1.1 --- build/pkgs/pip/checksums.ini | 6 +++--- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index a89f768a51a..fabc83e4fa0 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=f74aa5460852ab99f4433212af87949168a8181c -md5=246523bd34dd356e7506adf54d206b12 -cksum=3347074384 +sha1=85a0cca517b854373dfcf43c3cac4ff726120c7f +md5=7fe80e6b3f94b5350284ed536d5b3a23 +cksum=3136634190 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index a8f5438c0a5..2d978e312b5 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -21.0.1 +21.1.1 From 3386fa12fa5e377d79f17f96bbed9c2479308b36 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 16:37:35 -0700 Subject: [PATCH 311/706] build/pkgs/toml: New --- build/pkgs/toml/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/toml/checksums.ini | 5 +++++ build/pkgs/toml/dependencies | 4 ++++ build/pkgs/toml/install-requires.txt | 1 + build/pkgs/toml/package-version.txt | 1 + build/pkgs/toml/spkg-install.in | 2 ++ build/pkgs/toml/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/toml/SPKG.rst create mode 100644 build/pkgs/toml/checksums.ini create mode 100644 build/pkgs/toml/dependencies create mode 100644 build/pkgs/toml/install-requires.txt create mode 100644 build/pkgs/toml/package-version.txt create mode 100644 build/pkgs/toml/spkg-install.in create mode 100644 build/pkgs/toml/type diff --git a/build/pkgs/toml/SPKG.rst b/build/pkgs/toml/SPKG.rst new file mode 100644 index 00000000000..e1b53b8f79e --- /dev/null +++ b/build/pkgs/toml/SPKG.rst @@ -0,0 +1,18 @@ +toml: Python Library for Tom's Obvious, Minimal Language +======================================================== + +Description +----------- + +Python Library for Tom's Obvious, Minimal Language + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/toml/ + diff --git a/build/pkgs/toml/checksums.ini b/build/pkgs/toml/checksums.ini new file mode 100644 index 00000000000..27467959676 --- /dev/null +++ b/build/pkgs/toml/checksums.ini @@ -0,0 +1,5 @@ +tarball=toml-VERSION.tar.gz +sha1=00137fc72f31100edd1c03670081b03053b6c836 +md5=59bce5d8d67e858735ec3f399ec90253 +cksum=3015531877 +upstream_url=https://pypi.io/packages/source/t/toml/toml-VERSION.tar.gz diff --git a/build/pkgs/toml/dependencies b/build/pkgs/toml/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/toml/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/toml/install-requires.txt b/build/pkgs/toml/install-requires.txt new file mode 100644 index 00000000000..bd79a658fe7 --- /dev/null +++ b/build/pkgs/toml/install-requires.txt @@ -0,0 +1 @@ +toml diff --git a/build/pkgs/toml/package-version.txt b/build/pkgs/toml/package-version.txt new file mode 100644 index 00000000000..5eef0f10e8c --- /dev/null +++ b/build/pkgs/toml/package-version.txt @@ -0,0 +1 @@ +0.10.2 diff --git a/build/pkgs/toml/spkg-install.in b/build/pkgs/toml/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/toml/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/toml/type b/build/pkgs/toml/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/toml/type @@ -0,0 +1 @@ +standard From 71aeec7be29d04c90ef8c1ccaff06d1feff35b1e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 16:47:27 -0700 Subject: [PATCH 312/706] build/pkgs/typing_extensions: New --- build/pkgs/typing_extensions/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/typing_extensions/checksums.ini | 5 +++++ build/pkgs/typing_extensions/dependencies | 4 ++++ .../typing_extensions/install-requires.txt | 1 + .../pkgs/typing_extensions/package-version.txt | 1 + build/pkgs/typing_extensions/spkg-install.in | 2 ++ build/pkgs/typing_extensions/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/typing_extensions/SPKG.rst create mode 100644 build/pkgs/typing_extensions/checksums.ini create mode 100644 build/pkgs/typing_extensions/dependencies create mode 100644 build/pkgs/typing_extensions/install-requires.txt create mode 100644 build/pkgs/typing_extensions/package-version.txt create mode 100644 build/pkgs/typing_extensions/spkg-install.in create mode 100644 build/pkgs/typing_extensions/type diff --git a/build/pkgs/typing_extensions/SPKG.rst b/build/pkgs/typing_extensions/SPKG.rst new file mode 100644 index 00000000000..da23c19d36d --- /dev/null +++ b/build/pkgs/typing_extensions/SPKG.rst @@ -0,0 +1,18 @@ +typing_extensions: Backported and Experimental Type Hints for Python 3.5+ +========================================================================= + +Description +----------- + +Backported and Experimental Type Hints for Python 3.5+ + +License +------- + +PSF + +Upstream Contact +---------------- + +https://pypi.org/project/typing-extensions/ + diff --git a/build/pkgs/typing_extensions/checksums.ini b/build/pkgs/typing_extensions/checksums.ini new file mode 100644 index 00000000000..6a9672ec92f --- /dev/null +++ b/build/pkgs/typing_extensions/checksums.ini @@ -0,0 +1,5 @@ +tarball=typing_extensions-VERSION.tar.gz +sha1=efa40572330e9a3c38ba519028f36d7f93647a39 +md5=9b5b33ae64c94479fa0862cf8ae89d58 +cksum=1588180971 +upstream_url=https://pypi.io/packages/source/t/typing_extensions/typing_extensions-VERSION.tar.gz diff --git a/build/pkgs/typing_extensions/dependencies b/build/pkgs/typing_extensions/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/typing_extensions/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/typing_extensions/install-requires.txt b/build/pkgs/typing_extensions/install-requires.txt new file mode 100644 index 00000000000..3492fa57976 --- /dev/null +++ b/build/pkgs/typing_extensions/install-requires.txt @@ -0,0 +1 @@ +typing-extensions diff --git a/build/pkgs/typing_extensions/package-version.txt b/build/pkgs/typing_extensions/package-version.txt new file mode 100644 index 00000000000..37c18af77ed --- /dev/null +++ b/build/pkgs/typing_extensions/package-version.txt @@ -0,0 +1 @@ +3.10.0.0 diff --git a/build/pkgs/typing_extensions/spkg-install.in b/build/pkgs/typing_extensions/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/typing_extensions/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/typing_extensions/type b/build/pkgs/typing_extensions/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/typing_extensions/type @@ -0,0 +1 @@ +standard From ecdf3e17bf79246065033531d95181a96cacdd86 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 16:47:47 -0700 Subject: [PATCH 313/706] build/pkgs/importlib_metadata/dependencies: Update --- build/pkgs/importlib_metadata/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/importlib_metadata/dependencies b/build/pkgs/importlib_metadata/dependencies index 88d3a620850..c136b9290b7 100644 --- a/build/pkgs/importlib_metadata/dependencies +++ b/build/pkgs/importlib_metadata/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) vcversioner zipp | $(PYTHON_TOOLCHAIN) +$(PYTHON) zipp typing_extensions | $(PYTHON_TOOLCHAIN) toml ---------- All lines of this file are ignored except the first. From ac49be02c68f399b1e38912fe3ff1c5c437b7aa3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 17:40:02 -0700 Subject: [PATCH 314/706] build/pkgs/numpy: Update to 1.20.3 --- build/pkgs/numpy/checksums.ini | 6 +++--- build/pkgs/numpy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index bf10ae97dc2..74d283e01f0 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,5 +1,5 @@ tarball=numpy-VERSION.zip -sha1=12b7a872c4d2ddcb22b5796f19ba4349e4fe0038 -md5=5e1b381630af4d18db0fedd56b6d8da2 -cksum=3027595668 +sha1=56731f9eedf3bd42de2940515cfe3fadea7dfcca +md5=949d9114af9accc25ede1daa359c4227 +cksum=2320980313 upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.zip diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 769e37e159d..f5b00dc262b 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.20.2 +1.20.3 From 02f68482174d6a4b49d0a8cd1e3575ee3c007fc5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 17:40:21 -0700 Subject: [PATCH 315/706] build/pkgs/scipy: Update to 1.6.3 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index dd13a526515..55b9191bcdd 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=7ef8a684f9feb4fd24d35e87f9d1f69eb6ec793e -md5=cbcb9b39bd9d877ad3deeccc7c37bb7f -cksum=852682618 +sha1=7de2a7d7bfce73111c1d8aa3248bec089127e48c +md5=05b4ca400aa1157290abff69016f1cab +cksum=484570496 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index fdd3be6df54..266146b87cb 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.6.2 +1.6.3 From 0ea116fd402d027c4ea82daf9c4d4ccc16aabdd7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 17:52:46 -0700 Subject: [PATCH 316/706] build/pkgs/python3: Update to 3.9.5 --- build/pkgs/python3/checksums.ini | 6 +++--- build/pkgs/python3/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 825afb53913..f012638d349 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,5 +1,5 @@ tarball=Python-VERSION.tar.xz -sha1=110ca5bca7989f9558a54ee6762e6774a4b9644a -md5=f0dc9000312abeb16de4eccce9a870ab -cksum=1344509533 +sha1=edc80e5e33fc3d3fae53e6b95ae4ca9277809b9b +md5=71f7ada6bec9cdbf4538adc326120cfd +cksum=1916830623 upstream_url=https://www.python.org/ftp/python/VERSION/Python-VERSION.tar.xz diff --git a/build/pkgs/python3/package-version.txt b/build/pkgs/python3/package-version.txt index 2009c7dfad9..11aaa06863c 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.9.2 +3.9.5 From ac5398499c314b8a59b5994c2cfa8b5945f3b311 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 18:32:03 -0700 Subject: [PATCH 317/706] src/sage/manifolds/subsets/__init__.py: New --- src/sage/manifolds/subsets/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/sage/manifolds/subsets/__init__.py diff --git a/src/sage/manifolds/subsets/__init__.py b/src/sage/manifolds/subsets/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 06a469e494a5de312fb4f083577f811371994aca Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Tue, 11 May 2021 18:33:04 -0700 Subject: [PATCH 318/706] Fix segfault in Weil polynomials --- src/sage/rings/polynomial/weil/power_sums.c | 6 +++++- src/sage/rings/polynomial/weil/weil_polynomials.pyx | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/weil/power_sums.c b/src/sage/rings/polynomial/weil/power_sums.c index 9f7e1f9c9fe..a210a874902 100644 --- a/src/sage/rings/polynomial/weil/power_sums.c +++ b/src/sage/rings/polynomial/weil/power_sums.c @@ -52,8 +52,11 @@ int _fmpz_poly_all_real_roots(fmpz *poly, long n, fmpz *w, int force_squarefree, fmpz *d = w + 2*n+1; fmpz *t; - if (n <= 2) return(1); _fmpz_vec_set(f0, poly, n); + /* Sanitize input so that n = deg(f0). */ + while ((n > 2) && fmpz_is_zero(f0+n-1)) + n--; + if (n <= 2) return(1); if (a != NULL && b != NULL) fmpz_addmul(f0, a, b); _fmpz_poly_derivative(f1, f0, n); n--; @@ -74,6 +77,7 @@ int _fmpz_poly_all_real_roots(fmpz *poly, long n, fmpz *w, int force_squarefree, _fmpz_vec_scalar_mul_fmpz(f0, f0, n, d); _fmpz_vec_scalar_addmul_fmpz(f0, f1, n, c); + /* If f0 = 0, we win unless we are insisting on squarefree. */ if (!force_squarefree && _fmpz_vec_is_zero(f0, n)) return(1); /* If we miss any one sign change, we cannot have enough. */ diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx index 419c31ab98e..3a6857f0b2b 100755 --- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx +++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx @@ -540,6 +540,14 @@ class WeilPolynomials(): sage: u in WeilPolynomials(6, 11, 1, [(1,0),(1,11),(6,11)]) True + Test that :trac:`31809` is resolved:: + + sage: from sage.rings.polynomial.weil.weil_polynomials import WeilPolynomials + sage: foo = list(WeilPolynomials(12, 3, lead=(1,0,9,2,46), squarefree=False)) + sage: bar = list(WeilPolynomials(12, 3, lead=(1,0,9,2,46), squarefree=True)) + sage: bar == [f for f in foo if f.is_squarefree()] + True + """ def __init__(self, d, q, sign=1, lead=1, node_limit=None, parallel=False, squarefree=False, polring=None): r""" From 3b590a1b93cf88ece6911351c1a0b574feecb83e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 19:33:59 -0700 Subject: [PATCH 319/706] build/bin/sage-dist-helpers (sdh_pip_install): No longer try to uninstall an old version first with sage-pip-uninstall --- build/bin/sage-dist-helpers | 8 ----- build/bin/sage-pip-uninstall | 63 ------------------------------------ 2 files changed, 71 deletions(-) delete mode 100755 build/bin/sage-pip-uninstall diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index 95b5770b748..f11fa83ac27 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -211,14 +211,6 @@ sdh_setup_bdist_wheel() { sdh_pip_install() { echo "Installing $PKG_NAME" - if [ -n "$SAGE_DESTDIR" ]; then - local sudo="" - else - local sudo="$SAGE_SUDO" - fi - $sudo sage-pip-uninstall "$@" || \ - echo 2>&1 "Warning: Failure trying to uninstall a previous version of $PKG_NAME" - mkdir -p dist rm -f dist/*.whl python3 -m pip wheel --wheel-dir=dist --no-binary :all: --verbose --no-deps --no-index --isolated --no-build-isolation "$@" || \ diff --git a/build/bin/sage-pip-uninstall b/build/bin/sage-pip-uninstall deleted file mode 100755 index cf737d0b6bf..00000000000 --- a/build/bin/sage-pip-uninstall +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -# This command ensures that any previous installations of the same package -# are uninstalled. - -# Only argument must be "." and will be ignored. -if [ $# -gt 1 ]; then - echo >&2 "$0 requires . as only argument" - exit 1 -fi -if [ "$1" != "." ]; then - echo >&2 "$0 requires . as final argument" - exit 1 -fi - -# Note: "sage-pip-uninstall" is meant to be run after $(PYTHON) has -# been installed (either as an spkg or as a venv over a system python3). -# It is then guaranteed by sage-env that PATH is set in a way that "python3" -# refers to the correct python3. -PYTHON=python3 - -# The PIP variable is only used to determine the name of the lock file. -PIP=pip3 - -# For PEP 517 packages, do not try to uninstall -if [ ! -f setup.py ]; then - exit 0 -fi - -# Find out the name of the package that we are installing -name="$($PYTHON setup.py --name)" - -if [ $? -ne 0 ]; then - echo >&2 "Error: could not determine package name" - exit 1 -fi - -if [ $(echo "$name" | wc -l) -gt 1 ]; then - name="$(echo "$name" | tail -1)" - echo >&2 "Warning: This package has a badly-behaved setup.py which outputs" - echo >&2 "more than the package name for 'setup.py --name'; using the last" - echo >&2 "line as the package name: $name" -fi - -# We should avoid running pip while uninstalling a package because that -# is prone to race conditions. Therefore, we use a lockfile while -# running pip. This is implemented in the Python script sage-flock -LOCK="$SAGE_LOCAL/var/lock/$PIP.lock" - -# Keep uninstalling as long as it succeeds -while true; do - out=$(sage-flock -x $LOCK $PYTHON -m pip uninstall --disable-pip-version-check -y "$name" 2>&1) - if [ $? -ne 0 ]; then - # Uninstall failed - echo >&2 "$out" - exit 1 - fi - - # Uninstall succeeded, which may mean that the package was not - # installed to begin with. - if [[ "$out" != *"not installed" ]]; then - break - fi -done From f014e79c131285fb6ea74c9b721998ed7bfe8bff Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 12 May 2021 08:12:13 +0200 Subject: [PATCH 320/706] Install pari spkg if test for rnfdisc bug fix fails --- build/pkgs/pari/spkg-configure.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 719b8089eeb..4e4e1055685 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -108,6 +108,7 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) + sage_spkg_install_pari=yes fi fi dnl end GP test From 044ae53ba7aa59038a5ce2472b921ac54c5ce7f4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 12 May 2021 16:39:20 +1000 Subject: [PATCH 321/706] Move the is_sqrt_disc() check to the parent (mostly). --- src/sage/rings/number_field/number_field.py | 23 +- .../number_field_element_quadratic.pxd | 12 +- .../number_field_element_quadratic.pyx | 235 +++++++++++++----- 3 files changed, 203 insertions(+), 67 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index f4cdb9e4f31..45603234f99 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -10198,8 +10198,8 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a embedding = embedding, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes) - if n%2: - self.__zeta_order = 2*n + if n % 2: + self.__zeta_order = 2 * n else: self.__zeta_order = n ## quadratic number fields require this: @@ -10215,12 +10215,13 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a self._standard_embedding = not CDF.has_coerce_map_from(self) or CDF(self.gen()).imag() > 0 self._cache_an_element = None - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic if n == 4: + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic self._D = ZZ(-1) self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ(1))) else: ## n is 3 or 6 + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic_nonsqrt self._D = ZZ(-3) one_half = ZZ(1)/ZZ(2) if n == 3: @@ -11507,16 +11508,22 @@ def __init__(self, polynomial, name=None, latex_name=None, check=True, embedding embedding=embedding, latex_name=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structure=structure) self._standard_embedding = True + + # set the generator and element class c, b, a = [QQ(t) for t in self.defining_polynomial().list()] - if a.is_one() and b.is_zero() and c.is_one(): - self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian - else: - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic - # set the generator Dpoly = b*b - 4*a*c D = (Dpoly.numer() * Dpoly.denom()).squarefree_part(bound=10000) self._D = D parts = -b/(2*a), (Dpoly/D).sqrt()/(2*a) + + if a.is_one() and b.is_zero() and c.is_one(): + self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian + else: + if number_field_element_quadratic.is_sqrt_disc(parts[0], parts[1]): + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic + else: + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic_nonsqrt + self._NumberField_generic__gen = self._element_class(self, parts) # we must set the flag _standard_embedding *before* any element creation diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pxd b/src/sage/rings/number_field/number_field_element_quadratic.pxd index dd068999bc4..056ab308c61 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pxd +++ b/src/sage/rings/number_field/number_field_element_quadratic.pxd @@ -1,6 +1,7 @@ from sage.libs.gmp.types cimport mpz_t from sage.libs.arb.types cimport arb_t from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational from .number_field_element cimport NumberFieldElement, NumberFieldElement_absolute @@ -10,7 +11,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cdef Integer D cdef bint standard_embedding cpdef NumberFieldElement galois_conjugate(self) - cdef bint is_sqrt_disc(self) cpdef list _coefficients(self) @@ -20,5 +20,15 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cpdef tuple parts(self) +cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): + cpdef real_part(self) + cpdef imag_part(self) + +cdef class NumberFieldElement_quadratic_nonsqrt(NumberFieldElement_quadratic): + pass + cdef class OrderElement_quadratic(NumberFieldElement_quadratic): pass + +cpdef bint is_sqrt_disc(Rational ad, Rational bd) + diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index fdca08e537a..f04e490c645 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -112,6 +112,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): TESTS:: sage: from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic + sage: from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic_nonsqrt We set up some fields:: @@ -127,32 +128,32 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: NumberFieldElement_quadratic(K, x-1) a - 1 - sage: NumberFieldElement_quadratic(F, x-1) + sage: NumberFieldElement_quadratic_nonsqrt(F, x-1) b - 1 From triples of Integers:: sage: NumberFieldElement_quadratic(K, (1,2,3)) 2/3*a + 1/3 - sage: NumberFieldElement_quadratic(F, (1,2,3)) + sage: NumberFieldElement_quadratic_nonsqrt(F, (1,2,3)) 4/9*b + 1/9 - sage: NumberFieldElement_quadratic(F, (1,2,3)).parts() + sage: NumberFieldElement_quadratic_nonsqrt(F, (1,2,3)).parts() (1/3, 2/3) From pairs of Rationals:: sage: NumberFieldElement_quadratic(K, (1/2,1/3)) 1/3*a + 1/2 - sage: NumberFieldElement_quadratic(F, (1/2,1/3)) + sage: NumberFieldElement_quadratic_nonsqrt(F, (1/2,1/3)) 2/9*b + 7/18 - sage: NumberFieldElement_quadratic(F, (1/2,1/3)).parts() + sage: NumberFieldElement_quadratic_nonsqrt(F, (1/2,1/3)).parts() (1/2, 1/3) Direct from Rationals:: sage: NumberFieldElement_quadratic(K, 2/3) 2/3 - sage: NumberFieldElement_quadratic(F, 2/3) + sage: NumberFieldElement_quadratic_nonsqrt(F, 2/3) 2/3 This checks a bug when converting from lists:: @@ -950,18 +951,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): return (ad, bd) - cdef bint is_sqrt_disc(self): - r""" - Returns true if self is `\sqrt{D}`. - - EXAMPLES:: - - sage: F. = NumberField(x^2 - x + 7) - sage: b.denominator() # indirect doctest - 1 - """ - return mpz_cmp_ui(self.denom, 1)==0 and mpz_cmp_ui(self.a, 0)==0 and mpz_cmp_ui(self.b, 1)==0 - ######################################################### # Comparisons ######################################################### @@ -1819,7 +1808,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): def real(self): r""" Return the real part of ``self``, which is either ``self`` (if - ``self`` lives it a totally real field) or a rational number. + ``self`` lives in a totally real field) or a rational number. EXAMPLES:: @@ -1944,34 +1933,26 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: K. = NumberField(x^2+41) sage: a._coefficients() [0, 1] - sage: K. = NumberField(x^2+x+41) - sage: a._coefficients() - [0, 1] - sage: b = 3*a+1/5 - sage: b._coefficients() - [1/5, 3] + sage: K.zero()._coefficients() + [] + sage: (3/2*K.one())._coefficients() + [3/2] """ - # In terms of the generator... - cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? - cdef Rational const = Rational.__new__(Rational) + # In terms of the generator... Rational const = Rational.__new__(Rational) cdef Rational lin = Rational.__new__(Rational) - ad, bd = self.parts() if not self: return [] + ad, bd = self.parts() if not bd: return [ad] - if gen.is_sqrt_disc(): - return [ad,bd] - else: - alpha, beta = gen.parts() - scale = bd/beta - return [ad - scale*alpha, scale] + return [ad,bd] def denominator(self): - """ - Return the denominator of self. This is the LCM of the denominators of - the coefficients of self, and thus it may well be `> 1` even when the - element is an algebraic integer. + r""" + Return the denominator of ``self``. + + This is the LCM of the denominators of the coefficients of `self``, and + thus it may well be `> 1` even when the element is an algebraic integer. EXAMPLES:: @@ -1995,27 +1976,14 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: b.is_integral() True """ - # In terms of the generator... - cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? cdef Integer denom - if gen.is_sqrt_disc(): - denom = Integer.__new__(Integer) - mpz_set(denom.value, self.denom) - return denom - else: - c = self._coefficients() - if len(c) == 2: - const, lin = c - elif len(c) == 1: - const = c[0] - lin = Rational(0) - else: - const = lin = Rational(0) - return const.denominator().lcm(lin.denominator()) + denom = Integer.__new__(Integer) + mpz_set(denom.value, self.denom) + return denom def numerator(self): - """ - Return self*self.denominator(). + r""" + Return ``self * self.denominator()``. EXAMPLES:: @@ -2026,7 +1994,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: b.numerator() 2*a + 1 """ - return self*self.denominator() + return self * self.denominator() ######################################################### @@ -2536,6 +2504,65 @@ cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): from sage.symbolic.ring import SR return SR(self).log(*args, **kwds) + +cdef class NumberFieldElement_quadratic_nonsqrt(NumberFieldElement_quadratic): + r""" + A NumberFieldElement_quadratic object gives an efficient representation of + an element of a quadratic extension of `\QQ` for the case when + :func:`is_sqrt_disc()` is ``False``. + """ + def denominator(self): + r""" + Return the denominator of ``self``. + + This is the LCM of the denominators of the coefficients of `self``, and + thus it may well be `> 1` even when the element is an algebraic integer. + + EXAMPLES:: + + sage: K. = NumberField(x^2 - 5) + sage: b = (a + 1)/2 + sage: b.denominator() + 2 + sage: b.is_integral() + True + + sage: K. = NumberField(x^2-x+7) + sage: c.denominator() + 1 + """ + c = self._coefficients() + if len(c) == 2: + const, lin = c + elif len(c) == 1: + const = c[0] + lin = Rational(0) + else: + const = lin = Rational(0) + return const.denominator().lcm(lin.denominator()) + + cpdef list _coefficients(self): + """ + EXAMPLES:: + + sage: F. = NumberField(x^2 - x + 7) + sage: b._coefficients() + [0, 1] + """ + # In terms of the generator... + cdef Rational const = Rational.__new__(Rational) + cdef Rational lin = Rational.__new__(Rational) + if not self: + return [] + ad, bd = self.parts() + if not bd: + return [ad] + + cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? + alpha, beta = gen.parts() + scale = bd/beta + return [ad - scale*alpha, scale] + cdef class OrderElement_quadratic(NumberFieldElement_quadratic): """ Element of an order in a quadratic field. @@ -2732,6 +2759,65 @@ cdef class OrderElement_quadratic(NumberFieldElement_quadratic): R = self.parent() return R(_inverse_mod_generic(self, I)) + cpdef list _coefficients(self): + """ + EXAMPLES:: + + sage: K. = NumberField(x^2-27) + sage: R = K.ring_of_integers() + sage: aa = R.gen(1) + sage: aa._coefficients() + [0, 1/3] + """ + # In terms of the generator... + cdef Rational const = Rational.__new__(Rational) + cdef Rational lin = Rational.__new__(Rational) + if not self: + return [] + ad, bd = self.parts() + if not bd: + return [ad] + cdef NumberFieldElement_quadratic gen = self.number_field().gen() + alpha, beta = gen.parts() + if is_sqrt_disc(alpha, beta): + return [ad,bd] + else: + scale = bd/beta + return [ad - scale*alpha, scale] + + def denominator(self): + r""" + Return the denominator of ``self``. + + This is the LCM of the denominators of the coefficients of `self``, and + thus it may well be `> 1` even when the element is an algebraic integer. + + EXAMPLES:: + + sage: K. = NumberField(x^2-27) + sage: R = K.ring_of_integers() + sage: aa = R.gen(1) + sage: aa.denominator() + 3 + """ + # In terms of the generator... + cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? + cdef Integer denom + cdef tuple parts = gen.parts() + cdef Rational alpha, beta, const, lin + alpha = (parts[0]) + beta = (parts[1]) + if is_sqrt_disc(alpha, beta): + denom = Integer.__new__(Integer) + mpz_set(denom.value, self.denom) + return denom + else: + parts = self.parts() + const = (parts[0]) + lin = (parts[1]) + scale = lin / beta + const = const - scale * alpha + return const.denominator().lcm(scale.denominator()) cdef class Z_to_quadratic_field_element(Morphism): """ @@ -2928,3 +3014,36 @@ cdef class Q_to_quadratic_field_element(Morphism): To: Cyclotomic Field of order 6 and degree 2 """ return "Natural" + +##################################################################### +## Helper function + +cpdef bint is_sqrt_disc(Rational ad, Rational bd): + r""" + Return ``True`` if the pair ``(ad, bd)`` is `\sqrt{D}`. + + EXAMPLES:: + + sage: F. = NumberField(x^2 - x + 7) + sage: b.denominator() # indirect doctest + 1 + """ + cdef mpz_t a, b, denom + mpz_init(a) + mpz_init(b) + mpz_init(denom) + + mpz_lcm(denom, mpq_denref(ad.value), mpq_denref(bd.value)) + mpz_divexact(a, denom, mpq_denref(ad.value)) + mpz_mul(a, a, mpq_numref(ad.value)) + mpz_divexact(b, denom, mpq_denref(bd.value)) + mpz_mul(b, b, mpq_numref(bd.value)) + + cdef bint ret = mpz_cmp_ui(denom, 1) == 0 and mpz_cmp_ui(a, 0) == 0 and mpz_cmp_ui(b, 1) == 0 + + mpz_clear(a) + mpz_clear(b) + mpz_clear(denom) + + return ret + From 443cf6a55281bf664cf4b45130597df4440abf2d Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Wed, 12 May 2021 00:59:19 -0700 Subject: [PATCH 322/706] first commit, added a pushout, now the example in the description works as intended --- src/sage/structure/parent.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index a5a16bacdd4..ad5f0174667 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1339,8 +1339,9 @@ cdef class Parent(sage.structure.category_object.CategoryObject): return self.Hom(im_gens).natural_map() from sage.structure.sequence import Sequence_generic, Sequence if codomain is None: + from sage.categories.pushout import pushout im_gens = Sequence(im_gens) - codomain = im_gens.universe() + codomain = pushout(self, im_gens.universe()) if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) # Not all homsets accept category/check/base_map as arguments From 79983a997f37ef15e9edade5c67a8a6ffe747d7c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 15:03:33 +0200 Subject: [PATCH 323/706] fix pickling of representation objects with backend normaliz --- .../geometry/polyhedron/backend_normaliz.py | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 95c489c8669..e119667d811 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1272,12 +1272,28 @@ def __getstate__(self): A vertex at (0, 0, 1, 0), A vertex at (0, 1, 0, 0), A vertex at (1, 0, 0, 0)), - '_normaliz_field': Rational Field}) + '_normaliz_field': Rational Field, + '_pickle_equations': [(-1, 1, 1, 1, 1)], + '_pickle_inequalities': [(0, 0, 0, 0, 1), + (0, 0, 0, 1, 0), + (0, 0, 1, 0, 0), + (0, 1, 0, 0, 0)], + '_pickle_lines': [], + '_pickle_rays': [], + '_pickle_vertices': [(0, 0, 0, 1), + (0, 0, 1, 0), + (0, 1, 0, 0), + (1, 0, 0, 0)]}) """ state = super(Polyhedron_normaliz, self).__getstate__() state = (state[0], state[1].copy()) # Remove the unpicklable entries. del state[1]['_normaliz_cone'] + state[1]["_pickle_vertices"] = [v._vector for v in self.vertices()] + state[1]["_pickle_rays"] = [v._vector for v in self.rays()] + state[1]["_pickle_lines"] = [v._vector for v in self.lines()] + state[1]["_pickle_inequalities"] = [v._vector for v in self.inequalities()] + state[1]["_pickle_equations"] = [v._vector for v in self.equations()] return state def __setstate__(self, state): @@ -1327,6 +1343,15 @@ def __setstate__(self, state): sage: P == P2 # optional - pynormaliz True """ + if "_pickle_vertices" in state[1]: + vertices = state[1].pop("_pickle_vertices") + rays = state[1].pop("_pickle_rays") + lines = state[1].pop("_pickle_lines") + inequalities = state[1].pop("_pickle_inequalities") + equations = state[1].pop("_pickle_equations") + else: + vertices = None + super(Polyhedron_normaliz, self).__setstate__(state) if self.is_empty(): @@ -1334,9 +1359,16 @@ def __setstate__(self, state): self._normaliz_cone = None return + if vertices is None: + vertices = self.vertices() + rays = self.rays() + lines = self.lines() + inequalities = self.inequalities() + equations = self.equations() + self._normaliz_cone = \ self._cone_from_Vrepresentation_and_Hrepresentation( - self.vertices(), self.rays(), self.lines(), self.inequalities(), self.equations()) + vertices, rays, lines, inequalities, equations) def integral_hull(self): r""" From 5e65a5e65dee0d4f9a4d35ac3a081f3ca5adc90a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 15:51:30 +0200 Subject: [PATCH 324/706] add doctest --- src/sage/geometry/polyhedron/backend_normaliz.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index e119667d811..ab12d367b49 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -1342,6 +1342,12 @@ def __setstate__(self, state): sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone, normaliz_field=P1._normaliz_field) # optional - pynormaliz sage: P == P2 # optional - pynormaliz True + + Test that :trac:`31820` is fixed:: + + sage: P = polytopes.cube(backend='normaliz') # optional - pynormaliz + sage: v = P.Vrepresentation()[0] # optional - pynormaliz + sage: v1 = loads(v.dumps()) # optional - pynormaliz """ if "_pickle_vertices" in state[1]: vertices = state[1].pop("_pickle_vertices") From b285d958fab3bcb034fcc9f90191e3600bc25c55 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 15:43:05 +0200 Subject: [PATCH 325/706] run testsuite on combinatorial polyhedron --- src/sage/geometry/polyhedron/base.py | 16 ++++++++++++++ .../combinatorial_polyhedron/base.pyx | 21 ++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..fe85ea48d65 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -3537,6 +3537,22 @@ def combinatorial_polyhedron(self): from sage.geometry.polyhedron.combinatorial_polyhedron.base import CombinatorialPolyhedron return CombinatorialPolyhedron(self) + def _test_combinatorial_polyhedron(self, tester=None, **options): + """ + Run test suite of combinatorial polyhedron. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_combinatorial_polyhedron() + """ + from sage.misc.sage_unittest import TestSuite + + tester = self._tester(tester=tester, **options) + tester.info("\n Running the test suite of self.combinatorial_polyhedron()") + TestSuite(self.combinatorial_polyhedron()).run(verbose=tester._verbose, + prefix=tester._prefix+" ") + tester.info(tester._prefix+" ", newline = False) + def simplicity(self): r""" Return the largest integer `k` such that the polytope is `k`-simple. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..881f5e26557 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -642,11 +642,11 @@ cdef class CombinatorialPolyhedron(SageObject): """ # Give a constructor by list of facets. if not self.is_bounded(): - return (CombinatorialPolyhedron, (self.facets(), + return (CombinatorialPolyhedron, (self.incidence_matrix(), self.Vrepresentation(), self.Hrepresentation(), True, self.far_face_tuple())) else: - return (CombinatorialPolyhedron, (self.facets(), + return (CombinatorialPolyhedron, (self.incidence_matrix(), self.Vrepresentation(), self.Hrepresentation())) def Vrepresentation(self): @@ -978,7 +978,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C.facets() () """ - if unlikely(self.dimension() == 0): + if unlikely(self.dimension() <= 0): # Special attention for this trivial case. # Facets are defined to be nontrivial faces of codimension 1. # The empty face is trivial. @@ -2756,6 +2756,21 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._far_face_tuple + def __eq__(self, other): + r""" + Return whether ``self`` and ``other`` are equal. + """ + if not isinstance(other, CombinatorialPolyhedron): + return False + cdef CombinatorialPolyhedron other_C = other + return (self.n_facets() == other.n_facets() + and self.Vrepresentation() == other.Vrepresentation() + and self.facet_names() == other_C.facet_names() + and self.equalities() == other_C.equalities() + and self.dimension() == other.dimension() + and self.far_face_tuple() == other_C.far_face_tuple() + and self.incidence_matrix() == other.incidence_matrix()) + # Methods to obtain a different combinatorial polyhedron. From 82db4082aa4c29a7572bb22981e03f524cabbc36 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 21:13:44 +0200 Subject: [PATCH 326/706] reintroduce spaces to fix merge conflict --- src/sage/geometry/polyhedron/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 11803a76767..58d2c60d98a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10229,10 +10229,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self From 10a4531721d2700fd717e2b3a1364508ffd971c3 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 12 May 2021 21:55:26 +0200 Subject: [PATCH 327/706] Fix pari test suite after the rnfdisc fix --- build/pkgs/pari/patches/pari-rnfdisc.patch | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/build/pkgs/pari/patches/pari-rnfdisc.patch b/build/pkgs/pari/patches/pari-rnfdisc.patch index 3a26e966873..39d325911e8 100644 --- a/build/pkgs/pari/patches/pari-rnfdisc.patch +++ b/build/pkgs/pari/patches/pari-rnfdisc.patch @@ -16,4 +16,20 @@ index b2b63ada5..531f5c558 100644 pol = nfX_to_monic(nf, pol, NULL); fa = idealfactor_partial(nf, disc, lim); P = gel(fa,1); l = lg(P); - +diff --git a/src/test/32/rnf b/src/test/32/rnf +index 6bd4585..d24e1ce 100644 (file) +--- a/src/test/32/rnf ++++ b/src/test/32/rnf +@@ -832,9 +832,9 @@ error("inconsistent dimensions in idealtwoelt.") + 0 + 0 + 1 +-[[7361, 3786, 318, 5823; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1], [-3, 6, -2, 0] +-~] +-[2, -1] ++[[433, 322, 318, 1318/17; 0, 1, 0, 12/17; 0, 0, 1, 5/17; 0, 0, 0, 1/17], [25 ++/17, -12/17, 12/17, 16/17]~] ++[1, -1] + *** at top-level: rnfdedekind(nf,P,pr2,1) + *** ^----------------------- + *** rnfdedekind: sorry, Dedekind in the difficult case is not yet implemented. From f460b23a68b891ba6baad0f221d2956f7d61ee35 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 22:28:48 +0200 Subject: [PATCH 328/706] improve documentation --- .../combinatorial_face.pyx | 85 ++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 62bf03ecf21..f447c8fcd50 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -758,12 +758,16 @@ cdef class CombinatorialFace(SageObject): r""" Return ``self`` as combinatorial polyhedron. - If ``quotient`` is ``True`` return the quotient of the polyhedron by ``F``. + If ``quotient`` is ``True``, return the quotient of the + polyhedron by ``self``. + Let ``G`` be the face corresponding to ``self`` in the dual/polar polytope. + The ``quotient`` is the dual/polar of ``G``. Let `[\hat{0], \hat{1}]` be the face lattice of the ambient polyhedron and `F` be ``self`` as element of the face lattice. - `F` as polyhedron corresponds to `[\hat{0}, F]` and - the quotient by `F` corresponds to `[F, \hat{1}]`. + The face lattice of ``self`` as polyhedron corresponds to + `[\hat{0}, F]` and the face lattice of the quotient by ``self`` + corresponds to `[F, \hat{1}]`. EXAMPLES:: @@ -789,6 +793,81 @@ cdef class CombinatorialFace(SageObject): sage: C2.f_vector() (1, 6, 6, 1) + The Vrepresentation of the face as polyhedron is given by the + ambient Vrepresentation of the face in that order:: + + sage: P = polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: C1 = f.as_combinatorial_polyhedron() + sage: C.Vrepresentation() + (A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (1, -1, 1), + A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1), + A vertex at (-1, 1, -1), + A vertex at (-1, 1, 1)) + sage: f.ambient_Vrepresentation() + (A vertex at (1, -1, -1), + A vertex at (1, -1, 1), + A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1)) + sage: C1.Vrepresentation() + (0, 1, 2, 3) + + To obtain the facets of the face as polyhedron, + we compute the meet of each facet with the face. + The first representative of each element strictly + contained in the face is kept:: + + sage: C.facets(names=False) + ((0, 1, 2, 3), + (1, 2, 6, 7), + (2, 3, 4, 7), + (4, 5, 6, 7), + (0, 1, 5, 6), + (0, 3, 4, 5)) + sage: C1.facets(names=False) + ((0, 1), (1, 2), (2, 3), (0, 3)) + + The Hrepresentation of the quotient by the face is given by the + ambient Hrepresentation of the face in that order:: + + sage: it = C.face_iter(1) + sage: f = next(it) + sage: C1 = f.as_combinatorial_polyhedron(quotient=True) + sage: C.Hrepresentation() + (An inequality (-1, 0, 0) x + 1 >= 0, + An inequality (0, -1, 0) x + 1 >= 0, + An inequality (0, 0, -1) x + 1 >= 0, + An inequality (1, 0, 0) x + 1 >= 0, + An inequality (0, 0, 1) x + 1 >= 0, + An inequality (0, 1, 0) x + 1 >= 0) + sage: f.ambient_Hrepresentation() + (An inequality (0, 0, 1) x + 1 >= 0, An inequality (0, 1, 0) x + 1 >= 0) + sage: C1.Hrepresentation() + (0, 1) + + To obtain the vertices of the face as polyhedron, + we compute the join of each vertex with the face. + The first representative of each element strictly + containing the face is kept:: + + sage: [g.ambient_H_indices() for g in C.face_iter(0)] + [(3, 4, 5), + (0, 4, 5), + (2, 3, 5), + (0, 2, 5), + (1, 3, 4), + (0, 1, 4), + (1, 2, 3), + (0, 1, 2)] + sage: [g.ambient_H_indices() for g in C1.face_iter(0)] + [(1,), (0,)] + The method is not implemented for unbounded polyhedra:: sage: P = Polyhedron(rays=[[0,1]])*polytopes.cube() From 21739de7506f1deb9486eb7d9960870da6d3e58e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 12 May 2021 13:38:58 -0700 Subject: [PATCH 329/706] src/sage/manifolds/subset.py: Simplify code, make pyflakes happy --- src/sage/manifolds/subset.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 261bad4170a..07d0b78a969 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2187,8 +2187,7 @@ def label(element): old, res = res, res._intersection_subset(other) # The last one gets the name other = others[-1] - old, res = res, res._intersection_subset(other, name=name, latex_name=latex_name) - return res + return res._intersection_subset(other, name=name, latex_name=latex_name) @staticmethod def _reduce_intersection_members(subsets): @@ -2419,8 +2418,7 @@ def label(element): old, res = res, res._union_subset(other) # The last one gets the name other = others[-1] - old, res = res, res._union_subset(other, name=name, latex_name=latex_name) - return res + return res._union_subset(other, name=name, latex_name=latex_name) @staticmethod def _reduce_union_members(subsets): From 6bbd4ae1295dbc7301571abddccb0e6c7487565d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 12 May 2021 13:41:02 -0700 Subject: [PATCH 330/706] src/sage/manifolds/subset.py: Add coding header --- src/sage/manifolds/subset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 07d0b78a969..8336b1e0a11 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Subsets of Topological Manifolds From 479f743f27fd76a062a0247d1b8d7bb4c9f451f3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 08:42:00 +0200 Subject: [PATCH 331/706] better variable names for documentation --- .../combinatorial_face.pyx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index f447c8fcd50..8d068de872c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -776,21 +776,21 @@ cdef class CombinatorialFace(SageObject): sage: it = C.face_iter(4) sage: f = next(it); f A 4-dimensional face of a 7-dimensional combinatorial polyhedron - sage: C1 = f.as_combinatorial_polyhedron(); C1 + sage: F = f.as_combinatorial_polyhedron(); F A 4-dimensional combinatorial polyhedron with 5 facets - sage: C1.f_vector() + sage: F.f_vector() (1, 5, 10, 10, 5, 1) - sage: C1_alt = polytopes.cyclic_polytope(4,5).combinatorial_polyhedron() - sage: C1_alt.vertex_facet_graph().is_isomorphic(C1.vertex_facet_graph()) + sage: F_alt = polytopes.cyclic_polytope(4,5).combinatorial_polyhedron() + sage: F_alt.vertex_facet_graph().is_isomorphic(F.vertex_facet_graph()) True Obtaining the quotient:: - sage: C2 = f.as_combinatorial_polyhedron(quotient=True); C2 + sage: Q = f.as_combinatorial_polyhedron(quotient=True); Q A 2-dimensional combinatorial polyhedron with 6 facets - sage: C2 + sage: Q A 2-dimensional combinatorial polyhedron with 6 facets - sage: C2.f_vector() + sage: Q.f_vector() (1, 6, 6, 1) The Vrepresentation of the face as polyhedron is given by the @@ -800,7 +800,7 @@ cdef class CombinatorialFace(SageObject): sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) sage: f = next(it) - sage: C1 = f.as_combinatorial_polyhedron() + sage: F = f.as_combinatorial_polyhedron() sage: C.Vrepresentation() (A vertex at (1, -1, -1), A vertex at (1, 1, -1), @@ -815,7 +815,7 @@ cdef class CombinatorialFace(SageObject): A vertex at (1, -1, 1), A vertex at (-1, -1, 1), A vertex at (-1, -1, -1)) - sage: C1.Vrepresentation() + sage: F.Vrepresentation() (0, 1, 2, 3) To obtain the facets of the face as polyhedron, @@ -830,7 +830,7 @@ cdef class CombinatorialFace(SageObject): (4, 5, 6, 7), (0, 1, 5, 6), (0, 3, 4, 5)) - sage: C1.facets(names=False) + sage: F.facets(names=False) ((0, 1), (1, 2), (2, 3), (0, 3)) The Hrepresentation of the quotient by the face is given by the @@ -838,7 +838,7 @@ cdef class CombinatorialFace(SageObject): sage: it = C.face_iter(1) sage: f = next(it) - sage: C1 = f.as_combinatorial_polyhedron(quotient=True) + sage: Q = f.as_combinatorial_polyhedron(quotient=True) sage: C.Hrepresentation() (An inequality (-1, 0, 0) x + 1 >= 0, An inequality (0, -1, 0) x + 1 >= 0, @@ -848,7 +848,7 @@ cdef class CombinatorialFace(SageObject): An inequality (0, 1, 0) x + 1 >= 0) sage: f.ambient_Hrepresentation() (An inequality (0, 0, 1) x + 1 >= 0, An inequality (0, 1, 0) x + 1 >= 0) - sage: C1.Hrepresentation() + sage: Q.Hrepresentation() (0, 1) To obtain the vertices of the face as polyhedron, @@ -865,7 +865,7 @@ cdef class CombinatorialFace(SageObject): (0, 1, 4), (1, 2, 3), (0, 1, 2)] - sage: [g.ambient_H_indices() for g in C1.face_iter(0)] + sage: [g.ambient_H_indices() for g in Q.face_iter(0)] [(1,), (0,)] The method is not implemented for unbounded polyhedra:: From 4bceb9b22e889380d36ad78097e82c921aa95309 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Thu, 13 May 2021 14:05:45 +0200 Subject: [PATCH 332/706] Trac #31669: use or_subcategory --- src/sage/algebras/commutative_dga.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index b54d919591e..acf83f4d8ba 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -1033,8 +1033,7 @@ def __init__(self, base, R=None, I=None, names=None, degrees=None, category=None sage: TestSuite(A).run() """ self._degrees = tuple(degrees) - if category is None: - category = Algebras(R.base_ring()).Graded() + category = Algebras(R.base_ring()).Graded().or_subcategory(category) QuotientRing_nc.__init__(self, R, I, names, category=category) def _repr_(self): From 901b789440ea1bdc2520aacaae8f3d5f8cf61370 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:05:41 +0200 Subject: [PATCH 333/706] join_of_Vrep and meet_of_facets for face iterator --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 426 +++++++++++++++++- 2 files changed, 425 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 63ed7858024..f3a1d885fe8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -69,6 +69,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int find_face(self, face_t face) except -1 @cython.final cdef class FaceIterator(FaceIterator_base): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fc4929bf29f..74ddf1bdaae 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -176,7 +176,7 @@ AUTHOR: from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check -from .conversions cimport bit_rep_to_Vrep_list +from .conversions cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron @@ -481,6 +481,391 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() + def meet_of_facets(self, *indices): + r""" + Construct the meet of the facets indicated by the indices. + + This is the largest face contained in all facets with the given indices. + + The iterator must be reseted if not newly initialized. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.meet_of_facets(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 3) + sage: it.meet_of_facets(1,5).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.meet_of_facets().ambient_H_indices() + () + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 2, 3, 4) + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,6).ambient_H_indices() + (1, 6) + sage: it.meet_of_facets(1,2,6).ambient_H_indices() + (1, 2, 6, 7) + sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + + sage: s = cones.schur(4) + sage: C = CombinatorialPolyhedron(s) + sage: it = C.face_iter() + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,2,3).ambient_H_indices() + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.meet_of_facets(9,11) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.meet_of_facets(9,11).ambient_H_indices() + (9, 11) + + """ + if self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def join_of_Vrep(self, *indices): + r""" + Construct the join of the Vrepresentatives indicated by the indices. + + This is the smallest face containing all Vrepresentatives with the given indices. + + The iterator must be reseted if not newly initialized. + + .. NOTE:: + + In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + may not te well defined. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (0, 1, 2, 3) + sage: it.join_of_Vrep(1,5).ambient_V_indices() + (0, 1, 5, 6) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.join_of_Vrep().ambient_V_indices() + () + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (1, 3) + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,6).ambient_V_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + sage: it.join_of_Vrep(8) + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.join_of_Vrep(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.join_of_Vrep(1,10).ambient_V_indices() + (1, 10) + + In case of an unbounded polyhedron, we try to make sense of the input:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0, 1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: it.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + + sage: P = Polyhedron(rays=[[1,0], [0,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + """ + if not self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def _meet_of_coatoms(self, *indices): + r""" + Construct the meet of the coatoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In non-dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._meet_of_coatoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._meet_of_coatoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._meet_of_coatoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._meet_of_coatoms(-1) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to size_t + sage: it._meet_of_coatoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + + The empty face is detected correctly, even with lines or rays:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(1,2,4,5) + A -1-dimensional face of a Polyhedron in ZZ^4 + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(0) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(1, 2) + A -1-dimensional face of a Polyhedron in QQ^2 + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef size_t i + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + for i in indices: + assert 0 <= i < n_coatoms, "index out of range" + face_intersection(face, face, coatoms.data.faces[i]) + + if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The meet is contained in the far face and therefore is the empty face. + face_clear(face) + + self.find_face(face) + output = self.current() + self.reset() + return output + + def _join_of_atoms(self, *indices): + r""" + Construct the join of atoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._join_of_atoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In non-dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._join_of_atoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it._join_of_atoms(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it._join_of_atoms(1,10).ambient_V_indices() + (1, 10) + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._join_of_atoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._join_of_atoms(-1) + Traceback (most recent call last): + ... + AssertionError: index out of range + sage: it._join_of_atoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef face_t pseudo_face = face_mem.data.faces[1] + cdef size_t i + + assert all(i in range(n_atoms) for i in indices), "index out of range" + + # Initialize a pseudo_face as indicated by the indices. + for i in indices: + face_add_atom(pseudo_face, i) + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + # Now we intersect all faces that contain our pseudo_face. + for i in range(n_coatoms): + if face_issubset(pseudo_face, coatoms.data.faces[i]): + face_intersection(face, face, coatoms.data.faces[i]) + + if not indices: + # The neutral element of the join. + face_clear(face) + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The join is not well-defined. + # We allow for unbounded polyhedra to compute the join, even with rays. + # However, the result is not necesarrily well-defined. + raise ValueError("the join is not well-defined") + + self.find_face(face) + output = self.current() + self.reset() + return output + cdef int ignore_subsets(self) except -1: r""" Ignore sub-/supfaces of the current face. @@ -572,6 +957,43 @@ cdef class FaceIterator_base(SageObject): """ return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep) + cdef int find_face(self, face_t face) except -1: + """ + Iterate until the current face is ``face``. + + The value can then be obtained with :meth:`current`. + """ + cdef size_t n_atoms = face_len_atoms(face) + + if n_atoms == self.coatoms.n_atoms(): + # The face is the universe. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = self.structure.dimension + return 0 + elif n_atoms == 0: + # The face is the empty face. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = -1 + return 0 + + cdef int d = self.next_dimension() + while self.structure.current_dimension != self.structure.dimension: + if face_issubset(face, self.structure.face): + if face_issubset(self.structure.face, face): + # Found our face. + return 0 + else: + # The face is not a subface/supface of the current face. + self.ignore_subsets() + + d = self.next_dimension() + + raise ValueError("the face appears to be incorrect") + + + cdef class FaceIterator(FaceIterator_base): r""" A class to iterate over all combinatorial faces of a polyhedron. @@ -1036,7 +1458,7 @@ cdef class FaceIterator_geom(FaceIterator_base): .. SEEALSO:: - See :class:`FaceIterator`. + :class:`FaceIterator_base`. """ def __init__(self, P, dual=None, output_dimension=None): r""" From 2a5a7821e5575651b8cb18b2c23977af172368e4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:21:02 +0200 Subject: [PATCH 334/706] expose in combinatorial_polyhedron --- .../combinatorial_polyhedron/base.pyx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..b5f60202be2 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2248,6 +2248,56 @@ cdef class CombinatorialPolyhedron(SageObject): return (False, None) return False + def join_of_Vrep(self, *indices): + r""" + Return the smallest face containing all Vrepresentatives indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.join_of_Vrep`. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(4) + sage: C = CombinatorialPolyhedron(P) + sage: C.join_of_Vrep(0,1) + A 1-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.join_of_Vrep(0,3).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(8).ambient_V_indices() + (8,) + sage: C.join_of_Vrep().ambient_V_indices() + () + """ + return self.face_iter().join_of_Vrep(*indices) + + def meet_of_facets(self, *indices): + r""" + Return the largest face contained all facets indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + + EXAMPLES:: + + sage: P = polytopes.dodecahedron() + sage: C = CombinatorialPolyhedron(P) + sage: C.meet_of_facets(0) + A 2-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.meet_of_facets(0).ambient_H_indices() + (0,) + sage: C.meet_of_facets(0,1).ambient_H_indices() + (0, 1) + sage: C.meet_of_facets(0,3).ambient_H_indices() + (0, 3) + sage: C.meet_of_facets(0,2,3).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + sage: C.meet_of_facets().ambient_H_indices() + () + """ + return self.face_iter().meet_of_facets(*indices) + def face_iter(self, dimension=None, dual=None): r""" Iterator over all proper faces of specified dimension. From 6cecfd7442e4b27ced16f554137687cdb226f9d6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 13:44:48 +0200 Subject: [PATCH 335/706] raise index error for index error; fix doctests --- src/sage/geometry/polyhedron/base.py | 6 ++++++ .../polyhedron/combinatorial_polyhedron/base.pyx | 8 ++++---- .../combinatorial_polyhedron/face_iterator.pyx | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..f811b337133 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6891,6 +6891,12 @@ def facets(self): return () return self.faces(self.dimension()-1) + def join_of_Vrep(self, *Vrepresentatives): + return self.face_generator().join_of_Vrep(*Vrepresentatives) + + def meet_of_facets(self, *facets): + return self.face_generator().meet_of_facets(*facets) + @cached_method(do_pickle=True) def f_vector(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index b5f60202be2..56aac10efd1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2262,8 +2262,8 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.join_of_Vrep(0,1) A 1-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.join_of_Vrep(0,3).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(0,11).ambient_V_indices() + (0, 1, 10, 11, 12, 13) sage: C.join_of_Vrep(8).ambient_V_indices() (8,) sage: C.join_of_Vrep().ambient_V_indices() @@ -2289,8 +2289,8 @@ cdef class CombinatorialPolyhedron(SageObject): (0,) sage: C.meet_of_facets(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,3).ambient_H_indices() - (0, 3) + sage: C.meet_of_facets(0,2).ambient_H_indices() + (0, 2) sage: C.meet_of_facets(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: C.meet_of_facets().ambient_H_indices() diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 74ddf1bdaae..d102c944dfa 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -525,7 +525,7 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_facets(1,2,3).ambient_H_indices() Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -587,7 +587,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(8) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -699,7 +699,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range The empty face is detected correctly, even with lines or rays:: @@ -737,7 +737,8 @@ cdef class FaceIterator_base(SageObject): face_add_atom(face, i) for i in indices: - assert 0 <= i < n_coatoms, "index out of range" + if not 0 <= i < n_coatoms: + raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): @@ -817,11 +818,11 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(-1) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range sage: it._join_of_atoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range """ if unlikely(self.structure.face_status != 0): raise ValueError("please reset the face iterator") @@ -837,7 +838,8 @@ cdef class FaceIterator_base(SageObject): cdef face_t pseudo_face = face_mem.data.faces[1] cdef size_t i - assert all(i in range(n_atoms) for i in indices), "index out of range" + if not all(i in range(n_atoms) for i in indices): + raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. for i in indices: From 9a66e324a1e5dcd3b176e4a78e49599149bc4c93 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 14:24:53 +0200 Subject: [PATCH 336/706] expose in Polyhedron_base --- .../geometry/polyhedra_quickref.rst | 2 + src/sage/geometry/polyhedron/base.py | 136 +++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index d684c689c3e..968800e3925 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,6 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index f811b337133..cfa7a6ffb4d 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6892,10 +6892,142 @@ def facets(self): return self.faces(self.dimension()-1) def join_of_Vrep(self, *Vrepresentatives): - return self.face_generator().join_of_Vrep(*Vrepresentatives) + r""" + Return the smallest face that contains in ``Vrepresentatives``. + + INPUT: + + - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + .. NOTE:: + + In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^5 + sage: P.join_of_Vrep(1,3,4).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + + The input is flexible:: + + sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) + A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + + In case of an unbounded polyhedron, the join may not be well-defined:: + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep(0,1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: P.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(1,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + """ + from sage.geometry.polyhedron.representation import Vrepresentation + from sage.geometry.polyhedron.face import PolyhedronFace + + new_indices = [0]*len(Vrepresentatives) + for i, v in enumerate(Vrepresentatives): + if isinstance(v, PolyhedronFace) and facet.dim() == 0: + v = v.ambient_V_indices()[0] + + if v in ZZ: + new_indices[i] = v + elif isinstance(v, Vrepresentation): + new_indices[i] = v.index() + else: + raise ValueError("{} is not a Vrepresentative".format(v)) + + return self.face_generator().join_of_Vrep(*new_indices) def meet_of_facets(self, *facets): - return self.face_generator().meet_of_facets(*facets) + r""" + Return the largest face that is contained in ``facets``. + + INPUT: + + - ``facets`` -- facets or indices of facets; + the indices are assumed to be the indices of the Hrepresentation + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.meet_of_facets() + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices + sage: P.meet_of_facets(1) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices + sage: P.meet_of_facets(2) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices + sage: P.meet_of_facets(2,3,4) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(2,3,4).ambient_H_indices() + (0, 2, 3, 4) + + The indices are the indices of the Hrepresentation:: + + sage: P.meet_of_facets(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a facet + + The input is flexible:: + + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + + TESTS: + + The offset is taken correctly:: + + sage: P = polytopes.permutahedron(3, backend='field') + sage: P.Hrepresentation() + (An inequality (1, 1, 0) x - 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0, + An inequality (0, 2, 0) x - 2 >= 0, + An inequality (-4, -4, 0) x + 20 >= 0, + An inequality (-8, 0, 0) x + 24 >= 0, + An equation (-1/6, -1/6, -1/6) x + 1 == 0) + sage: P.meet_of_facets(0) + A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + """ + from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.face import PolyhedronFace + + # Equations are ignored by combinatorial polyhedron for indexing. + offset = 0 + if self.n_equations() and self.Hrepresentation(0).is_equation(): + offset = self.n_equations() + + new_indices = [0]*len(facets) + for i, facet in enumerate(facets): + if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + H_indices = facet.ambient_H_indices() + facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] + + if facet in ZZ and facet >= offset: + new_indices[i] = facet - offset + elif isinstance(facet, Inequality): + new_indices[i] = facet.index() - offset + else: + raise ValueError("{} is not a facet".format(facet)) + + return self.face_generator().meet_of_facets(*new_indices) @cached_method(do_pickle=True) def f_vector(self): From 977c088966acadcb1b7fd6589e996a68793db697 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 7 Sep 2020 15:36:08 +0200 Subject: [PATCH 337/706] fix doctests --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cfa7a6ffb4d..1538c31cdc3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6912,8 +6912,8 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^5 - sage: P.join_of_Vrep(1,3,4).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: P.join_of_Vrep(0,12,13).ambient_V_indices() + (0, 12, 13, 68) The input is flexible:: @@ -6971,12 +6971,12 @@ def meet_of_facets(self, *facets): A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices sage: P.meet_of_facets(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(2) + sage: P.meet_of_facets(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(2,3,4) + sage: P.meet_of_facets(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(2,3,4).ambient_H_indices() - (0, 2, 3, 4) + sage: P.meet_of_facets(1,3,7).ambient_H_indices() + (0, 1, 3, 7) The indices are the indices of the Hrepresentation:: @@ -6987,7 +6987,7 @@ def meet_of_facets(self, *facets): The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices TESTS: @@ -6996,13 +6996,13 @@ def meet_of_facets(self, *facets): sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() - (An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (0, 0, 1) x - 1 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (0, 1, 1) x - 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0, - An inequality (0, 2, 0) x - 2 >= 0, - An inequality (-4, -4, 0) x + 20 >= 0, - An inequality (-8, 0, 0) x + 24 >= 0, - An equation (-1/6, -1/6, -1/6) x + 1 == 0) + An inequality (1, 0, 1) x - 3 >= 0, + An inequality (1, 1, 0) x - 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_facets(0) A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices """ From 15e2c58d57980e142c8ac90e7081d4e6256a5e84 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 16 Jan 2021 21:33:30 +0100 Subject: [PATCH 338/706] improved documentation --- src/sage/geometry/polyhedron/base.py | 35 +++++++++++++------ .../face_iterator.pyx | 18 +++++----- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1538c31cdc3..19db7fd6ebc 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -6992,7 +6992,8 @@ def meet_of_facets(self, *facets): TESTS: - The offset is taken correctly:: + Equations are not considered by the combinatorial polyhedron. + We check that the index corresponds to the Hrepresentation index:: sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() @@ -7003,8 +7004,20 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0) - A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(0).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + + sage: P = polytopes.permutahedron(3, backend='ppl') + sage: P.Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, + An inequality (1, 1, 0) x - 3 >= 0, + An inequality (-1, -1, 0) x + 5 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (-1, 0, 0) x + 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0) + sage: P.meet_of_facets(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) """ from sage.geometry.polyhedron.representation import Inequality from sage.geometry.polyhedron.face import PolyhedronFace @@ -10323,10 +10336,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index d102c944dfa..49c7d1b7e05 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -487,7 +487,7 @@ cdef class FaceIterator_base(SageObject): This is the largest face contained in all facets with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. EXAMPLES:: @@ -527,7 +527,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -554,11 +554,11 @@ cdef class FaceIterator_base(SageObject): This is the smallest face containing all Vrepresentatives with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. NOTE:: - In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives may not te well defined. EXAMPLES:: @@ -589,7 +589,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -604,7 +604,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(1,10).ambient_V_indices() (1, 10) - In case of an unbounded polyhedron, we try to make sense of the input:: + In the case of an unbounded polyhedron, we try to make sense of the input:: sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) sage: it = P.face_generator() @@ -646,7 +646,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the meet of the coatoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -754,7 +754,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the join of atoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -785,7 +785,7 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(1) A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() From deca8e0aa9df9eb7d4f77d9e6e984962c07c1543 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 20:38:18 +0200 Subject: [PATCH 339/706] fix a variable name and add a doctest --- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 19db7fd6ebc..9613fdecf01 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6920,6 +6920,13 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + :: + + sage: P = polytopes.cube() + sage: a, b = P.faces(0)[:2] + sage: P.join_of_Vrep(a, b) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + In case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) @@ -6941,7 +6948,7 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): - if isinstance(v, PolyhedronFace) and facet.dim() == 0: + if isinstance(v, PolyhedronFace) and v.dim() == 0: v = v.ambient_V_indices()[0] if v in ZZ: From be121ad494ccdd78ae2e4e4ca7a15ae9a64c0d94 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:19:34 +0200 Subject: [PATCH 340/706] implement only subfaces/supfaces for face iterator --- .../face_data_structure.pxd | 3 + .../face_iterator.pxd | 2 + .../face_iterator.pyx | 187 +++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index 2b509aaec01..f166a7d8fea 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -186,3 +186,6 @@ cdef inline void swap_faces(face_t a, face_t b) nogil: tmp[0] = a[0] a[0] = b[0] b[0] = tmp[0] + +cdef inline bint faces_are_identical(face_t a, face_t b) nogil: + return a.atoms.limbs == b.atoms.limbs diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index f3a1d885fe8..c515c755b19 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -16,6 +16,7 @@ cdef struct iter_s: int dimension # dimension of the polyhedron int output_dimension # only faces of this (dual?) dimension are considered int lowest_dimension # don't consider faces below this (dual?) dimension + int highest_dimension # don't consider faces above this (dual?) dimension size_t _index # this counts the number of seen faces, useful for hasing the faces # ``visited_all`` points to faces, of which we have visited all faces already. @@ -69,6 +70,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int only_subsets(self) except -1 cdef int find_face(self, face_t face) except -1 @cython.final diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 49c7d1b7e05..4ef16257eb9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -223,7 +223,8 @@ cdef class FaceIterator_base(SageObject): self.structure.dual = dual self.structure.face_status = 0 self.structure.dimension = C.dimension() - self.structure.current_dimension = self.structure.dimension -1 + self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self._mem = MemoryAllocator() # We will not yield the empty face. @@ -347,6 +348,7 @@ cdef class FaceIterator_base(SageObject): self.structure.face_status = 0 self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces() self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self.structure.first_time[self.structure.dimension - 1] = True self.structure.yet_to_visit = self.coatoms.n_faces() @@ -451,6 +453,33 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_non_simplex_faces 127 + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``ignore_subfaces`` after ``only_subfaces:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.only_subfaces() + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot ignore a face after setting iterator to only visit subsets + + Face iterator must be set to a face first:: + + sage: it = C.face_iter(dual=False) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") @@ -458,9 +487,9 @@ cdef class FaceIterator_base(SageObject): def ignore_supfaces(self): r""" - The iterator will not visit any faces of the current face. + The iterator will not visit any faces containing the current face. - Only possible when not in dual mode. + Only possible when in dual mode. EXAMPLES:: @@ -476,6 +505,15 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_faces_with_non_simplex_quotient 4845 + + Face iterator must be in dual mode:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.ignore_supfaces() + Traceback (most recent call last): + ... + ValueError: only possible when in dual mode """ if unlikely(not self.dual): raise ValueError("only possible when in dual mode") @@ -880,6 +918,8 @@ cdef class FaceIterator_base(SageObject): """ if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 3): + raise ValueError("cannot ignore a face after setting iterator to only visit subsets") if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 @@ -891,13 +931,144 @@ cdef class FaceIterator_base(SageObject): add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face) self.structure.face_status = 2 + def only_subfaces(self): + r""" + The iterator will visit all (remaining) subfaces of the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: next(it).ambient_H_indices() + () + sage: next(it).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_H_indices() + (5,) + sage: next(it).ambient_H_indices() + (4,) + sage: it.only_subfaces() + sage: list(f.ambient_H_indices() for f in it) + [(4, 5), (3, 4), (1, 4), (0, 4), (3, 4, 5), (0, 4, 5), (1, 3, 4), (0, 1, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: next(it).ambient_H_indices() + (15,) + sage: next(it).ambient_H_indices() + (14,) + sage: it.only_subfaces() + sage: all(14 in f.ambient_H_indices() for f in it) + True + + Face iterator needs to be set to a face first:: + + sage: it = C.face_iter() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``only_subfaces`` after ``ignore_subfaces:: + + sage: it = C.face_iter() + sage: _ = next(it) + sage: it.ignore_subfaces() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot visit subsets after ignoring a face + """ + if unlikely(self.dual): + raise ValueError("only possible when not in dual mode") + self.only_subsets() + + def only_supfaces(self): + r""" + The iterator will visit all (remaining) faces + containing the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cross_polytope(3) + sage: it = P.face_generator() + sage: next(it).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_V_indices() + () + sage: next(it).ambient_V_indices() + (5,) + sage: next(it).ambient_V_indices() + (4,) + sage: it.only_supfaces() + sage: list(f.ambient_V_indices() for f in it) + [(4, 5), (3, 4), (2, 4), (0, 4), (3, 4, 5), (2, 4, 5), (0, 3, 4), (0, 2, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=True) + sage: next(it).ambient_V_indices() + (23,) + sage: next(it).ambient_V_indices() + (22,) + sage: it.only_supfaces() + sage: all(22 in f.ambient_V_indices() for f in it) + True + """ + if unlikely(not self.dual): + raise ValueError("only possible when in dual mode") + self.only_subsets() + + cdef int only_subsets(self) except -1: + r""" + Only visit sub-/supfaces of the current face and then + terminate. + + See :meth:`FaceIterator_base.only_subfaces` and + :meth:`FaceIterator_base.only_supfaces`. + """ + if unlikely(self.structure.face_status == 0): + raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 2): + raise ValueError("cannot only visit subsets after ignoring a face") + + cdef face_list_t* faces = &self.structure.new_faces[self.structure.current_dimension] + cdef size_t yet_to_visit = self.structure.yet_to_visit + + if unlikely(yet_to_visit >= faces[0].n_faces + or not faces_are_identical(faces[0].faces[yet_to_visit], self.structure.face)): + raise ValueError("iterator is not set to the correct face") + + swap_faces(faces[0].faces[yet_to_visit], faces[0].faces[faces[0].n_faces - 1]) + + self.structure.face_status = 3 + self.structure.yet_to_visit = 0 + # This will work: + # ``next_dimension`` will first call ``next_face_loop`` and then check + # for the dimension. By this time the current dimension has changed. + self.structure.highest_dimension = self.structure.current_dimension - 1 + cdef inline CombinatorialFace next_face(self): r""" Set attribute ``face`` to the next face and return it as :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): return None return CombinatorialFace(self) @@ -1295,7 +1466,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return face @@ -1588,7 +1759,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return self.current() @@ -1616,9 +1787,9 @@ cdef inline int next_dimension(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ - cdef int dim = structure.dimension + cdef int max_dim = structure.highest_dimension structure.face_status = 0 - while (not next_face_loop(structure)) and (structure.current_dimension < dim): + while (not next_face_loop(structure)) and (structure.current_dimension <= max_dim): sig_check() structure._index += 1 return structure.current_dimension From ee42e9895eb8830089e328fae674616ca3e9245c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:26 +0200 Subject: [PATCH 341/706] fix bug revealed by a_maximal_chain --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 4ef16257eb9..51f01c56ece 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1068,7 +1068,7 @@ cdef class FaceIterator_base(SageObject): :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): return None return CombinatorialFace(self) @@ -1466,7 +1466,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return face @@ -1759,7 +1759,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return self.current() From 05ec5b29b59e078d5601c4cfa5b1828aa940eab8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:47 +0200 Subject: [PATCH 342/706] fix error message in doctest --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 51f01c56ece..352a5761afc 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -989,7 +989,7 @@ cdef class FaceIterator_base(SageObject): sage: it.only_subfaces() Traceback (most recent call last): ... - ValueError: cannot visit subsets after ignoring a face + ValueError: cannot only visit subsets after ignoring a face """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") From 6ddf2284a5ae25d4680e78615e881df6ef5bf0d9 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 16:49:56 +0200 Subject: [PATCH 343/706] fix the order of the coatoms when resetting the face iterator --- .../face_iterator.pyx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 352a5761afc..1e6c103c75f 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -333,6 +333,25 @@ cdef class FaceIterator_base(SageObject): sage: it.reset() sage: next(it).ambient_V_indices() (0, 3, 4, 5) + + TESTS: + + Resetting will fix the order of the coatoms after ``only_subsets``:: + + sage: P = polytopes.Birkhoff_polytope(3) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=False) + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) + sage: face = next(it) + sage: face.ambient_H_indices() + (7,) + sage: it.only_subfaces() + sage: it.reset() + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, @@ -354,6 +373,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + # ``only_subsets`` might have messed up the coatoms. + face_list_shallow_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data) + def __next__(self): r""" Must be implemented by a derived class. From f0a0afa8353b11cc6594425a7376ca04a8582a37 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 15:16:39 +0200 Subject: [PATCH 344/706] implement is_subface for combinatorial face --- .../combinatorial_face.pyx | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 1d790726f23..aa2d066c23c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -72,7 +72,7 @@ from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice -from .face_data_structure cimport face_len_atoms, face_init, face_copy +from .face_data_structure cimport face_len_atoms, face_init, face_copy, face_issubset from .face_list_data_structure cimport bit_rep_to_coatom_rep cdef extern from "Python.h": @@ -347,6 +347,46 @@ cdef class CombinatorialFace(SageObject): # They are faces of the same polyhedron obtained in the same way. return hash(self) < hash(other) + def is_subface(self, other): + r""" + Return whether ``self`` is contained in ``other``. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: C = P.combinatorial_polyhedron() + sage: P = polytopes.cube() + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: face = next(it) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: face2 = next(it) + sage: face2.ambient_V_indices() + (0, 1, 5, 6) + sage: face.is_subface(face2) + False + sage: face2.is_subface(face) + False + sage: it.only_subfaces() + sage: face3 = next(it) + sage: face3.ambient_V_indices() + (0, 5) + sage: face3.is_subface(face2) + True + sage: face3.is_subface(face) + True + """ + cdef CombinatorialFace other_face + if isinstance(other, CombinatorialFace): + other_face = other + if not self._dual: + return face_issubset(self.face, other_face.face) + else: + return face_issubset(other_face.face, self.face) + else: + raise ValueError("other must be a face") + def dimension(self): r""" Return the dimension of the face. From c632f5609f5e898cebcfb285fd015ad89b89203a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 09:20:30 +0200 Subject: [PATCH 345/706] only allow subface check for faces of identical combinatorial polyhedron --- .../combinatorial_face.pyx | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index aa2d066c23c..4c1641a7b7a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -376,14 +376,73 @@ cdef class CombinatorialFace(SageObject): True sage: face3.is_subface(face) True + + Works for faces of the same combinatorial polyhedron; + also from different iterators:: + + sage: it = C.face_iter(dual=True) + sage: v7 = next(it); v7.ambient_V_indices() + (7,) + sage: v6 = next(it); v6.ambient_V_indices() + (6,) + sage: v5 = next(it); v5.ambient_V_indices() + (5,) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: face.is_subface(v7) + False + sage: v7.is_subface(face) + False + sage: v6.is_subface(face) + False + sage: v5.is_subface(face) + True + sage: face2.ambient_V_indices() + (0, 1, 5, 6) + sage: face2.is_subface(v7) + False + sage: v7.is_subface(face2) + False + sage: v6.is_subface(face2) + True + sage: v5.is_subface(face2) + True + + Only implemented for faces of the same combintatorial polyhedron:: + + sage: P1 = polytopes.cube() + sage: C1 = P1.combinatorial_polyhedron() + sage: it = C1.face_iter() + sage: other_face = next(it) + sage: other_face.ambient_V_indices() + (0, 3, 4, 5) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: C is C1 + False + sage: face.is_subface(other_face) + Traceback (most recent call last): + ... + NotImplementedError: is_subface only implemented for faces of the same polyhedron """ cdef CombinatorialFace other_face if isinstance(other, CombinatorialFace): other_face = other - if not self._dual: - return face_issubset(self.face, other_face.face) + if self._dual == other_face._dual: + if self.atoms is other_face.atoms: + if not self._dual: + return face_issubset(self.face, other_face.face) + else: + return face_issubset(other_face.face, self.face) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: - return face_issubset(other_face.face, self.face) + if self.atoms is other_face.coatoms: + self_indices = self.ambient_V_indices() + other_indices = other.ambient_V_indices() + return all(i in other_indices for i in self_indices) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: raise ValueError("other must be a face") From 8e69de0930a8d46719f084b49d881a7c3849e78c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 14 May 2021 10:10:02 +1000 Subject: [PATCH 346/706] Changing from _nonsqrt to _sqrt and fixing bugs. --- src/sage/matrix/matrix_cyclo_dense.pyx | 33 +-- src/sage/rings/number_field/number_field.py | 14 +- .../number_field/number_field_element.pyx | 2 +- .../number_field_element_quadratic.pxd | 8 +- .../number_field_element_quadratic.pyx | 199 +++++++++--------- src/sage/rings/qqbar.py | 2 +- 6 files changed, 130 insertions(+), 128 deletions(-) diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index bcd549b7057..fab112304f3 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -72,7 +72,6 @@ from sage.rings.real_mpfr import create_RealNumber as RealNumber from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.number_field.number_field import NumberField_quadratic from sage.rings.number_field.number_field_element cimport NumberFieldElement from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic @@ -201,74 +200,76 @@ cdef class Matrix_cyclo_dense(Matrix_dense): # cyclotomic field is implemented. cdef Py_ssize_t k, c cdef NumberFieldElement v + cdef NumberFieldElement_quadratic qv cdef mpz_t numer, denom cdef fmpz_t ftmp # The i,j entry is the (i * self._ncols + j)'th column. c = i * self._ncols + j - if type(value) is NumberFieldElement_quadratic: + if self._degree == 2: # Must be coded differently, since elements of # quadratic number fields are stored differently. + qv = value if self._n == 4: fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c), - (value).a) + qv.a) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c)) fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c), - (value).b) + qv.b) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c)) elif self._n == 3: # mat[0,c] = (x.a + x.b) / x.denom fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c), - (value).a) + qv.a) # NOTE: it would be convenient here to have fmpz_add_mpz fmpz_init(ftmp) - fmpz_set_mpz(ftmp, (value).b) + fmpz_set_mpz(ftmp, qv.b) fmpz_add(fmpq_mat_entry_num(self._matrix._matrix, 0, c), fmpq_mat_entry_num(self._matrix._matrix, 0, c), ftmp) fmpz_clear(ftmp) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c)) # mat[1,c] = (2 x.b) / x.denom fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c), - (value).b) + qv.b) fmpz_mul_si(fmpq_mat_entry_num(self._matrix._matrix, 1, c), fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c)) else: # self._n is 6 fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 0, c), - (value).a) + qv.a) # NOTE: it would be convenient here to have fmpz_add_mpz fmpz_init(ftmp) - fmpz_set_mpz(ftmp, (value).b) + fmpz_set_mpz(ftmp, qv.b) fmpz_sub(fmpq_mat_entry_num(self._matrix._matrix, 0, c), fmpq_mat_entry_num(self._matrix._matrix, 0, c), ftmp) fmpz_clear(ftmp) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 0, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 0, c)) fmpz_set_mpz(fmpq_mat_entry_num(self._matrix._matrix, 1, c), - (value).b) + qv.b) fmpz_mul_si(fmpq_mat_entry_num(self._matrix._matrix, 1, c), fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2) fmpz_set_mpz(fmpq_mat_entry_den(self._matrix._matrix, 1, c), - (value).denom) + qv.denom) fmpq_canonicalise(fmpq_mat_entry(self._matrix._matrix, 1, c)) return diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 45603234f99..2993dac8623 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -10177,7 +10177,7 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a :: sage: type(CyclotomicField(4).zero()) - + sage: type(CyclotomicField(6).one()) sage: type(CyclotomicField(6).an_element()) @@ -10216,12 +10216,12 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a self._cache_an_element = None if n == 4: - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic + self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian self._D = ZZ(-1) self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ(1))) else: ## n is 3 or 6 - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic_nonsqrt + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic self._D = ZZ(-3) one_half = ZZ(1)/ZZ(2) if n == 3: @@ -11491,9 +11491,9 @@ def __init__(self, polynomial, name=None, latex_name=None, check=True, embedding sage: k. = QuadraticField(7) sage: type(k.zero()) - + sage: type(k.one()) - + sage: TestSuite(k).run() @@ -11520,9 +11520,9 @@ def __init__(self, polynomial, name=None, latex_name=None, check=True, embedding self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian else: if number_field_element_quadratic.is_sqrt_disc(parts[0], parts[1]): - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic_sqrt else: - self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic_nonsqrt + self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic self._NumberField_generic__gen = self._element_class(self, parts) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 10dab491021..759b162a141 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -357,7 +357,7 @@ cdef class NumberFieldElement(FieldElement): sage: cf4(one) 1 sage: type(cf4(1)) - + sage: cf33 = CyclotomicField(33) ; z33 = cf33.0 sage: cf66 = CyclotomicField(66) ; z66 = cf66.0 sage: z33._lift_cyclotomic_element(cf66) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pxd b/src/sage/rings/number_field/number_field_element_quadratic.pxd index 056ab308c61..7a4f063de73 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pxd +++ b/src/sage/rings/number_field/number_field_element_quadratic.pxd @@ -20,13 +20,13 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cpdef tuple parts(self) -cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): +cdef class NumberFieldElement_quadratic_sqrt(NumberFieldElement_quadratic): + pass + +cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic_sqrt): cpdef real_part(self) cpdef imag_part(self) -cdef class NumberFieldElement_quadratic_nonsqrt(NumberFieldElement_quadratic): - pass - cdef class OrderElement_quadratic(NumberFieldElement_quadratic): pass diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index f04e490c645..cfaeae3a203 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -112,7 +112,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): TESTS:: sage: from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic - sage: from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic_nonsqrt + sage: from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic_sqrt We set up some fields:: @@ -126,34 +126,34 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): We construct elements of these fields in various ways - firstly, from polynomials:: - sage: NumberFieldElement_quadratic(K, x-1) + sage: NumberFieldElement_quadratic_sqrt(K, x-1) a - 1 - sage: NumberFieldElement_quadratic_nonsqrt(F, x-1) + sage: NumberFieldElement_quadratic(F, x-1) b - 1 From triples of Integers:: - sage: NumberFieldElement_quadratic(K, (1,2,3)) + sage: NumberFieldElement_quadratic_sqrt(K, (1,2,3)) 2/3*a + 1/3 - sage: NumberFieldElement_quadratic_nonsqrt(F, (1,2,3)) + sage: NumberFieldElement_quadratic(F, (1,2,3)) 4/9*b + 1/9 - sage: NumberFieldElement_quadratic_nonsqrt(F, (1,2,3)).parts() + sage: NumberFieldElement_quadratic(F, (1,2,3)).parts() (1/3, 2/3) From pairs of Rationals:: - sage: NumberFieldElement_quadratic(K, (1/2,1/3)) + sage: NumberFieldElement_quadratic_sqrt(K, (1/2,1/3)) 1/3*a + 1/2 - sage: NumberFieldElement_quadratic_nonsqrt(F, (1/2,1/3)) + sage: NumberFieldElement_quadratic(F, (1/2,1/3)) 2/9*b + 7/18 - sage: NumberFieldElement_quadratic_nonsqrt(F, (1/2,1/3)).parts() + sage: NumberFieldElement_quadratic(F, (1/2,1/3)).parts() (1/2, 1/3) Direct from Rationals:: - sage: NumberFieldElement_quadratic(K, 2/3) + sage: NumberFieldElement_quadratic_sqrt(K, 2/3) 2/3 - sage: NumberFieldElement_quadratic_nonsqrt(F, 2/3) + sage: NumberFieldElement_quadratic(F, 2/3) 2/3 This checks a bug when converting from lists:: @@ -172,7 +172,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: F. = QuadraticField(-7) sage: c = a + 7 sage: type(c) # indirect doctest - + """ self.D = parent._D cdef Integer a, b, denom @@ -1930,22 +1930,23 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): """ EXAMPLES:: - sage: K. = NumberField(x^2+41) - sage: a._coefficients() + sage: F. = NumberField(x^2 - x + 7) + sage: b._coefficients() [0, 1] - sage: K.zero()._coefficients() - [] - sage: (3/2*K.one())._coefficients() - [3/2] """ - # In terms of the generator... Rational const = Rational.__new__(Rational) + # In terms of the generator... + cdef Rational const = Rational.__new__(Rational) cdef Rational lin = Rational.__new__(Rational) if not self: return [] ad, bd = self.parts() if not bd: return [ad] - return [ad,bd] + + cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? + alpha, beta = gen.parts() + scale = bd/beta + return [ad - scale*alpha, scale] def denominator(self): r""" @@ -1956,30 +1957,26 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): EXAMPLES:: - sage: K. = NumberField(x^2+x+41) - sage: a.denominator() - 1 - sage: b = (2*a+1)/6 - sage: b.denominator() - 6 - sage: K(1).denominator() - 1 - sage: K(1/2).denominator() - 2 - sage: K(0).denominator() - 1 - sage: K. = NumberField(x^2 - 5) sage: b = (a + 1)/2 sage: b.denominator() 2 sage: b.is_integral() True + + sage: K. = NumberField(x^2-x+7) + sage: c.denominator() + 1 """ - cdef Integer denom - denom = Integer.__new__(Integer) - mpz_set(denom.value, self.denom) - return denom + c = self._coefficients() + if len(c) == 2: + const, lin = c + elif len(c) == 1: + const = c[0] + lin = Rational(0) + else: + const = lin = Rational(0) + return const.denominator().lcm(lin.denominator()) def numerator(self): r""" @@ -2359,7 +2356,69 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): else: return n -cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): + +cdef class NumberFieldElement_quadratic_sqrt(NumberFieldElement_quadratic): + r""" + A NumberFieldElement_quadratic object gives an efficient representation of + an element of a quadratic extension of `\QQ` for the case when + :func:`is_sqrt_disc()` is ``True``. + """ + def denominator(self): + r""" + Return the denominator of ``self``. + + This is the LCM of the denominators of the coefficients of `self``, and + thus it may well be `> 1` even when the element is an algebraic integer. + + EXAMPLES:: + + sage: K. = NumberField(x^2+x+41) + sage: a.denominator() + 1 + sage: b = (2*a+1)/6 + sage: b.denominator() + 6 + sage: K(1).denominator() + 1 + sage: K(1/2).denominator() + 2 + sage: K(0).denominator() + 1 + + sage: K. = NumberField(x^2 - 5) + sage: b = (a + 1)/2 + sage: b.denominator() + 2 + sage: b.is_integral() + True + """ + cdef Integer denom + denom = Integer.__new__(Integer) + mpz_set(denom.value, self.denom) + return denom + + cpdef list _coefficients(self): + """ + EXAMPLES:: + + sage: K. = NumberField(x^2+41) + sage: a._coefficients() + [0, 1] + sage: K.zero()._coefficients() + [] + sage: (3/2*K.one())._coefficients() + [3/2] + """ + # In terms of the generator... Rational const = Rational.__new__(Rational) + cdef Rational lin = Rational.__new__(Rational) + if not self: + return [] + ad, bd = self.parts() + if not bd: + return [ad] + return [ad,bd] + +cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic_sqrt): r""" An element of `\QQ[i]`. @@ -2504,65 +2563,6 @@ cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic): from sage.symbolic.ring import SR return SR(self).log(*args, **kwds) - -cdef class NumberFieldElement_quadratic_nonsqrt(NumberFieldElement_quadratic): - r""" - A NumberFieldElement_quadratic object gives an efficient representation of - an element of a quadratic extension of `\QQ` for the case when - :func:`is_sqrt_disc()` is ``False``. - """ - def denominator(self): - r""" - Return the denominator of ``self``. - - This is the LCM of the denominators of the coefficients of `self``, and - thus it may well be `> 1` even when the element is an algebraic integer. - - EXAMPLES:: - - sage: K. = NumberField(x^2 - 5) - sage: b = (a + 1)/2 - sage: b.denominator() - 2 - sage: b.is_integral() - True - - sage: K. = NumberField(x^2-x+7) - sage: c.denominator() - 1 - """ - c = self._coefficients() - if len(c) == 2: - const, lin = c - elif len(c) == 1: - const = c[0] - lin = Rational(0) - else: - const = lin = Rational(0) - return const.denominator().lcm(lin.denominator()) - - cpdef list _coefficients(self): - """ - EXAMPLES:: - - sage: F. = NumberField(x^2 - x + 7) - sage: b._coefficients() - [0, 1] - """ - # In terms of the generator... - cdef Rational const = Rational.__new__(Rational) - cdef Rational lin = Rational.__new__(Rational) - if not self: - return [] - ad, bd = self.parts() - if not bd: - return [ad] - - cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? - alpha, beta = gen.parts() - scale = bd/beta - return [ad - scale*alpha, scale] - cdef class OrderElement_quadratic(NumberFieldElement_quadratic): """ Element of an order in a quadratic field. @@ -2777,12 +2777,13 @@ cdef class OrderElement_quadratic(NumberFieldElement_quadratic): ad, bd = self.parts() if not bd: return [ad] + cdef NumberFieldElement_quadratic gen = self.number_field().gen() alpha, beta = gen.parts() if is_sqrt_disc(alpha, beta): - return [ad,bd] + return [ad, bd] else: - scale = bd/beta + scale = bd / beta return [ad - scale*alpha, scale] def denominator(self): diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 81e333be4f5..b32e0e60421 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -573,7 +573,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.number_field.number_field import NumberField, GaussianField, CyclotomicField -from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic, NumberFieldElement_gaussian +from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian from sage.arith.all import factor from . import infinity from sage.categories.action import Action From 2661da45b41293e2b6224c551414f34ab948f542 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 14 May 2021 10:21:50 +1000 Subject: [PATCH 347/706] Removed some unneed imports in complex_mpc.pyx. --- src/sage/rings/complex_mpc.pyx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 3bcb2a3790d..debfac174d0 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -89,10 +89,6 @@ from sage.misc.superseded import deprecated_function_alias cimport gmpy2 gmpy2.import_gmpy2() -NumberFieldElement_quadratic = None -AlgebraicNumber_base = None -AlgebraicNumber = None -AlgebraicReal = None AA = None QQbar = None CDF = CLF = RLF = None @@ -105,19 +101,10 @@ def late_import(): sage: sage.rings.complex_mpc.late_import() """ - global NumberFieldElement_quadratic - global AlgebraicNumber_base - global AlgebraicNumber - global AlgebraicReal global AA, QQbar global CLF, RLF, CDF - if NumberFieldElement_quadratic is None: - import sage.rings.number_field.number_field_element_quadratic as nfeq - NumberFieldElement_quadratic = nfeq.NumberFieldElement_quadratic + if AA is None: import sage.rings.qqbar - AlgebraicNumber_base = sage.rings.qqbar.AlgebraicNumber_base - AlgebraicNumber = sage.rings.qqbar.AlgebraicNumber - AlgebraicReal = sage.rings.qqbar.AlgebraicReal AA = sage.rings.qqbar.AA QQbar = sage.rings.qqbar.QQbar from .real_lazy import CLF, RLF From 32b2bcaefddc4fa3d2aee6fa690ce1466cbb5948 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 14 May 2021 21:11:24 +0200 Subject: [PATCH 348/706] Remove call to deprecated set_dpi_cor It is a no-op and throws deprecation warnings with matplotlib 3.4 --- src/sage/plot/arrow.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 0247872298d..63de0ad5dd7 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -397,7 +397,6 @@ def __init__(self, patch, n): self._n = n def get_paths(self, renderer): - self._patch.set_dpi_cor(renderer.points_to_pixels(1.)) paths, fillables = self._patch.get_path_in_displaycoord() return paths From a209a54f08f62e7649b25cda204ab3aff45d9ff3 Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Fri, 14 May 2021 14:19:55 -0700 Subject: [PATCH 349/706] commented the change --- src/sage/structure/parent.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index ad5f0174667..709d06127a5 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1342,6 +1342,8 @@ cdef class Parent(sage.structure.category_object.CategoryObject): from sage.categories.pushout import pushout im_gens = Sequence(im_gens) codomain = pushout(self, im_gens.universe()) + #the pushout should fix t/31818 and ensure that the ring over which the codomain is defined + #contains the ring over which the domain is defined if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) # Not all homsets accept category/check/base_map as arguments From 565dcdf9cd45adfe2967afddde3be0100435fc43 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 15 May 2021 19:53:47 +1000 Subject: [PATCH 350/706] Moving __getitem__ to the sqrt class. --- .../number_field/number_field_element.pyx | 2 +- .../number_field_element_quadratic.pyx | 84 ++++++++++--------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 759b162a141..46bfe312231 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -357,7 +357,7 @@ cdef class NumberFieldElement(FieldElement): sage: cf4(one) 1 sage: type(cf4(1)) - + sage: cf33 = CyclotomicField(33) ; z33 = cf33.0 sage: cf66 = CyclotomicField(66) ; z66 = cf66.0 sage: z33._lift_cyclotomic_element(cf66) diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index cfaeae3a203..f1ad9519b54 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -868,42 +868,6 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): self.arb_set_imag(acb_imagref(res.value), R._prec) return res - def __getitem__(self, n): - """ - Return the ``n``-th coefficient of this number field element, - written as a polynomial in the generator. - - Note that ``n`` must be either ``0`` or ``1``. - - EXAMPLES:: - - sage: K. = NumberField(x^2-13) - sage: elt = a/4 + 1/3 - sage: elt[0] - 1/3 - sage: elt[1] - 1/4 - - sage: K.zero()[0] - 0 - sage: K.zero()[1] - 0 - - sage: K.one()[0] - 1 - sage: K.one()[1] - 0 - - sage: elt[2] - Traceback (most recent call last): - ... - IndexError: index must be either 0 or 1 - """ - try: - return self.parts()[n] - except IndexError: # So we have a better error message - raise IndexError("index must be either 0 or 1") - cpdef tuple parts(self): r""" This function returns a pair of rationals `a` and `b` such that self `= @@ -2413,10 +2377,50 @@ cdef class NumberFieldElement_quadratic_sqrt(NumberFieldElement_quadratic): cdef Rational lin = Rational.__new__(Rational) if not self: return [] - ad, bd = self.parts() - if not bd: - return [ad] - return [ad,bd] + cdef tuple parts = self.parts() + if not (parts[1]): + return [parts[0]] + return list(parts) + + def __getitem__(self, n): + """ + Return the ``n``-th coefficient of this number field element, + written as a polynomial in the generator. + + Note that ``n`` must be either ``0`` or ``1``. + + EXAMPLES:: + + sage: K. = NumberField(x^2-13) + sage: elt = a/4 + 1/3 + sage: elt[0] + 1/3 + sage: elt[1] + 1/4 + + sage: K.zero()[0] + 0 + sage: K.zero()[1] + 0 + + sage: K.one()[0] + 1 + sage: K.one()[1] + 0 + + sage: elt[2] + Traceback (most recent call last): + ... + IndexError: index must be either 0 or 1 + + sage: C. = CyclotomicField(3) + sage: list(z3) + [0, 1] + """ + try: + return self.parts()[n] + except IndexError: # So we have a better error message + raise IndexError("index must be either 0 or 1") cdef class NumberFieldElement_gaussian(NumberFieldElement_quadratic_sqrt): r""" From 87a2bea6f0726af527f366812af907cc824abfda Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 May 2021 11:22:59 -0700 Subject: [PATCH 351/706] Polyhedron_base.affine_hull_manifold: In the full-dimensional case, do not create a submanifold --- src/sage/geometry/polyhedron/base.py | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4871799aa0c..97a68e533e4 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10519,7 +10519,10 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambient_space=None, names=None, **kwds): """ - Return the affine hull of ``self`` as a submanifold of the ambient Euclidean space. + Return the affine hull of ``self`` as a manifold. + + If ``self`` is full-dimensional, it is just the ambient Euclidean space. + Otherwise, it is a Riemannian submanifold of the ambient Euclidean space. INPUT: @@ -10575,15 +10578,26 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien ....: for FM in submanifolds) + D.plot() Graphics3d Object + Full-dimensional case:: + + sage: cube = polytopes.cube(); cube + A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices + sage: cube.affine_hull_manifold() + Euclidean space E^3 + """ if ambient_space is None: from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace ambient_space = EuclideanSpace(self.ambient_dim(), start_index=start_index) + + if self.is_full_dimensional(): + return ambient_space + CE = ambient_space.default_chart() from sage.manifolds.manifold import Manifold if name is None: - name = 'H' + name, latex_name = self._affine_hull_name_latex_name() H = Manifold(self.dim(), name, ambient=ambient_space, structure="Riemannian", latex_name=latex_name, start_index=start_index) if names is None: @@ -10616,7 +10630,17 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien var=list(foliation_parameters), t_inverse=foliation_scalar_fields) return H - def _polymake_init_(self): + def _affine_hull_name_latex_name(self, name=None, latex_name=None): + if name is None: + name = 'P' + if latex_name is None: + latex_name = name + operator = 'aff' + aff_name = f'{operator}_{name}' + aff_latex_name = r'\mathop{\mathrm{' + operator + '}}(' + latex_name + ')' + return aff_name, aff_latex_name + +o def _polymake_init_(self): """ Return a polymake "Polytope" object corresponding to ``self``. From 8af2c7126f785403d8df4a7dd7c22afdc90937d9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 May 2021 11:29:06 -0700 Subject: [PATCH 352/706] Polyhedron_base._affine_hull_name_latex_name: Add docstring --- src/sage/geometry/polyhedron/base.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 97a68e533e4..e3583ab9828 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10631,6 +10631,18 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien return H def _affine_hull_name_latex_name(self, name=None, latex_name=None): + """ + Return the default name of the affine hull. + + EXAMPLES:: + + sage: polytopes.cube()._affine_hull_name_latex_name('C', r'\square') + ('aff_C', '\\mathop{\\mathrm{aff}}(\\square)') + + sage: Polyhedron(vertices=[[0, 1], [1, 0]])._affine_hull_name_latex_name() + ('aff_P', '\\mathop{\\mathrm{aff}}(P)') + """ + if name is None: name = 'P' if latex_name is None: From 8c6759a490b2011708b34a7feba92d789c62f885 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 May 2021 11:50:02 -0700 Subject: [PATCH 353/706] Make docstrings raw --- src/sage/geometry/polyhedron/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e3583ab9828..24df4b9d326 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10518,7 +10518,7 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambient_space=None, names=None, **kwds): - """ + r""" Return the affine hull of ``self`` as a manifold. If ``self`` is full-dimensional, it is just the ambient Euclidean space. @@ -10631,7 +10631,7 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien return H def _affine_hull_name_latex_name(self, name=None, latex_name=None): - """ + r""" Return the default name of the affine hull. EXAMPLES:: From dbcfae4ad0be537b0faa1b501bcae8e8efea60a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 May 2021 20:51:56 +0200 Subject: [PATCH 354/706] fix doc detail in set partition --- src/sage/combinat/set_partition.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 21900263007..71bac648136 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -16,7 +16,7 @@ This module defines a class for immutable partitioning of a set. For mutable version see :func:`DisjointSet`. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen , # # Distributed under the terms of the GNU General Public License (GPL) @@ -28,8 +28,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.sets.set import Set, Set_generic @@ -55,6 +55,7 @@ from sage.sets.disjoint_set import DisjointSet from sage.combinat.posets.hasse_diagram import HasseDiagram + class AbstractSetPartition(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" @@ -444,6 +445,7 @@ def coarsenings(self): [{}] """ SP = SetPartitions(len(self)) + def union(s): # Return the partition obtained by combining, for every # part of s, those parts of self which are indexed by @@ -500,12 +502,14 @@ def conjugate(self): """ def next(a, support): return support[(support.index(a)+1) % len(support)] + def addback(S, terminals, rsupport): out = list(S) for a in terminals*2: if a not in out and next(a, rsupport) in out: out.append(a) return out + def pre_conjugate(sp): if len(sp) <= 1: return SetPartition([[a] for S in sp for a in S]) @@ -526,6 +530,7 @@ def pre_conjugate(sp): return SetPartition([[support[-support.index(a)-1] for a in S] for S in pre_conjugate(self)]) + class SetPartition(AbstractSetPartition, metaclass=InheritComparisonClasscallMetaclass): r""" @@ -1997,6 +2002,7 @@ def plot(self, angle=None, color='black', base_set_dict=None): diag.axes(False) return diag + class SetPartitions(UniqueRepresentation, Parent): r""" An (unordered) partition of a set `S` is a set of pairwise @@ -2188,7 +2194,7 @@ def from_restricted_growth_word_blocks(self, w): integers such that each letter is at most 1 larger than all the letters before to a set partition of `\{1,...,n\}`. - ``w[i]` is the index of the block containing ``i+1`` when + ``w[i]`` is the index of the block containing ``i+1`` when sorting the blocks by their minimal element. INPUT: @@ -2653,6 +2659,7 @@ def is_strict_refinement(self, s, t): return False return True + class SetPartitions_all(SetPartitions): r""" All set partitions. @@ -2696,6 +2703,7 @@ def __iter__(self): yield self.element_class(self, list(x)) n += 1 + class SetPartitions_set(SetPartitions): """ Set partitions of a fixed set `S`. @@ -2865,6 +2873,7 @@ def base_set_cardinality(self): """ return len(self._set) + class SetPartitions_setparts(SetPartitions_set): r""" Set partitions with fixed partition sizes corresponding to an @@ -3024,7 +3033,6 @@ def _set_partition_poset(self): covers[first].append(i) return HasseDiagram(covers) - def __iter__(self): """ An iterator for all the set partitions of the given set with @@ -3083,6 +3091,7 @@ def __contains__(self, x): return False return sorted(map(len, x)) == list(reversed(self._parts)) + class SetPartitions_setn(SetPartitions_set): """ Set partitions with a given number of blocks. @@ -3267,6 +3276,7 @@ def cyclic_permutations_of_set_partition(set_part): """ return list(cyclic_permutations_of_set_partition_iterator(set_part)) + def cyclic_permutations_of_set_partition_iterator(set_part): """ Iterates over all combinations of cyclic permutations of each cell From 19a9cebabdcdcba2aace57f814a014a6468133e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 May 2021 13:05:55 -0700 Subject: [PATCH 355/706] Polyhedron_base.affine_hull_manifold: Add argument ambient_chart --- src/sage/geometry/polyhedron/base.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 24df4b9d326..7fae4784430 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10517,7 +10517,7 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): tester.assertFalse(data.polyhedron.base_ring() is AA) def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambient_space=None, - names=None, **kwds): + ambient_chart=None, names=None, **kwds): r""" Return the affine hull of ``self`` as a manifold. @@ -10527,8 +10527,12 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien INPUT: - ``ambient_space`` -- a :class:`~sage.manifolds.differentiable.examples.euclidean.EuclideanSpace` - of the ambient dimension (default: a new instance of ``EuclideanSpace``) - the ambient space. + of the ambient dimension (default: the manifold of ``ambient_chart``, if provided; + otherwise, a new instance of ``EuclideanSpace``). + + - ``ambient_chart`` -- a chart on ``ambient_space``. + + - ``names`` -- names for the coordinates on the affine hull. - optional arguments accepted by :meth:`~sage.geometry.polyhedron.base.affine_hull_projection`. @@ -10587,13 +10591,20 @@ def affine_hull_manifold(self, name=None, latex_name=None, start_index=0, ambien """ if ambient_space is None: - from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace - ambient_space = EuclideanSpace(self.ambient_dim(), start_index=start_index) + if ambient_chart is not None: + ambient_space = ambient_chart.manifold() + else: + from sage.manifolds.differentiable.examples.euclidean import EuclideanSpace + ambient_space = EuclideanSpace(self.ambient_dim(), start_index=start_index) + if ambient_space.dimension() != self.ambient_dim(): + raise ValueError('ambient_space and ambient_chart must match the ambient dimension') if self.is_full_dimensional(): return ambient_space - CE = ambient_space.default_chart() + if ambient_chart is None: + ambient_chart = ambient_space.default_chart() + CE = ambient_chart from sage.manifolds.manifold import Manifold if name is None: From 62390c9a35dcf76f2eb76178458ff978fef2ced2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 16 May 2021 15:11:23 -0700 Subject: [PATCH 356/706] src/sage/geometry/polyhedron/base.py: Fix typo --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7fae4784430..1f06ec4def2 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -10663,7 +10663,7 @@ def _affine_hull_name_latex_name(self, name=None, latex_name=None): aff_latex_name = r'\mathop{\mathrm{' + operator + '}}(' + latex_name + ')' return aff_name, aff_latex_name -o def _polymake_init_(self): + def _polymake_init_(self): """ Return a polymake "Polytope" object corresponding to ``self``. From 9a66ea54eb614ffc7fc8fa461b97d374a947fa7d Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Sun, 16 May 2021 15:44:15 -0700 Subject: [PATCH 357/706] changed the pushout to an error --- src/sage/structure/parent.pyx | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 709d06127a5..e6102795b17 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1334,16 +1334,46 @@ cdef class Parent(sage.structure.category_object.CategoryObject): Traceback (most recent call last): ... TypeError: natural coercion morphism from Rational Field to Integer Ring not defined + + Implicit codomain: If no codomain is specified, one is built from images of the + canonical basis. A ``ValueError`` is raised if one gives vectors defined + over a smaller ring than the domain. + + sage: V = QQ^3; [a, b, c] = V.basis() + sage: V.hom([b, c, a]) + Vector space morphism represented by the matrix: + [0 1 0] + [0 0 1] + [1 0 0] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field + sage: V.hom(identity_matrix(3)) + Traceback (most recent call last): + ... + ValueError: The image of basis vectors must be defined over a ring containing the base ring of the domain + sage: V.hom(identity_matrix(QQ, 3)) + Vector space morphism represented by the matrix: + [1 0 0] + [0 1 0] + [0 0 1] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field + sage: V.hom(identity_matrix(3), V) + Vector space morphism represented by the matrix: + [1 0 0] + [0 1 0] + [0 0 1] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 3 over Rational Field """ if isinstance(im_gens, Parent): return self.Hom(im_gens).natural_map() from sage.structure.sequence import Sequence_generic, Sequence if codomain is None: - from sage.categories.pushout import pushout im_gens = Sequence(im_gens) - codomain = pushout(self, im_gens.universe()) - #the pushout should fix t/31818 and ensure that the ring over which the codomain is defined - #contains the ring over which the domain is defined + codomain = im_gens.universe() + if not(self.base_ring().is_subring(codomain.base_ring())): + raise ValueError("The base ring of the domain must be contained in the base ring of the codomain") if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) # Not all homsets accept category/check/base_map as arguments From dff03f3f3bf75481f5ad174b0a6e3a645defbcf8 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Mon, 17 May 2021 00:58:52 +0200 Subject: [PATCH 358/706] #31795 : simplify the code of homogeneous_components --- src/sage/rings/polynomial/multi_polynomial.pyx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 93553dd2687..9ad3f77ffb5 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -844,17 +844,11 @@ cdef class MPolynomial(CommutativeRingElement): sage: (a^6 + b^3 + b*c + a^2*c + c + a + 1).homogeneous_components() {0: 1, 1: a, 3: c, 5: a^2*c + b*c, 6: a^6 + b^3} """ - parts = {} - for c, m in zip(self.coefficients(), self.monomials()): - d = m.degree() - D = parts.get(d) - if D is None: - D = {} - parts[d] = D - e, = m.exponents() - D[e] = c - r = self.parent() - return {d: r(D) for d, D in parts.items()} + from collections import defaultdict + d = defaultdict(self.parent()) + for c,m in self: + d[m.degree()] += c*m + return dict(d) cpdef _mod_(self, other): """ From 2d7fda7417de199893f5b58c2e38c5096fbdb5d9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 09:49:48 -0700 Subject: [PATCH 359/706] src/sage/manifolds/subset.py: Simplify code more to make pyflakes happy --- src/sage/manifolds/subset.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 8336b1e0a11..2d44d7aa406 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2185,10 +2185,9 @@ def label(element): if not others: return res for other in others[:-1]: - old, res = res, res._intersection_subset(other) + res = res._intersection_subset(other) # The last one gets the name - other = others[-1] - return res._intersection_subset(other, name=name, latex_name=latex_name) + return res._intersection_subset(others[-1], name=name, latex_name=latex_name) @staticmethod def _reduce_intersection_members(subsets): @@ -2416,10 +2415,9 @@ def label(element): if not others: return res for other in others[:-1]: - old, res = res, res._union_subset(other) + res = res._union_subset(other) # The last one gets the name - other = others[-1] - return res._union_subset(other, name=name, latex_name=latex_name) + return res._union_subset(others[-1], name=name, latex_name=latex_name) @staticmethod def _reduce_union_members(subsets): From b52d759212a264f3c4805f76393ac327357302c2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 18 May 2021 09:36:39 +1000 Subject: [PATCH 360/706] Fixing output type for singular.pyx. --- src/sage/libs/singular/singular.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index fc01a6f3061..674a36cba81 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -255,7 +255,7 @@ cdef object si2sa_NF(number *n, ring *_ring, object base): sage: f.lc() 1024*a sage: type(f.lc()) - + """ cdef poly *z cdef number *c From b851790d9ffcd8d9c2cee52550ec20af7af29b60 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 21:10:24 -0700 Subject: [PATCH 361/706] build/pkgs/normaliz: Update to 3.8.10 --- build/pkgs/normaliz/checksums.ini | 6 +++--- build/pkgs/normaliz/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/normaliz/checksums.ini b/build/pkgs/normaliz/checksums.ini index 708adca47bf..c3afbfb2656 100644 --- a/build/pkgs/normaliz/checksums.ini +++ b/build/pkgs/normaliz/checksums.ini @@ -1,5 +1,5 @@ tarball=normaliz-VERSION.tar.gz -sha1=068103675d605b6a905b809611a4d2f506adf505 -md5=5d843dd1af6802bff412865dfe7b2322 -cksum=3186058075 +sha1=7486d046c5e8e352d6d7c3544a0e6a1164e9b1fd +md5=136edc12b5c027bb1a019e06fb8d9113 +cksum=1640404889 upstream_url=https://github.com/Normaliz/Normaliz/releases/download/vVERSION/normaliz-VERSION.tar.gz diff --git a/build/pkgs/normaliz/package-version.txt b/build/pkgs/normaliz/package-version.txt index 203e6d5c9a6..d20cc2bf020 100644 --- a/build/pkgs/normaliz/package-version.txt +++ b/build/pkgs/normaliz/package-version.txt @@ -1 +1 @@ -3.8.9 +3.8.10 From 9759d20ec79831be86d85e63de78865afe33091f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 21:10:41 -0700 Subject: [PATCH 362/706] build/pkgs/pynormaliz: Update to 2.14 --- build/pkgs/pynormaliz/checksums.ini | 6 +++--- build/pkgs/pynormaliz/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pynormaliz/checksums.ini b/build/pkgs/pynormaliz/checksums.ini index f777a64c2d3..a7e0febfe80 100644 --- a/build/pkgs/pynormaliz/checksums.ini +++ b/build/pkgs/pynormaliz/checksums.ini @@ -1,5 +1,5 @@ tarball=PyNormaliz-VERSION.tar.gz -sha1=c01c7a734deeb09e1dd236fb53575b152b99657b -md5=6ff9ccc61592190fdb88fe08a50e062c -cksum=3111232777 +sha1=f24f5c4a1b9b7a084ad1f2a0b95374d1a83e31b2 +md5=51e67733702d6cea3cd81144888d9dc7 +cksum=3201946747 upstream_url=https://pypi.io/packages/source/p/pynormaliz/PyNormaliz-VERSION.tar.gz diff --git a/build/pkgs/pynormaliz/package-version.txt b/build/pkgs/pynormaliz/package-version.txt index ae656d47364..123a39a8e91 100644 --- a/build/pkgs/pynormaliz/package-version.txt +++ b/build/pkgs/pynormaliz/package-version.txt @@ -1 +1 @@ -2.13 +2.14 From 956ddcc3a1ca806e5757975abb2ee40ddb1861e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 21:51:11 -0700 Subject: [PATCH 363/706] build/pkgs/fplll: Update to 5.4.1 --- build/pkgs/fplll/checksums.ini | 6 +++--- build/pkgs/fplll/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/fplll/checksums.ini b/build/pkgs/fplll/checksums.ini index 2e355eb4de9..c346ac9603d 100644 --- a/build/pkgs/fplll/checksums.ini +++ b/build/pkgs/fplll/checksums.ini @@ -1,5 +1,5 @@ tarball=fplll-VERSION.tar.gz -sha1=a62fc9aa2a4736e9680a336b092f432c31ddac43 -md5=db2f0f46d4de3e8ab8ed73a79d19ffc3 -cksum=1237963825 +sha1=fe1b225f2bff07b7b832ae8b20ffc85acfd231cd +md5=44db0a42c33e5aa60264b32ab0063351 +cksum=3420408626 upstream_url=https://github.com/fplll/fplll/releases/download/VERSION/fplll-VERSION.tar.gz diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index 8a30e8f94a3..ade65226e0a 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.4.0 +5.4.1 From b2ea24ebac6012309d924fcc11d1a506cc87c772 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 May 2021 21:58:20 -0700 Subject: [PATCH 364/706] build/pkgs/fpylll: Update to 0.5.6 --- build/pkgs/fpylll/checksums.ini | 6 +++--- build/pkgs/fpylll/install-requires.txt | 2 +- build/pkgs/fpylll/package-version.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/fpylll/checksums.ini b/build/pkgs/fpylll/checksums.ini index 72ca88799e7..5255ba6aec8 100644 --- a/build/pkgs/fpylll/checksums.ini +++ b/build/pkgs/fpylll/checksums.ini @@ -1,5 +1,5 @@ tarball=fpylll-VERSION.tar.gz -sha1=3976fb58ee9520a85f29a2ff041acb171c49fcc0 -md5=c53cc961721322ad8e8f74e41ea5bb37 -cksum=2257998255 +sha1=e2a005b53fcc3a9ec1f1111f144eb75b6de7fb44 +md5=5ea3a5fe30646311ef28ca1f8e9bf2bc +cksum=690866680 upstream_url=https://github.com/fplll/fpylll/releases/download/VERSION/fpylll-VERSION.tar.gz diff --git a/build/pkgs/fpylll/install-requires.txt b/build/pkgs/fpylll/install-requires.txt index 166267d5e85..0461a00816f 100644 --- a/build/pkgs/fpylll/install-requires.txt +++ b/build/pkgs/fpylll/install-requires.txt @@ -1 +1 @@ -fpylll ==0.5.5 +fpylll >=0.5.5, <=0.5.6 diff --git a/build/pkgs/fpylll/package-version.txt b/build/pkgs/fpylll/package-version.txt index d1d899fa33a..b49b25336d4 100644 --- a/build/pkgs/fpylll/package-version.txt +++ b/build/pkgs/fpylll/package-version.txt @@ -1 +1 @@ -0.5.5 +0.5.6 From 8ddc70821d20807c3b8c1f3667397ea2c9ecbf7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 18 May 2021 09:25:44 +0200 Subject: [PATCH 365/706] remove deprecated stuff in function factory --- src/sage/symbolic/function_factory.py | 31 ++++++++++----------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/sage/symbolic/function_factory.py b/src/sage/symbolic/function_factory.py index 68ac5e0c3b0..57534da2320 100644 --- a/src/sage/symbolic/function_factory.py +++ b/src/sage/symbolic/function_factory.py @@ -63,7 +63,7 @@ def _maxima_init_(self): sage: f._maxima_init_() "'f" """ - return "'%s"%self.name() + return "'%s" % self.name() def _fricas_init_(self): """ @@ -78,7 +78,7 @@ def _fricas_init_(self): sage: f._fricas_init_() 'operator("f")' """ - return 'operator("%s")'%self.name() + return 'operator("%s")' % self.name() def _sympy_(self): from sympy import Function @@ -104,7 +104,7 @@ def __reduce__(self): if func: if not callable(func): raise ValueError(func_name + "_func" + " parameter must be callable") - setattr(NewSymbolicFunction, '_%s_'%func_name, func) + setattr(NewSymbolicFunction, '_%s_' % func_name, func) return NewSymbolicFunction() @@ -148,14 +148,12 @@ def unpickle_function(name, nargs, latex_name, conversions, evalf_params_first, return function_factory(*args) -def function(s, *args, **kwds): +def function(s, **kwds): r""" Create a formal symbolic function with the name *s*. INPUT: - - ``args`` - arguments to the function, if specified returns the new - function evaluated at the given arguments (deprecated as of :trac:`17447`) - ``nargs=0`` - number of arguments the function accepts, defaults to variable number of arguments, or 0 - ``latex_name`` - name used when printing in latex mode @@ -201,7 +199,7 @@ def function(s, *args, **kwds): sage: foo(x, y) + foo(y, z)^2 foo(y, z)^2 + foo(x, y) - In Sage 4.0, you need to use :meth:`substitute_function` to + You need to use :meth:`substitute_function` to replace all occurrences of a function with another:: sage: g.substitute_function(cr, cos) @@ -210,7 +208,7 @@ def function(s, *args, **kwds): sage: g.substitute_function(cr, (sin(x) + cos(x)).function(x)) b*(cos(a) - sin(a)) - In Sage 4.0, basic arithmetic with unevaluated functions is no + Basic arithmetic with unevaluated functions is no longer supported:: sage: x = var('x') @@ -339,7 +337,7 @@ def function(s, *args, **kwds): if not isinstance(s, str): raise TypeError("expect string as first argument") - # create the function + # create the function or functions if ',' in s: names = s.split(',') elif ' ' in s: @@ -348,18 +346,11 @@ def function(s, *args, **kwds): names = [s] names = [sn.strip() for sn in names if sn.strip()] - funcs = [function_factory(name, **kwds) for name in names] + funcs = tuple(function_factory(name, **kwds) for name in names) - if len(args) > 0: - from sage.misc.superseded import deprecation - deprecation(17447, "Calling function('f',x) is deprecated. Use function('f')(x) instead.") - res = [f(*args) for f in funcs] - else: - res = funcs - - if len(res) == 1: - return res[0] - return tuple(res) + if len(funcs) == 1: + return funcs[0] + return funcs def deprecated_custom_evalf_wrapper(func): From b49a061c2960e10fa19d8d322ab10cf42dadd82d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 10:06:59 +0200 Subject: [PATCH 366/706] equalities -> equations --- .../combinatorial_polyhedron/base.pxd | 9 ++- .../combinatorial_polyhedron/base.pyx | 75 ++++++++++++------- .../combinatorial_face.pxd | 2 +- .../combinatorial_face.pyx | 12 +-- .../face_iterator.pxd | 2 +- .../face_iterator.pyx | 2 +- .../polyhedron_face_lattice.pxd | 2 +- .../polyhedron_face_lattice.pyx | 4 +- 8 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 59c77ec0faa..cc928cd0af8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -12,12 +12,12 @@ cdef class CombinatorialPolyhedron(SageObject): # Do not assume any of those attributes to be initialized, use the corresponding methods instead. cdef tuple _Vrep # the names of VRep, if they exist - cdef tuple _facet_names # the names of HRep without equalities, if they exist - cdef tuple _equalities # stores equalities, given on input (might belong to Hrep) + cdef tuple _facet_names # the names of HRep without equations, if they exist + cdef tuple _equations # stores equations, given on input (might belong to Hrep) cdef int _dimension # stores dimension, -2 on init - cdef unsigned int _n_Hrepresentation # Hrep might include equalities + cdef unsigned int _n_Hrepresentation # Hrep might include equations cdef unsigned int _n_Vrepresentation # Vrep might include rays/lines - cdef size_t _n_facets # length Hrep without equalities + cdef size_t _n_facets # length Hrep without equations cdef bint _bounded # ``True`` iff Polyhedron is bounded cdef ListOfFaces _bitrep_facets # facets in bit representation cdef ListOfFaces _bitrep_Vrep # vertices in bit representation @@ -44,6 +44,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple Vrep(self) cdef tuple facet_names(self) + cdef tuple equations(self) cdef tuple equalities(self) cdef unsigned int n_Vrepresentation(self) cdef unsigned int n_Hrepresentation(self) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..bd2cf9a79be 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -12,7 +12,7 @@ the ridges and the face lattice. Terminology used in this module: - Vrep -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrep -- inequalities and equalities of the polyhedron. +- Hrep -- inequalities and equations of the polyhedron. - Facets -- facets of the polyhedron. - Vrepresentation -- represents a face by the list of Vrep it contains. - Hrepresentation -- represents a face by a list of Hrep it is contained in. @@ -337,7 +337,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._edges = NULL self._ridges = NULL self._face_lattice_incidences = NULL - self._equalities = () + self._equations = () self._all_faces = None self._mem_tuple = () cdef MemoryAllocator mem @@ -413,20 +413,20 @@ cdef class CombinatorialPolyhedron(SageObject): Vinv = None if facets: - # store facets names and compute equalities + # store facets names and compute equations facets = tuple(facets) - test = [1] * len(facets) # 0 if that facet is an equality + test = [1] * len(facets) # 0 if that facet is an equation for i in range(len(facets)): if hasattr(facets[i], "is_inequality"): - # We remove equalities. - # At the moment only equalities with this attribute ``True`` + # We remove equations. + # At the moment only equations with this attribute ``True`` # will be detected. if not facets[i].is_inequality(): test[i] = 0 self._facet_names = tuple(facets[i] for i in range(len(facets)) if test[i]) - self._equalities = tuple(facets[i] for i in range(len(facets)) if not test[i]) + self._equations = tuple(facets[i] for i in range(len(facets)) if not test[i]) else: self._facet_names = None @@ -682,7 +682,7 @@ cdef class CombinatorialPolyhedron(SageObject): def Hrepresentation(self): r""" - Return a list of names of facets and possibly some equalities. + Return a list of names of facets and possibly some equations. EXAMPLES:: @@ -716,7 +716,7 @@ cdef class CombinatorialPolyhedron(SageObject): (M(0, 1), M(1, 0)) """ if self.facet_names() is not None: - return self.equalities() + self.facet_names() + return self.equations() + self.facet_names() else: return tuple(smallInteger(i) for i in range(self.n_Hrepresentation())) @@ -1091,17 +1091,17 @@ cdef class CombinatorialPolyhedron(SageObject): incidence_matrix.set_immutable() return incidence_matrix - # If equalities are present, we add them as first columns. - n_equalities = 0 + # If equations are present, we add them as first columns. + n_equations = 0 if self.facet_names() is not None: - n_equalities = len(self.equalities()) - for Hindex in range(n_equalities): + n_equations = len(self.equations()) + for Hindex in range(n_equations): for Vindex in range(self.n_Vrepresentation()): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) facet_iter = self.face_iter(self.dimension() - 1, dual=False) for facet in facet_iter: - Hindex = facet.ambient_H_indices()[0] + n_equalities + Hindex = facet.ambient_H_indices()[0] + n_equations for Vindex in facet.ambient_V_indices(): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) @@ -1249,7 +1249,7 @@ cdef class CombinatorialPolyhedron(SageObject): deprecation(28603, "the method edge_graph of CombinatorialPolyhedron is deprecated; use vertex_graph", 3) return Graph(self.edges(names=names), format="list_of_edges") - def ridges(self, add_equalities=False, names=True): + def ridges(self, add_equations=False, names=True, add_equalities=False): r""" Return the ridges. @@ -1263,7 +1263,7 @@ cdef class CombinatorialPolyhedron(SageObject): INPUT: - - ``add_equalities`` -- if ``True``, then equalities of the polyhedron + - ``add_equations`` -- if ``True``, then equations of the polyhedron will be added (only applicable when ``names`` is ``True``) - ``names`` -- if ``False``, then the facets are given by their indices @@ -1279,7 +1279,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.ridges() ((An inequality (1, 0) x - 1 >= 0, An inequality (-1, 0) x + 2 >= 0),) - sage: C.ridges(add_equalities=True) + sage: C.ridges(add_equations=True) (((An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0), (An inequality (-1, 0) x + 2 >= 0, An equation (1, 1) x - 3 == 0)),) @@ -1330,12 +1330,26 @@ cdef class CombinatorialPolyhedron(SageObject): TESTS: - Testing that ``add_equalities`` is ignored if ``names`` is ``False``:: + Testing that ``add_equations`` is ignored if ``names`` is ``False``:: sage: C = CombinatorialPolyhedron(polytopes.simplex()) - sage: C.ridges(names=False, add_equalities=True) + sage: C.ridges(names=False, add_equations=True) ((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1)) + + The keyword ``add_equalities`` is deprecated:: + + sage: C = CombinatorialPolyhedron(polytopes.simplex()) + sage: r = C.ridges(add_equations=True) + sage: r1 = C.ridges(add_equalities=True) + doctest:...: DeprecationWarning: the keyword ``add_equalities`` is deprecated; use ``add_equations`` + See https://trac.sagemath.org/31834 for details. + sage: r == r1 + True """ + if add_equalities: + from sage.misc.superseded import deprecation + deprecation(31834, "the keyword ``add_equalities`` is deprecated; use ``add_equations``", 3) + add_equations = True if self._ridges is NULL: # compute the ridges. if not self.is_bounded(): @@ -1364,11 +1378,11 @@ cdef class CombinatorialPolyhedron(SageObject): return f(self._get_edge(self._ridges, i, 1)) cdef size_t j - if add_equalities and names: - # Also getting the equalities for each facet. + if add_equations and names: + # Also getting the equations for each facet. return tuple( - (((facet_one(i),) + self.equalities()), - ((facet_two(i),) + self.equalities())) + (((facet_one(i),) + self.equations()), + ((facet_two(i),) + self.equations())) for i in range(n_ridges)) else: return tuple((facet_one(i), facet_two(i)) @@ -1414,7 +1428,7 @@ cdef class CombinatorialPolyhedron(SageObject): V = list(facet.ambient_Hrepresentation() for facet in face_iter) else: V = list(facet.ambient_V_indices() for facet in face_iter) - E = self.ridges(names=names, add_equalities=True) + E = self.ridges(names=names, add_equations=True) if not names: # If names is false, the ridges are given as tuple of indices, # i.e. (1,2) instead of (('f1',), ('f2',)). @@ -2691,13 +2705,18 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._facet_names - cdef tuple equalities(self): + cdef tuple equations(self): r""" - Return the names of the equalities. + Return the names of the equations. - If not equalities are given, return ``None``. + If not equations are given, return ``None``. """ - return self._equalities + return self._equations + + cdef tuple equalities(self): + from sage.misc.superseded import deprecation + deprecation(31834, "the method equalities of CombinatorialPolyhedron is deprecated; use equations", 3) + return self.equations() cdef unsigned int n_Vrepresentation(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..4c50daeaf55 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -23,7 +23,7 @@ cdef class CombinatorialFace(SageObject): cdef bint _initialized_from_face_lattice # some copies from ``CombinatorialPolyhedron`` - cdef tuple _ambient_Vrep, _ambient_facets, _equalities + cdef tuple _ambient_Vrep, _ambient_facets, _equations # Atoms and coatoms are the vertices/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 1d790726f23..7157d72ff77 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -181,7 +181,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = it.structure.dimension self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names - self._equalities = it._equalities + self._equations = it._equations self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -206,7 +206,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = all_faces.dimension self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names - self._equalities = all_faces._equalities + self._equations = all_faces._equations self._initialized_from_face_lattice = True @@ -580,7 +580,7 @@ cdef class CombinatorialFace(SageObject): defining the face. It consists of the facets/inequalities that contain the face - and the equalities defining the ambient polyhedron. + and the equations defining the ambient polyhedron. EXAMPLES:: @@ -626,12 +626,12 @@ cdef class CombinatorialFace(SageObject): # if not dual, the facet-representation corresponds to the coatom-representation length = self.set_coatom_rep() # fill self.coatom_repr_face return tuple(self._ambient_facets[self.coatom_rep[i]] - for i in range(length)) + self._equalities + for i in range(length)) + self._equations else: # if dual, the facet-representation corresponds to the atom-representation length = self.set_atom_rep() # fill self.atom_repr_face return tuple(self._ambient_facets[self.atom_rep[i]] - for i in range(length)) + self._equalities + for i in range(length)) + self._equations def ambient_H_indices(self): r""" @@ -694,7 +694,7 @@ cdef class CombinatorialFace(SageObject): and equations of the face. The facet-representation consists of the facets - that contain the face and of the equalities of the polyhedron. + that contain the face and of the equations of the polyhedron. INPUT: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 63ed7858024..fe334403672 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -55,7 +55,7 @@ cdef class FaceIterator_base(SageObject): cdef MemoryAllocator _mem # some copies from ``CombinatorialPolyhedron`` - cdef tuple _Vrep, _facet_names, _equalities + cdef tuple _Vrep, _facet_names, _equations cdef bint _bounded # Atoms and coatoms are the vertices/facets of the Polyedron. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fc4929bf29f..a1438030ea3 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -252,7 +252,7 @@ cdef class FaceIterator_base(SageObject): self.atoms = C.bitrep_Vrep() self._Vrep = C.Vrep() self._facet_names = C.facet_names() - self._equalities = C.equalities() + self._equations = C.equations() self._bounded = C.is_bounded() self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 951fd46db41..35423d468cb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -15,7 +15,7 @@ cdef class PolyhedronFaceLattice: cdef size_t *coatom_rep # a place where coatom-representation of face will be stored # some copies from CombinatorialPolyhedron - cdef tuple _Vrep, _facet_names, _equalities + cdef tuple _Vrep, _facet_names, _equations # Atoms and coatoms are the Vrep/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms Vrepresentatives and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index 6c9f5c678b5..c2c37a6c850 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -9,7 +9,7 @@ the face lattice of a polyhedron. Terminology in this module: - Vrep -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrep -- inequalities and equalities of the polyhedron. +- Hrep -- inequalities and equations of the polyhedron. - Facets -- facets of the polyhedron. - Coatoms -- the faces from which all others are constructed in the face iterator. This will be facets or Vrep. @@ -138,7 +138,7 @@ cdef class PolyhedronFaceLattice: cdef FaceIterator face_iter = C._face_iter(self.dual, -2) self._Vrep = C.Vrep() self._facet_names = C.facet_names() - self._equalities = C.equalities() + self._equations = C.equations() # copy f_vector for later use f_vector = C.f_vector() From 9dc75c26e4d3089e46444eec12aec0bba9e9b4d8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 12:17:56 +0200 Subject: [PATCH 367/706] have equations always last --- .../combinatorial_polyhedron/base.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index bd2cf9a79be..0610e050460 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -689,13 +689,13 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = polytopes.permutahedron(3) sage: C = CombinatorialPolyhedron(P) sage: C.Hrepresentation() - (An equation (1, 1, 1) x - 6 == 0, - An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (1, 1, 0) x - 3 >= 0, An inequality (-1, -1, 0) x + 5 >= 0, An inequality (0, 1, 0) x - 1 >= 0, An inequality (-1, 0, 0) x + 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0) + An inequality (0, -1, 0) x + 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: points = [(1,0,0), (0,1,0), (0,0,1), ....: (-1,0,0), (0,-1,0), (0,0,-1)] @@ -716,7 +716,7 @@ cdef class CombinatorialPolyhedron(SageObject): (M(0, 1), M(1, 0)) """ if self.facet_names() is not None: - return self.equations() + self.facet_names() + return self.facet_names() + self.equations() else: return tuple(smallInteger(i) for i in range(self.n_Hrepresentation())) @@ -1041,7 +1041,7 @@ cdef class CombinatorialPolyhedron(SageObject): :: - sage: P = polytopes.permutahedron(5) + sage: P = polytopes.permutahedron(5, backend='field') sage: C = P.combinatorial_polyhedron() sage: C.incidence_matrix.clear_cache() sage: C.incidence_matrix() == P.incidence_matrix() @@ -1091,17 +1091,17 @@ cdef class CombinatorialPolyhedron(SageObject): incidence_matrix.set_immutable() return incidence_matrix - # If equations are present, we add them as first columns. - n_equations = 0 + # If equations are present, we add them as last columns. + n_facets = self.n_facets() if self.facet_names() is not None: n_equations = len(self.equations()) - for Hindex in range(n_equations): + for Hindex in range(n_facets, n_facets + n_equations): for Vindex in range(self.n_Vrepresentation()): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) facet_iter = self.face_iter(self.dimension() - 1, dual=False) for facet in facet_iter: - Hindex = facet.ambient_H_indices()[0] + n_equations + Hindex = facet.ambient_H_indices()[0] for Vindex in facet.ambient_V_indices(): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) From a7d13f5d8f7eb14dc755f3e4bd41cdfda01e6d8a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 12:42:32 +0200 Subject: [PATCH 368/706] make Hrep methods of combinatorial face more consistent --- .../combinatorial_polyhedron/base.pyx | 6 +-- .../combinatorial_face.pyx | 54 +++++++++++++++---- .../face_iterator.pyx | 3 +- src/sage/geometry/polyhedron/face.py | 4 +- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0610e050460..624bf7b5928 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2253,7 +2253,7 @@ cdef class CombinatorialPolyhedron(SageObject): vertex_iter = self._face_iter(True, 0) n_facets = self.n_facets() for vertex in vertex_iter: - if vertex.n_ambient_Hrepresentation() == n_facets - 1: + if vertex.n_ambient_Hrepresentation(add_equations=False) == n_facets - 1: if certificate: return (True, vertex.ambient_Vrepresentation()[0]) return True @@ -2310,11 +2310,11 @@ cdef class CombinatorialPolyhedron(SageObject): An inequality (0, 0, 0, -1, 0) x + 5 >= 0, An equation (1, 1, 1, 1, 1) x - 15 == 0) sage: face.ambient_H_indices() - (25, 29) + (25, 29, 30) sage: face = next(it); face A 2-dimensional face of a 4-dimensional combinatorial polyhedron sage: face.ambient_H_indices() - (24, 29) + (24, 29, 30) sage: face.ambient_V_indices() (32, 89, 90, 94) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 7157d72ff77..77856af971b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -633,19 +633,23 @@ cdef class CombinatorialFace(SageObject): return tuple(self._ambient_facets[self.atom_rep[i]] for i in range(length)) + self._equations - def ambient_H_indices(self): + def ambient_H_indices(self, add_equations=True): r""" Return the indices of the Hrepresentation objects of the ambient polyhedron defining the face. + INPUT: + + - ``add_equations`` -- boolean (default: ``True``); whether or not to include the equations + EXAMPLES:: sage: P = polytopes.permutahedron(5) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (28, 29) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (25, 29) sage: P = polytopes.cyclic_polytope(4,6) @@ -664,21 +668,37 @@ cdef class CombinatorialFace(SageObject): sage: face.ambient_H_indices() (4, 5, 7) + Add the indices of the equation:: + + sage: face.ambient_H_indices(add_equations=True) + (4, 5, 7) + .. SEEALSO:: :meth:`ambient_Hrepresentation`. """ - cdef size_t length + cdef size_t length, i + cdef size_t n_facets, n_equations + cdef tuple equations + + if add_equations and self._ambient_facets: + n_facets = len(self._ambient_facets) + n_equations = len(self._equations) + equations = tuple(smallInteger(i) + for i in range(n_facets, n_facets + n_equations)) + else: + equations = () + if not self._dual: # if not dual, the facet-representation corresponds to the coatom-representation length = self.set_coatom_rep() # fill self.coatom_repr_face return tuple(smallInteger(self.coatom_rep[i]) - for i in range(length)) + for i in range(length)) + equations else: # if dual, the facet-representation corresponds to the atom-representation length = self.set_atom_rep() # fill self.atom_repr_face return tuple(smallInteger(self.atom_rep[i]) - for i in range(length)) + for i in range(length)) + equations def Hrepr(self, names=True): r""" @@ -720,12 +740,16 @@ cdef class CombinatorialFace(SageObject): else: return self.ambient_H_indices() - def n_ambient_Hrepresentation(self): + def n_ambient_Hrepresentation(self, add_equations=True): r""" Return the length of the :meth:`CombinatorialFace.ambient_H_indices`. Might be faster than then using ``len``. + INPUT: + + - ``add_equations`` -- boolean (default: ``True``); whether or not to count the equations + EXAMPLES:: sage: P = polytopes.cube() @@ -734,6 +758,17 @@ cdef class CombinatorialFace(SageObject): sage: all(face.n_ambient_Hrepresentation() == len(face.ambient_Hrepresentation()) for face in it) True + Specifying whether to count the equations or not:: + + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: f.n_ambient_Hrepresentation(add_equations=True) + 3 + sage: f.n_ambient_Hrepresentation(add_equations=False) + 2 + TESTS:: sage: P = polytopes.cube() @@ -744,10 +779,11 @@ cdef class CombinatorialFace(SageObject): doctest:...: DeprecationWarning: n_Hrepr is deprecated. Please use n_ambient_Hrepresentation instead. See https://trac.sagemath.org/28614 for details. """ + cdef size_t n_equations = len(self._equations) if add_equations else 0 if not self._dual: - return smallInteger(self.set_coatom_rep()) + return smallInteger(self.set_coatom_rep() + n_equations) else: - return smallInteger(self.n_atom_rep()) + return smallInteger(self.n_atom_rep() + n_equations) n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index a1438030ea3..3e83ff5edb9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -469,7 +469,8 @@ cdef class FaceIterator_base(SageObject): sage: it = C.face_iter(dual=True) sage: n_faces_with_non_simplex_quotient = 1 sage: for face in it: - ....: if face.n_ambient_Hrepresentation() > C.dimension() - face.dimension() + 1: + ....: n_facets = face.n_ambient_Hrepresentation(add_equations=False) + ....: if n_facets > C.dimension() - face.dimension() + 1: ....: n_faces_with_non_simplex_quotient += 1 ....: else: ....: it.ignore_supfaces() diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 7a00f46ab19..1877ceab5fb 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -848,12 +848,12 @@ def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): if polyhedron.backend() in ('ppl',): # Equations before inequalities in Hrep. H_indices = tuple(range(n_equations)) - H_indices += tuple(x+n_equations for x in combinatorial_face.ambient_H_indices()) + H_indices += tuple(x+n_equations for x in combinatorial_face.ambient_H_indices(add_equations=False)) elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'polymake'): # Equations after the inequalities in Hrep. n_ieqs = polyhedron.n_inequalities() H_indices = tuple(range(n_ieqs, n_ieqs + n_equations)) - H_indices += tuple(x for x in combinatorial_face.ambient_H_indices()) + H_indices += tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) else: raise NotImplementedError("unknown backend") From d2ee51ed4e3267241e127e39a6e79d72548b5e95 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 14:14:52 +0200 Subject: [PATCH 369/706] meaningful doctest for ambient_H_indices --- .../combinatorial_polyhedron/combinatorial_face.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 77856af971b..a71b5024f62 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -670,8 +670,13 @@ cdef class CombinatorialFace(SageObject): Add the indices of the equation:: - sage: face.ambient_H_indices(add_equations=True) - (4, 5, 7) + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: next(it).ambient_H_indices(add_equations=True) + (28, 29, 30) + sage: next(it).ambient_H_indices(add_equations=False) + (25, 29) .. SEEALSO:: From 0111d1fa10c593d5be99d961901d3735e2aebc1a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 14:39:38 +0200 Subject: [PATCH 370/706] make add_equalities more stable --- .../combinatorial_face.pxd | 1 + .../combinatorial_face.pyx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 4c50daeaf55..0d7731f4265 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -24,6 +24,7 @@ cdef class CombinatorialFace(SageObject): # some copies from ``CombinatorialPolyhedron`` cdef tuple _ambient_Vrep, _ambient_facets, _equations + cdef size_t _n_equations, _n_ambient_facets # Atoms and coatoms are the vertices/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index a71b5024f62..65979ba866e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -182,6 +182,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names self._equations = it._equations + self._n_equations = len(self._equations) if self._equations else 0 self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -207,6 +208,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names self._equations = all_faces._equations + self._n_equations = len(self._equations) if self._equations else 0 self._initialized_from_face_lattice = True @@ -226,6 +228,10 @@ cdef class CombinatorialFace(SageObject): # Reverse the hash index in dual mode to respect inclusion of faces. self._hash_index = -self._hash_index - 1 + self._n_ambient_facets = self.atoms.n_faces() + else: + self._n_ambient_facets = self.coatoms.n_faces() + def _repr_(self): r""" Return a description of the combinatorial face. @@ -686,11 +692,10 @@ cdef class CombinatorialFace(SageObject): cdef size_t n_facets, n_equations cdef tuple equations - if add_equations and self._ambient_facets: - n_facets = len(self._ambient_facets) - n_equations = len(self._equations) + if add_equations and self._equations: equations = tuple(smallInteger(i) - for i in range(n_facets, n_facets + n_equations)) + for i in range(self._n_ambient_facets, + self._n_ambient_facets + self._n_equations)) else: equations = () @@ -784,7 +789,7 @@ cdef class CombinatorialFace(SageObject): doctest:...: DeprecationWarning: n_Hrepr is deprecated. Please use n_ambient_Hrepresentation instead. See https://trac.sagemath.org/28614 for details. """ - cdef size_t n_equations = len(self._equations) if add_equations else 0 + cdef size_t n_equations = self._n_equations if add_equations else 0 if not self._dual: return smallInteger(self.set_coatom_rep() + n_equations) else: From aafd615c13d45305339b99fefa18572f50986e0c Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Tue, 18 May 2021 16:47:47 +0200 Subject: [PATCH 371/706] multivariate polynomial rings should be callable with no argument --- src/sage/rings/polynomial/multi_polynomial_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 91b350b61da..c939e54ebc2 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -174,7 +174,7 @@ def __hash__(self): return hash((self.base_ring(), self.ngens(), self.variable_names(), self.term_order())) - def __call__(self, x, check=True): + def __call__(self, x=0, check=True): """ Convert ``x`` to an element of this multivariate polynomial ring, possibly non-canonically. From 78882fda4b40a5d4038f220ebdba1059f0518456 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 18:23:35 +0200 Subject: [PATCH 372/706] improved documentation and removed redundant definitions --- .../combinatorial_face.pyx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 65979ba866e..439a6f7d7bf 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -653,11 +653,22 @@ cdef class CombinatorialFace(SageObject): sage: P = polytopes.permutahedron(5) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices(add_equations=False) + sage: face = next(it) + sage: face.ambient_H_indices(add_equations=False) (28, 29) - sage: next(it).ambient_H_indices(add_equations=False) + sage: face2 = next(it) + sage: face2.ambient_H_indices(add_equations=False) (25, 29) + Add the indices of the equation:: + + sage: face.ambient_H_indices(add_equations=True) + (28, 29, 30) + sage: face2.ambient_H_indices(add_equations=True) + (25, 29, 30) + + Another example:: + sage: P = polytopes.cyclic_polytope(4,6) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter() @@ -674,22 +685,11 @@ cdef class CombinatorialFace(SageObject): sage: face.ambient_H_indices() (4, 5, 7) - Add the indices of the equation:: - - sage: P = polytopes.permutahedron(5) - sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices(add_equations=True) - (28, 29, 30) - sage: next(it).ambient_H_indices(add_equations=False) - (25, 29) - .. SEEALSO:: :meth:`ambient_Hrepresentation`. """ cdef size_t length, i - cdef size_t n_facets, n_equations cdef tuple equations if add_equations and self._equations: From 697ed2c5a5bf2d253416c78380b40b2e80a096ac Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Tue, 18 May 2021 19:38:20 +0200 Subject: [PATCH 373/706] #31836 : CDF() should return CDF.zero() --- src/sage/rings/complex_double.pyx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 411f5a3dea4..b2a94c1228e 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -277,7 +277,7 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): """ return r"\Bold{C}" - def __call__(self, x, im=None): + def __call__(self, x=None, im=None): """ Create a complex double using ``x`` and optionally an imaginary part ``im``. @@ -329,8 +329,19 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): True sage: b == CC(a) True + + TESTS:: + + Check that :trac:`31836` is fixed:: + + sage: a = CDF() ; a + 0.0 + sage: a.parent() + Complex Double Field """ # We implement __call__ to gracefully accept the second argument. + if x is None: + return self.zero() if im is not None: x = x, im return Parent.__call__(self, x) From f50e0ea22f697a718abf47e65d238d1fd560b8b4 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Tue, 18 May 2021 20:28:13 +0200 Subject: [PATCH 374/706] #31836 : typo --- src/sage/rings/complex_double.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index b2a94c1228e..15c738eac0d 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -330,7 +330,7 @@ cdef class ComplexDoubleField_class(sage.rings.ring.Field): sage: b == CC(a) True - TESTS:: + TESTS: Check that :trac:`31836` is fixed:: From 5e3f78502102d1659d1cc5379d2c60f75c19475d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 May 2021 11:28:21 -0700 Subject: [PATCH 375/706] build/pkgs/pynormaliz/install-requires.txt: Fix to 2.14 --- build/pkgs/pynormaliz/install-requires.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pynormaliz/install-requires.txt b/build/pkgs/pynormaliz/install-requires.txt index b1e222ae76c..ecc1ec0712b 100644 --- a/build/pkgs/pynormaliz/install-requires.txt +++ b/build/pkgs/pynormaliz/install-requires.txt @@ -1 +1 @@ -pynormaliz ==2.12 +pynormaliz ==2.14 From c5550ea642296628fda17a8dd5214ffd03d097aa Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Tue, 18 May 2021 20:45:55 +0200 Subject: [PATCH 376/706] 31795: use .zero as default factory This is about 10% faster. --- src/sage/rings/polynomial/multi_polynomial.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 9ad3f77ffb5..13ba2fec3e6 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -845,8 +845,8 @@ cdef class MPolynomial(CommutativeRingElement): {0: 1, 1: a, 3: c, 5: a^2*c + b*c, 6: a^6 + b^3} """ from collections import defaultdict - d = defaultdict(self.parent()) - for c,m in self: + d = defaultdict(self.parent().zero) + for c, m in self: d[m.degree()] += c*m return dict(d) From 58ae38a63634067053a002879b3d32d227a76168 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 15:31:30 +0200 Subject: [PATCH 377/706] fix normaliz triangulation after upgrade --- src/sage/geometry/polyhedron/backend_normaliz.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 95c489c8669..b55a23fa078 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -258,7 +258,8 @@ def _nmz_result(self, normaliz_cone, property): sage: p = Polyhedron(vertices=[(0,0),(1,1),(a,3),(-1,a**2)], rays=[(-1,-a)], backend='normaliz') # optional - pynormaliz sage: sorted(p._nmz_result(p._normaliz_cone, 'VerticesOfPolyhedron')) # optional - pynormaliz [[-1, a^2, 1], [1, 1, 1], [a, 3, 1]] - sage: sorted(p._nmz_result(p._normaliz_cone, 'TriangulationGenerators')) # optional - pynormaliz + sage: triangulation_generators = p._nmz_result(p._normaliz_cone, 'Triangulation')[1] # optional - pynormaliz + sage: sorted(triangulation_generators) # optional - pynormaliz [[-a^2, -3, 0], [-1, a^2, 1], [0, 0, 1], [1, 1, 1], [a, 3, 1]] sage: p._nmz_result(p._normaliz_cone, 'AffineDim') == 2 # optional - pynormaliz True @@ -1592,19 +1593,18 @@ def _triangulate_normaliz(self): # Compute the triangulation. assert cone - nmz_triangulation = self._nmz_result(cone, "Triangulation") # Normaliz does not guarantee that the order of generators is kept during # computation of the triangulation. # Those are the generators that the indices of the triangulation correspond to: - nmz_new_generators = self._nmz_result(cone, "TriangulationGenerators") + nmz_triangulation, nmz_triangulation_generators = self._nmz_result(cone, "Triangulation") base_ring = self.base_ring() v_list = self.vertices_list() r_list = self.rays_list() new_to_old = {} - for i, g in enumerate(nmz_new_generators): + for i, g in enumerate(nmz_triangulation_generators): if self.is_compact(): d = base_ring(g[-1]) vertex = [base_ring(x) / d for x in g[:-1]] From 67fb789e185ba7946ec4c55d333e7328ebaf9ddb Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 18 May 2021 12:33:55 -0700 Subject: [PATCH 378/706] switched the fix to free modules homspaces --- src/sage/modules/free_module_homspace.py | 2 ++ src/sage/structure/parent.pyx | 33 ------------------------ 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 77cb4ff9891..699db08caad 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -187,6 +187,8 @@ def __call__(self, A, check=True): # Let us hope that FreeModuleMorphism knows to handle # that case pass + if not(self.codomain().has_coerce_map_from(self.domain())) and not(A.is_zero()): + raise ValueError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") return free_module_morphism.FreeModuleMorphism(self, A) @cached_method diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index e6102795b17..a5a16bacdd4 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1334,37 +1334,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): Traceback (most recent call last): ... TypeError: natural coercion morphism from Rational Field to Integer Ring not defined - - Implicit codomain: If no codomain is specified, one is built from images of the - canonical basis. A ``ValueError`` is raised if one gives vectors defined - over a smaller ring than the domain. - - sage: V = QQ^3; [a, b, c] = V.basis() - sage: V.hom([b, c, a]) - Vector space morphism represented by the matrix: - [0 1 0] - [0 0 1] - [1 0 0] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field - sage: V.hom(identity_matrix(3)) - Traceback (most recent call last): - ... - ValueError: The image of basis vectors must be defined over a ring containing the base ring of the domain - sage: V.hom(identity_matrix(QQ, 3)) - Vector space morphism represented by the matrix: - [1 0 0] - [0 1 0] - [0 0 1] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field - sage: V.hom(identity_matrix(3), V) - Vector space morphism represented by the matrix: - [1 0 0] - [0 1 0] - [0 0 1] - Domain: Vector space of dimension 3 over Rational Field - Codomain: Vector space of dimension 3 over Rational Field """ if isinstance(im_gens, Parent): return self.Hom(im_gens).natural_map() @@ -1372,8 +1341,6 @@ cdef class Parent(sage.structure.category_object.CategoryObject): if codomain is None: im_gens = Sequence(im_gens) codomain = im_gens.universe() - if not(self.base_ring().is_subring(codomain.base_ring())): - raise ValueError("The base ring of the domain must be contained in the base ring of the codomain") if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) # Not all homsets accept category/check/base_map as arguments From a5276e9dda61b8bb0c29ad30c51517a0685cc710 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 19 May 2021 13:37:48 +0100 Subject: [PATCH 379/706] Fixed minor typos in documentation. --- src/sage/numerical/gauss_legendre.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index fba5a931d2e..2fcc2d7c797 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -53,7 +53,7 @@ def nodes(degree,prec): A list of (node,weight) pairs. - EXAMPLES: + EXAMPLES:: The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. The weights can be computed by a straightforward formula (note that evaluating @@ -183,11 +183,11 @@ def integrate_vector(f,prec,epsilon=None): INPUT: - - `f` -- callable. Vector-valued integrand. + - ``f`` -- callable. Vector-valued integrand. - - `prec` -- integer. Binary precision to be used. + - ``prec`` -- integer. Binary precision to be used. - - `epsilon` -- Multiprecision float. Target error bound. + - ``epsilon`` -- Multiprecision float. Target error bound. OUTPUT: From 25de863dde75e7ab0e4997e14d74cfd6ac7e797b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 21:53:56 +0200 Subject: [PATCH 380/706] pycodestyle and fix order of equations of faces --- src/sage/geometry/polyhedron/backend_cdd.py | 4 +-- src/sage/geometry/polyhedron/base.py | 16 +++++----- src/sage/geometry/polyhedron/face.py | 34 +++++++++++++-------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 44236fee102..d839bc3ca5e 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -252,8 +252,8 @@ def _init_from_cdd_output(self, cddout): An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0) sage: [x.ambient_Hrepresentation() for x in P.facets()] - [(An equation (1, 1) x - 3 == 0, An inequality (1, 0) x - 1 >= 0), - (An equation (1, 1) x - 3 == 0, An inequality (0, 1) x - 1 >= 0)] + [(An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0), + (An inequality (0, 1) x - 1 >= 0, An equation (1, 1) x - 3 == 0)] """ cddout = cddout.splitlines() diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..a2293ec9c8b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -10185,10 +10185,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 1877ceab5fb..7e0780fea79 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -614,22 +614,32 @@ def _repr_(self): if self.n_vertices() > 0: desc += ' defined as the convex hull of ' desc += repr(self.n_vertices()) - if self.n_vertices() == 1: desc += ' vertex' - else: desc += ' vertices' + if self.n_vertices() == 1: + desc += ' vertex' + else: + desc += ' vertices' if self.n_rays() > 0: - if self.n_lines() > 0: desc += ", " - else: desc += " and " + if self.n_lines() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_rays()) - if self.n_rays() == 1: desc += ' ray' - else: desc += ' rays' + if self.n_rays() == 1: + desc += ' ray' + else: + desc += ' rays' if self.n_lines() > 0: - if self.n_rays() > 0: desc += ", " - else: desc += " and " + if self.n_rays() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_lines()) - if self.n_lines() == 1: desc += ' line' - else: desc += ' lines' + if self.n_lines() == 1: + desc += ' line' + else: + desc += ' lines' return desc @@ -852,8 +862,8 @@ def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'polymake'): # Equations after the inequalities in Hrep. n_ieqs = polyhedron.n_inequalities() - H_indices = tuple(range(n_ieqs, n_ieqs + n_equations)) - H_indices += tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) + H_indices = tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) + H_indices += tuple(range(n_ieqs, n_ieqs + n_equations)) else: raise NotImplementedError("unknown backend") From 24c80038bdb56340fc165e96510c41609450eaaa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 19 May 2021 18:37:58 -0700 Subject: [PATCH 381/706] build/pkgs/mongodb: Split out from perl_cpan_polymake_prereq --- .../pkgs/perl_cpan_polymake_prereq/distros/cpan.txt | 7 ++++++- .../perl_cpan_polymake_prereq/distros/debian.txt | 1 - .../perl_cpan_polymake_prereq/distros/fedora.txt | 9 +++++++-- .../perl_cpan_polymake_prereq/distros/freebsd.txt | 7 ++++++- .../perl_cpan_polymake_prereq/distros/gentoo.txt | 8 +++++++- build/pkgs/perl_mongodb/SPKG.rst | 13 +++++++++++++ build/pkgs/perl_mongodb/distros/cpan.txt | 1 + build/pkgs/perl_mongodb/distros/debian.txt | 1 + build/pkgs/perl_mongodb/distros/fedora.txt | 1 + build/pkgs/perl_mongodb/distros/freebsd.txt | 1 + build/pkgs/perl_mongodb/distros/gentoo.txt | 1 + build/pkgs/perl_mongodb/spkg-configure.m4 | 9 +++++++++ build/pkgs/perl_mongodb/spkg-install | 6 ++++++ build/pkgs/perl_mongodb/type | 1 + build/pkgs/polymake/SPKG.rst | 6 +++--- 15 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 build/pkgs/perl_mongodb/SPKG.rst create mode 100644 build/pkgs/perl_mongodb/distros/cpan.txt create mode 100644 build/pkgs/perl_mongodb/distros/debian.txt create mode 100644 build/pkgs/perl_mongodb/distros/fedora.txt create mode 100644 build/pkgs/perl_mongodb/distros/freebsd.txt create mode 100644 build/pkgs/perl_mongodb/distros/gentoo.txt create mode 100644 build/pkgs/perl_mongodb/spkg-configure.m4 create mode 100755 build/pkgs/perl_mongodb/spkg-install create mode 100644 build/pkgs/perl_mongodb/type diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt index 13e1dc9fc9e..fbd93fc89a2 100644 --- a/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/cpan.txt @@ -1 +1,6 @@ -XML::Writer XML::LibXML XML::LibXSLT File::Slurp JSON SVG MongoDB +XML::Writer +XML::LibXML +XML::LibXSLT +File::Slurp +JSON +SVG diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt index 34c5682b266..216a40ab2c9 100644 --- a/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/debian.txt @@ -7,4 +7,3 @@ libjson-perl libsvg-perl libterm-readkey-perl libterm-readline-gnu-perl -libmongodb-perl diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt index 1ea99958152..89a8a7622bf 100644 --- a/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/fedora.txt @@ -1,2 +1,7 @@ -perl-ExtUtils-Embed perl-File-Slurp perl-JSON perl-MongoDB perl-Term-ReadLine-Gnu -perl-XML-Writer perl-XML-LibXML perl-XML-LibXSLT +perl-ExtUtils-Embed +perl-File-Slurp +perl-JSON +perl-Term-ReadLine-Gnu +perl-XML-Writer +perl-XML-LibXML +perl-XML-LibXSLT diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/freebsd.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/freebsd.txt index ad6913a88d7..9b2b62bccd9 100644 --- a/build/pkgs/perl_cpan_polymake_prereq/distros/freebsd.txt +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/freebsd.txt @@ -1 +1,6 @@ -textproc/p5-XML-Writer textproc/p5-XML-LibXML textproc/p5-XML-LibXSLT devel/p5-File-Slurp converters/p5-JSON textproc/p5-SVG databases/p5-MongoDB +textproc/p5-XML-Writer +textproc/p5-XML-LibXML +textproc/p5-XML-LibXSLT +devel/p5-File-Slurp +converters/p5-JSON +textproc/p5-SVG diff --git a/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt b/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt index ecfce6042c2..67835e72530 100644 --- a/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt +++ b/build/pkgs/perl_cpan_polymake_prereq/distros/gentoo.txt @@ -1 +1,7 @@ -XML-Writer XML-LibXML XML-LibXSLT File-Slurp dev-perl/Term-ReadLine-Gnu JSON SVG dev-perl/MongoDB +XML-Writer +XML-LibXML +XML-LibXSLT +File-Slurp +dev-perl/Term-ReadLine-Gnu +JSON +SVG diff --git a/build/pkgs/perl_mongodb/SPKG.rst b/build/pkgs/perl_mongodb/SPKG.rst new file mode 100644 index 00000000000..89384fad4b6 --- /dev/null +++ b/build/pkgs/perl_mongodb/SPKG.rst @@ -0,0 +1,13 @@ +perl_mongodb: A prerequisite for polymake's PolyDB feature +========================================================== + +Description +----------- + +This script package represents the Perl package MongoDB, which is needed for +the PolyDB feature of polymake. + +License +------- + +Various free software licenses diff --git a/build/pkgs/perl_mongodb/distros/cpan.txt b/build/pkgs/perl_mongodb/distros/cpan.txt new file mode 100644 index 00000000000..df37c12a455 --- /dev/null +++ b/build/pkgs/perl_mongodb/distros/cpan.txt @@ -0,0 +1 @@ +MongoDB diff --git a/build/pkgs/perl_mongodb/distros/debian.txt b/build/pkgs/perl_mongodb/distros/debian.txt new file mode 100644 index 00000000000..1c063ceb440 --- /dev/null +++ b/build/pkgs/perl_mongodb/distros/debian.txt @@ -0,0 +1 @@ +libmongodb-perl diff --git a/build/pkgs/perl_mongodb/distros/fedora.txt b/build/pkgs/perl_mongodb/distros/fedora.txt new file mode 100644 index 00000000000..c6b5b90f9f0 --- /dev/null +++ b/build/pkgs/perl_mongodb/distros/fedora.txt @@ -0,0 +1 @@ +perl-MongoDB diff --git a/build/pkgs/perl_mongodb/distros/freebsd.txt b/build/pkgs/perl_mongodb/distros/freebsd.txt new file mode 100644 index 00000000000..ba89f051ecb --- /dev/null +++ b/build/pkgs/perl_mongodb/distros/freebsd.txt @@ -0,0 +1 @@ +databases/p5-MongoDB diff --git a/build/pkgs/perl_mongodb/distros/gentoo.txt b/build/pkgs/perl_mongodb/distros/gentoo.txt new file mode 100644 index 00000000000..c174107689f --- /dev/null +++ b/build/pkgs/perl_mongodb/distros/gentoo.txt @@ -0,0 +1 @@ +dev-perl/MongoDB diff --git a/build/pkgs/perl_mongodb/spkg-configure.m4 b/build/pkgs/perl_mongodb/spkg-configure.m4 new file mode 100644 index 00000000000..e2b737203a2 --- /dev/null +++ b/build/pkgs/perl_mongodb/spkg-configure.m4 @@ -0,0 +1,9 @@ +SAGE_SPKG_CONFIGURE( + [perl_mongodb], [ + m4_pushdef([MODULES], m4_include(build/pkgs/perl_mongodb/distros/cpan.txt)) + AX_PROG_PERL_MODULES(MODULES, + [], + [sage_spkg_install_perl_mongodb=yes + ] + ) +]) diff --git a/build/pkgs/perl_mongodb/spkg-install b/build/pkgs/perl_mongodb/spkg-install new file mode 100755 index 00000000000..7f087781bb9 --- /dev/null +++ b/build/pkgs/perl_mongodb/spkg-install @@ -0,0 +1,6 @@ +#! /usr/bin/env bash +echo Error: The optional prerequisite perl_mongodb of the package polymake are not installed. +echo Please install CPAN package $(cat distros/cpan.txt) +echo manually, either using the system package recommended by ./configure +echo or directly from CPAN. +exit 1 diff --git a/build/pkgs/perl_mongodb/type b/build/pkgs/perl_mongodb/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/perl_mongodb/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/polymake/SPKG.rst b/build/pkgs/polymake/SPKG.rst index c8ab646b67a..20bb07d0ec6 100644 --- a/build/pkgs/polymake/SPKG.rst +++ b/build/pkgs/polymake/SPKG.rst @@ -28,11 +28,11 @@ Polymake needs a working installation of Perl, including its shared library and some modules (XML::Writer XML::LibXML XML::LibXSLT Term::ReadLine::Gnu JSON SVG). The Polymake interface in Sage additionally needs File::Slurp. For full functionality including -polymake's polyDB, also the Perl module MongoDB is required. +polymake's polyDB, also the Perl module MongoDB is needed. These are not provided by a Sage package. The script package -perl_cpan_polymake_prereq will signal an error at build time if these -prerequisites are not met. +perl_cpan_polymake_prereq will signal an error at build time if the +required prerequisites are not met. The configure script will inform you about the equivalent system packages that you should install. Otherwise, you can use CPAN (see From fdbe95f9cf2b4787a59a459231074a7c528397f3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 10:48:00 +0200 Subject: [PATCH 382/706] use cython.parallel.threadid instead of omp_get_thread_num --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fd6280b48d7..c6b07ec048f 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -185,8 +185,7 @@ from .base cimport CombinatorialPolyhedron from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace from .face_list_data_structure cimport * -from cython.parallel cimport prange -cimport openmp +from cython.parallel cimport prange, threadid cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -1368,9 +1367,9 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=num_threads, nogil=True): - _parallel_f_vector(structures[openmp.omp_get_thread_num()], + _parallel_f_vector(structures[threadid()], parallelization_depth, - parallel_structs[openmp.omp_get_thread_num()], + parallel_structs[threadid()], i) # Gather the results. From faf86c3332681c882d641e4c8dd80102848f0e2f Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Thu, 20 May 2021 19:52:57 +0200 Subject: [PATCH 383/706] 31795: further performance tweaks for homogeneous_components --- src/sage/rings/polynomial/multi_polynomial.pyx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 13ba2fec3e6..88a93f61e72 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -844,11 +844,17 @@ cdef class MPolynomial(CommutativeRingElement): sage: (a^6 + b^3 + b*c + a^2*c + c + a + 1).homogeneous_components() {0: 1, 1: a, 3: c, 5: a^2*c + b*c, 6: a^6 + b^3} """ + cdef ETuple e from collections import defaultdict - d = defaultdict(self.parent().zero) - for c, m in self: - d[m.degree()] += c*m - return dict(d) + d = defaultdict(dict) + if self._parent.term_order()._weights: + for c, m in self: + d[m.degree()][m.exponents()[0]] = c + else: + # Otherwise it is unweighted, so we use a faster implementation + for e, c in self.iterator_exp_coeff(): + d[e.unweighted_degree()][e] = c + return {k: self._parent(d[k]) for k in d} cpdef _mod_(self, other): """ From da0536b759b98143a162aef6948a489b4c32aa25 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 21 May 2021 11:23:31 +0200 Subject: [PATCH 384/706] Replace reach with _language_naive --- src/sage/combinat/words/morphism.py | 32 ++++------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index d9a0db1b2fe..ca927d607b5 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3446,7 +3446,8 @@ def impl(): if w is None: w = self._morph - f = self.restrict_domain(self.reach(w)) + reach = self._language_naive(2, self._domain(w)) + f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain g, _, k, _ = f.simplify_injective() g._codomain = g._domain @@ -3502,7 +3503,8 @@ def infinite_repetitions_growing(self, w=None): """ if w is None: w = self._morph - f = self.restrict_domain(self.reach(w)) + reach = self._language_naive(2, self._domain(w)) + f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain g, _, k, _ = f.simplify_injective() g._codomain = g._domain @@ -3540,32 +3542,6 @@ def infinite_repetitions_growing(self, w=None): return result - def reach(self, w): - r""" - Return the set of letters which occur in words of - `\{m^n(w) | n \ge 0\}`, where `m` is this morphism and `w` is a word - (finite iterable is enough) inputted as a parameter. - - Requires this morphism to be an endomorphism. - - EXAMPLES:: - - sage: sorted(WordMorphism('a->ac,b->ce,c->bd,d->d,e->').reach('c')) - ['b', 'c', 'd', 'e'] - """ - if not self.is_endomorphism(): - raise TypeError(f'self ({self}) is not an endomorphism') - - visited = set(w) - todo = list(visited) - while todo: - a = todo.pop() - for b in self.image(a): - if b not in visited: - visited.add(b) - todo.append(b) - return visited - def simplify(self, Z=None): r""" If this morphism is simplifiable, return morphisms `h` and `k` such that From 9a54a5bc376c297c3c7cff32f3c5f07f132505ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 May 2021 11:43:38 +0200 Subject: [PATCH 385/706] some details in affine permutations --- src/sage/combinat/affine_permutation.py | 127 ++++++++++++------------ 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 114895b417c..8caffd1a81e 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -187,7 +187,7 @@ def apply_simple_reflection(self, i, side='right'): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.apply_simple_reflection(3) Type A affine permutation with window [3, -1, 6, 0, 5, 4, 10, 9] sage: p.apply_simple_reflection(11) @@ -208,7 +208,7 @@ def __call__(self, i): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.value(1) #indirect doctest 3 @@ -229,7 +229,7 @@ def is_i_grassmannian(self, i=0, side="right"): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.is_i_grassmannian() False @@ -250,7 +250,7 @@ def index_set(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: A.index_set() (0, 1, 2, 3, 4, 5, 6, 7) """ @@ -266,7 +266,7 @@ def lower_covers(self,side="right"): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.lower_covers() [Type A affine permutation with window [-1, 3, 0, 6, 5, 4, 10, 9], @@ -283,7 +283,7 @@ def is_one(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.is_one() False @@ -299,7 +299,7 @@ def reduced_word(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.reduced_word() [0, 7, 4, 1, 0, 7, 5, 4, 2, 1] @@ -323,7 +323,7 @@ def signature(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.signature() 1 @@ -337,7 +337,7 @@ def to_weyl_group_element(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.to_weyl_group_element() [ 0 -1 0 1 0 0 1 0] @@ -367,7 +367,7 @@ def grassmannian_quotient(self, i=0, side='right'): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: gq=p.grassmannian_quotient() sage: gq @@ -504,7 +504,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.apply_simple_reflection_right(3) Type A affine permutation with window [3, -1, 6, 0, 5, 4, 10, 9] sage: p.apply_simple_reflection_right(11) @@ -534,7 +534,7 @@ def apply_simple_reflection_left(self, i): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.apply_simple_reflection_left(3) Type A affine permutation with window [4, -1, 0, 6, 5, 3, 10, 9] sage: p.apply_simple_reflection_left(11) @@ -573,7 +573,7 @@ def apply_simple_reflection_left(self, i): l.append(self[m]) return type(self)(self.parent(), l, check=False) - def has_right_descent(self, i): + def has_right_descent(self, i) -> bool: r""" Determine whether there is a descent at ``i``. @@ -583,7 +583,7 @@ def has_right_descent(self, i): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.has_right_descent(1) True sage: p.has_right_descent(9) @@ -593,7 +593,7 @@ def has_right_descent(self, i): """ return self.value(i)>self.value(i+1) - def has_left_descent(self, i): + def has_left_descent(self, i) -> bool: r""" Determine whether there is a descent at ``i``. @@ -603,7 +603,7 @@ def has_left_descent(self, i): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.has_left_descent(1) True sage: p.has_left_descent(9) @@ -640,7 +640,7 @@ def flip_automorphism(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.flip_automorphism() Type A affine permutation with window [0, -1, 5, 4, 3, 9, 10, 6] @@ -655,7 +655,7 @@ def promotion(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.promotion() Type A affine permutation with window [2, 4, 0, 1, 7, 6, 5, 11] @@ -685,7 +685,7 @@ def maximal_cyclic_factor(self, typ='decreasing', side='right', verbose=False): EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.maximal_cyclic_factor() [7, 5, 4, 2, 1] sage: p.maximal_cyclic_factor(side='left') @@ -751,7 +751,7 @@ def maximal_cyclic_decomposition(self, typ='decreasing', side='right', verbose=F EXAMPLES:: - sage: p=AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) + sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.maximal_cyclic_decomposition() [[0, 7], [4, 1, 0], [7, 5, 4, 2, 1]] sage: p.maximal_cyclic_decomposition(side='left') @@ -763,7 +763,7 @@ def maximal_cyclic_decomposition(self, typ='decreasing', side='right', verbose=F TESTS:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: S=p.maximal_cyclic_decomposition() sage: p==prod(A.from_word(l) for l in S) @@ -819,7 +819,7 @@ def to_lehmer_code(self, typ='decreasing', side='right'): EXAMPLES:: sage: import itertools - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: orders = ('increasing','decreasing') sage: sides = ('left','right') @@ -886,7 +886,7 @@ def is_fully_commutative(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.is_fully_commutative() False @@ -927,7 +927,7 @@ def to_bounded_partition(self, typ='decreasing', side='right'): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',2,1]) + sage: A = AffinePermutationGroup(['A',2,1]) sage: p=A.from_lehmer_code([4,1,0]) sage: p.to_bounded_partition() [2, 1, 1, 1] @@ -950,7 +950,7 @@ def to_core(self, typ='decreasing', side='right'): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',2,1]) + sage: A = AffinePermutationGroup(['A',2,1]) sage: p=A.from_lehmer_code([4,1,0]) sage: p.to_bounded_partition() [2, 1, 1, 1] @@ -975,7 +975,7 @@ def to_dominant(self, typ='decreasing', side='right'): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.to_dominant() Type A affine permutation with window [-2, -1, 1, 3, 4, 8, 10, 13] @@ -1006,11 +1006,11 @@ def tableau_of_word(self, w, typ='decreasing', side='right', alpha=None): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: p.tableau_of_word(p.reduced_word()) [[], [1, 6, 9], [2, 7, 10], [], [3], [4, 8], [], [5]] - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: w=p.reduced_word() sage: w @@ -1149,7 +1149,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: - sage: C=AffinePermutationGroup(['C',4,1]) + sage: C = AffinePermutationGroup(['C',4,1]) sage: x=C([-1,5,3,7]) sage: for i in C.index_set(): x.apply_simple_reflection_right(i) Type C affine permutation with window [1, 5, 3, 7] @@ -1225,7 +1225,7 @@ def apply_simple_reflection_left(self, i): l.append(self[m]) return type(self)(self.parent(), l, check=False) - def has_right_descent(self, i): + def has_right_descent(self, i) -> bool: r""" Determine whether there is a descent at index ``i``. @@ -1246,7 +1246,7 @@ def has_right_descent(self, i): """ return self.value(i) > self.value(i+1) - def has_left_descent(self, i): + def has_left_descent(self, i) -> bool: r""" Determine whether there is a descent at ``i``. @@ -1329,7 +1329,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: - sage: B=AffinePermutationGroup(['B',4,1]) + sage: B = AffinePermutationGroup(['B',4,1]) sage: p=B([-5,1,6,-2]) sage: p.apply_simple_reflection_right(1) Type B affine permutation with window [1, -5, 6, -2] @@ -1359,7 +1359,7 @@ def apply_simple_reflection_left(self, i): EXAMPLES:: - sage: B=AffinePermutationGroup(['B',4,1]) + sage: B = AffinePermutationGroup(['B',4,1]) sage: p=B([-5,1,6,-2]) sage: p.apply_simple_reflection_left(0) Type B affine permutation with window [-5, -2, 6, 1] @@ -1409,7 +1409,7 @@ def apply_simple_reflection_left(self, i): l.append(self[m]) return type(self)(self.parent(), l, check=False) - def has_right_descent(self, i): + def has_right_descent(self, i) -> bool: r""" Determines whether there is a descent at index ``i``. @@ -1428,7 +1428,7 @@ def has_right_descent(self, i): return self.value(-2) > self.value(1) return self.value(i) > self.value(i+1) - def has_left_descent(self, i): + def has_left_descent(self, i) -> bool: r""" Determines whether there is a descent at ``i``. @@ -1438,7 +1438,7 @@ def has_left_descent(self, i): EXAMPLES:: - sage: B=AffinePermutationGroup(['B',4,1]) + sage: B = AffinePermutationGroup(['B',4,1]) sage: p=B([-5,1,6,-2]) sage: [p.has_left_descent(i) for i in B.index_set()] [True, True, False, False, True] @@ -1497,7 +1497,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: - sage: D=AffinePermutationGroup(['D',4,1]) + sage: D = AffinePermutationGroup(['D',4,1]) sage: p=D([1,-6,5,-2]) sage: p.apply_simple_reflection_right(0) Type D affine permutation with window [6, -1, 5, -2] @@ -1530,7 +1530,7 @@ def apply_simple_reflection_left(self, i): EXAMPLES:: - sage: D=AffinePermutationGroup(['D',4,1]) + sage: D = AffinePermutationGroup(['D',4,1]) sage: p=D([1,-6,5,-2]) sage: p.apply_simple_reflection_left(0) Type D affine permutation with window [-2, -6, 5, 1] @@ -1584,7 +1584,7 @@ def apply_simple_reflection_left(self, i): l.append(self[m]) return type(self)(self.parent(), l, check=False) - def has_right_descent(self, i): + def has_right_descent(self, i) -> bool: r""" Determine whether there is a descent at index ``i``. @@ -1594,7 +1594,7 @@ def has_right_descent(self, i): EXAMPLES:: - sage: D=AffinePermutationGroup(['D',4,1]) + sage: D = AffinePermutationGroup(['D',4,1]) sage: p=D([1,-6,5,-2]) sage: [p.has_right_descent(i) for i in D.index_set()] [True, True, False, True, False] @@ -1605,7 +1605,7 @@ def has_right_descent(self, i): return self.value(i) > self.value(i+2) return self.value(i) > self.value(i+1) - def has_left_descent(self, i): + def has_left_descent(self, i) -> bool: r""" Determine whether there is a descent at ``i``. @@ -1615,7 +1615,7 @@ def has_left_descent(self, i): EXAMPLES:: - sage: D=AffinePermutationGroup(['D',4,1]) + sage: D = AffinePermutationGroup(['D',4,1]) sage: p=D([1,-6,5,-2]) sage: [p.has_left_descent(i) for i in D.index_set()] [True, True, False, True, True] @@ -1670,7 +1670,7 @@ def value(self, i, base_window=False): EXAMPLES:: - sage: G=AffinePermutationGroup(['G',2,1]) + sage: G = AffinePermutationGroup(['G',2,1]) sage: p=G([2, 10, -5, 12, -3, 5]) sage: [p.value(i) for i in [1..12]] [2, 10, -5, 12, -3, 5, 8, 16, 1, 18, 3, 11] @@ -1706,7 +1706,7 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: - sage: G=AffinePermutationGroup(['G',2,1]) + sage: G = AffinePermutationGroup(['G',2,1]) sage: p=G([2, 10, -5, 12, -3, 5]) sage: p.apply_simple_reflection_right(0) Type G affine permutation with window [-9, -1, -5, 12, 8, 16] @@ -1745,7 +1745,7 @@ def apply_simple_reflection_left(self, i): EXAMPLES:: - sage: G=AffinePermutationGroup(['G',2,1]) + sage: G = AffinePermutationGroup(['G',2,1]) sage: p=G([2, 10, -5, 12, -3, 5]) sage: p.apply_simple_reflection_left(0) Type G affine permutation with window [0, 10, -7, 14, -3, 7] @@ -1786,7 +1786,7 @@ def apply_simple_reflection_left(self, i): l.append(self[m]) return type(self)(self.parent(), l, check=False) - def has_right_descent(self, i): + def has_right_descent(self, i) -> bool: r""" Determines whether there is a descent at index `i`. @@ -1807,7 +1807,7 @@ def has_right_descent(self, i): return self.value(0) > self.value(2) return self.value(i) > self.value(i+1) - def has_left_descent(self, i): + def has_left_descent(self, i) -> bool: r""" Determines whether there is a descent at ``i``. @@ -1969,19 +1969,19 @@ def AffinePermutationGroup(cartan_type): We can also form affine permutation groups in types `B`, `C`, `D`, and `G`:: - sage: B=AffinePermutationGroup(['B',4,1]) + sage: B = AffinePermutationGroup(['B',4,1]) sage: B.an_element() Type B affine permutation with window [-1, 3, 4, 11] - sage: C=AffinePermutationGroup(['C',4,1]) + sage: C = AffinePermutationGroup(['C',4,1]) sage: C.an_element() Type C affine permutation with window [2, 3, 4, 10] - sage: D=AffinePermutationGroup(['D',4,1]) + sage: D = AffinePermutationGroup(['D',4,1]) sage: D.an_element() Type D affine permutation with window [-1, 3, 11, 5] - sage: G=AffinePermutationGroup(['G',2,1]) + sage: G = AffinePermutationGroup(['G',2,1]) sage: G.an_element() Type G affine permutation with window [0, 4, -1, 8, 3, 7] """ @@ -2054,7 +2054,7 @@ def _test_coxeter_relations(self, **options): TESTS:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: A._test_coxeter_relations() """ tester = self._tester(**options) @@ -2094,7 +2094,7 @@ def weyl_group(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: A.weyl_group() Weyl Group of type ['A', 7, 1] (as a matrix group acting on the root space) """ @@ -2106,7 +2106,7 @@ def classical(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: A.classical() Symmetric group of order 8! as a permutation group """ @@ -2221,7 +2221,7 @@ def from_word(self, w): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: A.from_word([0, 7, 4, 1, 0, 7, 5, 4, 2, 1]) Type A affine permutation with window [3, -1, 0, 6, 5, 4, 10, 9] @@ -2235,7 +2235,7 @@ def _an_element_(self): EXAMPLES:: - sage: A=AffinePermutationGroup(['A',7,1]) + sage: A = AffinePermutationGroup(['A',7,1]) sage: p=A([3, -1, 0, 6, 5, 4, 10, 9]) sage: A.from_word([0, 7, 4, 1, 0, 7, 5, 4, 2, 1]) Type A affine permutation with window [3, -1, 0, 6, 5, 4, 10, 9] @@ -2261,7 +2261,7 @@ def one(self): TESTS:: - sage: A=AffinePermutationGroup(['A',5,1]) + sage: A = AffinePermutationGroup(['A',5,1]) sage: A==loads(dumps(A)) True sage: TestSuite(A).run() @@ -2363,7 +2363,7 @@ def one(self): EXAMPLES:: sage: ct=CartanType(['C',4,1]) - sage: C=AffinePermutationGroup(ct) + sage: C = AffinePermutationGroup(ct) sage: C.one() Type C affine permutation with window [1, 2, 3, 4] sage: C.one()*C.one()==C.one() @@ -2371,7 +2371,7 @@ def one(self): TESTS:: - sage: C=AffinePermutationGroup(['C',4,1]) + sage: C = AffinePermutationGroup(['C',4,1]) sage: C==loads(dumps(C)) True sage: TestSuite(C).run() @@ -2388,12 +2388,6 @@ class AffinePermutationGroupTypeB(AffinePermutationGroupTypeC): #------------------------ Element = AffinePermutationTypeB -class AffinePermutationGroupTypeC(AffinePermutationGroupTypeC): - #------------------------ - #Type-specific methods. - #(Methods in all types, but with specific definition.) - #------------------------ - Element = AffinePermutationTypeC class AffinePermutationGroupTypeD(AffinePermutationGroupTypeC): #------------------------ @@ -2402,6 +2396,7 @@ class AffinePermutationGroupTypeD(AffinePermutationGroupTypeC): #------------------------ Element = AffinePermutationTypeD + class AffinePermutationGroupTypeG(AffinePermutationGroupGeneric): #------------------------ #Type-specific methods. @@ -2419,7 +2414,7 @@ def one(self): TESTS:: - sage: G=AffinePermutationGroup(['G',2,1]) + sage: G = AffinePermutationGroup(['G',2,1]) sage: G==loads(dumps(G)) True sage: TestSuite(G).run() From 1e3fcfda334e3fec694f8a2bc273347869d7017d Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Sat, 22 May 2021 13:57:34 +0200 Subject: [PATCH 386/706] Trac 31845: fix norm and trace for inexact zero in Z_q --- .../libs/linkages/padics/unram_shared.pxi | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/linkages/padics/unram_shared.pxi b/src/sage/libs/linkages/padics/unram_shared.pxi index 901514ec168..bcd76ad3123 100644 --- a/src/sage/libs/linkages/padics/unram_shared.pxi +++ b/src/sage/libs/linkages/padics/unram_shared.pxi @@ -129,6 +129,12 @@ def norm_unram(self, base = None): 4*7^2 + 7^3 + O(7^22) sage: b*b.frobenius() 4*7^2 + 7^3 + O(7^22) + + Check that :trac:`31845` is fixed:: + + sage: R. = Zq(4) + sage: (a - a).norm() + O(2^20) """ if base is not None: if base is self.parent(): @@ -138,7 +144,7 @@ def norm_unram(self, base = None): if self._is_exact_zero(): return self.parent().ground_ring()(0) elif self._is_inexact_zero(): - return self.ground_ring(0, self.valuation()) + return self.parent().ground_ring()(0, self.valuation()) if self.valuation() == 0: return self.parent().ground_ring()(self.matrix_mod_pn().det()) else: @@ -155,10 +161,10 @@ def trace_unram(self, base = None): Return the absolute or relative trace of this element. If ``base`` is given then ``base`` must be a subfield of the - parent `L` of ``self``, in which case the norm is the relative - norm from `L` to ``base``. + parent `L` of ``self``, in which case the trace is the relative + trace from `L` to ``base``. - In all other cases, the norm is the absolute norm down to + In all other cases, the trace is the absolute trace down to `\QQ_p` or `\ZZ_p`. EXAMPLES:: @@ -202,6 +208,12 @@ def trace_unram(self, base = None): 4*5 + 5^2 + 5^3 + 2*5^4 sage: (a+b).trace() 4*5 + 5^2 + 5^3 + 2*5^4 + + Check that :trac:`31845` is fixed:: + + sage: R. = Zq(4) + sage: (a - a).trace() + O(2^20) """ if base is not None: if base is self.parent(): @@ -211,7 +223,7 @@ def trace_unram(self, base = None): if self._is_exact_zero(): return self.parent().ground_ring()(0) elif self._is_inexact_zero(): - return self.ground_ring(0, self.precision_absolute()) + return self.parent().ground_ring()(0, self.precision_absolute()) if self.valuation() >= 0: return self.parent().ground_ring()(self.matrix_mod_pn().trace()) else: From 17720f3c5240919ec231fc2f7402aefb13b89694 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 22 May 2021 13:59:13 +0200 Subject: [PATCH 387/706] delete duplication in doctest --- .../polyhedron/combinatorial_polyhedron/combinatorial_face.pyx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 4c1641a7b7a..2481c5ff352 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -353,8 +353,6 @@ cdef class CombinatorialFace(SageObject): EXAMPLES:: - sage: P = polytopes.cube() - sage: C = P.combinatorial_polyhedron() sage: P = polytopes.cube() sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter() From 1e24242a53b3360decb860247e71d5182c229231 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 23 May 2021 08:44:17 +0900 Subject: [PATCH 388/706] Improve performance of function fields --- .../en/reference/function_fields/index.rst | 15 +- src/sage/matrix/matrix2.pyx | 20 +- .../rings/function_field/function_field.py | 11 +- .../hermite_form_polynomial.pyx | 187 ++++++++++++++++++ src/sage/rings/function_field/ideal.py | 7 +- src/sage/rings/function_field/order.py | 12 +- 6 files changed, 220 insertions(+), 32 deletions(-) create mode 100644 src/sage/rings/function_field/hermite_form_polynomial.pyx diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index 4f581f569df..205398197b8 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -4,9 +4,8 @@ Algebraic Function Fields Sage allows basic computations with elements and ideals in orders of algebraic function fields over arbitrary constant fields. Advanced computations, like computing the genus or a basis of the Riemann-Roch space of a divisor, are -available for function fields over finite fields, number fields, and `\QQbar`. - -A reference for the basic theory of algebraic function fields is [Stich2009]_. +available for function fields over finite fields, number fields, and the +algebraic closure of `\QQ`. .. toctree:: :maxdepth: 1 @@ -22,4 +21,14 @@ A reference for the basic theory of algebraic function fields is [Stich2009]_. sage/rings/function_field/maps sage/rings/function_field/constructor +A basic reference for the theory of algebraic function fields is [Stich2009]_. + +A Support Module +---------------- + +.. toctree:: + :maxdepth: 1 + + sage/rings/function_field/hermite_form_polynomial + .. include:: ../footer.txt diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 1925fd80a0e..a8b715bfd28 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -2386,14 +2386,14 @@ cdef class Matrix(Matrix1): r""" Computes the Pfaffian of ``self`` using the Baer-Faddeev-LeVerrier algorithm. - + .. WARNING:: - + This method assumes that the base ring is an `\QQ`-algebra. OUTPUT: - - an element (possibly coerced) originated from the base ring of + - an element (possibly coerced) originated from the base ring of ``self`` representing the Pfaffian EXAMPLES: @@ -2409,7 +2409,7 @@ cdef class Matrix(Matrix1): ....: (1/2, -3/2, -1, -5/2, -1/2, 0)]) sage: A.pfaffian(algorithm='bfl') -1/2 - + TESTS:: sage: A = random_matrix(ZZ[x], 6) @@ -15313,7 +15313,7 @@ cdef class Matrix(Matrix1): cdef Py_ssize_t i = 0 cdef Py_ssize_t j = 0 - cdef Py_ssize_t k, l + cdef Py_ssize_t k, l, c if transformation: from sage.matrix.constructor import identity_matrix @@ -15349,9 +15349,9 @@ cdef class Matrix(Matrix1): U.set_unsafe(k, c, p * Ukc + q * Ulc) U.set_unsafe(l, c, (-f) * Ukc + e * Ulc) if i != k: - A.swap_rows(i,k) + A.swap_rows_c(i,k) if transformation: - U.swap_rows(i,k) + U.swap_rows_c(i,k) pivot_cols.append(j) i += 1 j += 1 @@ -15366,9 +15366,9 @@ cdef class Matrix(Matrix1): coeff = normalization(pivot) for c in range(j,n): A.set_unsafe(i, c, A.get_unsafe(i,c) * coeff) - if transformation: - for c in range(m): - U.set_unsafe(i, c, U.get_unsafe(i,c) * coeff) + if transformation: + for c in range(m): + U.set_unsafe(i, c, U.get_unsafe(i,c) * coeff) pivot = A.get_unsafe(i,j) for k in range(i): diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index d0ad9e582a2..c1b75a627ec 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -3534,6 +3534,7 @@ def _maximal_order_basis(self): in some algorithms. """ from sage.matrix.constructor import matrix + from .hermite_form_polynomial import reversed_hermite_form k = self.constant_base_field() K = self.base_field() # rational function field @@ -3608,14 +3609,8 @@ def _maximal_order_basis(self): basis_V = [to_V(bvec) for bvec in _basis] l = lcm([vvec.denominator() for vvec in basis_V]) - # Why do we have 'reversed' here? I don't know. But without it, the - # time to get hermite_form_reversed dramatically increases. - _mat = matrix([[coeff.numerator() for coeff in l*v] for v in reversed(basis_V)]) - - # compute the reversed hermite form - _mat.reverse_rows_and_columns() - _mat._hermite_form_euclidean(normalization=lambda p: ~p.lc()) - _mat.reverse_rows_and_columns() + _mat = matrix([[coeff.numerator() for coeff in l*v] for v in basis_V]) + reversed_hermite_form(_mat) basis = [fr_V(v) / l for v in _mat if not v.is_zero()] return basis diff --git a/src/sage/rings/function_field/hermite_form_polynomial.pyx b/src/sage/rings/function_field/hermite_form_polynomial.pyx new file mode 100644 index 00000000000..823ae0b55fa --- /dev/null +++ b/src/sage/rings/function_field/hermite_form_polynomial.pyx @@ -0,0 +1,187 @@ +r""" +Hermite form computation for function fields + +This module provides an optimized implementation of the algorithm computing +Hermite forms of matrices over polynomials. This is the workhorse of the +function field machinery of Sage. + +EXAMPLES:: + + sage: P. = PolynomialRing(QQ) + sage: A = matrix(P,3,[-(x-1)^((i-j+1) % 3) for i in range(3) for j in range(3)]) + sage: A + [ -x + 1 -1 -x^2 + 2*x - 1] + [-x^2 + 2*x - 1 -x + 1 -1] + [ -1 -x^2 + 2*x - 1 -x + 1] + sage: from sage.rings.function_field.hermite_form_polynomial import reversed_hermite_form + sage: B = copy(A) + sage: U = reversed_hermite_form(B, transformation=True) + sage: U * A == B + True + sage: B + [x^3 - 3*x^2 + 3*x - 2 0 0] + [ 0 x^3 - 3*x^2 + 3*x - 2 0] + [ x^2 - 2*x + 1 x - 1 1] + +The function :func:`reversed_hermite_form` computes the reversed hermite form, +which is reversed both row-wise and column-wise from the usual hermite form. +Let us check it:: + + sage: A.reverse_rows_and_columns() + sage: C = copy(A.hermite_form()) + sage: C.reverse_rows_and_columns() + sage: C + [x^3 - 3*x^2 + 3*x - 2 0 0] + [ 0 x^3 - 3*x^2 + 3*x - 2 0] + [ x^2 - 2*x + 1 x - 1 1] + sage: C == B + True + +AUTHORS: + +- Kwankyu Lee (2021-05-21): initial version + +""" +#***************************************************************************** +# Copyright (C) 2021 Kwankyu Lee +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.matrix.matrix cimport Matrix +from sage.rings.polynomial.polynomial_element cimport Polynomial +from sage.matrix.constructor import identity_matrix + +def reversed_hermite_form(Matrix mat, bint transformation=False): + """ + Transform the matrix in place to reversed hermite normal form and + optionally return the transformation matrix. + + INPUT: + + - ``transformation`` -- boolean (default: ``False``); if ``True``, + return the transformation matrix + + EXAMPLES:: + + sage: from sage.rings.function_field.hermite_form_polynomial import reversed_hermite_form + sage: P. = PolynomialRing(QQ) + sage: A = matrix(P,3,[-(x-1)^((i-2*j) % 4) for i in range(3) for j in range(3)]) + sage: A + [ -1 -x^2 + 2*x - 1 -1] + [ -x + 1 -x^3 + 3*x^2 - 3*x + 1 -x + 1] + [ -x^2 + 2*x - 1 -1 -x^2 + 2*x - 1] + sage: B = copy(A) + sage: U = reversed_hermite_form(B, transformation=True) + sage: U * A == B + True + sage: B + [ 0 0 0] + [ 0 x^4 - 4*x^3 + 6*x^2 - 4*x 0] + [ 1 x^2 - 2*x + 1 1] + """ + cdef Matrix A = mat + cdef Matrix U + + cdef Py_ssize_t m = A.nrows() + cdef Py_ssize_t n = A.ncols() + cdef Py_ssize_t i = m - 1 + cdef Py_ssize_t j = n - 1 + cdef Py_ssize_t k, l, c, ip + + cdef Polynomial a, b, d, p, q, e, f, Aic, Alc, Uic, Ulc + cdef Polynomial zero = A.base_ring().zero() + + cdef int di, dk + + cdef list pivot_cols = [] + + if transformation: + U = identity_matrix(A.base_ring(), m) + + while j >= 0: + # find the first row with nonzero entry in jth column + k = i + while k >= 0 and not A.get_unsafe(k, j): + k -= 1 + if k >= 0: + # swap the kth row with the ith row + if k < i: + A.swap_rows_c(i, k) + if transformation: + U.swap_rows_c(i, k) + k -= 1 + # put the row with the smalllest degree to the ith row + di = A.get_unsafe(i, j).degree() + while k >= 0 and A.get_unsafe(k, j): + dk = A.get_unsafe(k, j).degree() + if dk < di: + A.swap_rows_c(i, k) + di = dk + if transformation: + U.swap_rows_c(i, k) + k -= 1 + l = i - 1 + while True: + # find a row with nonzero entry in the jth column + while l >= 0 and not A.get_unsafe(l, j): + l -= 1 + if l < 0: + break + + a = A.get_unsafe(i, j) + b = A.get_unsafe(l, j) + d, p, q = a.xgcd(b) # p * a + q * b = d = gcd(a,b) + e = a // d + f = -(b // d) + + A.set_unsafe(i, j, d) + A.set_unsafe(l, j, zero) + + for c in range(j): + Aic = A.get_unsafe(i, c) + Alc = A.get_unsafe(l, c) + A.set_unsafe(i, c, p * Aic + q * Alc) + A.set_unsafe(l, c, f * Aic + e * Alc) + if transformation: + for c in range(m): + Uic = U.get_unsafe(i, c) + Ulc = U.get_unsafe(l, c) + U.set_unsafe(i, c, p * Uic + q * Ulc) + U.set_unsafe(l, c, f * Uic + e * Ulc) + pivot_cols.append(j) + i -= 1 + j -= 1 + + # reduce entries below pivots + for i in range(len(pivot_cols)): + j = pivot_cols[i] + ip = m - 1 - i + pivot = A.get_unsafe(ip, j) + + # normalize the leading coefficient to one + coeff = ~pivot.lc() + for c in range(j + 1): + A.set_unsafe(ip, c, A.get_unsafe(ip, c) * coeff) + if transformation: + for c in range(m): + U.set_unsafe(ip, c, U.get_unsafe(ip, c) * coeff) + + pivot = A.get_unsafe(ip,j) + for k in range(ip + 1, m): + q = -( A.get_unsafe(k, j) // pivot) + if q: + for c in range(j + 1): + A.set_unsafe(k, c, A.get_unsafe(k, c) + + q * A.get_unsafe(ip, c)) + if transformation: + for c in range(m): + U.set_unsafe(k, c, U.get_unsafe(k, c) + + q * U.get_unsafe(ip, c)) + + if transformation: + return U + diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index da29c8963c0..a4245d53be4 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -110,6 +110,8 @@ from .divisor import divisor +from .hermite_form_polynomial import reversed_hermite_form + class FunctionFieldIdeal(Element): """ @@ -1591,10 +1593,7 @@ def intersect(self, other): M = block_matrix([[I,I],[A,O],[O,B]]) # reversed Hermite form - M.reverse_rows_and_columns() - U = M._hermite_form_euclidean(transformation=True, - normalization=lambda p: ~p.lc()) - U.reverse_rows_and_columns() + U = reversed_hermite_form(M, transformation=True) vecs = [U[i][:n] for i in range(n)] diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index 58ae8bf092a..fd6a09ff9cb 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -137,6 +137,8 @@ FunctionFieldIdealInfinite_rational, FunctionFieldIdealInfinite_polymod) +from .hermite_form_polynomial import reversed_hermite_form + class FunctionFieldOrder_base(CachedRepresentation, Parent): """ @@ -1475,10 +1477,8 @@ def _ideal_from_vectors_and_denominator(self, vecs, d=1, check=True): # so that we get a unique hnf. Here the hermite form # algorithm also makes the pivots monic. - # compute the reverse hermite form with zero rows deleted - mat.reverse_rows_and_columns() - mat._hermite_form_euclidean(normalization=lambda p: ~p.lc()) - mat.reverse_rows_and_columns() + # compute the reversed hermite form with zero rows deleted + reversed_hermite_form(mat) i = 0 while i < mat.nrows() and mat.row(i).is_zero(): i += 1 @@ -2605,9 +2605,7 @@ def ideal_with_gens_over_base(self, gens): k = x * k h2 = block_matrix([[h],[k]]) - h2.reverse_rows_and_columns() - h2._hermite_form_euclidean(normalization=lambda p: ~p.lc()) - h2.reverse_rows_and_columns() + reversed_hermite_form(h2) i = 0 while i < h2.nrows() and h2.row(i).is_zero(): i += 1 From 8ed706f7cbdb539ced4ec3a780d28da8f799273b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 23 May 2021 20:30:25 +0200 Subject: [PATCH 389/706] assume type to safe some time --- .../combinatorial_face.pyx | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 2481c5ff352..ce7a97635ab 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -347,7 +347,7 @@ cdef class CombinatorialFace(SageObject): # They are faces of the same polyhedron obtained in the same way. return hash(self) < hash(other) - def is_subface(self, other): + def is_subface(self, CombinatorialFace other): r""" Return whether ``self`` is contained in ``other``. @@ -423,26 +423,21 @@ cdef class CombinatorialFace(SageObject): ... NotImplementedError: is_subface only implemented for faces of the same polyhedron """ - cdef CombinatorialFace other_face - if isinstance(other, CombinatorialFace): - other_face = other - if self._dual == other_face._dual: - if self.atoms is other_face.atoms: - if not self._dual: - return face_issubset(self.face, other_face.face) - else: - return face_issubset(other_face.face, self.face) + if self._dual == other._dual: + if self.atoms is other.atoms: + if not self._dual: + return face_issubset(self.face, other.face) else: - raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") + return face_issubset(other.face, self.face) else: - if self.atoms is other_face.coatoms: - self_indices = self.ambient_V_indices() - other_indices = other.ambient_V_indices() - return all(i in other_indices for i in self_indices) - else: - raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: - raise ValueError("other must be a face") + if self.atoms is other.coatoms: + self_indices = self.ambient_V_indices() + other_indices = other.ambient_V_indices() + return all(i in other_indices for i in self_indices) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") def dimension(self): r""" From c0c8295950d26fd59fe3a0311a9f7df7a873e05a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 23 May 2021 21:12:45 +0200 Subject: [PATCH 390/706] optimized code --- .../combinatorial_face.pxd | 1 + .../combinatorial_face.pyx | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..ba98b97ac9b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -29,6 +29,7 @@ cdef class CombinatorialFace(SageObject): # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. cdef ListOfFaces atoms, coatoms + cpdef dimension(self) cdef size_t n_atom_rep(self) except -1 cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index ce7a97635ab..afb83598398 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -423,6 +423,10 @@ cdef class CombinatorialFace(SageObject): ... NotImplementedError: is_subface only implemented for faces of the same polyhedron """ + cdef size_t length_self, length_other, counter_self, counter_other + cdef size_t* self_v_indices + cdef size_t* other_v_indices + if self._dual == other._dual: if self.atoms is other.atoms: if not self._dual: @@ -433,13 +437,37 @@ cdef class CombinatorialFace(SageObject): raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: if self.atoms is other.coatoms: - self_indices = self.ambient_V_indices() - other_indices = other.ambient_V_indices() - return all(i in other_indices for i in self_indices) + if self.dimension() > other.dimension(): + return False + if self._dual: + length_self = self.set_coatom_rep() + self_v_indices = self.coatom_rep + length_other = other.set_atom_rep() + other_v_indices = other.atom_rep + else: + length_self = self.set_atom_rep() + self_v_indices = self.atom_rep + length_other = other.set_coatom_rep() + other_v_indices = other.coatom_rep + if length_self > length_other: + return False + + # Check if every element in self_v_indices is contained in other_v_indices. + counter_self = 0 + counter_other = 0 + while counter_self < length_self and counter_other < length_other: + if self_v_indices[counter_self] > other_v_indices[counter_other]: + counter_other += 1 + elif self_v_indices[counter_self] == other_v_indices[counter_other]: + counter_self += 1 + counter_other += 1 + else: + return False + return counter_self == length_self else: raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") - def dimension(self): + cpdef dimension(self): r""" Return the dimension of the face. From 7a6a6bf65c75cd087e09fc3d9459a5dbb1d4d8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 May 2021 14:27:16 +0200 Subject: [PATCH 391/706] convert polygamma to fricas --- src/sage/functions/gamma.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py index b1cd9a80e3b..08f549777c2 100644 --- a/src/sage/functions/gamma.py +++ b/src/sage/functions/gamma.py @@ -551,9 +551,9 @@ def _eval_(self, x, y): from sage.rings.infinity import Infinity return Infinity elif x == 1: - return 1-exp(-y) - elif (2*x).is_integer(): - return self(x,y,hold=True)._sympy_() + return 1 - exp(-y) + elif (2 * x).is_integer(): + return self(x, y, hold=True)._sympy_() else: return None @@ -618,7 +618,7 @@ def _derivative_(self, x, y, diff_param=None): raise NotImplementedError("cannot differentiate gamma_inc_lower in the" " first parameter") else: - return exp(-y)*y**(x - 1) + return exp(-y) * y**(x - 1) def _mathematica_init_evaled_(self, *args): r""" @@ -715,7 +715,7 @@ def gamma(a, *args, **kwds): if not args: return gamma1(a, **kwds) if len(args) > 1: - raise TypeError("Symbolic function gamma takes at most 2 arguments (%s given)"% (len(args) + 1)) + raise TypeError("Symbolic function gamma takes at most 2 arguments (%s given)" % (len(args) + 1)) return gamma_inc(a, args[0], **kwds) @@ -790,11 +790,15 @@ def __init__(self): -5.28903989659219 sage: psi(x)._sympy_() polygamma(0, x) + sage: psi(x)._fricas_() # optional - fricas + digamma(x) """ GinacFunction.__init__(self, "psi", nargs=1, latex_name=r'\psi', conversions=dict(mathematica='PolyGamma', maxima='psi[0]', - sympy='digamma')) + sympy='digamma', + fricas='digamma')) + class Function_psi2(GinacFunction): def __init__(self): @@ -841,11 +845,14 @@ def __init__(self): psi(2, x) + 1 sage: psi(2, x)._sympy_() polygamma(2, x) + sage: psi(2, x)._fricas_() # optional - fricas + polygamma(2,x) """ GinacFunction.__init__(self, "psi", nargs=2, latex_name=r'\psi', conversions=dict(mathematica='PolyGamma', sympy='polygamma', - giac='Psi')) + giac='Psi', + fricas='polygamma')) def _maxima_init_evaled_(self, *args): """ @@ -868,11 +875,12 @@ def _maxima_init_evaled_(self, *args): else: args_maxima.append(str(a)) n, x = args_maxima - return "psi[%s](%s)"%(n, x) + return "psi[%s](%s)" % (n, x) psi1 = Function_psi1() psi2 = Function_psi2() + def psi(x, *args, **kwds): r""" The digamma function, `\psi(x)`, is the logarithmic derivative of the @@ -924,6 +932,7 @@ def psi(x, *args, **kwds): # two functions with different number of arguments and the same name symbol_table['functions']['psi'] = psi + def _swap_psi(a, b): return psi(b, a) register_symbol(_swap_psi, {'giac': 'Psi'}) From f2d37a10436b79f9425cf1f224d6ee9bd48a0869 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Mon, 24 May 2021 19:05:33 +0200 Subject: [PATCH 392/706] Trac #31854: pass name and latex_name to copy method --- src/sage/manifolds/differentiable/tensorfield.py | 5 ++--- src/sage/manifolds/scalarfield.py | 5 ++--- src/sage/manifolds/section.py | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 52dff82b202..de4fbf1a033 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -1021,9 +1021,8 @@ def set_restriction(self, rst): if self._domain is rst._domain: self.copy_from(rst) else: - self._restrictions[rst._domain] = rst.copy() - self._restrictions[rst._domain].set_name(name=self._name, - latex_name=self._latex_name) + self._restrictions[rst._domain] = rst.copy(name=self._name, + latex_name=self._latex_name) self._is_zero = False # a priori def restrict(self, subdomain, dest_map=None): diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index e741e67169a..dc9c421f1c1 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -2016,9 +2016,8 @@ def set_restriction(self, rst): if not rst._domain.is_subset(self._domain): raise ValueError("the domain of the declared restriction is not " + "a subset of the field's domain") - self._restrictions[rst._domain] = rst.copy() - self._restrictions[rst._domain].set_name(name=self._name, - latex_name=self._latex_name) + self._restrictions[rst._domain] = rst.copy(name=self._name, + latex_name=self._latex_name) for chart, expr in rst._express.items(): intersection = chart._domain.intersection(rst._domain) self._express[chart.restrict(intersection)] = expr diff --git a/src/sage/manifolds/section.py b/src/sage/manifolds/section.py index 29ed3a88040..66d857ec0a6 100644 --- a/src/sage/manifolds/section.py +++ b/src/sage/manifolds/section.py @@ -627,9 +627,8 @@ def set_restriction(self, rst): if self.is_immutable(): raise ValueError("the restrictions of an immutable element " "cannot be changed") - self._restrictions[rst._domain] = rst.copy() - self._restrictions[rst._domain].set_name(name=self._name, - latex_name=self._latex_name) + self._restrictions[rst._domain] = rst.copy(name=self._name, + latex_name=self._latex_name) self._is_zero = False # a priori def restrict(self, subdomain): From 2a7d94055fd5c61d9453d848a28218dea1effe71 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 May 2021 17:18:03 -0700 Subject: [PATCH 393/706] build/pkgs/pyzmq: Update to 22.0.3 --- build/pkgs/pyzmq/checksums.ini | 6 +++--- build/pkgs/pyzmq/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index 71ef027aefd..335c3de7696 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,5 +1,5 @@ tarball=pyzmq-VERSION.tar.gz -sha1=1f65a40ff85536a6ebb6ddd7105075061d37d2e2 -md5=200abc1a75bdcfff7adf61304f46f55e -cksum=788738364 +sha1=fbfdd0bf96656d57d2738d31359dfce68434a39c +md5=09250d80764548936b87ab6a05c8dc5d +cksum=535366819 upstream_url=https://pypi.io/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index e021724948e..8b740661820 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -19.0.2 +22.0.3 From a64d920fe40843d8d48c5c4fa3b931dedf692c48 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 May 2021 17:32:20 -0700 Subject: [PATCH 394/706] build/pkgs/babel: Update to 2.9.1, add upstream_url --- build/pkgs/babel/checksums.ini | 7 ++++--- build/pkgs/babel/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini index 0c06f1c56df..a98fa598a37 100644 --- a/build/pkgs/babel/checksums.ini +++ b/build/pkgs/babel/checksums.ini @@ -1,4 +1,5 @@ tarball=Babel-VERSION.tar.gz -sha1=6aed99e4fb8a2a75de7815599f610cdcbb81e3c2 -md5=c384ac03026e8fe6f9b90f55201f1bff -cksum=3805303136 +sha1=1ce15f82eba5184cabe6ac1491cb58262e27adfd +md5=7166099733d78aa857d74fa50d8ff58c +cksum=1695340328 +upstream_url=https://pypi.io/packages/source/b/babel/Babel-VERSION.tar.gz diff --git a/build/pkgs/babel/package-version.txt b/build/pkgs/babel/package-version.txt index e70b4523ae7..dedcc7d4335 100644 --- a/build/pkgs/babel/package-version.txt +++ b/build/pkgs/babel/package-version.txt @@ -1 +1 @@ -2.6.0 +2.9.1 From 745ecbf6e45e43f2567417eb2723297c1f7d66d2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:33:44 +0100 Subject: [PATCH 395/706] Trac #27365: integrate with measure='induced' --- src/sage/geometry/polyhedron/base.py | 63 ++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..aa5eccc52d6 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8228,7 +8228,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): else: raise TypeError("the measure should be `ambient`, `induced`, `induced_rational`, or `induced_lattice`") - def integrate(self, polynomial, **kwds): + def integrate(self, polynomial, measure='ambient', **kwds): r""" Return the integral of a polynomial over a polytope. @@ -8308,13 +8308,68 @@ def integrate(self, polynomial, **kwds): """ if self.base_ring() == RDF: raise TypeError("LattE integrale cannot be applied over inexact rings") - elif not self.is_full_dimensional(): - raise NotImplementedError("the polytope must be full-dimensional") - else: + + if polynomial == 0 or polynomial == '[]': + return self.base_ring().zero() + + if not self.is_compact(): + raise NotImplementedError( + 'integration over non-compact polyhedra not allowed') + + if measure == 'ambient': + if not self.is_full_dimensional(): + return self.base_ring().zero() + from sage.interfaces.latte import integrate return integrate(self.cdd_Hrepresentation(), polynomial, cdd=True, **kwds) + elif measure == 'induced' or measure == 'induced_nonnormalized': + # if polyhedron is actually full-dimensional, + # return with ambient measure + if self.is_full_dimensional(): + return self.integrate(polynomial, measure='ambient', **kwds) + + if isinstance(polynomial, six.string_types): + raise NotImplementedError( + 'LattE description strings for polynomials not allowed ' + 'when using measure="induced"') + + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + + # use an orthogonal transformation + affine_hull = self.affine_hull(orthogonal=True, return_all_data=True) + polyhedron = affine_hull['polyhedron'] + L = affine_hull['linear_transformation'] + v0 = affine_hull['polyhedron_base'] + vi = [v.vector() + for v in affine_hull['polyhedron_base_vertices']] + + # columns of W are equal to the vertices of affine_hull['polyhedron'] + # in an order compatible with the vectors vi + W = matrix([list(L(v)) for v in vi]).transpose() + + # transform the polynomial + t = vector(PolynomialRing(polynomial.base_ring(), 't', len(vi)).gens()) + beta = W.inverse() * t + hom_images = v0 + sum(b * v for b, v in zip(beta, vi)) + hom = polynomial.parent().hom(list(hom_images)) + polynomial_in_affine_hull = hom(polynomial) + + from sage.interfaces.latte import integrate + I = integrate(polyhedron.cdd_Hrepresentation(), + polynomial_in_affine_hull, + cdd=True, **kwds) + if measure == 'induced_nonnormalized': + return I + else: + A = L.matrix() + Adet = (A.transpose() * A).det() + return I / sqrt(Adet) + + else: + raise ValueError('unknown measure "{}"'.format(measure)) + def contains(self, point): """ Test whether the polyhedron contains the given ``point``. From fa14f86c8a87c337daf7704cb0bdeed6e3797254 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:34:17 +0100 Subject: [PATCH 396/706] Trac #27365: add some doctests --- src/sage/geometry/polyhedron/base.py | 34 +++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index aa5eccc52d6..c6b6eb8d2e9 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8270,9 +8270,28 @@ def integrate(self, polynomial, measure='ambient', **kwds): sage: x, y = polygens(QQ, 'x, y') sage: P = Polyhedron(vertices=[[0,0],[1,1]]) sage: P.integrate(x*y) # optional - latte_int - Traceback (most recent call last): - ... - NotImplementedError: the polytope must be full-dimensional + 0 + sage: P.integrate(x*y, measure='induced') # optional - latte_int # not tested (see :trac:`27364`) + + Another non full-dimensional polytope integration:: + + sage: R. = QQ[] + sage: P = polytopes.simplex(2) + sage: V = P.volume(measure='induced'); V + 1/2*sqrt(3) + sage: P.integrate(R(1), measure='induced') + 1/2*sqrt(3) + sage: bool(_ == V) + True + + Computing the mass center:: + + sage: P.integrate(x, measure='induced') / V + 1/3 + sage: P.integrate(y, measure='induced') / V + 1/3 + sage: P.integrate(z, measure='induced') / V + 1/3 TESTS: @@ -8305,6 +8324,15 @@ def integrate(self, polynomial, measure='ambient', **kwds): Traceback (most recent call last): ... TypeError: LattE integrale cannot be applied over inexact rings + + Integration of zero-polynomial:: + + sage: R. = QQ[] + sage: P = polytopes.simplex(2) + sage: P.integrate(R(0)) + 0 + sage: P.integrate('[]') # with LattE description string + 0 """ if self.base_ring() == RDF: raise TypeError("LattE integrale cannot be applied over inexact rings") From 8ce5bd4de3bccc78e4d6a9e7544bdce90f6d2b47 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 16:44:42 +0100 Subject: [PATCH 397/706] Trac #27365: extend docstring --- src/sage/geometry/polyhedron/base.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c6b6eb8d2e9..17135ee1fa7 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8239,7 +8239,15 @@ def integrate(self, polynomial, measure='ambient', **kwds): - ``polynomial`` -- A multivariate polynomial or a valid LattE description string for polynomials - - ``**kwds`` -- additional keyword arguments that are passed to the engine + - ``measure`` -- string. The measure to use. Allowed values are: + + * ``ambient`` (default): Lebesgue measure of ambient space + * ``induced``: Lebesgue measure of the affine hull + * ``induced_nonnormalized``: Lebesgue measure of the affine hull + without the normalization by `\sqrt{\det(A^\top A)}` (with + `A` being the affine transformation map). + + - ``**kwds`` -- additional keyword arguments that are passed to the engine. OUTPUT: From fc4aa376e34e66a1cb4421eb90cbae6b0179798c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 26 Feb 2019 17:07:52 +0100 Subject: [PATCH 398/706] Trac #27365: include link to affine_hull --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 17135ee1fa7..9745aecad25 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8245,7 +8245,7 @@ def integrate(self, polynomial, measure='ambient', **kwds): * ``induced``: Lebesgue measure of the affine hull * ``induced_nonnormalized``: Lebesgue measure of the affine hull without the normalization by `\sqrt{\det(A^\top A)}` (with - `A` being the affine transformation map). + `A` being the affine transformation map; see :meth:`affine_hull`). - ``**kwds`` -- additional keyword arguments that are passed to the engine. From 121947ec23a49bd21408314204da10d5c6a4648e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:54:40 +0100 Subject: [PATCH 399/706] Trac #27365: update to follow changes in .affine_hull --- src/sage/geometry/polyhedron/base.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 9745aecad25..6334fe9ea8b 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8376,22 +8376,14 @@ def integrate(self, polynomial, measure='ambient', **kwds): # use an orthogonal transformation affine_hull = self.affine_hull(orthogonal=True, return_all_data=True) polyhedron = affine_hull['polyhedron'] - L = affine_hull['linear_transformation'] - v0 = affine_hull['polyhedron_base'] - vi = [v.vector() - for v in affine_hull['polyhedron_base_vertices']] - - # columns of W are equal to the vertices of affine_hull['polyhedron'] - # in an order compatible with the vectors vi - W = matrix([list(L(v)) for v in vi]).transpose() - - # transform the polynomial - t = vector(PolynomialRing(polynomial.base_ring(), 't', len(vi)).gens()) - beta = W.inverse() * t - hom_images = v0 + sum(b * v for b, v in zip(beta, vi)) - hom = polynomial.parent().hom(list(hom_images)) + coordinate_images = affine_hull['coordinate_images'] + + hom = polynomial.parent().hom(coordinate_images) polynomial_in_affine_hull = hom(polynomial) + if polynomial_in_affine_hull == 0: + return self.base_ring().zero() + from sage.interfaces.latte import integrate I = integrate(polyhedron.cdd_Hrepresentation(), polynomial_in_affine_hull, @@ -8399,7 +8391,7 @@ def integrate(self, polynomial, measure='ambient', **kwds): if measure == 'induced_nonnormalized': return I else: - A = L.matrix() + A = affine_hull['affine_map'][0].matrix() Adet = (A.transpose() * A).det() return I / sqrt(Adet) From 84c56fc67c614288b149b3372de44ac48e203268 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 10:55:14 +0100 Subject: [PATCH 400/706] Trac #27365: doctest boundary case --- src/sage/geometry/polyhedron/base.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6334fe9ea8b..7f26d015e69 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8341,6 +8341,13 @@ def integrate(self, polynomial, measure='ambient', **kwds): 0 sage: P.integrate('[]') # with LattE description string 0 + + :: + + sage: R. = QQ[] + sage: P = Polyhedron(vertices=[(0, 0, 1), (0, 1, 0)]) + sage: P.integrate(x^2) + 0 """ if self.base_ring() == RDF: raise TypeError("LattE integrale cannot be applied over inexact rings") From d6590b5fa99f56468658a6870e5b66b963d2cc6c Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 12:49:30 +0100 Subject: [PATCH 401/706] Trac #27365: factor out .integrate_latte method --- src/sage/geometry/polyhedron/base.py | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7f26d015e69..83aa307a4bd 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8405,6 +8405,42 @@ def integrate(self, polynomial, measure='ambient', **kwds): else: raise ValueError('unknown measure "{}"'.format(measure)) + def _integrate_latte_(self, polynomial, **kwds): + r""" + Return the integral of a polynomial over this polytope by calling LattE. + + INPUT: + + - ``polynomial`` -- a multivariate polynomial or + a valid LattE description string for polynomials + + - ``**kwds`` -- additional keyword arguments that are passed + to the engine + + OUTPUT: + + The integral of the polynomial over the polytope. + + .. NOTE:: + + The polytope triangulation algorithm is used. This function depends + on LattE (i.e., the ``latte_int`` optional package). + + TESTS:: + + sage: P = polytopes.cube() + sage: x, y, z = polygens(QQ, 'x, y, z') + sage: P._integrate_latte_(x^2 + y^2*z^2) # optional - latte_int + 32/9 + """ + from sage.interfaces.latte import integrate + + if self.base_ring() == RDF: + raise TypeError("LattE integrale cannot be applied over inexact rings.") + return integrate(self.cdd_Hrepresentation(), + polynomial, + cdd=True, **kwds) + def contains(self, point): """ Test whether the polyhedron contains the given ``point``. From 65c3b658c46e6f06b400af936a235bb915b43dc1 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 12:50:29 +0100 Subject: [PATCH 402/706] Trac #27365: use new method --- src/sage/geometry/polyhedron/base.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 83aa307a4bd..e52e0e907d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8363,9 +8363,7 @@ def integrate(self, polynomial, measure='ambient', **kwds): if not self.is_full_dimensional(): return self.base_ring().zero() - from sage.interfaces.latte import integrate - return integrate(self.cdd_Hrepresentation(), polynomial, - cdd=True, **kwds) + return self._integrate_latte_(polynomial, **kwds) elif measure == 'induced' or measure == 'induced_nonnormalized': # if polyhedron is actually full-dimensional, @@ -8388,13 +8386,8 @@ def integrate(self, polynomial, measure='ambient', **kwds): hom = polynomial.parent().hom(coordinate_images) polynomial_in_affine_hull = hom(polynomial) - if polynomial_in_affine_hull == 0: - return self.base_ring().zero() - - from sage.interfaces.latte import integrate - I = integrate(polyhedron.cdd_Hrepresentation(), - polynomial_in_affine_hull, - cdd=True, **kwds) + I = polyhedron.integrate(polynomial_in_affine_hull, + measure='ambient', **kwds) if measure == 'induced_nonnormalized': return I else: From 599ad516697532ebd081af864dbfdcdd8e15578a Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:12:51 +0100 Subject: [PATCH 403/706] Trac #27365: use AA (instead of SR) for return value if possible --- src/sage/geometry/polyhedron/base.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e52e0e907d1..7a2933223f5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8285,20 +8285,18 @@ def integrate(self, polynomial, measure='ambient', **kwds): sage: R. = QQ[] sage: P = polytopes.simplex(2) - sage: V = P.volume(measure='induced'); V + sage: V = AA(P.volume(measure='induced')); V.radical_expression() 1/2*sqrt(3) - sage: P.integrate(R(1), measure='induced') - 1/2*sqrt(3) - sage: bool(_ == V) + sage: P.integrate(R(1), measure='induced') == V True Computing the mass center:: - sage: P.integrate(x, measure='induced') / V + sage: (P.integrate(x, measure='induced') / V).radical_expression() 1/3 - sage: P.integrate(y, measure='induced') / V + sage: (P.integrate(y, measure='induced') / V).radical_expression() 1/3 - sage: P.integrate(z, measure='induced') / V + sage: (P.integrate(z, measure='induced') / V).radical_expression() 1/3 TESTS: @@ -8393,6 +8391,10 @@ def integrate(self, polynomial, measure='ambient', **kwds): else: A = affine_hull['affine_map'][0].matrix() Adet = (A.transpose() * A).det() + try: + Adet = AA.coerce(Adet) + except TypeError: + pass return I / sqrt(Adet) else: From 57edadd8aa688d541da99cf7bd9886c7a6b9227f Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:13:20 +0100 Subject: [PATCH 404/706] Trac #27365: activate one inactive doctest --- src/sage/geometry/polyhedron/base.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 7a2933223f5..e15cbee2de3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8279,7 +8279,12 @@ def integrate(self, polynomial, measure='ambient', **kwds): sage: P = Polyhedron(vertices=[[0,0],[1,1]]) sage: P.integrate(x*y) # optional - latte_int 0 - sage: P.integrate(x*y, measure='induced') # optional - latte_int # not tested (see :trac:`27364`) + sage: ixy = P.integrate(x*y, measure='induced'); ixy # optional - latte_int + 0.4714045207910317? + sage: ixy.parent() + Algebraic Real Field + sage: ixy.radical_expression() # convert to a symbolic expression + 1/3*sqrt(2) Another non full-dimensional polytope integration:: From 1c10622a97cf71a1612f38594f036673f051368e Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:15:38 +0100 Subject: [PATCH 405/706] Trac #27365: rename polynomial --> function --- src/sage/geometry/polyhedron/base.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e15cbee2de3..8b60ee50d61 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8228,9 +8228,9 @@ def volume(self, measure='ambient', engine='auto', **kwds): else: raise TypeError("the measure should be `ambient`, `induced`, `induced_rational`, or `induced_lattice`") - def integrate(self, polynomial, measure='ambient', **kwds): + def integrate(self, function, measure='ambient', **kwds): r""" - Return the integral of a polynomial over a polytope. + Return the integral of ``function`` over this polytope. INPUT: @@ -8352,10 +8352,7 @@ def integrate(self, polynomial, measure='ambient', **kwds): sage: P.integrate(x^2) 0 """ - if self.base_ring() == RDF: - raise TypeError("LattE integrale cannot be applied over inexact rings") - - if polynomial == 0 or polynomial == '[]': + if function == 0 or function == '[]': return self.base_ring().zero() if not self.is_compact(): @@ -8366,15 +8363,15 @@ def integrate(self, polynomial, measure='ambient', **kwds): if not self.is_full_dimensional(): return self.base_ring().zero() - return self._integrate_latte_(polynomial, **kwds) + return self._integrate_latte_(function, **kwds) elif measure == 'induced' or measure == 'induced_nonnormalized': # if polyhedron is actually full-dimensional, # return with ambient measure if self.is_full_dimensional(): - return self.integrate(polynomial, measure='ambient', **kwds) + return self.integrate(function, measure='ambient', **kwds) - if isinstance(polynomial, six.string_types): + if isinstance(function, six.string_types): raise NotImplementedError( 'LattE description strings for polynomials not allowed ' 'when using measure="induced"') @@ -8386,10 +8383,10 @@ def integrate(self, polynomial, measure='ambient', **kwds): polyhedron = affine_hull['polyhedron'] coordinate_images = affine_hull['coordinate_images'] - hom = polynomial.parent().hom(coordinate_images) - polynomial_in_affine_hull = hom(polynomial) + hom = function.parent().hom(coordinate_images) + function_in_affine_hull = hom(function) - I = polyhedron.integrate(polynomial_in_affine_hull, + I = polyhedron.integrate(function_in_affine_hull, measure='ambient', **kwds) if measure == 'induced_nonnormalized': return I From b86258b798f92107220e07ac5a1cf92deac32087 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:15:57 +0100 Subject: [PATCH 406/706] Trac #27365: update docstring --- src/sage/geometry/polyhedron/base.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8b60ee50d61..c8406d932b2 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8234,20 +8234,23 @@ def integrate(self, function, measure='ambient', **kwds): INPUT: - - ``P`` -- Polyhedron + - ``self`` -- Polyhedron - - ``polynomial`` -- A multivariate polynomial or a valid LattE description string for - polynomials + - ``function`` -- a multivariate polynomial or + a valid LattE description string for polynomials - - ``measure`` -- string. The measure to use. Allowed values are: + - ``measure`` -- string, the measure to use + + Allowed values are: - * ``ambient`` (default): Lebesgue measure of ambient space - * ``induced``: Lebesgue measure of the affine hull + * ``ambient`` (default): Lebesgue measure of ambient space, + * ``induced``: Lebesgue measure of the affine hull, * ``induced_nonnormalized``: Lebesgue measure of the affine hull without the normalization by `\sqrt{\det(A^\top A)}` (with - `A` being the affine transformation map; see :meth:`affine_hull`). + `A` being the affine transformation matrix; see :meth:`affine_hull`). - - ``**kwds`` -- additional keyword arguments that are passed to the engine. + - ``**kwds`` -- additional keyword arguments that + are passed to the engine OUTPUT: From 43b68a907d51a3425f8940b1c6f94ef4e0cb5554 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:22:25 +0100 Subject: [PATCH 407/706] Trac #27365: src.sage.interfaces.latte: check for zero polynomial --- src/sage/interfaces/latte.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/interfaces/latte.py b/src/sage/interfaces/latte.py index 776518dc388..1d013b5c935 100644 --- a/src/sage/interfaces/latte.py +++ b/src/sage/interfaces/latte.py @@ -432,12 +432,21 @@ def to_latte_polynomial(polynomial): sage: to_latte_polynomial(f) '[[3, [2, 4, 6]], [7, [0, 3, 5]]]' + sage: to_latte_polynomial(x.parent().zero()) + '[]' + Testing a univariate polynomial:: sage: x = polygen(QQ, 'x') sage: to_latte_polynomial((x-1)^2) '[[1, [0]], [-2, [1]], [1, [2]]]' + + sage: to_latte_polynomial(x.parent().zero()) + '[]' """ + if polynomial == 0: + return str([]) + from sage.rings.polynomial.polydict import ETuple coefficients_list = polynomial.coefficients() From ddedd052a6be31fa78bce13599a98a369f45929b Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:22:50 +0100 Subject: [PATCH 408/706] Trac #27365: minor restructure of code (for clearification) in src.sage.interfaces.latte --- src/sage/interfaces/latte.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/latte.py b/src/sage/interfaces/latte.py index 1d013b5c935..6b130a32776 100644 --- a/src/sage/interfaces/latte.py +++ b/src/sage/interfaces/latte.py @@ -459,8 +459,8 @@ def to_latte_polynomial(polynomial): exponents_list = [[exponent_vector_i] for exponent_vector_i in polynomial.exponents()] # assuming that the order in coefficients() and exponents() methods match - monomials_list = zip(coefficients_list, exponents_list) - monomials_list = [list(monomial_i) for monomial_i in monomials_list] - monomials_list = str(monomials_list) + monomials_list = [list(monomial_i) + for monomial_i + in zip(coefficients_list, exponents_list)] - return monomials_list + return str(monomials_list) From 46bccaabefa7e9e79ea8565a5875840115db6ca2 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Thu, 7 Mar 2019 13:25:24 +0100 Subject: [PATCH 409/706] Trac #27365: cleanup one import --- src/sage/geometry/polyhedron/base.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c8406d932b2..4d404e5d9f0 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8379,8 +8379,6 @@ def integrate(self, function, measure='ambient', **kwds): 'LattE description strings for polynomials not allowed ' 'when using measure="induced"') - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - # use an orthogonal transformation affine_hull = self.affine_hull(orthogonal=True, return_all_data=True) polyhedron = affine_hull['polyhedron'] From 8e29094dac4e65d77835e737355d2c4af00e1b52 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Tue, 12 Mar 2019 11:26:37 +0100 Subject: [PATCH 410/706] Trac #27365: boundary case: integration over polyhedron with only one vertex --- src/sage/geometry/polyhedron/base.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4d404e5d9f0..b690a2c7ca0 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8430,11 +8430,22 @@ def _integrate_latte_(self, polynomial, **kwds): sage: x, y, z = polygens(QQ, 'x, y, z') sage: P._integrate_latte_(x^2 + y^2*z^2) # optional - latte_int 32/9 + + :: + + sage: R = PolynomialRing(QQ, '', 0) + sage: Polyhedron(vertices=[()]).integrate(R(42)) + 42 """ from sage.interfaces.latte import integrate if self.base_ring() == RDF: raise TypeError("LattE integrale cannot be applied over inexact rings.") + if self.dimension() == 0: + vertices = self.vertices() + assert len(self.vertices()) == 1 + vertex = tuple(vertices[0]) + return polynomial(vertex) return integrate(self.cdd_Hrepresentation(), polynomial, cdd=True, **kwds) From 790aec5be0858468431990e3d8333294fa525c33 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Apr 2021 22:56:35 -0700 Subject: [PATCH 411/706] Polyhedron_base.integrate: Update for new affine_hull_projection output --- src/sage/geometry/polyhedron/base.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index b690a2c7ca0..ef5f43b1624 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8374,15 +8374,17 @@ def integrate(self, function, measure='ambient', **kwds): if self.is_full_dimensional(): return self.integrate(function, measure='ambient', **kwds) - if isinstance(function, six.string_types): + if isinstance(function, str): raise NotImplementedError( 'LattE description strings for polynomials not allowed ' 'when using measure="induced"') # use an orthogonal transformation - affine_hull = self.affine_hull(orthogonal=True, return_all_data=True) - polyhedron = affine_hull['polyhedron'] - coordinate_images = affine_hull['coordinate_images'] + affine_hull_data = self.affine_hull_projection(orthogonal=True, return_all_data=True) + polyhedron = affine_hull_data.polyhedron + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(affine_hull_data.section_linear_map.base_ring(), 'x', self.dim()) + coordinate_images = affine_hull_data.section_linear_map.matrix().transpose() * vector(R.gens()) + affine_hull_data.section_translation hom = function.parent().hom(coordinate_images) function_in_affine_hull = hom(function) @@ -8392,7 +8394,7 @@ def integrate(self, function, measure='ambient', **kwds): if measure == 'induced_nonnormalized': return I else: - A = affine_hull['affine_map'][0].matrix() + A = affine_hull_data.projection_linear_map.matrix() Adet = (A.transpose() * A).det() try: Adet = AA.coerce(Adet) From a25c0ef213339db4e042106ca6d90731e9b7c3de Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 May 2021 23:43:03 -0700 Subject: [PATCH 412/706] src/sage/geometry/polyhedron/base.py: Add some # optional - latte_int --- src/sage/geometry/polyhedron/base.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index ef5f43b1624..ba5fa5e5a33 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8272,8 +8272,8 @@ def integrate(self, function, measure='ambient', **kwds): be obtained if we transform to rational coordinates:: sage: P = 1.4142*polytopes.cube() - sage: P_QQ = Polyhedron(vertices = [[QQ(vi) for vi in v] for v in P.vertex_generator()]) - sage: RDF(P_QQ.integrate(x^2*y^2*z^2)) # optional - latte_int + sage: P_QQ = Polyhedron(vertices=[[QQ(vi) for vi in v] for v in P.vertex_generator()]) + sage: RDF(P_QQ.integrate(x^2*y^2*z^2)) # optional - latte_int 6.703841212195228 Integral over a non full-dimensional polytope:: @@ -8284,9 +8284,12 @@ def integrate(self, function, measure='ambient', **kwds): 0 sage: ixy = P.integrate(x*y, measure='induced'); ixy # optional - latte_int 0.4714045207910317? - sage: ixy.parent() + sage: ixy.parent() # optional - latte_int Algebraic Real Field - sage: ixy.radical_expression() # convert to a symbolic expression + + Convert to a symbolic expression:: + + sage: ixy.radical_expression() # optional - latte_int 1/3*sqrt(2) Another non full-dimensional polytope integration:: @@ -8295,16 +8298,16 @@ def integrate(self, function, measure='ambient', **kwds): sage: P = polytopes.simplex(2) sage: V = AA(P.volume(measure='induced')); V.radical_expression() 1/2*sqrt(3) - sage: P.integrate(R(1), measure='induced') == V + sage: P.integrate(R(1), measure='induced') == V # optional - latte_int True Computing the mass center:: - sage: (P.integrate(x, measure='induced') / V).radical_expression() + sage: (P.integrate(x, measure='induced') / V).radical_expression() # optional - latte_int 1/3 - sage: (P.integrate(y, measure='induced') / V).radical_expression() + sage: (P.integrate(y, measure='induced') / V).radical_expression() # optional - latte_int 1/3 - sage: (P.integrate(z, measure='induced') / V).radical_expression() + sage: (P.integrate(z, measure='induced') / V).radical_expression() # optional - latte_int 1/3 TESTS: From 33243667cd933ae95f41fef7685654c53d97cd55 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 May 2021 23:50:31 -0700 Subject: [PATCH 413/706] Polyhedron_base._integrate_latte_: Fix error message --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index ba5fa5e5a33..4c49d97ea5f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8445,7 +8445,7 @@ def _integrate_latte_(self, polynomial, **kwds): from sage.interfaces.latte import integrate if self.base_ring() == RDF: - raise TypeError("LattE integrale cannot be applied over inexact rings.") + raise TypeError("LattE integrale cannot be applied over inexact rings") if self.dimension() == 0: vertices = self.vertices() assert len(self.vertices()) == 1 From 5de63bee53fdefbdb1a99ed978c72db2b611410d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 25 May 2021 09:06:08 +0200 Subject: [PATCH 414/706] cache atom_rep and coatom_rep of combinatorial face --- .../combinatorial_polyhedron/combinatorial_face.pxd | 2 ++ .../combinatorial_polyhedron/combinatorial_face.pyx | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index ba98b97ac9b..378ecad5b00 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -12,7 +12,9 @@ cdef class CombinatorialFace(SageObject): cdef MemoryAllocator _mem cdef size_t *atom_rep # a place where atom-representation of face will be stored + cdef size_t _n_atom_rep cdef size_t *coatom_rep # a place where coatom-representation of face will be stored + cdef size_t _n_coatom_rep cdef int _dimension # dimension of current face, dual dimension if ``dual`` cdef int _ambient_dimension # dimension of the polyhedron diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index afb83598398..482335fca2b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -876,6 +876,8 @@ cdef class CombinatorialFace(SageObject): Compute the number of atoms in the current face by counting the number of set bits. """ + if self.atom_rep is not NULL: + return self._n_atom_rep return face_len_atoms(self.face) cdef size_t set_coatom_rep(self) except -1: @@ -883,16 +885,18 @@ cdef class CombinatorialFace(SageObject): Set ``coatom_rep`` to be the coatom-representation of the current face. Return its length. """ - if not self.coatom_rep: + if self.coatom_rep is NULL: self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) - return bit_rep_to_coatom_rep(self.face, self.coatoms.data, self.coatom_rep) + self._n_coatom_rep = bit_rep_to_coatom_rep(self.face, self.coatoms.data, self.coatom_rep) + return self._n_coatom_rep cdef size_t set_atom_rep(self) except -1: r""" Set ``atom_rep`` to be the atom-representation of the current face. Return its length. """ - if not self.atom_rep: + if self.atom_rep is NULL: self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) - return bit_rep_to_Vrep_list(self.face, self.atom_rep) + self._n_atom_rep = bit_rep_to_Vrep_list(self.face, self.atom_rep) + return self._n_atom_rep From 8c363596f23af0ddfa1c57d096079907a6485c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 May 2021 15:53:23 +0200 Subject: [PATCH 415/706] add doctests for fricas digamma and polygamma --- src/sage/interfaces/fricas.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 9c5f201a073..2b64ac08164 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1461,6 +1461,16 @@ def _sage_expression(fricas_InputForm): sage: fricas(s).sage() # optional - fricas 1/3840*n^10 - 5/2304*n^9 + 5/1152*n^8 + 31/5760*n^7 - 229/11520*n^6 - 5/2304*n^5 + 1/36*n^4 - 1/960*n^3 - 1/80*n^2 + Some checks for digamma and polygamma (:trac:`31853`):: + + sage: fricas.digamma(1.0) # optional - fricas + - 0.5772156649_0153286061 + sage: psi(1.0) + -0.577215664901533 + sage: fricas.polygamma(1, 1.0) # optional - fricas + 1.6449340668482269 + sage: psi(1, 1).n() + 1.64493406684823 Check that :trac:`25224` is fixed:: From c77da15982f82935844ec215f3d3a5a1bd842a70 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 25 May 2021 17:01:25 +0200 Subject: [PATCH 416/706] add translation from euler_gamma --- src/sage/interfaces/fricas.py | 11 +++++++++-- src/sage/symbolic/constants.py | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 2b64ac08164..8ad02d62186 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1463,15 +1463,22 @@ def _sage_expression(fricas_InputForm): Some checks for digamma and polygamma (:trac:`31853`):: - sage: fricas.digamma(1.0) # optional - fricas + sage: fricas.digamma(1.0) # optional - fricas - 0.5772156649_0153286061 sage: psi(1.0) -0.577215664901533 - sage: fricas.polygamma(1, 1.0) # optional - fricas + sage: fricas.polygamma(1, 1.0) # optional - fricas 1.6449340668482269 sage: psi(1, 1).n() 1.64493406684823 + sage: var("w") + w + sage: fricas.laplace(log(x), x, w).sage() # optional - fricas + -(euler_gamma + log(w))/w + sage: fricas(laplace(log(x), x, w)).sage() # optional - fricas + -(euler_gamma + log(w))/w + Check that :trac:`25224` is fixed:: sage: integrate(log(x)/(1-x),x,algorithm='fricas') # optional - fricas diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index a223991c08d..a24bbcebc38 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -955,7 +955,8 @@ def __init__(self, name='euler_gamma'): """ conversions = dict(kash='EulerGamma(R)', maple='gamma', mathematica='EulerGamma', pari='Euler', - maxima='%gamma', pynac='Euler', giac='euler_gamma') + maxima='%gamma', pynac='Euler', giac='euler_gamma', + fricas='-digamma(1)') Constant.__init__(self, name, conversions=conversions, latex=r'\gamma', domain='positive') From 4fe09c451cd72675be7e88846acd8e95b682fadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 May 2021 17:28:52 +0200 Subject: [PATCH 417/706] add method stack_sort for permutations --- src/sage/combinat/permutation.py | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 42f858f8cb1..6a1e031ba78 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -49,6 +49,7 @@ :meth:`~sage.combinat.permutation.Permutation.rank` | Returns the rank of ``self`` in lexicographic ordering (on the symmetric group containing ``self``). :meth:`~sage.combinat.permutation.Permutation.to_inversion_vector` | Returns the inversion vector of a permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.inversions` | Returns a list of the inversions of permutation ``self``. + :meth:`~sage.combinat.permutation.Permutation.stack_sort` | Returns the permutation obtained by sorting ``self`` through one stack. :meth:`~sage.combinat.permutation.Permutation.to_digraph` | Return a digraph representation of ``self``. :meth:`~sage.combinat.permutation.Permutation.show` | Displays the permutation as a drawing. :meth:`~sage.combinat.permutation.Permutation.number_of_inversions` | Returns the number of inversions in the permutation ``self``. @@ -1597,6 +1598,49 @@ def inversions(self): return [tuple([i+1,j+1]) for i in range(n-1) for j in range(i+1,n) if p[i]>p[j]] + def stack_sort(self) -> "Permutation": + """ + Return the stack sort of a permutation. + + This is another permutation obtained through the + process of sorting using one stack. If the result is the identity + permutation, the original permutation is *stack-sortable*. + + See :wikipedia:`Stack-sortable_permutation` + + EXAMPLES:: + + sage: p = Permutation([2,1,5,3,4,9,7,8,6]) + sage: p.stack_sort() + [1, 2, 3, 4, 5, 7, 6, 8, 9] + + sage: S5 = Permutations(5) + sage: len([1 for s in S5 if s.stack_sort() == S5.one()]) + 42 + + TESTS:: + + sage: p = Permutation([]) + sage: p.stack_sort() + [] + sage: p = Permutation([1]) + sage: p.stack_sort() + [1] + """ + stack = [] + sorted_p = [] + for j in self: + if stack: + for i in reversed(stack): + if i < j: + sorted_p.append(i) + stack.pop() + else: + break + stack.append(j) + sorted_p.extend(reversed(stack)) + return Permutation(sorted_p) + def to_digraph(self): r""" Return a digraph representation of ``self``. From e785da44e02d8b48cb7c1fd02017027bbd2b4788 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 25 May 2021 17:30:02 +0200 Subject: [PATCH 418/706] fix translation of Gamma --- src/sage/interfaces/fricas.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 9c5f201a073..caa76c289cc 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1569,6 +1569,13 @@ def _sage_expression(fricas_InputForm): 0 sage: fricas(A).D(x).sage() - diff(A, x) # optional - fricas 0 + + Check that :trac:`31858` is fixed:: + + sage: fricas.Gamma(3/4).sage() # optional - fricas + gamma(3/4) + sage: fricas.Gamma(3, 2).sage() # optional - fricas + gamma(3, 2) """ from sage.libs.pynac.pynac import register_symbol from sage.symbolic.constants import e, pi, I @@ -1577,6 +1584,7 @@ def _sage_expression(fricas_InputForm): from sage.functions.trig import sin, cos, tan, cot, sec, csc from sage.functions.hyperbolic import tanh, sinh, cosh, coth, sech, csch from sage.functions.other import abs + from sage.functions.gamma import gamma from sage.misc.functional import symbolic_sum, symbolic_prod from sage.rings.infinity import infinity register_symbol(I, {'fricas': '%i'}) @@ -1597,6 +1605,7 @@ def _sage_expression(fricas_InputForm): register_symbol(coth, {'fricas': 'coth'}) register_symbol(sech, {'fricas': 'sech'}) register_symbol(csch, {'fricas': 'csch'}) + register_symbol(gamma, {'fricas': 'Gamma'}) register_symbol(lambda x, y: x + y, {'fricas': '+'}) register_symbol(lambda x, y: x - y, {'fricas': '-'}) register_symbol(lambda x, y: x * y, {'fricas': '*'}) From bec32a1159ad9eef2f8abdd9440fd042662a3b27 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 25 May 2021 17:31:56 +0200 Subject: [PATCH 419/706] add classical doctest --- src/sage/interfaces/fricas.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index caa76c289cc..de28818aa29 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -1572,6 +1572,8 @@ def _sage_expression(fricas_InputForm): Check that :trac:`31858` is fixed:: + sage: fricas.Gamma(3/2).sage() # optional - fricas + 1/2*sqrt(pi) sage: fricas.Gamma(3/4).sage() # optional - fricas gamma(3/4) sage: fricas.Gamma(3, 2).sage() # optional - fricas From 25c62c109d27bebd27008804a8b26fb130bcccfd Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 25 May 2021 13:06:02 -0700 Subject: [PATCH 420/706] fixed bugs --- src/sage/modules/free_module_homspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 699db08caad..c850bf1d929 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -187,8 +187,8 @@ def __call__(self, A, check=True): # Let us hope that FreeModuleMorphism knows to handle # that case pass - if not(self.codomain().has_coerce_map_from(self.domain())) and not(A.is_zero()): - raise ValueError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") + if not(self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring())) and not(A.is_zero()): + raise RuntimeError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") return free_module_morphism.FreeModuleMorphism(self, A) @cached_method From 4d9fd9ae99ddc64e42b0637225703ad5ec83a134 Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 25 May 2021 13:08:17 -0700 Subject: [PATCH 421/706] changed the error type --- src/sage/modules/free_module_homspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index c850bf1d929..f8b1aa230e9 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -188,7 +188,7 @@ def __call__(self, A, check=True): # that case pass if not(self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring())) and not(A.is_zero()): - raise RuntimeError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") + raise TypeError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") return free_module_morphism.FreeModuleMorphism(self, A) @cached_method From b7b63517d416115733a8e2e57d4409d93a3c3176 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 27 May 2021 11:09:24 +0100 Subject: [PATCH 422/706] Added gauss_legendre reference file sage/numerical/gauss_legendre was added to the index.rst file. Documentation is now built for the reference manual. --- src/doc/en/reference/numerical/index.rst | 1 + src/sage/numerical/gauss_legendre.pyx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/numerical/index.rst b/src/doc/en/reference/numerical/index.rst index 254faadec8f..6a17afe2dfb 100644 --- a/src/doc/en/reference/numerical/index.rst +++ b/src/doc/en/reference/numerical/index.rst @@ -13,6 +13,7 @@ Numerical Optimization sage/numerical/linear_tensor_constraints sage/numerical/optimize sage/numerical/interactive_simplex_method + sage/numerical/gauss_legendre Linear Optimization (LP) and Mixed Integer Linear Optimization (MIP) Solver backends ------------------------------------------------------------------------------------ diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 2fcc2d7c797..d03280070df 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -3,12 +3,12 @@ Gauss-Legendre integration for vector-valued functions Routine to perform Gauss-Legendre integration for vector-functions. +EXAMPLES:: + AUTHORS: - Nils Bruin (2017-06-06): initial version -EXAMPLES:: - NOTE: The code here is directly based on mpmath (see http://mpmath.org), but has a highly From 0bba178a95a4452e33981b966a7a88d1f3f4a92a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 27 May 2021 12:46:29 +0100 Subject: [PATCH 423/706] fix some 32 vs 64 bit discrepancies in tests --- src/sage/libs/pari/__init__.py | 6 ++++-- src/sage/libs/pari/tests.py | 7 ++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/pari/__init__.py b/src/sage/libs/pari/__init__.py index e9112b2e3e1..507b98c5098 100644 --- a/src/sage/libs/pari/__init__.py +++ b/src/sage/libs/pari/__init__.py @@ -163,10 +163,12 @@ sage: e = pari([0,0,0,-82,0]).ellinit() sage: eta1 = e.elleta(precision=50)[0] sage: eta1.sage() - 3.6054636014326520859158205642077267748 + 3.6054636014326520859158205642077267748 # 64-bit + 3.605463601432652085915820564 # 32-bit sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() - 3.605463601432652085915820564207726774810268996598024745444380641429820491740 + 3.605463601432652085915820564207726774810268996598024745444380641429820491740 # 64-bit + 3.60546360143265208591582056420772677481026899659802474544 # 32-bit """ diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index 2aa382269a4..f34b30146d4 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -1762,11 +1762,12 @@ sage: e = pari([0,0,0,-82,0]).ellinit() sage: eta1 = e.elleta(precision=50)[0] sage: eta1.sage() - 3.6054636014326520859158205642077267748 + 3.6054636014326520859158205642077267748 # 64-bit + 3.605463601432652085915820564 # 32-bit sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() - 3.605463601432652085915820564207726774810268996598024745444380641429820491740 - + 3.605463601432652085915820564207726774810268996598024745444380641429820491740 # 64-bit + 3.60546360143265208591582056420772677481026899659802474544 # 32-bit sage: from cypari2 import Pari sage: pari = Pari() From 32b52cc022fbc68463bb0206c8e1fe1e181bec84 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 27 May 2021 14:47:08 +0200 Subject: [PATCH 424/706] add tag #random --- src/sage/rings/padics/relaxed_template.pxi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/relaxed_template.pxi b/src/sage/rings/padics/relaxed_template.pxi index ba15d4f5d3c..9ef4ea4e704 100644 --- a/src/sage/rings/padics/relaxed_template.pxi +++ b/src/sage/rings/padics/relaxed_template.pxi @@ -2637,9 +2637,9 @@ cdef class RelaxedElement_random(RelaxedElementWithDigits): It is guaranteed that `a` and `b` are equal at any precision:: - sage: a[:30] + sage: a[:30] # random ...?343214211432220241412003314311 - sage: b[:30] + sage: b[:30] # random ...?343214211432220241412003314311 sage: a == b From a5f537642e6f0fe740395f22d8ebf33d731d0880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 27 May 2021 15:04:12 +0200 Subject: [PATCH 425/706] 31865: fixing doctest --- src/sage/symbolic/integration/external.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 76604397d57..d83c448ea9f 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -156,6 +156,7 @@ def request_wolfram_alpha(input, verbose=False): 'error', 'host', 'id', + 'inputstring', 'numpods', 'parsetimedout', 'parsetiming', From fd87c9560169f249778915246e1b45500717e79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 27 May 2021 15:12:44 +0200 Subject: [PATCH 426/706] 31866: fixing 3 doctests --- src/sage/databases/oeis.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index ee8f31a75c4..4ad63ab25d9 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -48,12 +48,13 @@ sage: c.examples() # optional -- internet 0: Pi = 3.1415926535897932384... 1: = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + ...)))) - 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...] + 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...]. sage: c.comments() # optional -- internet 0: The first 5821569425 terms were computed by _Eric W. Weisstein_ on Sep 18 2011. 1: The first 10672905501 terms were computed by _Eric W. Weisstein_ on Jul 17 2013. 2: The first 15000000000 terms were computed by _Eric W. Weisstein_ on Jul 27 2013. + 3: The first 30113021586 terms were computed by _Syed Fahad_ on Apr 27 2021. :: @@ -1706,7 +1707,7 @@ def examples(self): sage: c.examples() # optional -- internet 0: Pi = 3.1415926535897932384... 1: = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + ...)))) - 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...] + 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...]. TESTS:: From 402e8e4d2941c9af52d56e3012d57c6f3d74acba Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 27 May 2021 16:21:47 +0200 Subject: [PATCH 427/706] update to newest beta --- build/pkgs/memory_allocator/SPKG.rst | 13 ++++++++++--- build/pkgs/memory_allocator/checksums.ini | 9 ++++----- build/pkgs/memory_allocator/package-version.txt | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/build/pkgs/memory_allocator/SPKG.rst b/build/pkgs/memory_allocator/SPKG.rst index 9a4b057f8f3..42975c02f55 100644 --- a/build/pkgs/memory_allocator/SPKG.rst +++ b/build/pkgs/memory_allocator/SPKG.rst @@ -1,12 +1,14 @@ MemoryAllocator: An extension class to allocate memory easily with cython. ========================================================================== +This extension class started as part of the Sage software. + Description ----------- -development website: https://github.com/kliem/memory_allocator +development website: https://github.com/sagemath/memory_allocator -PyPI page: XXX +PyPI page: https://pypi.org/project/memory_allocator License ------- @@ -16,4 +18,9 @@ GPL-3.0 Upstream Contact ---------------- -https://github.com/kliem/memory_allocator +https://github.com/sagemath/memory_allocator + +Dependencies +------------ + +- Cython diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini index eb282679743..14e6cf2a2b4 100644 --- a/build/pkgs/memory_allocator/checksums.ini +++ b/build/pkgs/memory_allocator/checksums.ini @@ -1,5 +1,4 @@ -tarball=pycparser-VERSION.tar.gz -sha1=a016b58e101e9ba9bddc01c40aab3baca048c056 -md5=af75874705418a4d4d7e7333d3318909 -cksum=1342787485 -upstream_url=https://github.com/kliem/memory_allocator/archive/refs/tags/vVERSION.tar.gz +sha1=d0eeb3be4462a41b316086a33f4dfbca08b887b4 +md5=74c8a21dd6e79452bae99881d40ab2f9 +cksum=3681970071 +upstream_url=https://github.com/sagemath/memory_allocator/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/memory_allocator/package-version.txt b/build/pkgs/memory_allocator/package-version.txt index 388bb06819f..4ab2ff64ead 100644 --- a/build/pkgs/memory_allocator/package-version.txt +++ b/build/pkgs/memory_allocator/package-version.txt @@ -1 +1 @@ -0.1.0-alpha +0.1.0-beta.1 From 229a8e0e386527733a991d4702bd31473f595fdd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 13:05:59 -0700 Subject: [PATCH 428/706] build/pkgs/singular: Update to 4.2.0p3 --- build/pkgs/singular/SPKG.rst | 3 --- build/pkgs/singular/checksums.ini | 8 ++++---- build/pkgs/singular/package-version.txt | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build/pkgs/singular/SPKG.rst b/build/pkgs/singular/SPKG.rst index b754451aac7..31c09493083 100644 --- a/build/pkgs/singular/SPKG.rst +++ b/build/pkgs/singular/SPKG.rst @@ -35,9 +35,6 @@ Dependencies Special Update/Build Instructions --------------------------------- -The current upstream tarball is made from the branch at -https://github.com/mkoeppe/Singular/tree/Release-4-2-0-p1%2Bsage - Other notes: - If the environment variable SAGE_DEBUG is set to "yes", then diff --git a/build/pkgs/singular/checksums.ini b/build/pkgs/singular/checksums.ini index 01b340193bf..549f039ff70 100644 --- a/build/pkgs/singular/checksums.ini +++ b/build/pkgs/singular/checksums.ini @@ -1,5 +1,5 @@ tarball=singular-VERSION.tar.gz -sha1=dc0f618a669fb8c5e56166d82edc494a4e5504be -md5=2354d9456c9fad0353b3157aff82796f -cksum=2872654555 -upstream_url=https://github.com/mkoeppe/Singular/releases/download/singular-VERSION/singular-VERSION.tar.gz +sha1=3b3287b54ba42b884a2be12c0f1b9b5d812636bd +md5=39d9d121ab8a6d2571a4f34e4d66b235 +cksum=987736501 +upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/4-2-0/singular-VERSION.tar.gz diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 59d3d633161..8a498833f9c 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.2.0p1+2021-04-06+sage +4.2.0p3 From e694b15834292f2962862176c2de8714731ced05 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Sep 2020 09:01:38 -0700 Subject: [PATCH 429/706] build/pkgs/tox: Make it a standard package --- build/pkgs/tox/checksums.ini | 5 +++++ build/pkgs/tox/package-version.txt | 1 + build/pkgs/tox/requirements.txt | 1 - build/pkgs/tox/spkg-install.in | 1 + build/pkgs/tox/type | 2 +- 5 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 build/pkgs/tox/checksums.ini create mode 100644 build/pkgs/tox/package-version.txt delete mode 100644 build/pkgs/tox/requirements.txt create mode 100644 build/pkgs/tox/spkg-install.in diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini new file mode 100644 index 00000000000..2ae4ae99f72 --- /dev/null +++ b/build/pkgs/tox/checksums.ini @@ -0,0 +1,5 @@ +tarball=tox-VERSION.tar.gz +sha1=a2a435c46cc0f03a55413506cb9d737e62d6047d +md5=e3219ce6f0543141244b77cd701b0ea1 +cksum=2446556337 +upstream_url=https://pypi.io/packages/source/t/tox/tox-VERSION.tar.gz diff --git a/build/pkgs/tox/package-version.txt b/build/pkgs/tox/package-version.txt new file mode 100644 index 00000000000..eb9b76c9f59 --- /dev/null +++ b/build/pkgs/tox/package-version.txt @@ -0,0 +1 @@ +3.20.0 diff --git a/build/pkgs/tox/requirements.txt b/build/pkgs/tox/requirements.txt deleted file mode 100644 index 053148f8486..00000000000 --- a/build/pkgs/tox/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -tox diff --git a/build/pkgs/tox/spkg-install.in b/build/pkgs/tox/spkg-install.in new file mode 100644 index 00000000000..deba1bb42bb --- /dev/null +++ b/build/pkgs/tox/spkg-install.in @@ -0,0 +1 @@ +cd src && sdh_pip_install . diff --git a/build/pkgs/tox/type b/build/pkgs/tox/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/tox/type +++ b/build/pkgs/tox/type @@ -1 +1 @@ -optional +standard From 9c7a1c2333882039d19da02872a691808c0f9e22 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 27 Dec 2020 10:02:51 -0800 Subject: [PATCH 430/706] build/pkgs: Add tox dependencies: filelock py virtualenv pluggy --- build/pkgs/filelock/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/filelock/checksums.ini | 5 +++++ build/pkgs/filelock/dependencies | 4 ++++ build/pkgs/filelock/install-requires.txt | 1 + build/pkgs/filelock/package-version.txt | 1 + build/pkgs/filelock/spkg-install.in | 2 ++ build/pkgs/filelock/type | 1 + build/pkgs/pluggy/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/pluggy/checksums.ini | 5 +++++ build/pkgs/pluggy/dependencies | 4 ++++ build/pkgs/pluggy/install-requires.txt | 1 + build/pkgs/pluggy/package-version.txt | 1 + build/pkgs/pluggy/spkg-install.in | 2 ++ build/pkgs/pluggy/type | 1 + build/pkgs/py/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/py/checksums.ini | 5 +++++ build/pkgs/py/dependencies | 4 ++++ build/pkgs/py/install-requires.txt | 1 + build/pkgs/py/package-version.txt | 1 + build/pkgs/py/spkg-install.in | 2 ++ build/pkgs/py/type | 1 + build/pkgs/tox/dependencies | 2 +- build/pkgs/virtualenv/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/virtualenv/checksums.ini | 5 +++++ build/pkgs/virtualenv/dependencies | 4 ++++ build/pkgs/virtualenv/install-requires.txt | 1 + build/pkgs/virtualenv/package-version.txt | 1 + build/pkgs/virtualenv/spkg-install.in | 2 ++ build/pkgs/virtualenv/type | 1 + 29 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/filelock/SPKG.rst create mode 100644 build/pkgs/filelock/checksums.ini create mode 100644 build/pkgs/filelock/dependencies create mode 100644 build/pkgs/filelock/install-requires.txt create mode 100644 build/pkgs/filelock/package-version.txt create mode 100644 build/pkgs/filelock/spkg-install.in create mode 100644 build/pkgs/filelock/type create mode 100644 build/pkgs/pluggy/SPKG.rst create mode 100644 build/pkgs/pluggy/checksums.ini create mode 100644 build/pkgs/pluggy/dependencies create mode 100644 build/pkgs/pluggy/install-requires.txt create mode 100644 build/pkgs/pluggy/package-version.txt create mode 100644 build/pkgs/pluggy/spkg-install.in create mode 100644 build/pkgs/pluggy/type create mode 100644 build/pkgs/py/SPKG.rst create mode 100644 build/pkgs/py/checksums.ini create mode 100644 build/pkgs/py/dependencies create mode 100644 build/pkgs/py/install-requires.txt create mode 100644 build/pkgs/py/package-version.txt create mode 100644 build/pkgs/py/spkg-install.in create mode 100644 build/pkgs/py/type create mode 100644 build/pkgs/virtualenv/SPKG.rst create mode 100644 build/pkgs/virtualenv/checksums.ini create mode 100644 build/pkgs/virtualenv/dependencies create mode 100644 build/pkgs/virtualenv/install-requires.txt create mode 100644 build/pkgs/virtualenv/package-version.txt create mode 100644 build/pkgs/virtualenv/spkg-install.in create mode 100644 build/pkgs/virtualenv/type diff --git a/build/pkgs/filelock/SPKG.rst b/build/pkgs/filelock/SPKG.rst new file mode 100644 index 00000000000..b83beec0cc9 --- /dev/null +++ b/build/pkgs/filelock/SPKG.rst @@ -0,0 +1,18 @@ +filelock +======== + +Description +----------- + +A platform independent file lock. + +License +------- + +Public Domain + +Upstream Contact +---------------- + +https://pypi.org/project/filelock/ + diff --git a/build/pkgs/filelock/checksums.ini b/build/pkgs/filelock/checksums.ini new file mode 100644 index 00000000000..a7a437ebb3c --- /dev/null +++ b/build/pkgs/filelock/checksums.ini @@ -0,0 +1,5 @@ +tarball=filelock-VERSION.tar.gz +sha1=ca03bf213ee1d7a9b6353cebc265072aae40fdcb +md5=c1fe6d9a7433a7ca6ce4f36e273317d1 +cksum=2927344437 +upstream_url=https://pypi.io/packages/source/f/filelock/filelock-VERSION.tar.gz diff --git a/build/pkgs/filelock/dependencies b/build/pkgs/filelock/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/filelock/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/filelock/install-requires.txt b/build/pkgs/filelock/install-requires.txt new file mode 100644 index 00000000000..83c2e35706e --- /dev/null +++ b/build/pkgs/filelock/install-requires.txt @@ -0,0 +1 @@ +filelock diff --git a/build/pkgs/filelock/package-version.txt b/build/pkgs/filelock/package-version.txt new file mode 100644 index 00000000000..f93fc9f42ea --- /dev/null +++ b/build/pkgs/filelock/package-version.txt @@ -0,0 +1 @@ +3.0.12 diff --git a/build/pkgs/filelock/spkg-install.in b/build/pkgs/filelock/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/filelock/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/filelock/type b/build/pkgs/filelock/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/filelock/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pluggy/SPKG.rst b/build/pkgs/pluggy/SPKG.rst new file mode 100644 index 00000000000..61fa263e874 --- /dev/null +++ b/build/pkgs/pluggy/SPKG.rst @@ -0,0 +1,18 @@ +pluggy +====== + +Description +----------- + +plugin and hook calling mechanisms for python + +License +------- + +MIT license + +Upstream Contact +---------------- + +https://pypi.org/project/pluggy/ + diff --git a/build/pkgs/pluggy/checksums.ini b/build/pkgs/pluggy/checksums.ini new file mode 100644 index 00000000000..9a6e585fcc9 --- /dev/null +++ b/build/pkgs/pluggy/checksums.ini @@ -0,0 +1,5 @@ +tarball=pluggy-VERSION.tar.gz +sha1=828b2c10996d902b8c47f2fded0e101c636b9ff9 +md5=7f610e28b8b34487336b585a3dfb803d +cksum=3074963981 +upstream_url=https://pypi.io/packages/source/p/pluggy/pluggy-VERSION.tar.gz diff --git a/build/pkgs/pluggy/dependencies b/build/pkgs/pluggy/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/pluggy/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pluggy/install-requires.txt b/build/pkgs/pluggy/install-requires.txt new file mode 100644 index 00000000000..11bdb5c1f5f --- /dev/null +++ b/build/pkgs/pluggy/install-requires.txt @@ -0,0 +1 @@ +pluggy diff --git a/build/pkgs/pluggy/package-version.txt b/build/pkgs/pluggy/package-version.txt new file mode 100644 index 00000000000..c317a91891f --- /dev/null +++ b/build/pkgs/pluggy/package-version.txt @@ -0,0 +1 @@ +0.13.1 diff --git a/build/pkgs/pluggy/spkg-install.in b/build/pkgs/pluggy/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/pluggy/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/pluggy/type b/build/pkgs/pluggy/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pluggy/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/py/SPKG.rst b/build/pkgs/py/SPKG.rst new file mode 100644 index 00000000000..b0d28f93e9a --- /dev/null +++ b/build/pkgs/py/SPKG.rst @@ -0,0 +1,18 @@ +py +== + +Description +----------- + +library with cross-python path, ini-parsing, io, code, log facilities + +License +------- + +MIT license + +Upstream Contact +---------------- + +https://pypi.org/project/py/ + diff --git a/build/pkgs/py/checksums.ini b/build/pkgs/py/checksums.ini new file mode 100644 index 00000000000..ea29f7298f5 --- /dev/null +++ b/build/pkgs/py/checksums.ini @@ -0,0 +1,5 @@ +tarball=py-VERSION.tar.gz +sha1=690e4e3dcaeafe02ad4af36233148e7e10032d1a +md5=5f108bfe00d5468cbdb8071051f86a55 +cksum=220330409 +upstream_url=https://pypi.io/packages/source/p/py/py-VERSION.tar.gz diff --git a/build/pkgs/py/dependencies b/build/pkgs/py/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/py/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/py/install-requires.txt b/build/pkgs/py/install-requires.txt new file mode 100644 index 00000000000..edfce786a4d --- /dev/null +++ b/build/pkgs/py/install-requires.txt @@ -0,0 +1 @@ +py diff --git a/build/pkgs/py/package-version.txt b/build/pkgs/py/package-version.txt new file mode 100644 index 00000000000..81c871de46b --- /dev/null +++ b/build/pkgs/py/package-version.txt @@ -0,0 +1 @@ +1.10.0 diff --git a/build/pkgs/py/spkg-install.in b/build/pkgs/py/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/py/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/py/type b/build/pkgs/py/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/py/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/tox/dependencies b/build/pkgs/tox/dependencies index 7b139dc904c..a8747364ef2 100644 --- a/build/pkgs/tox/dependencies +++ b/build/pkgs/tox/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) packaging six | $(PYTHON_TOOLCHAIN) +$(PYTHON) packaging six filelock pluggy py toml virtualenv importlib_metadata | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/virtualenv/SPKG.rst b/build/pkgs/virtualenv/SPKG.rst new file mode 100644 index 00000000000..928b7175121 --- /dev/null +++ b/build/pkgs/virtualenv/SPKG.rst @@ -0,0 +1,18 @@ +virtualenv +========== + +Description +----------- + +Virtual Python Environment builder + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/virtualenv/ + diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini new file mode 100644 index 00000000000..8992f7e3624 --- /dev/null +++ b/build/pkgs/virtualenv/checksums.ini @@ -0,0 +1,5 @@ +tarball=virtualenv-VERSION.tar.gz +sha1=fd4d72b017ca7e3e64f2ec2f890e45857cd9c510 +md5=ad5f4586b6a03088a399692949712007 +cksum=300371404 +upstream_url=https://pypi.io/packages/source/v/virtualenv/virtualenv-VERSION.tar.gz diff --git a/build/pkgs/virtualenv/dependencies b/build/pkgs/virtualenv/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/virtualenv/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/virtualenv/install-requires.txt b/build/pkgs/virtualenv/install-requires.txt new file mode 100644 index 00000000000..66072c76450 --- /dev/null +++ b/build/pkgs/virtualenv/install-requires.txt @@ -0,0 +1 @@ +virtualenv diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt new file mode 100644 index 00000000000..5e83434edb2 --- /dev/null +++ b/build/pkgs/virtualenv/package-version.txt @@ -0,0 +1 @@ +20.2.2 diff --git a/build/pkgs/virtualenv/spkg-install.in b/build/pkgs/virtualenv/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/virtualenv/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/virtualenv/type b/build/pkgs/virtualenv/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/virtualenv/type @@ -0,0 +1 @@ +standard From cc2f4ae779d1dc5b03ee94f0f5e1e17c422363ad Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 1 Jan 2021 20:31:11 -0800 Subject: [PATCH 431/706] build/pkgs/{distlib,appdirs,importlib_resources}: Add dependencies of virtualenv --- build/pkgs/appdirs/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/appdirs/checksums.ini | 5 +++++ build/pkgs/appdirs/dependencies | 4 ++++ build/pkgs/appdirs/install-requires.txt | 1 + build/pkgs/appdirs/package-version.txt | 1 + build/pkgs/appdirs/spkg-install.in | 2 ++ build/pkgs/appdirs/type | 1 + build/pkgs/distlib/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/distlib/checksums.ini | 5 +++++ build/pkgs/distlib/dependencies | 4 ++++ build/pkgs/distlib/install-requires.txt | 1 + build/pkgs/distlib/package-version.txt | 1 + build/pkgs/distlib/spkg-install.in | 2 ++ build/pkgs/distlib/type | 1 + build/pkgs/importlib_resources/SPKG.rst | 16 ++++++++++++++++ build/pkgs/importlib_resources/checksums.ini | 5 +++++ build/pkgs/importlib_resources/dependencies | 4 ++++ .../importlib_resources/install-requires.txt | 1 + .../importlib_resources/package-version.txt | 1 + build/pkgs/importlib_resources/spkg-install.in | 2 ++ build/pkgs/importlib_resources/type | 1 + build/pkgs/virtualenv/dependencies | 2 +- 22 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/appdirs/SPKG.rst create mode 100644 build/pkgs/appdirs/checksums.ini create mode 100644 build/pkgs/appdirs/dependencies create mode 100644 build/pkgs/appdirs/install-requires.txt create mode 100644 build/pkgs/appdirs/package-version.txt create mode 100644 build/pkgs/appdirs/spkg-install.in create mode 100644 build/pkgs/appdirs/type create mode 100644 build/pkgs/distlib/SPKG.rst create mode 100644 build/pkgs/distlib/checksums.ini create mode 100644 build/pkgs/distlib/dependencies create mode 100644 build/pkgs/distlib/install-requires.txt create mode 100644 build/pkgs/distlib/package-version.txt create mode 100644 build/pkgs/distlib/spkg-install.in create mode 100644 build/pkgs/distlib/type create mode 100644 build/pkgs/importlib_resources/SPKG.rst create mode 100644 build/pkgs/importlib_resources/checksums.ini create mode 100644 build/pkgs/importlib_resources/dependencies create mode 100644 build/pkgs/importlib_resources/install-requires.txt create mode 100644 build/pkgs/importlib_resources/package-version.txt create mode 100644 build/pkgs/importlib_resources/spkg-install.in create mode 100644 build/pkgs/importlib_resources/type diff --git a/build/pkgs/appdirs/SPKG.rst b/build/pkgs/appdirs/SPKG.rst new file mode 100644 index 00000000000..6018e327f90 --- /dev/null +++ b/build/pkgs/appdirs/SPKG.rst @@ -0,0 +1,18 @@ +appdirs: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". +========================================================================================================== + +Description +----------- + +A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir". + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/appdirs/ + diff --git a/build/pkgs/appdirs/checksums.ini b/build/pkgs/appdirs/checksums.ini new file mode 100644 index 00000000000..7b26025094c --- /dev/null +++ b/build/pkgs/appdirs/checksums.ini @@ -0,0 +1,5 @@ +tarball=appdirs-VERSION.tar.gz +sha1=1fa04e44b1084338cb7b21e9cf44fce5efb81840 +md5=d6bca12613174185dd9abc8a29f4f012 +cksum=1191718163 +upstream_url=https://pypi.io/packages/source/a/appdirs/appdirs-VERSION.tar.gz diff --git a/build/pkgs/appdirs/dependencies b/build/pkgs/appdirs/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/appdirs/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/appdirs/install-requires.txt b/build/pkgs/appdirs/install-requires.txt new file mode 100644 index 00000000000..d64bc321a11 --- /dev/null +++ b/build/pkgs/appdirs/install-requires.txt @@ -0,0 +1 @@ +appdirs diff --git a/build/pkgs/appdirs/package-version.txt b/build/pkgs/appdirs/package-version.txt new file mode 100644 index 00000000000..1c99cf0e809 --- /dev/null +++ b/build/pkgs/appdirs/package-version.txt @@ -0,0 +1 @@ +1.4.4 diff --git a/build/pkgs/appdirs/spkg-install.in b/build/pkgs/appdirs/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/appdirs/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/appdirs/type b/build/pkgs/appdirs/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/appdirs/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/distlib/SPKG.rst b/build/pkgs/distlib/SPKG.rst new file mode 100644 index 00000000000..a4496c0427a --- /dev/null +++ b/build/pkgs/distlib/SPKG.rst @@ -0,0 +1,18 @@ +distlib: Distribution utilities +=============================== + +Description +----------- + +Distribution utilities + +License +------- + +Python license + +Upstream Contact +---------------- + +https://pypi.org/project/distlib/ + diff --git a/build/pkgs/distlib/checksums.ini b/build/pkgs/distlib/checksums.ini new file mode 100644 index 00000000000..718023bc038 --- /dev/null +++ b/build/pkgs/distlib/checksums.ini @@ -0,0 +1,5 @@ +tarball=distlib-VERSION.zip +sha1=1c575431e31c32d25596c360e81bba7fe4638669 +md5=4baf787d8aceb260d6f77cb31bf27cf6 +cksum=2902365751 +upstream_url=https://pypi.io/packages/source/d/distlib/distlib-VERSION.zip diff --git a/build/pkgs/distlib/dependencies b/build/pkgs/distlib/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/distlib/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/distlib/install-requires.txt b/build/pkgs/distlib/install-requires.txt new file mode 100644 index 00000000000..f68bb07272d --- /dev/null +++ b/build/pkgs/distlib/install-requires.txt @@ -0,0 +1 @@ +distlib diff --git a/build/pkgs/distlib/package-version.txt b/build/pkgs/distlib/package-version.txt new file mode 100644 index 00000000000..9e11b32fcaa --- /dev/null +++ b/build/pkgs/distlib/package-version.txt @@ -0,0 +1 @@ +0.3.1 diff --git a/build/pkgs/distlib/spkg-install.in b/build/pkgs/distlib/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/distlib/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/distlib/type b/build/pkgs/distlib/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/distlib/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/importlib_resources/SPKG.rst b/build/pkgs/importlib_resources/SPKG.rst new file mode 100644 index 00000000000..3cb5d240ced --- /dev/null +++ b/build/pkgs/importlib_resources/SPKG.rst @@ -0,0 +1,16 @@ +importlib_resources: Read resources from Python packages +======================================================== + +Description +----------- + +Read resources from Python packages + +License +------- + +Upstream Contact +---------------- + +https://pypi.org/project/importlib-resources/ + diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini new file mode 100644 index 00000000000..70264e8d630 --- /dev/null +++ b/build/pkgs/importlib_resources/checksums.ini @@ -0,0 +1,5 @@ +tarball=importlib_resources-VERSION.tar.gz +sha1=0013136267182579db0ae1df25274a704db896fc +md5=9e6819c7f5c06d7fa7821321b7038af4 +cksum=1660967409 +upstream_url=https://pypi.io/packages/source/i/importlib_resources/importlib_resources-VERSION.tar.gz diff --git a/build/pkgs/importlib_resources/dependencies b/build/pkgs/importlib_resources/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/importlib_resources/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/importlib_resources/install-requires.txt b/build/pkgs/importlib_resources/install-requires.txt new file mode 100644 index 00000000000..2b0146fc669 --- /dev/null +++ b/build/pkgs/importlib_resources/install-requires.txt @@ -0,0 +1 @@ +importlib-resources diff --git a/build/pkgs/importlib_resources/package-version.txt b/build/pkgs/importlib_resources/package-version.txt new file mode 100644 index 00000000000..627a3f43a64 --- /dev/null +++ b/build/pkgs/importlib_resources/package-version.txt @@ -0,0 +1 @@ +4.1.1 diff --git a/build/pkgs/importlib_resources/spkg-install.in b/build/pkgs/importlib_resources/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/importlib_resources/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/importlib_resources/type b/build/pkgs/importlib_resources/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/importlib_resources/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/virtualenv/dependencies b/build/pkgs/virtualenv/dependencies index 0738c2d7777..fe7890ee1d0 100644 --- a/build/pkgs/virtualenv/dependencies +++ b/build/pkgs/virtualenv/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) appdirs distlib filelock six importlib_metadata importlib_resources | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From 5488ecd9be89b3bc60be1fe58b5544b62dc562ce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 16:04:17 -0700 Subject: [PATCH 432/706] build/pkgs: Update tox and dependencies to latest --- build/pkgs/appdirs/type | 2 +- build/pkgs/distlib/type | 2 +- build/pkgs/filelock/SPKG.rst | 4 ++-- build/pkgs/importlib_resources/SPKG.rst | 2 ++ build/pkgs/importlib_resources/checksums.ini | 6 +++--- build/pkgs/importlib_resources/package-version.txt | 2 +- build/pkgs/importlib_resources/type | 2 +- build/pkgs/pluggy/SPKG.rst | 4 ++-- build/pkgs/py/SPKG.rst | 4 ++-- build/pkgs/tox/SPKG.rst | 13 +++++-------- build/pkgs/tox/checksums.ini | 6 +++--- build/pkgs/tox/install-requires.txt | 3 +-- build/pkgs/tox/package-version.txt | 2 +- build/pkgs/tox/spkg-install.in | 3 ++- build/pkgs/virtualenv/SPKG.rst | 4 ++-- build/pkgs/virtualenv/checksums.ini | 6 +++--- build/pkgs/virtualenv/package-version.txt | 2 +- 17 files changed, 33 insertions(+), 34 deletions(-) diff --git a/build/pkgs/appdirs/type b/build/pkgs/appdirs/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/appdirs/type +++ b/build/pkgs/appdirs/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/distlib/type b/build/pkgs/distlib/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/distlib/type +++ b/build/pkgs/distlib/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/filelock/SPKG.rst b/build/pkgs/filelock/SPKG.rst index b83beec0cc9..d39b439be2e 100644 --- a/build/pkgs/filelock/SPKG.rst +++ b/build/pkgs/filelock/SPKG.rst @@ -1,5 +1,5 @@ -filelock -======== +filelock: A platform independent file lock +========================================== Description ----------- diff --git a/build/pkgs/importlib_resources/SPKG.rst b/build/pkgs/importlib_resources/SPKG.rst index 3cb5d240ced..ea519afbe99 100644 --- a/build/pkgs/importlib_resources/SPKG.rst +++ b/build/pkgs/importlib_resources/SPKG.rst @@ -9,6 +9,8 @@ Read resources from Python packages License ------- +Apache2 + Upstream Contact ---------------- diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini index 70264e8d630..7b2b8a13aee 100644 --- a/build/pkgs/importlib_resources/checksums.ini +++ b/build/pkgs/importlib_resources/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_resources-VERSION.tar.gz -sha1=0013136267182579db0ae1df25274a704db896fc -md5=9e6819c7f5c06d7fa7821321b7038af4 -cksum=1660967409 +sha1=d4b853132e9b9a0c58610e23df380c84be428c08 +md5=a4586b3cbb3d39c7a5e7ffc49d9ceb53 +cksum=1484460315 upstream_url=https://pypi.io/packages/source/i/importlib_resources/importlib_resources-VERSION.tar.gz diff --git a/build/pkgs/importlib_resources/package-version.txt b/build/pkgs/importlib_resources/package-version.txt index 627a3f43a64..76e9e619d63 100644 --- a/build/pkgs/importlib_resources/package-version.txt +++ b/build/pkgs/importlib_resources/package-version.txt @@ -1 +1 @@ -4.1.1 +5.1.4 diff --git a/build/pkgs/importlib_resources/type b/build/pkgs/importlib_resources/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/importlib_resources/type +++ b/build/pkgs/importlib_resources/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/pluggy/SPKG.rst b/build/pkgs/pluggy/SPKG.rst index 61fa263e874..b15967007ed 100644 --- a/build/pkgs/pluggy/SPKG.rst +++ b/build/pkgs/pluggy/SPKG.rst @@ -1,5 +1,5 @@ -pluggy -====== +pluggy: plugin and hook calling mechanisms for python +===================================================== Description ----------- diff --git a/build/pkgs/py/SPKG.rst b/build/pkgs/py/SPKG.rst index b0d28f93e9a..bf0409acda7 100644 --- a/build/pkgs/py/SPKG.rst +++ b/build/pkgs/py/SPKG.rst @@ -1,5 +1,5 @@ -py -== +py: library with cross-python path, ini-parsing, io, code, log facilities +========================================================================= Description ----------- diff --git a/build/pkgs/tox/SPKG.rst b/build/pkgs/tox/SPKG.rst index 007e28f02a3..442f4e85756 100644 --- a/build/pkgs/tox/SPKG.rst +++ b/build/pkgs/tox/SPKG.rst @@ -1,21 +1,18 @@ -tox: A command line driven CI frontend and development task automation tool -=========================================================================== +tox: tox is a generic virtualenv management and test command line tool +====================================================================== Description ----------- -Command line driven CI frontend and development task automation tool. - -The Sage library uses tox as an entry point for testing and linting. See ``src/tox.ini`` and ``sage --advanced``. - -Sage-the-distribution uses tox for portability testing. See ``SAGE_ROOT/tox.ini``. +tox is a generic virtualenv management and test command line tool License ------- -- MIT License +MIT Upstream Contact ---------------- https://pypi.org/project/tox/ + diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini index 2ae4ae99f72..c202fbcb281 100644 --- a/build/pkgs/tox/checksums.ini +++ b/build/pkgs/tox/checksums.ini @@ -1,5 +1,5 @@ tarball=tox-VERSION.tar.gz -sha1=a2a435c46cc0f03a55413506cb9d737e62d6047d -md5=e3219ce6f0543141244b77cd701b0ea1 -cksum=2446556337 +sha1=e98277cf47d1e4ca9dbaeeaf7e0e301700c9694b +md5=9fe85f2cab666ad764f5a076c4544956 +cksum=2996434561 upstream_url=https://pypi.io/packages/source/t/tox/tox-VERSION.tar.gz diff --git a/build/pkgs/tox/install-requires.txt b/build/pkgs/tox/install-requires.txt index eb752fd0762..053148f8486 100644 --- a/build/pkgs/tox/install-requires.txt +++ b/build/pkgs/tox/install-requires.txt @@ -1,2 +1 @@ -# Matches version checked in spkg-configure.m4 -tox >=2.5.0 +tox diff --git a/build/pkgs/tox/package-version.txt b/build/pkgs/tox/package-version.txt index eb9b76c9f59..9b2f2a16885 100644 --- a/build/pkgs/tox/package-version.txt +++ b/build/pkgs/tox/package-version.txt @@ -1 +1 @@ -3.20.0 +3.23.1 diff --git a/build/pkgs/tox/spkg-install.in b/build/pkgs/tox/spkg-install.in index deba1bb42bb..37ac1a53437 100644 --- a/build/pkgs/tox/spkg-install.in +++ b/build/pkgs/tox/spkg-install.in @@ -1 +1,2 @@ -cd src && sdh_pip_install . +cd src +sdh_pip_install . diff --git a/build/pkgs/virtualenv/SPKG.rst b/build/pkgs/virtualenv/SPKG.rst index 928b7175121..9b869626ce4 100644 --- a/build/pkgs/virtualenv/SPKG.rst +++ b/build/pkgs/virtualenv/SPKG.rst @@ -1,5 +1,5 @@ -virtualenv -========== +virtualenv: Virtual Python Environment builder +============================================== Description ----------- diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini index 8992f7e3624..177479bc005 100644 --- a/build/pkgs/virtualenv/checksums.ini +++ b/build/pkgs/virtualenv/checksums.ini @@ -1,5 +1,5 @@ tarball=virtualenv-VERSION.tar.gz -sha1=fd4d72b017ca7e3e64f2ec2f890e45857cd9c510 -md5=ad5f4586b6a03088a399692949712007 -cksum=300371404 +sha1=42c609ae86f5b6b3d491e4a865235e71ed35bfd3 +md5=6693920d5de24bd3e6ec8a6749c22b0d +cksum=2813528266 upstream_url=https://pypi.io/packages/source/v/virtualenv/virtualenv-VERSION.tar.gz diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt index 5e83434edb2..f065d76cc07 100644 --- a/build/pkgs/virtualenv/package-version.txt +++ b/build/pkgs/virtualenv/package-version.txt @@ -1 +1 @@ -20.2.2 +20.4.7 From b9c4ec8aadeb3c2977e9899cbd65bae7d9bc2761 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 16:09:18 -0700 Subject: [PATCH 433/706] build/pkgs/packaging/checksums.ini: Add upstream_url --- build/pkgs/packaging/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index 68db6c29e45..d129ec14022 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -2,3 +2,4 @@ tarball=packaging-VERSION.tar.gz sha1=6f8880ab84f05714a2549c1b54314b4f79fee319 md5=5377308b3ba89f2d78c05e7f485be65d cksum=1567718319 +upstream_url=https://pypi.io/packages/source/p/packaging/packaging-VERSION.tar.gz From ef12a3e489273a6c82c8629ab61b8032a82850a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 22:28:54 -0700 Subject: [PATCH 434/706] build/pkgs/pkgconfig/checksums.ini: Add upstream_url --- build/pkgs/pkgconfig/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/pkgconfig/checksums.ini b/build/pkgs/pkgconfig/checksums.ini index 35ac385cfa6..2bd38c7565f 100644 --- a/build/pkgs/pkgconfig/checksums.ini +++ b/build/pkgs/pkgconfig/checksums.ini @@ -2,3 +2,4 @@ tarball=pkgconfig-VERSION.tar.gz sha1=9db3e565c5bd85ceee16cbc162ab4f2f5019153c md5=0d889edf670b644bfeaa3bb9444169cb cksum=3590715331 +upstream_url=https://pypi.io/packages/source/p/pkgconfig/pkgconfig-VERSION.tar.gz From 7a6d0b61e5fc52cd1e7dc37c03b1d370da1c8595 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 16:09:18 -0700 Subject: [PATCH 435/706] build/pkgs/packaging/checksums.ini: Add upstream_url --- build/pkgs/packaging/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index 68db6c29e45..d129ec14022 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -2,3 +2,4 @@ tarball=packaging-VERSION.tar.gz sha1=6f8880ab84f05714a2549c1b54314b4f79fee319 md5=5377308b3ba89f2d78c05e7f485be65d cksum=1567718319 +upstream_url=https://pypi.io/packages/source/p/packaging/packaging-VERSION.tar.gz From ce2ec11d5f9cdb37e4b8d746836dbb5461e64ed7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 27 May 2021 22:35:15 -0700 Subject: [PATCH 436/706] build/pkgs/py/dependencies: Add setuptools_scm (a setup-requires) --- build/pkgs/py/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/py/dependencies b/build/pkgs/py/dependencies index 0738c2d7777..14a312e5dee 100644 --- a/build/pkgs/py/dependencies +++ b/build/pkgs/py/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) | $(PYTHON_TOOLCHAIN) setuptools_scm ---------- All lines of this file are ignored except the first. From c289a364a5095ca122cb45f757b94b4588191cbf Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 28 May 2021 17:49:58 +0100 Subject: [PATCH 437/706] boost is boost_cropped now --- build/pkgs/brial/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/brial/spkg-configure.m4 b/build/pkgs/brial/spkg-configure.m4 index a0f00838c75..3ebe25dd253 100644 --- a/build/pkgs/brial/spkg-configure.m4 +++ b/build/pkgs/brial/spkg-configure.m4 @@ -1,6 +1,6 @@ SAGE_SPKG_CONFIGURE([brial], [ dnl Trac #31624: Avoid C++ ABI issues - SAGE_SPKG_DEPCHECK([gcc boost m4ri], [ + SAGE_SPKG_DEPCHECK([gcc boost_cropped m4ri], [ # If we're using the system m4ri and boost, ensure that we can # compile and run an executable linked against both libbrial and # libbrial_groebner (both are used by SageMath). From 65e6330c118a89e21b8ba13bcfc9a407550bad63 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 28 May 2021 17:53:43 -0700 Subject: [PATCH 438/706] build/pkgs{appdirs,distlib,filelock,virtualenv}/spkg-configure.m4: Mark as not required if system tox is used --- build/pkgs/appdirs/spkg-configure.m4 | 7 +++++++ build/pkgs/distlib/spkg-configure.m4 | 7 +++++++ build/pkgs/filelock/spkg-configure.m4 | 9 +++++++++ build/pkgs/virtualenv/spkg-configure.m4 | 7 +++++++ 4 files changed, 30 insertions(+) create mode 100644 build/pkgs/appdirs/spkg-configure.m4 create mode 100644 build/pkgs/distlib/spkg-configure.m4 create mode 100644 build/pkgs/filelock/spkg-configure.m4 create mode 100644 build/pkgs/virtualenv/spkg-configure.m4 diff --git a/build/pkgs/appdirs/spkg-configure.m4 b/build/pkgs/appdirs/spkg-configure.m4 new file mode 100644 index 00000000000..098659476dc --- /dev/null +++ b/build/pkgs/appdirs/spkg-configure.m4 @@ -0,0 +1,7 @@ +SAGE_SPKG_CONFIGURE([appdirs], [ + sage_spkg_install_appdirs=yes + ], [dnl REQUIRED-CHECK + AC_REQUIRE([SAGE_SPKG_CONFIGURE_VIRTUALENV]) + dnl only needed as a dependency of virtualenv. + AS_VAR_SET([SPKG_REQUIRE], [$sage_spkg_install_virtualenv]) + ]) diff --git a/build/pkgs/distlib/spkg-configure.m4 b/build/pkgs/distlib/spkg-configure.m4 new file mode 100644 index 00000000000..00c32e2e0da --- /dev/null +++ b/build/pkgs/distlib/spkg-configure.m4 @@ -0,0 +1,7 @@ +SAGE_SPKG_CONFIGURE([distlib], [ + AC_REQUIRE([SAGE_SPKG_CONFIGURE_VIRTUALENV]) + sage_spkg_install_distlib=yes + ], [dnl REQUIRED-CHECK + dnl only needed as a dependency of virtualenv. + AS_VAR_SET([SPKG_REQUIRE], [$sage_spkg_install_virtualenv]) + ]) diff --git a/build/pkgs/filelock/spkg-configure.m4 b/build/pkgs/filelock/spkg-configure.m4 new file mode 100644 index 00000000000..b14299a9130 --- /dev/null +++ b/build/pkgs/filelock/spkg-configure.m4 @@ -0,0 +1,9 @@ +SAGE_SPKG_CONFIGURE([filelock], [ + sage_spkg_install_filelock=yes + ], [dnl REQUIRED-CHECK + AC_REQUIRE([SAGE_SPKG_CONFIGURE_VIRTUALENV]) + AC_REQUIRE([SAGE_SPKG_CONFIGURE_TOX]) + dnl only needed as a dependency of tox and virtualenv. + AS_IF([test $sage_spkg_install_virtualenv = no -a $sage_spkg_install_tox = no], + AS_VAR_SET([SPKG_REQUIRE], [no])) + ]) diff --git a/build/pkgs/virtualenv/spkg-configure.m4 b/build/pkgs/virtualenv/spkg-configure.m4 new file mode 100644 index 00000000000..9fd26115afb --- /dev/null +++ b/build/pkgs/virtualenv/spkg-configure.m4 @@ -0,0 +1,7 @@ +SAGE_SPKG_CONFIGURE([virtualenv], [ + sage_spkg_install_virtualenv=yes + ], [dnl REQUIRED-CHECK + AC_REQUIRE([SAGE_SPKG_CONFIGURE_TOX]) + dnl virtualenv is only needed when we cannot use system tox. + AS_VAR_SET([SPKG_REQUIRE], [$sage_spkg_install_tox]) + ]) From c68edea7a2be41681eb373e9b6fd560e1c0ea735 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 28 May 2021 19:13:39 -0700 Subject: [PATCH 439/706] build/pkgs/fplll/spkg-configure.m4: Accept 5.4.0 and 5.4.1 --- build/pkgs/fplll/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/fplll/spkg-configure.m4 b/build/pkgs/fplll/spkg-configure.m4 index f870e4dbf72..83ea99dee4c 100644 --- a/build/pkgs/fplll/spkg-configure.m4 +++ b/build/pkgs/fplll/spkg-configure.m4 @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([fplll], [ dnl Trac #31025: FPLLL/FPyLLL make no guarantee regarding compatibility dnl other than "whatever versions were released at the same time should work together" PKG_CHECK_MODULES([FPLLL], - [fplll = 5.4.0], + [fplll >= 5.4.0 fplll <= 5.4.1], [sage_spkg_install_fplll=no], [sage_spkg_install_fplll=yes]) ]) From 02aaa8ffd497212b6a42320c52ff5bd9b83f89c7 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 29 May 2021 11:12:07 +0200 Subject: [PATCH 440/706] Rename simplify methods --- src/sage/combinat/words/morphism.py | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index ca927d607b5..85f1fab5597 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3449,7 +3449,7 @@ def impl(): reach = self._language_naive(2, self._domain(w)) f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain - g, _, k, _ = f.simplify_injective() + g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) gb = g.restrict_domain(set(g._morph) - unbounded) @@ -3506,7 +3506,7 @@ def infinite_repetitions_growing(self, w=None): reach = self._language_naive(2, self._domain(w)) f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain - g, _, k, _ = f.simplify_injective() + g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) @@ -3542,7 +3542,7 @@ def infinite_repetitions_growing(self, w=None): return result - def simplify(self, Z=None): + def simplify_alphabet_size(self, Z=None): r""" If this morphism is simplifiable, return morphisms `h` and `k` such that this morphism is simplifiable with respect to `h` and `k`, otherwise @@ -3573,7 +3573,7 @@ def simplify(self, Z=None): Example of a simplifiable (non-injective) morphism:: sage: f = WordMorphism('a->aca,b->badc,c->acab,d->adc') - sage: h, k = f.simplify('xyz'); h, k + sage: h, k = f.simplify_alphabet_size('xyz'); h, k (WordMorphism: a->x, b->zy, c->xz, d->y, WordMorphism: x->aca, y->adc, z->b) sage: k * h == f True @@ -3583,7 +3583,7 @@ def simplify(self, Z=None): Example of a simplifiable (injective) morphism:: sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') - sage: h, k = f.simplify('xyz'); h, k + sage: h, k = f.simplify_alphabet_size('xyz'); h, k (WordMorphism: a->xyy, b->xyz, c->xzy, d->xzz, WordMorphism: x->ab, y->c, z->d) sage: k * h == f True @@ -3592,7 +3592,7 @@ def simplify(self, Z=None): Example of a non-simplifiable morphism:: - sage: WordMorphism('a->aa').simplify() + sage: WordMorphism('a->aa').simplify_alphabet_size() Traceback (most recent call last): ... ValueError: self (a->aa) is not simplifiable @@ -3600,7 +3600,7 @@ def simplify(self, Z=None): Example of an erasing morphism:: sage: f = WordMorphism('a->abc,b->cc,c->') - sage: h, k = f.simplify(); h, k + sage: h, k = f.simplify_alphabet_size(); h, k (WordMorphism: a->a, b->b, c->, WordMorphism: a->abc, b->cc) sage: k * h == f True @@ -3610,7 +3610,7 @@ def simplify(self, Z=None): Example of a morphism, that is not an endomorphism:: sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') - sage: h, k = f.simplify(NN); h, k + sage: h, k = f.simplify_alphabet_size(NN); h, k (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) sage: k * h == f True @@ -3704,17 +3704,17 @@ def try_create_h(f, k): return h, k - def simplify_injective(self): + def simplify_until_injective(self): r""" Return a quadruplet `(g, h, k, i)`, where `g` is an injective simplification of this morphism with respect to `h`, `k` and `i`. Requires this morphism to be an endomorphism. - This methods basically calls :meth:`simplify` until the returned - simplification is injective. If this morphism is already injective, a - quadruplet `(g, h, k, i)` is still returned, where `g` is this morphism, - `h` and `k` are the identity morphisms and `i` is 0. + This methods basically calls :meth:`simplify_alphabet_size` until the + returned simplification is injective. If this morphism is already + injective, a quadruplet `(g, h, k, i)` is still returned, where `g` + is this morphism, `h` and `k` are the identity morphisms and `i` is 0. Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with @@ -3727,7 +3727,7 @@ def simplify_injective(self): EXAMPLES:: sage: f = WordMorphism('a->abc,b->a,c->bc') - sage: g, h, k, i = f.simplify_injective(); g, h, k, i + sage: g, h, k, i = f.simplify_until_injective(); g, h, k, i (WordMorphism: a->aa, WordMorphism: a->aa, b->a, c->a, WordMorphism: a->abc, 2) sage: g.is_injective() True @@ -3744,6 +3744,6 @@ def simplify_injective(self): k = self.codomain().identity_morphism() i = 0 while not g.is_injective(): - h_new, k_new = g.simplify() + h_new, k_new = g.simplify_alphabet_size() g, h, k, i = h_new * k_new, h_new * h, k * k_new, i + 1 return g, h, k, i From 0d5f94abffe1013c181dce6134c2058a72171402 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 29 May 2021 12:51:04 +0200 Subject: [PATCH 441/706] Merge infinite_repetitions* methods --- src/sage/combinat/words/morphism.py | 265 ++++++++++++---------------- 1 file changed, 108 insertions(+), 157 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 85f1fab5597..51280ff3b03 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3238,17 +3238,17 @@ def is_pushy(self, w=None): Requires this morphism to be an endomorphism. - A language created by iterating a morphism is pushy if its words + A language created by iterating a morphism is pushy, if its words contain an infinite number of factors containing no growing letters. It turns out that this is equivalent to having at least one infinite repetition containing no growing letters. - See :meth:`infinite_repetitions` and :meth:`is_growing`. + See :meth:`infinite_repetitions_primitive_roots` and :meth:`is_growing`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES:: @@ -3257,7 +3257,7 @@ def is_pushy(self, w=None): sage: WordMorphism('a->abc,b->,c->bcb').is_pushy() True """ - return bool(self.infinite_repetitions_bounded(w)) + return bool(self.infinite_repetitions_primitive_roots(w, False)) def is_unboundedly_repetitive(self, w=None): r""" @@ -3266,16 +3266,16 @@ def is_unboundedly_repetitive(self, w=None): Requires this morphism to be an endomorphism. - A language created by iterating a morphism is unboundedly repetitive if + A language created by iterating a morphism is unboundedly repetitive, if it has at least one infinite repetition containing at least one growing letter. - See :meth:`infinite_repetitions` and :meth:`is_growing`. + See :meth:`infinite_repetitions_primitive_roots` and :meth:`is_growing`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES:: @@ -3284,7 +3284,7 @@ def is_unboundedly_repetitive(self, w=None): sage: WordMorphism('a->abc,b->,c->bcb').is_unboundedly_repetitive() False """ - return bool(self.infinite_repetitions_growing(w)) + return bool(self.infinite_repetitions_primitive_roots(w, True)) def is_repetitive(self, w=None): r""" @@ -3293,23 +3293,23 @@ def is_repetitive(self, w=None): Requires this morphism to be an endomorphism. - A language is repetitive if for each positive integer `k` there exists + A language is repetitive, if for each positive integer `k` there exists a word `u` such that `u^k` is a factor of some word of the language. - It turns that for languages created by iterating a morphism this is + It turns out that for languages created by iterating a morphism this is equivalent to having at least one infinite repetition (this property is also known as strong repetitiveness). - See :meth:`infinite_repetitions`. + See :meth:`infinite_repetitions_primitive_roots`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES: - This method can be used to check whether a purely morphic word is NOT + This method can be used to check whether a purely morphic word is not k-power free for all positive integers k. For example, the language containing just the Thue-Morse word and its prefixes is not repetitive, since the Thue-Morse word is cube-free:: @@ -3330,40 +3330,55 @@ def is_repetitive(self, w=None): """ return self.is_pushy(w) or self.is_unboundedly_repetitive(w) - def infinite_repetitions(self, w=None): + def infinite_repetitions_primitive_roots(self, w=None, allow_growing=None): r""" - Return the set of primitive infinite repetitions (up to conjugacy) - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. + Return the set of primitive roots (up to conjugacy) of infinite + repetitions from the language `\{m^n(w) | n \ge 0\}`, where `m` is this + morphism and `w` is a word inputted as a parameter. Requires this morphism to be an endomorphism. - A non-empty word `v` is an infinite repetition (also known as an - infinite periodic factor) of a language if for each positive integer - `k` the word `v^k` is a factor of some word from the language. + The word `v^\omega` is an infinite repetition (in other words, an + infinite periodic factor) of a language, if `v` is a non-empty word and + for each positive integer `k` the word `v^k` is a factor of some word + from the language. It turns out that a language created by iterating a + morphism has a finite number of primitive roots of infinite repetitions. - If `v` is an infinite repetition, then all its powers are also infinite - repetitions, therefore this method returns only the primitive ones. It - turns out that a language created by iterating a morphism has a finite - number of primitive infinite repetitions. - - Similarly, if `v` is an infinite repetition, then all its conjugates - are also infinite repetitions, therefore this method returns only the - lexicographically minimal one from each conjugacy class. + If `v` is a primitive root of an infinite repetition, then all its + conjugations are also primitive roots of an infinite repetition. For + simplicity's sake this method returns only the lexicographically minimal + one from each conjugacy class. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. + + - ``allow_growing`` -- boolean or ``None`` (default: ``None``). If + ``False``, return only the primitive roots that contain no growing + letters. If ``True``, return only the primitive roots that contain at + least one growing letter. If ``None``, return both. + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. EXAMPLES:: sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: inf_reps = m.infinite_repetitions('ac') + sage: inf_reps = m.infinite_repetitions_primitive_roots('ac') sage: sorted(inf_reps) [word: aab, word: de] - Incomplete check that these words are indeed infinite repetitions:: + ``allow_growing`` parameter:: + + sage: sorted(m.infinite_repetitions_primitive_roots('ac', True)) + [word: aab] + sage: sorted(m.infinite_repetitions_primitive_roots('ac', False)) + [word: de] + + Incomplete check that these words are indeed the primitive roots of + infinite repetitions:: sage: SL = m._language_naive(10, Word('ac')) sage: all(x in SL for x in inf_reps) @@ -3373,54 +3388,39 @@ def infinite_repetitions(self, w=None): sage: all(x^3 in SL for x in inf_reps) True - Larger example:: + Large example:: sage: m = WordMorphism('a->1b5,b->fcg,c->dae,d->432,e->678,f->f,g->g,1->2,2->3,3->4,4->1,5->6,6->7,7->8,8->5') - sage: sorted(m.infinite_repetitions('a')) + sage: sorted(m.infinite_repetitions_primitive_roots('a')) [word: 1432f2143f3214f4321f, word: 5678g8567g7856g6785g] - """ - return self.infinite_repetitions_bounded(w) | self.infinite_repetitions_growing(w) - - def infinite_repetitions_bounded(self, w=None): - r""" - Return the set of primitive infinite repetitions (up to conjugacy), - which contain no growing letters, - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. - - Requires this morphism to be an endomorphism. - - See :meth:`infinite_repetitions` and :meth:`is_growing`. - - INPUT: - - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` - - ALGORITHM: - The algorithm used is described in detail in [KS2015]_. + TESTS:: - EXAMPLES:: + sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: 1, word: 1519181716, word: 2, word: 2529282726] - sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: sorted(m.infinite_repetitions_bounded()) - [word: de] + sage: m = WordMorphism('a->b,b->b', codomain=FiniteWords('ab')) + sage: m.infinite_repetitions_primitive_roots() + set() sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') - sage: sorted(m.infinite_repetitions_bounded()) + sage: sorted(m.infinite_repetitions_primitive_roots()) [word: c, word: d] - TESTS:: + sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: ad, word: bc] - sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') - sage: sorted(m.infinite_repetitions_bounded()) - [word: 1, word: 1519181716, word: 2, word: 2529282726] + sage: m = WordMorphism('b->c,c->bcb') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: bc] - sage: WordMorphism('a->b,b->b', codomain=FiniteWords('ab')).infinite_repetitions() - set() + sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: ababcd] """ - def impl(): + def impl_no_growing(g, k): U = {} for x in unbounded: xg = g.image(x) @@ -3452,93 +3452,44 @@ def impl(): g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) - gb = g.restrict_domain(set(g._morph) - unbounded) - result = set() - for x in impl(): # UR. - result.add(x.minimal_conjugate()) - g, k = g.reversal(), k.reversal() - for x in impl(): # UL. - result.add(self.domain()(reversed(x)).minimal_conjugate()) - - return result - - def infinite_repetitions_growing(self, w=None): - r""" - Return the set of primitive infinite repetitions (up to conjugacy), - which contain at least one growing letter, - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. - - Requires this morphism to be an endomorphism. - - See :meth:`infinite_repetitions` and :meth:`is_growing`. - - INPUT: - - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` - - ALGORITHM: - - The algorithm used is described in detail in [KS2015]_. - - EXAMPLES:: - - sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: sorted(m.infinite_repetitions_growing()) - [word: aab] - - sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') - sage: sorted(m.infinite_repetitions_growing()) - [word: ad, word: bc] - sage: m = WordMorphism('b->c,c->bcb') - sage: sorted(m.infinite_repetitions_growing()) - [word: bc] - - sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') - sage: sorted(m.infinite_repetitions_growing()) - [word: ababcd] - """ - if w is None: - w = self._morph - reach = self._language_naive(2, self._domain(w)) - f = self.restrict_domain([x[0] for x in reach]) - f._codomain = f._domain - g, _, k, _ = f.simplify_until_injective() - g._codomain = g._domain - unbounded = set(g.growing_letters()) - - result = set() - for periodic_orbit in g.periodic_points(): - gq = g ** len(periodic_orbit) - for periodic_point in periodic_orbit: - # Check if this periodic point is a periodic infinite word. - periodic_point = periodic_point[:1] - occurred = set(periodic_point) - one_unbounded_twice = False - for _ in g.domain().alphabet(): - previous_length = periodic_point.length() - periodic_point = gq(periodic_point) - for i, letter in enumerate(periodic_point[previous_length:]): - if letter in unbounded: - if letter in occurred: - one_unbounded_twice = True - break - occurred.add(letter) - if one_unbounded_twice: + if allow_growing is not True: + gb = g.restrict_domain(set(g._morph) - unbounded) + for x in impl_no_growing(g, k): # UR. + result.add(x.minimal_conjugate()) + for x in impl_no_growing(g.reversal(), k.reversal()): # UL. + result.add(self.domain()(reversed(x)).minimal_conjugate()) + + if allow_growing is not False: + for periodic_orbit in g.periodic_points(): + gq = g ** len(periodic_orbit) + for periodic_point in periodic_orbit: + # Check if this periodic point is a periodic infinite word. + periodic_point = periodic_point[:1] + occurred = set(periodic_point) + one_unbounded_twice = False + for _ in g.domain().alphabet(): + previous_length = periodic_point.length() + periodic_point = gq(periodic_point) + for i, letter in enumerate(periodic_point[previous_length:]): + if letter in unbounded: + if letter in occurred: + one_unbounded_twice = True + break + occurred.add(letter) + if one_unbounded_twice: + break + if not one_unbounded_twice or letter != periodic_point[0]: break - if not one_unbounded_twice or letter != periodic_point[0]: - break - v = periodic_point[:previous_length + i] - vq = gq(v) - m = 0 - while vq[m * v.length() : (m + 1) * v.length()] == v: - m += 1 - if m * v.length() != vq.length(): - break - result.add(k(v).primitive().minimal_conjugate()) + v = periodic_point[:previous_length + i] + vq = gq(v) + m = 0 + while vq[m * v.length() : (m + 1) * v.length()] == v: + m += 1 + if m * v.length() != vq.length(): + break + result.add(k(v).primitive().minimal_conjugate()) return result @@ -3557,7 +3508,7 @@ def simplify_alphabet_size(self, Z=None): `Y \subseteq X`, then the morphism `g: Z^* \rightarrow Z^* = h \circ k` is a simplification of `f` (with respect to `h` and `k`). - Loosely speaking a morphism is simplifiable if it contains "more letters + Loosely speaking, a morphism is simplifiable if it contains "more letters than is needed". Non-injectivity implies simplifiability. Simplification preserves some properties of the original morphism (e.g. repetitiveness). @@ -3565,8 +3516,8 @@ def simplify_alphabet_size(self, Z=None): INPUT: - - ``Z`` -- iterable, whose elements are used as an alphabet for the - simplification, default is ``self.domain().alphabet()`` + - ``Z`` -- iterable (default: ``self.domain().alphabet()``), whose + elements are used as an alphabet for the simplification. EXAMPLES: From 1d9420445e04e5eea06af93e36ae8867b85344e0 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 29 May 2021 13:08:56 +0200 Subject: [PATCH 442/706] Update maxima to 5.45.0 --- build/pkgs/maxima/checksums.ini | 6 +++--- build/pkgs/maxima/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/maxima/checksums.ini b/build/pkgs/maxima/checksums.ini index 065f7d02e61..a804c7b831f 100644 --- a/build/pkgs/maxima/checksums.ini +++ b/build/pkgs/maxima/checksums.ini @@ -1,5 +1,5 @@ tarball=maxima-VERSION.tar.gz -sha1=5f1fce915675f46823c33638480dcc1fcaf447a1 -md5=75e040745161901968d9c99c7a258e5c -cksum=2316004676 +sha1=ed15d5285794413ba94412079eca3d0fa55a47bf +md5=9b9ae1dace55b1386739dabaa9122e60 +cksum=1765409766 upstream_url=https://sourceforge.net/projects/maxima/files/Maxima-source/VERSION-source/maxima-VERSION.tar.gz/download diff --git a/build/pkgs/maxima/package-version.txt b/build/pkgs/maxima/package-version.txt index d532a08f3cd..5a053acd6b8 100644 --- a/build/pkgs/maxima/package-version.txt +++ b/build/pkgs/maxima/package-version.txt @@ -1 +1 @@ -5.44.0 +5.45.0 From e32e2c060b5469f2a23dfa590c1d688db2227f70 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 29 May 2021 13:22:24 +0200 Subject: [PATCH 443/706] Update tests for maxima 5.45 --- src/sage/calculus/calculus.py | 6 +++--- src/sage/calculus/functional.py | 2 +- src/sage/symbolic/integration/integral.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 6109e18c45f..776bfd9bde8 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1563,12 +1563,12 @@ def laplace(ex, t, s, algorithm='maxima'): sage: inverse_laplace(L, s, t) t*e^(a + 2*t)*sin(t) - Unable to compute solution with Maxima:: + Heaviside step function can be handled with different interfaces. + Try with Maxima:: sage: laplace(heaviside(t-1), t, s) - laplace(heaviside(t - 1), t, s) + e^(-s)/s - Heaviside step function can be handled with different interfaces. Try with giac:: sage: laplace(heaviside(t-1), t, s, algorithm='giac') diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index cb11e909f3d..4410875cad2 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -27,7 +27,7 @@ sage: laplace( e^(x+a), x, a) e^a/(a - 1) sage: inverse_laplace( e^a/(a-1), x, a) - ilt(e^a/(a - 1), x, a) + dirac_delta(a)*e^a/(a - 1) """ from .calculus import SR diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 46b1a11a82a..80bd316c066 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -912,7 +912,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): 2*sqrt(x*sgn(x))/sgn(x) sage: integrate(sgn(x) - sgn(1-x), x) - x*(sgn(x) - sgn(-x + 1)) + sgn(-x + 1) + abs(x - 1) + abs(x) sage: integrate(1 / (1 + abs(x-5)), x, -5, 6) log(11) + log(2) From fb33c0f4f26e907ba58a75224efaf263f245d69d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 29 May 2021 12:50:45 -0700 Subject: [PATCH 444/706] RealSet: Allow initialization from RealLine and OpenInterval instances and OpenInterval closures --- src/sage/sets/real_set.py | 44 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..d6a2b504560 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -857,7 +857,25 @@ def rel_to_interval(op, val): else: raise ValueError(str(arg) + ' does not determine real interval') else: - raise ValueError(str(arg) + ' does not determine real interval') + from sage.manifolds.differentiable.examples.real_line import OpenInterval + from sage.manifolds.subsets.closure import ManifoldSubsetClosure + if isinstance(arg, OpenInterval): + lower, upper = RealSet._prep(arg.lower_bound(), arg.upper_bound()) + intervals.append(InternalRealInterval(lower, False, upper, False)) + elif (isinstance(arg, ManifoldSubsetClosure) + and isinstance(arg._subset, OpenInterval)): + interval = arg._subset + lower, upper = RealSet._prep(interval.lower_bound(), + interval.upper_bound()) + ambient = interval.manifold() + ambient_lower, ambient_upper = RealSet._prep(ambient.lower_bound(), + ambient.upper_bound()) + lower_closed = ambient_lower < lower + upper_closed = upper < ambient_upper + intervals.append(InternalRealInterval(lower, lower_closed, + upper, upper_closed)) + else: + raise ValueError(str(arg) + ' does not determine real interval') intervals = RealSet.normalize(intervals) return UniqueRepresentation.__classcall__(cls, *intervals) @@ -885,6 +903,30 @@ def __init__(self, *intervals): (0, 1) + (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set (0, 1) + [3, 4] + + Initialization from manifold objects:: + + sage: R = RealLine(); R + Real number line R + sage: RealSet(R) + (-oo, +oo) + sage: I02 = OpenInterval(0, 2); I + I + sage: RealSet(I02) + (0, 2) + sage: I01_of_R = OpenInterval(0, 1, ambient_interval=R); I01_of_R + Real interval (0, 1) + sage: RealSet(I01_of_R) + (0, 1) + sage: RealSet(I01_of_R.closure()) + [0, 1] + sage: I01_of_I02 = OpenInterval(0, 1, ambient_interval=I02); I01_of_I02 + Real interval (0, 1) + sage: RealSet(I01_of_I02) + (0, 1) + sage: RealSet(I01_of_I02.closure()) + (0, 1] + """ Parent.__init__(self, category = Sets()) self._intervals = intervals From 4b09685667a8dddec24847dd14067abbe2d50076 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 29 May 2021 15:01:21 -0700 Subject: [PATCH 445/706] RealSet: Put it in a suitable subcategory of TopologicalSpaces() --- src/sage/sets/real_set.py | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..b27e39fd25b 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -88,7 +88,7 @@ class RealSet. from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.sets_cat import Sets +from sage.categories.topological_spaces import TopologicalSpaces from sage.rings.all import ZZ from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity @@ -885,8 +885,37 @@ def __init__(self, *intervals): (0, 1) + (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set (0, 1) + [3, 4] + + Real sets belong to a subcategory of topological spaces:: + + sage: RealSet().category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet.point(1).category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet([1, 2]).category() + Join of Category of infinite sets and Category of compact topological spaces and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet((1, 2), (3, 4)).category() + Join of Category of infinite sets and Category of subobjects of sets and Category of topological spaces + """ - Parent.__init__(self, category = Sets()) + category = TopologicalSpaces() + if len(intervals) <= 1: + category = category.Connected() + if all(i.is_point() for i in intervals): + category = category.Subobjects().Finite() + else: + # Have at least one non-degenerate interval + category = category.Infinite() + inf = intervals[0].lower() + sup = intervals[-1].upper() + if not (len(intervals) == 1 and inf is minus_infinity and sup is infinity): + category = category.Subobjects() # subobject of real line + if inf is not minus_infinity and sup is not infinity: + # Bounded + if all(i.lower_closed() and i.upper_closed() + for i in intervals): + category = category.Compact() + Parent.__init__(self, category=category) self._intervals = intervals def __richcmp__(self, other, op): @@ -1017,6 +1046,59 @@ def get_interval(self, i): __getitem__ = get_interval + # ParentMethods of Subobjects + + @staticmethod + def ambient(): + """ + Construct the real line + + EXAMPLES:: + + sage: s = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)) + sage: s.ambient() + (-oo, +oo) + """ + return RealSet(minus_infinity, infinity) + + def lift(self, x): + """ + Lift ``x`` to the ambient space for ``self``. + + This version of the method just returns ``x``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.lift(1) + 1 + """ + return x + + def retract(self, x): + """ + Retract ``x`` to ``self``. + + It raises an error if ``x`` does not lie in the set ``self``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.retract(1) + 1 + sage: s.retract(2) + Traceback (most recent call last): + ... + ValueError: 2 is not an element of (0, 2) + """ + if x not in self: + raise ValueError(f'{x} is not an element of {self}') + return x + + # + @staticmethod def normalize(intervals): """ From bafcc51a74de1fe8f1c7bd1d4e869ac2270724da Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 30 May 2021 19:02:55 +0200 Subject: [PATCH 446/706] Disable libavif/libvmaf since linking breaks on Fedora 34 --- build/pkgs/libgd/spkg-install.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/pkgs/libgd/spkg-install.in b/build/pkgs/libgd/spkg-install.in index 03b23ab710e..dc73d868fa5 100644 --- a/build/pkgs/libgd/spkg-install.in +++ b/build/pkgs/libgd/spkg-install.in @@ -17,7 +17,10 @@ fi # We explicitly disable X and fontconfig support, since (1) X is not a SAGE dependency, # and (2) the gd build fails on a lot of OS X PPC machines when X is enabled. +# Also, libgd will try to link against system libavif/libvmaf and fail +# on Fedora 34 sdh_configure --without-jpeg --without-xpm --without-x --without-fontconfig \ + --without-avif \ --with-zlib="$SAGE_LOCAL" $LIBGD_CONFIGURE sdh_make sdh_make_install From 5b8ceca516427f34634ab0eeb17a53a70d62f0bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:01:36 -0700 Subject: [PATCH 447/706] InternalRealInterval, RealSet: Add _latex_ methods --- src/sage/sets/real_set.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..7aedf03abce 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -415,6 +415,17 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition + def _latex_(self): + from sage.misc.latex import latex + if self.is_point(): + return r'\{' + latex(self.lower()) + r'\}' + s = '[' if self._lower_closed else '(' + s += latex(self.lower()) + s += ', ' + s += latex(self.upper()) + s += ']' if self._upper_closed else ')' + return s + def closure(self): """ Return the closure @@ -1095,6 +1106,21 @@ def _repr_(self): return ' + '.join(map(repr, self._intervals)) # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + def _latex_(self): + """ + EXAMPLES:: + + sage: from cutgeneratingfunctionology.spam.real_set import RealSet + sage: latex(RealSet(0, 1)) + ( 0 , 1 ) + sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) + ( 0 , 1 ) \cup [ 2 , +\infty ) + """ + from sage.misc.latex import latex + if self.n_components() == 0: + return r'\emptyset' + else: + return r' \cup '.join(map(latex, self._intervals)) def _sympy_condition_(self, variable): """ From 69ca8543ccd948f9c01143dda49f574710efe0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:24:46 -0700 Subject: [PATCH 448/706] RealSet._repr_: Use unicode cup sign instead of + --- src/sage/sets/real_set.py | 84 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 7aedf03abce..170b762a5af 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -10,7 +10,7 @@ sage: RealSet(0,1) (0, 1) sage: RealSet((0,1), [2,3]) - (0, 1) + [2, 3] + (0, 1) ∪ [2, 3] sage: RealSet(-oo, oo) (-oo, +oo) @@ -42,7 +42,7 @@ Relations containing symbols and numeric values or constants:: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -753,12 +753,12 @@ def __classcall__(cls, *args): EXAMPLES:: sage: R = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)); R - (0, 1] + [2, 3) + (0, 1] ∪ [2, 3) :: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -893,9 +893,9 @@ def __init__(self, *intervals): sage: RealSet(i) # interval (0, 1) sage: RealSet(i, (3,4)) # tuple of two numbers = open set - (0, 1) + (3, 4) + (0, 1) ∪ (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set - (0, 1) + [3, 4] + (0, 1) ∪ [3, 4] """ Parent.__init__(self, category = Sets()) self._intervals = intervals @@ -1058,9 +1058,9 @@ def normalize(intervals): sage: RealSet((0, 1), [1, 2], (2, 3)) (0, 3) sage: RealSet((0, 1), (1, 2), (2, 3)) - (0, 1) + (1, 2) + (2, 3) + (0, 1) ∪ (1, 2) ∪ (2, 3) sage: RealSet([0, 1], [2, 3]) - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: RealSet((0, 2), (1, 3)) (0, 3) sage: RealSet(0,0) @@ -1088,7 +1088,7 @@ def normalize(intervals): def _repr_(self): """ - Return a string representation + Return a string representation of ``self``. OUTPUT: @@ -1102,9 +1102,7 @@ def _repr_(self): if self.n_components() == 0: return '{}' else: - # Switch to u'\u222A' (cup sign) with Python 3 - return ' + '.join(map(repr, self._intervals)) - # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): """ @@ -1437,26 +1435,26 @@ def intersection(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.intersection(s2) (1, 2) sage: s1 & s2 # syntactic sugar (1, 2) sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([0, 1], [2, 3]); s2 - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: s3 = RealSet([1, 2]); s3 [1, 2] sage: s1.intersection(s2) - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s1.intersection(s3) {} sage: s2.intersection(s3) - {1} + {2} + {1} ∪ {2} """ other = RealSet(*other) # TODO: this can be done in linear time since the intervals are already sorted @@ -1479,12 +1477,12 @@ def inf(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.inf() 0 sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.inf() -Infinity """ @@ -1503,12 +1501,12 @@ def sup(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.sup() +Infinity sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.sup() 3 """ @@ -1527,17 +1525,17 @@ def complement(self): EXAMPLES:: sage: RealSet(0,1).complement() - (-oo, 0] + [1, +oo) + (-oo, 0] ∪ [1, +oo) sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.complement() - (-oo, 0] + [2, 10) + (-oo, 0] ∪ [2, 10) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.complement() - (-10, 1] + [3, +oo) + (-10, 1] ∪ [3, +oo) """ n = self.n_components() if n == 0: @@ -1575,19 +1573,19 @@ def difference(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.difference(s2) - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s1 - s2 # syntactic sugar - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s2.difference(s1) - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s2 - s1 # syntactic sugar - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s1.difference(1,11) - (0, 1] + [11, +oo) + (0, 1] ∪ [11, +oo) """ other = RealSet(*other) return self.intersection(other.complement()) @@ -1609,7 +1607,7 @@ def contains(self, x): EXAMPLES:: sage: s = RealSet(0,2) + RealSet.unbounded_above_closed(10); s - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s.contains(1) True sage: s.contains(0) @@ -1703,7 +1701,7 @@ def is_disjoint_from(self, *other): EXAMPLES:: sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([1, 2]); s2 [1, 2] sage: s1.is_disjoint_from(s2) @@ -1808,15 +1806,15 @@ def __mul__(self, right): EXAMPLES:: sage: A = RealSet([0, 1/2], (2, infinity)); A - [0, 1/2] + (2, +oo) + [0, 1/2] ∪ (2, +oo) sage: 2 * A - [0, 1] + (4, +oo) + [0, 1] ∪ (4, +oo) sage: A * 100 - [0, 50] + (200, +oo) + [0, 50] ∪ (200, +oo) sage: 1.5 * A - [0.000000000000000, 0.750000000000000] + (3.00000000000000, +oo) + [0.000000000000000, 0.750000000000000] ∪ (3.00000000000000, +oo) sage: (-2) * A - (-oo, -4) + [-1, 0] + (-oo, -4) ∪ [-1, 0] """ if not isinstance(right, RealSet): return RealSet(*[e * right for e in self]) @@ -1832,8 +1830,8 @@ def __rmul__(self, other): TESTS:: sage: A = RealSet([0, 1/2], RealSet.unbounded_above_closed(2)); A - [0, 1/2] + [2, +oo) + [0, 1/2] ∪ [2, +oo) sage: pi * A - [0, 1/2*pi] + [2*pi, +oo) + [0, 1/2*pi] ∪ [2*pi, +oo) """ return self * other From dbdfc068f2d2ac39ae8171ea8737b2b1a535e021 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:26:42 -0700 Subject: [PATCH 449/706] InternalRealInterval, RealSet: Remove extra whitespace in latex, add documentation --- src/sage/sets/real_set.py | 47 ++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 170b762a5af..9106db527d4 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -382,6 +382,31 @@ def _repr_(self): s += ']' if self._upper_closed else ')' return s + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: RealSet.open_closed(1/2, pi)._latex_() + '(\\frac{1}{2}, \\pi]' + sage: (RealSet.point(sqrt(2)))._latex_() + '\\{\\sqrt{2}\\}' + """ + from sage.misc.latex import latex + if self.is_point(): + # Converting to str avoids the extra whitespace + # that LatexExpr add on concenation. We do not need + # the whitespace because we are wrapping it in + # non-letter characters. + return r'\{' + str(latex(self.lower())) + r'\}' + s = '[' if self._lower_closed else '(' + s += str(latex(self.lower())) + s += ', ' + s += str(latex(self.upper())) + s += ']' if self._upper_closed else ')' + return s + def _sympy_condition_(self, variable): """ Convert to a sympy conditional expression. @@ -415,17 +440,6 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition - def _latex_(self): - from sage.misc.latex import latex - if self.is_point(): - return r'\{' + latex(self.lower()) + r'\}' - s = '[' if self._lower_closed else '(' - s += latex(self.lower()) - s += ', ' - s += latex(self.upper()) - s += ']' if self._upper_closed else ')' - return s - def closure(self): """ Return the closure @@ -1105,20 +1119,21 @@ def _repr_(self): return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): - """ + r""" + Return a latex representation of ``self``. + EXAMPLES:: - sage: from cutgeneratingfunctionology.spam.real_set import RealSet sage: latex(RealSet(0, 1)) - ( 0 , 1 ) + (0, 1) sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) - ( 0 , 1 ) \cup [ 2 , +\infty ) + (0, 1) \cup [2, +\infty) """ from sage.misc.latex import latex if self.n_components() == 0: return r'\emptyset' else: - return r' \cup '.join(map(latex, self._intervals)) + return r' \cup '.join(latex(i) for i in self._intervals) def _sympy_condition_(self, variable): """ From 181b128686bf125fb0bb9a11210543dacce73ebe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 13:49:43 -0700 Subject: [PATCH 450/706] RealSet.is_open, is_closed, closure, interior, boundary: New --- src/sage/sets/real_set.py | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 9106db527d4..042b9bf6239 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -85,6 +85,8 @@ class RealSet. # http://www.gnu.org/licenses/ #***************************************************************************** +import itertools + from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -480,6 +482,23 @@ def interior(self): """ return InternalRealInterval(self._lower, False, self._upper, False) + def boundary_points(self): + """ + Generate the boundary points of ``self`` + + EXAMPLES:: + + sage: list(RealSet.open_closed(-oo, 1)[0].boundary_points()) + [1] + sage: list(RealSet.open(1, 2)[0].boundary_points()) + [1, 2] + + """ + if self._lower != minus_infinity: + yield self._lower + if self._upper != infinity: + yield self._upper + def is_connected(self, other): """ Test whether two intervals are connected @@ -1701,6 +1720,88 @@ def an_element(self): return i.upper() return (i.lower() + i.upper())/ZZ(2) + def is_open(self): + """ + Return whether ``self`` is an open set. + + EXAMPLES:: + + sage: RealSet().is_open() + True + sage: RealSet.point(1).is_open() + False + sage: RealSet((1, 2)).is_open() + True + sage: RealSet([1, 2], (3, 4)).is_open() + False + + """ + return all(not i.lower_closed() + and not i.upper_closed() + for i in self._intervals) + + def is_closed(self): + """ + Return whether ``self`` is a closed set. + + EXAMPLES:: + + sage: RealSet().is_closed() + True + sage: RealSet.point(1).is_closed() + True + sage: RealSet([1, 2]).is_closed() + True + sage: RealSet([1, 2], (3, 4)).is_closed() + False + """ + return all((i.lower_closed() or i.lower() is minus_infinity) + and (i.upper_closed() or i.upper() is infinity) + for i in self._intervals) + + def closure(self): + """ + Return the topological closure of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).closure() + (-oo, +oo) + sage: RealSet((1, 2), (2, 3)).closure() + [1, 3] + """ + return RealSet(*[i.closure() for i in self._intervals]) + + def interior(self): + """ + Return the topological interior of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).interior() + (-oo, +oo) + sage: RealSet.point(2).interior() + {} + sage: RealSet([1, 2], (3, 4)).interior() + (1, 2) ∪ (3, 4) + """ + return RealSet(*[i.interior() for i in self._intervals]) + + def boundary(self): + """ + Return the topological boundary of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).boundary() + {} + sage: RealSet.point(2).boundary() + {2} + sage: RealSet([1, 2], (3, 4)).boundary() + {1} ∪ {2} ∪ {3} ∪ {4} + """ + return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) + def is_disjoint_from(self, *other): """ Test whether the two sets are disjoint From 7f563381617c2b016a8b849f83d3387ccc25d621 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:09:43 -0700 Subject: [PATCH 451/706] PiecewiseFunction: Adjust doctests for changed RealSet repr --- src/sage/functions/piecewise.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..a0276824a3a 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -527,7 +527,7 @@ def restriction(self, parameters, variable, restricted_domain): sage: f = piecewise([((-oo, oo), x)]); f piecewise(x|-->x on (-oo, +oo); x) sage: f.restriction([[-1,1], [3,3]]) - piecewise(x|-->x on [-1, 1] + {3}; x) + piecewise(x|-->x on [-1, 1] ∪ {3}; x) """ restricted_domain = RealSet(*restricted_domain) new_param = [] @@ -559,7 +559,7 @@ def extension(self, parameters, variable, extension, extension_domain=None): ValueError: point 3 is not in the domain sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 @@ -583,7 +583,7 @@ def unextend_zero(self, parameters, variable): sage: f = piecewise([((-1,1), x)]); f piecewise(x|-->x on (-1, 1); x) sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 sage: h = g.unextend_zero() @@ -652,7 +652,7 @@ def piecewise_add(self, parameters, variable, other): sage: f = piecewise([([0,1], 1), ((2,3), x)]) sage: g = piecewise([((1/2, 2), x)]) sage: f.piecewise_add(g).unextend_zero() - piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) + (2, 3); x) + piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) ∪ (2, 3); x) """ points = ([minus_infinity] + sorted(set(self.end_points() + other.end_points())) + From 46eed0eeb3fc4e9509b7c55df967455ae49984d0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:25:17 -0700 Subject: [PATCH 452/706] RealSet.ambient: Change to a normal method --- src/sage/sets/real_set.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index b27e39fd25b..5d39fbe0029 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1048,10 +1048,9 @@ def get_interval(self, i): # ParentMethods of Subobjects - @staticmethod - def ambient(): + def ambient(self): """ - Construct the real line + Return the ambient space (the real line). EXAMPLES:: From 8fdb104f7c0b84b0db5a280a0265f690d1a867b5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:37:07 -0700 Subject: [PATCH 453/706] RealSet.boundary: Add another doctest --- src/sage/sets/real_set.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 042b9bf6239..433cd77cfb3 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1799,6 +1799,9 @@ def boundary(self): {2} sage: RealSet([1, 2], (3, 4)).boundary() {1} ∪ {2} ∪ {3} ∪ {4} + sage: RealSet((1, 2), (2, 3)).boundary() + {1} ∪ {2} ∪ {3} + """ return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) From cc1ba6c69d07021bf276d8613457e46a53d4d356 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sun, 30 May 2021 18:14:33 -0700 Subject: [PATCH 454/706] Correct use of long longs in hypergeometric_misc --- src/sage/modular/hypergeometric_misc.pyx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 0faa55e3701..7f3c8ee494d 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -24,12 +24,20 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, sage: D = 1 sage: hgm_coeffs(7, 1, 2, gamma, [0]*6, D, gtable, prec, False) [7, 2*7, 6*7, 7, 6, 4*7] + + Check issue from :trac:`28404`:: + + sage: H = Hyp(cyclotomic=[[10,2],[1,1,1,1,1]]) + sage: u = H.euler_factor(2,79) # indirect doctest + sage: u.reverse().is_weil_polynomial() + True + """ from sage.rings.padics.factory import Zp cdef int gl, j, k, l, v, gv cdef long long i, q1, w, w1, w2, q2, r, r1 - cdef bint flip + cdef bint flip, need_lift q1 = p ** f - 1 gl = len(gamma) @@ -55,6 +63,14 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtab2 = array.array('l', [0]) * q1 for r in range(q1): gtab2[r] = gtable[r].lift() % q2 + else: + gtab2 = array.array('q', [0]) * q1 + try: + for r in range(q1): + gtab2[r] = gtable[r] + except TypeError: + for r in range(q1): + gtab2[r] = gtable[r].lift() if f == 1: for r in range(q1): digit_count[r] = r @@ -108,7 +124,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, for j in range(-gv): w1 = w1 * w2 % q2 else: - w2 = gtable[r1] + w2 = gtab2[r1] if gv > 0: for j in range(gv): u *= w2 From 48f7a070bd168a3a039b6592f035c62a387b02bb Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Sun, 30 May 2021 20:17:58 -0600 Subject: [PATCH 455/706] trac 31869 fix pynac integer_content --- build/pkgs/pynac/package-version.txt | 2 +- .../pkgs/pynac/patches/integer_content.patch | 44 +++++++++++++++++++ src/sage/libs/pynac/pynac.pyx | 5 +++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/pynac/patches/integer_content.patch diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index 4373c7f55cb..94a2c2bf655 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.27.p7 +0.7.27.p8 diff --git a/build/pkgs/pynac/patches/integer_content.patch b/build/pkgs/pynac/patches/integer_content.patch new file mode 100644 index 00000000000..f93be4b6d45 --- /dev/null +++ b/build/pkgs/pynac/patches/integer_content.patch @@ -0,0 +1,44 @@ +diff --git a/ginac/normal.cpp b/ginac/normal.cpp +index 461bb44..2a52c9e 100644 +--- a/ginac/normal.cpp ++++ b/ginac/normal.cpp +@@ -87,7 +87,12 @@ numeric basic::integer_content() const + + numeric numeric::integer_content() const + { +- return abs(); ++ if (is_real()) { ++ return abs(); ++ } ++ else { ++ return real().numer().gcd(imag().numer()) / real().denom().lcm(imag().denom()); ++ } + } + + numeric add::integer_content() const +@@ -98,12 +103,12 @@ numeric add::integer_content() const + while (it != itend) { + GINAC_ASSERT(!is_exactly_a(it->rest)); + GINAC_ASSERT(is_exactly_a(it->coeff)); +- c = gcd(ex_to(it->coeff).numer(), c); +- l = lcm(ex_to(it->coeff).denom(), l); ++ c = gcd(ex_to(it->coeff).integer_content().numer(), c); ++ l = lcm(ex_to(it->coeff).integer_content().denom(), l); + it++; + } +- c = gcd(overall_coeff.numer(), c); +- l = lcm(overall_coeff.denom(), l); ++ c = gcd(overall_coeff.integer_content().numer(), c); ++ l = lcm(overall_coeff.integer_content().denom(), l); + return (c/l).abs(); + } + +@@ -117,7 +122,7 @@ numeric mul::integer_content() const + ++it; + } + #endif // def DO_GINAC_ASSERT +- return overall_coeff.abs(); ++ return overall_coeff.integer_content(); + } + + diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 4c645545ac5..da5f07d9e21 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -2427,6 +2427,11 @@ def init_pynac_I(): False sage: bool(z == y) True + + Check that :trac:`31869` is fixed:: + + sage: x * ((3*I + 4)*x - 5) + ((3*I + 4)*x - 5)*x """ global pynac_I, I from sage.rings.number_field.number_field import GaussianField From 1942c714237d5f2996587bfeda80404b57720696 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 19:22:48 -0700 Subject: [PATCH 456/706] ScalarField.codomain: New, put scalar fields in category of continuous maps --- src/sage/manifolds/differentiable/manifold.py | 4 ++-- src/sage/manifolds/manifold.py | 6 +++--- src/sage/manifolds/scalarfield.py | 15 +++++++++++++++ src/sage/manifolds/scalarfield_algebra.py | 7 ++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 61d94a414f8..d7af8ab4efc 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -196,7 +196,7 @@ Algebra of differentiable scalar fields on the 2-dimensional differentiable manifold S^2 sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces A differentiable manifold has a default vector frame, which, unless otherwise specified, is the coordinate frame associated with the first defined chart:: @@ -376,7 +376,7 @@ Algebra of differentiable scalar fields on the 1-dimensional complex manifold C* sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces A vector field on the Riemann sphere:: diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 41fb5a18234..aaa137fd7ef 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -188,7 +188,7 @@ sage: f.parent() Algebra of scalar fields on the 2-dimensional topological manifold S^2 sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces .. RUBRIC:: Example 2: the Riemann sphere as a topological manifold of @@ -295,7 +295,7 @@ Algebra of scalar fields on the Complex 1-dimensional topological manifold C* sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces AUTHORS: @@ -1833,7 +1833,7 @@ def scalar_field_algebra(self): sage: CU = U.scalar_field_algebra() ; CU Algebra of scalar fields on the Open subset U of the 3-dimensional topological manifold M sage: CU.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CU.zero() Scalar field zero on the Open subset U of the 3-dimensional topological manifold M diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 98e931e212e..42ad3fcba91 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -1580,6 +1580,21 @@ def domain(self): """ return self._domain + def codomain(self): + r""" + Return the codomain of the scalar field. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: f = M.scalar_field(x+2*y) + sage: f.codomain() + Real Field with 53 bits of precision + + """ + return self._domain.base_field() + def copy(self, name=None, latex_name=None): r""" Return an exact copy of the scalar field. diff --git a/src/sage/manifolds/scalarfield_algebra.py b/src/sage/manifolds/scalarfield_algebra.py index 3ee4b383f9d..3e05fb0371f 100644 --- a/src/sage/manifolds/scalarfield_algebra.py +++ b/src/sage/manifolds/scalarfield_algebra.py @@ -35,6 +35,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method from sage.categories.commutative_algebras import CommutativeAlgebras +from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.ring import SR from sage.manifolds.scalarfield import ScalarField @@ -86,11 +87,11 @@ class ScalarFieldAlgebra(UniqueRepresentation, Parent): :class:`~sage.symbolic.ring.SymbolicRing`):: sage: CM.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CM.base_ring() Symbolic Ring sage: CW.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CW.base_ring() Symbolic Ring @@ -383,7 +384,7 @@ def __init__(self, domain): if domain.base_field_type() in ['real', 'complex']: base_field = SR Parent.__init__(self, base=base_field, - category=CommutativeAlgebras(base_field)) + category=CommutativeAlgebras(base_field) & TopologicalSpaces().Homsets()) self._domain = domain self._populate_coercion_lists_() From b50d9c6047c1ad5167af7db8a8127f72ab18a853 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 31 May 2021 01:52:42 -0500 Subject: [PATCH 457/706] Fix doc building suggestion by slelievre --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 874589b7ff3..ad776c61cf6 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2567,7 +2567,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): ALGORITHM: Uses Cremona's ``eclib`` package, which computes a bound on the saturation index. To `p`-saturate, or prove `p`-saturation, we consider the reductions of the points - modulo primes `q` of good reduction such that `E(\FF_q)` has + modulo primes `q` of good reduction such that `E(\GF{q})` has order divisible by `p`. .. note:: From a089bf7954dfd24446279ce5f9ab77c838f062e7 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 31 May 2021 01:48:54 -0500 Subject: [PATCH 458/706] allow cross compiling --- build/pkgs/brial/spkg-configure.m4 | 36 ++++++++++++++++++++++-- build/pkgs/curl/spkg-configure.m4 | 7 ++++- build/pkgs/flint/spkg-configure.m4 | 3 +- build/pkgs/lcalc/spkg-configure.m4 | 18 ++++++++++-- build/pkgs/libbraiding/spkg-configure.m4 | 20 +++++++++++++ build/pkgs/mpfi/spkg-configure.m4 | 3 +- build/pkgs/ntl/spkg-configure.m4 | 4 +++ build/pkgs/openblas/spkg-configure.m4 | 3 +- build/pkgs/pari/spkg-configure.m4 | 11 +++++--- build/pkgs/sqlite/spkg-configure.m4 | 3 +- build/pkgs/symmetrica/spkg-configure.m4 | 3 +- 11 files changed, 96 insertions(+), 15 deletions(-) diff --git a/build/pkgs/brial/spkg-configure.m4 b/build/pkgs/brial/spkg-configure.m4 index a0f00838c75..6a94a42721b 100644 --- a/build/pkgs/brial/spkg-configure.m4 +++ b/build/pkgs/brial/spkg-configure.m4 @@ -7,7 +7,7 @@ SAGE_SPKG_CONFIGURE([brial], [ AC_LANG_PUSH(C++) SAVED_LIBS=$LIBS LIBS="$LIBS -lbrial -lbrial_groebner" - AC_MSG_CHECKING([if we can link against brial libraries]) + AC_MSG_CHECKING([if we can link against brial libraries and run]) AC_RUN_IFELSE([ AC_LANG_PROGRAM([ #include @@ -48,11 +48,43 @@ SAGE_SPKG_CONFIGURE([brial], [ ], [ AC_MSG_RESULT([yes]) sage_spkg_install_brial=no - ]) + ], [AC_MSG_RESULT([cross compiling. Assume yes]) + sage_spkg_install_brial=no]) ], [ AC_MSG_RESULT([no]) sage_spkg_install_brial=yes + ], + [ + AC_MSG_CHECKING([if we can link against brial libraries]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + USING_NAMESPACE_PBORI + USING_NAMESPACE_PBORIGB + + class MyConstant : public BooleConstant{ + public: void negate() { this->m_value = !this->m_value; } + }; + ],[ + BoolePolyRing r = BoolePolyRing(2, COrderEnums::dlex); + ReductionStrategy rs = ReductionStrategy(r); + rs.llReduceAll(); // uses groebner lib + if (2 != r.nVariables()) { return 1; } + if (r.constant(true) == r.constant(false)) { return 2; } + MyConstant f = MyConstant(); + f.negate(); // ensures v1.1.0+ if m_value isn't const + if (!f.isOne()) { return 3; } + return 0; + ]) + ],[ + AC_MSG_RESULT([yes]) + sage_spkg_install_brial=yes + ],[ + AC_MSG_RESULT([no]) + sage_spkg_install_brial=no + ]) ]) LIBS=$SAVED_LIBS AC_LANG_POP diff --git a/build/pkgs/curl/spkg-configure.m4 b/build/pkgs/curl/spkg-configure.m4 index d88561511b8..b938bd7547d 100644 --- a/build/pkgs/curl/spkg-configure.m4 +++ b/build/pkgs/curl/spkg-configure.m4 @@ -20,7 +20,12 @@ SAGE_SPKG_CONFIGURE([curl], [ AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ curl_easy_setopt(NULL,CURLOPT_URL,NULL); - ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no) + ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no, [ + dnl cross compiling. link only + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ + curl_easy_setopt(NULL,CURLOPT_URL,NULL); + ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no)] + ) ]) AS_IF([test "$sage_libcurl_cv_lib_curl_executable" = "no"], [sage_spkg_install_curl=yes]) ]) diff --git a/build/pkgs/flint/spkg-configure.m4 b/build/pkgs/flint/spkg-configure.m4 index b697e478d74..de3ad59cc2f 100644 --- a/build/pkgs/flint/spkg-configure.m4 +++ b/build/pkgs/flint/spkg-configure.m4 @@ -22,7 +22,8 @@ SAGE_SPKG_CONFIGURE([flint], [ [#endif]])], [AC_MSG_RESULT([GC not enabled. Good.])], [AC_MSG_RESULT([GC enabled. Incompatible with Sage.]) - sage_spkg_install_flint=yes]) + sage_spkg_install_flint=yes], + [AC_MSG_RESULT(["cross compiling. assuming GC is not enabled"])]) ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) diff --git a/build/pkgs/lcalc/spkg-configure.m4 b/build/pkgs/lcalc/spkg-configure.m4 index 19a87c8d304..4225eef4e7a 100644 --- a/build/pkgs/lcalc/spkg-configure.m4 +++ b/build/pkgs/lcalc/spkg-configure.m4 @@ -24,10 +24,22 @@ SAGE_SPKG_CONFIGURE([lcalc], [ AC_MSG_RESULT([no; install lcalc]) sage_spkg_install_lcalc=yes LIBS=$LCALC_SAVED_LIBS + ], [ + AC_MSG_RESULT([cross compiling; check linking only]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[initialize_globals(); + Complex x; + x = Pi*I; + L_function L4; + return 0;]] + )], [AC_MSG_RESULT([yes; use lcalc from the system])], [ + AC_MSG_RESULT([no; install lcalc]) + sage_spkg_install_lcalc=yes + LIBS=$LCALC_SAVED_LIBS + ]) + ]) ]) - ], [ - AC_MSG_RESULT([no. Install lcalc]) - sage_spkg_install_lcalc=yes]) ]) ]) m4_popdef([SAGE_LCALC_MINVER]) diff --git a/build/pkgs/libbraiding/spkg-configure.m4 b/build/pkgs/libbraiding/spkg-configure.m4 index 81f4d57f225..8fd86aa20ea 100644 --- a/build/pkgs/libbraiding/spkg-configure.m4 +++ b/build/pkgs/libbraiding/spkg-configure.m4 @@ -24,6 +24,26 @@ SAGE_SPKG_CONFIGURE([libbraiding], [ [ AC_MSG_RESULT([no]) sage_spkg_install_libbraiding=yes + ],[ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + using namespace Braiding; + ],[ + // Mimic BraidGroup(2)([1,1]).thurston_type() in SageMath. + // thurstontype == 1 corresponds to "periodic" + if (thurstontype(2, {1,1}) == 1) { return 0; } else { return 1; } + ]) + ], + [ + AC_MSG_RESULT([yes]) + sage_spkg_install_libbraiding=no + ], + [ + AC_MSG_RESULT([no]) + sage_spkg_install_libbraiding=yes + ]) ]) LIBS=$SAVED_LIBS AC_LANG_POP diff --git a/build/pkgs/mpfi/spkg-configure.m4 b/build/pkgs/mpfi/spkg-configure.m4 index 7805ef4a2dd..0cdb10a920e 100644 --- a/build/pkgs/mpfi/spkg-configure.m4 +++ b/build/pkgs/mpfi/spkg-configure.m4 @@ -28,7 +28,8 @@ SAGE_SPKG_CONFIGURE([mpfi], [ else return 1; ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) - sage_spkg_install_mpfi=yes]) + sage_spkg_install_mpfi=yes], + [AC_MSG_RESULT([cross compiling. assume yes])]) AC_LANG_POP(C)], [sage_spkg_install_mpfi=yes]) fi diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 48e41de6ea7..228c672cc4e 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -29,6 +29,10 @@ SAGE_SPKG_CONFIGURE([ntl], [ ], [ AC_MSG_RESULT([no]) sage_spkg_install_ntl=yes + ], [ + dnl assume that the person running cross-compiling + dnl knows what they are doing + AC_MSG_RESULT([yes]) ]) ]) diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 912248c474c..177bbb1d4ff 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -75,7 +75,8 @@ SAGE_SPKG_CONFIGURE([openblas], [ + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) return 1;]]) - ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])]) + ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])], + [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) AC_LANG_POP([C]) AC_MSG_RESULT([$HAVE_OPENBLAS]) ]) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 9a1b0c7db8c..70d0a8a590e 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -119,7 +119,8 @@ SAGE_SPKG_CONFIGURE([pari], [ [return vers!=$gp_version;]])], [AC_MSG_RESULT([libpari's and GP's versions match. Good])], [AC_MSG_RESULT([libpari's version does not match GP's version. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume they match])]) AC_MSG_CHECKING([is GP's version good enough? ]) AX_COMPARE_VERSION([$gp_version], [ge], [$SAGE_PARI_MINVER], [ AC_MSG_RESULT([yes]) @@ -142,13 +143,15 @@ SAGE_SPKG_CONFIGURE([pari], [ [[return strcmp(PARI_MT_ENGINE, "pthread") != 0]])], [AC_MSG_RESULT([yes. Good])], [AC_MSG_RESULT([no. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume yes])]) ], [AC_MSG_RESULT([libpari's datadir does not match GP's datadir. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume yes])]) ], [ AC_MSG_RESULT([no]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], [AC_MSG_RESULT([cross compiling. Assume yes])]) AC_LANG_POP() ], [sage_spkg_install_pari=yes]) fi dnl end main PARI test diff --git a/build/pkgs/sqlite/spkg-configure.m4 b/build/pkgs/sqlite/spkg-configure.m4 index c7fd821f593..901e27f19e3 100644 --- a/build/pkgs/sqlite/spkg-configure.m4 +++ b/build/pkgs/sqlite/spkg-configure.m4 @@ -26,7 +26,8 @@ SAGE_SPKG_CONFIGURE([sqlite], [ [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) LIBS="$SQLITE_SAVED_LIBS" - sage_spkg_install_sqlite=yes]) + sage_spkg_install_sqlite=yes], + [AC_MSG_RESULT([cross compiling. assume yes])]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MAJOR]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MINOR]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MICRO]) diff --git a/build/pkgs/symmetrica/spkg-configure.m4 b/build/pkgs/symmetrica/spkg-configure.m4 index 7dc77df703e..0a9b0d5c375 100644 --- a/build/pkgs/symmetrica/spkg-configure.m4 +++ b/build/pkgs/symmetrica/spkg-configure.m4 @@ -21,7 +21,8 @@ dnl check for one of its many functions [ende();]])], [AC_MSG_RESULT([appears to be a well-patched version.])], [AC_MSG_RESULT([buggy version. Sage will build its own.]) - sage_spkg_install_symmetrica=yes]) + sage_spkg_install_symmetrica=yes], + [AC_MSG_RESULT([cross compiling. Assume not buggy.])]) ], [sage_spkg_install_symmetrica=yes]) ], [sage_spkg_install_symmetrica=yes]) AC_LANG_POP(C) From b550a1d0639ba6bcad34ca678eeb8313ddb49f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Mon, 31 May 2021 16:25:36 +0200 Subject: [PATCH 459/706] 31886: Refresh graphs/graph_plot.py --- src/sage/graphs/graph_plot.py | 967 +++++++++++++++++++--------------- 1 file changed, 534 insertions(+), 433 deletions(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 073d36fd56e..b107ab586cc 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -8,20 +8,20 @@ sage: G = graphs.WheelGraph(15) sage: P = G.plot() - sage: P.show() # long time + sage: P.show() # long time .. PLOT:: sphinx_plot(graphs.WheelGraph(15)) -If you create a graph in Sage using the ``Graph`` command, then plot that graph, -the positioning of nodes is determined using the spring-layout algorithm. For -the special graph constructors, which you get using ``graphs.[tab]``, the -positions are preset. For example, consider the Petersen graph with default node -positioning vs. the Petersen graph constructed by this database:: +When plotting a graph created using Sage's ``Graph`` command, +node positions are determined using the spring-layout algorithm. +Special graphs available from ``graphs.*`` have preset positions. +For example, compare the two plots of the Petersen graph, +as obtained using ``Graph`` or as obtained from that database:: sage: petersen_spring = Graph(':I`ES@obGkqegW~') - sage: petersen_spring.show() # long time + sage: petersen_spring.show() # long time .. PLOT:: @@ -31,15 +31,15 @@ :: sage: petersen_database = graphs.PetersenGraph() - sage: petersen_database.show() # long time + sage: petersen_database.show() # long time .. PLOT:: petersen_database = graphs.PetersenGraph() sphinx_plot(petersen_database) -For all the constructors in this database (except some random graphs), the -position dictionary is filled in, instead of using the spring-layout algorithm. +All constructors in this database (except some random graphs) prefill +the position dictionary, bypassing the spring-layout positioning algorithm. **Plot options** @@ -65,21 +65,22 @@ Obviously, these values are overruled when arguments are given explicitly. -Here is how to define the default size of a graph drawing to be ``[6,6]``. The -first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` use this -option, while the third does not (a value for ``figsize`` is explicitly given):: +Here is how to define the default size of a graph drawing to be ``(6, 6)``. +The first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` +use this option, while the third does not (a value for ``figsize`` +is explicitly given):: sage: import sage.graphs.graph_plot - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [6,6] - sage: graphs.PetersenGraph().show() # long time + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (6, 6) + sage: graphs.PetersenGraph().show() # long time sage: graphs.ChvatalGraph().show() # long time - sage: graphs.PetersenGraph().show(figsize=[4,4]) # long time + sage: graphs.PetersenGraph().show(figsize=(4, 4)) # long time We can now reset the default to its initial value, and now display graphs as previously:: - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [4,4] - sage: graphs.PetersenGraph().show() # long time + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (4, 4) + sage: graphs.PetersenGraph().show() # long time sage: graphs.ChvatalGraph().show() # long time .. NOTE:: @@ -91,7 +92,7 @@ lines to `Sage's startup scripts <../../../repl/startup.html>`_. Example:: sage: import sage.graphs.graph_plot - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [4,4] + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (4, 4) **Index of methods and functions** @@ -108,76 +109,6 @@ :meth:`GraphPlot.layout_tree` | Compute a nice layout of a tree. """ -layout_options = { - 'layout': 'A layout algorithm -- one of : "acyclic", "circular" (plots the ' - 'graph with vertices evenly distributed on a circle), "ranked", ' - '"graphviz", "planar", "spring" (traditional spring layout, using the ' - 'graph\'s current positions as initial positions), or "tree" (the tree ' - 'will be plotted in levels, depending on minimum distance for the root).', - 'iterations': 'The number of times to execute the spring layout algorithm.', - 'heights': 'A dictionary mapping heights to the list of vertices at this height.', - 'spring': 'Use spring layout to finalize the current layout.', - 'tree_root': 'A vertex designation for drawing trees. A vertex of the tree ' - 'to be used as the root for the ``layout=\'tree\'`` option. If no root ' - 'is specified, then one is chosen close to the center of the tree. ' - 'Ignored unless ``layout=\'tree\'``.', - 'forest_roots': 'An iterable specifying which vertices to use as roots for ' - 'the ``layout=\'forest\'`` option. If no root is specified for a tree, ' - 'then one is chosen close to the center of the tree. ' - 'Ignored unless ``layout=\'forest\'``.', - 'tree_orientation': 'The direction of tree branches -- \'up\', \'down\', ' - '\'left\' or \'right\'.', - 'save_pos': 'Whether or not to save the computed position for the graph.', - 'dim': 'The dimension of the layout -- 2 or 3.', - 'prog': 'Which graphviz layout program to use -- one of "circo", "dot", ' - '"fdp", "neato", or "twopi".', - 'by_component': 'Whether to do the spring layout by connected component ' - '-- a boolean.', - } - -graphplot_options = layout_options.copy() - -graphplot_options.update( - {'pos': 'The position dictionary of vertices', - 'vertex_labels': 'Whether or not to draw vertex labels.', - 'vertex_color': 'Default color for vertices not listed ' - 'in vertex_colors dictionary.', - 'vertex_colors': 'Dictionary of vertex coloring : each ' - 'key is a color recognizable by matplotlib, and each ' - 'corresponding entry is a list of vertices. ', - 'vertex_size': 'The size to draw the vertices.', - 'vertex_shape': 'The shape to draw the vertices. ' - 'Currently unavailable for Multi-edged DiGraphs.', - 'edge_labels': 'Whether or not to draw edge labels.', - 'edge_style': 'The linestyle of the edges. It should be ' - 'one of "solid", "dashed", "dotted", dashdot", or ' - '"-", "--", ":", "-.", respectively. ', - 'edge_thickness': 'The thickness of the edges.', - 'edge_color': 'The default color for edges not listed in edge_colors.', - 'edge_colors': 'a dictionary specifying edge colors: each ' - 'key is a color recognized by matplotlib, and each ' - 'entry is a list of edges.', - 'color_by_label': 'Whether to color the edges according ' - 'to their labels. This also accepts a function or ' - 'dictionary mapping labels to colors.', - 'partition': 'A partition of the vertex set. If specified, ' - 'plot will show each cell in a different color. ' - 'vertex_colors takes precedence.', - 'loop_size': 'The radius of the smallest loop.', - 'dist': 'The distance between multiedges.', - 'max_dist': 'The max distance range to allow multiedges.', - 'talk': 'Whether to display the vertices in talk mode ' - '(larger and white).', - 'graph_border': 'Whether or not to draw a frame around the graph.', - 'edge_labels_background' : 'The color of the background of the edge labels'}) - - -_PLOT_OPTIONS_TABLE = "" -for key, value in graphplot_options.items(): - _PLOT_OPTIONS_TABLE += " ``"+str(key)+"`` | "+str(value)+"\n" -__doc__ = __doc__.format(PLOT_OPTIONS_TABLE=_PLOT_OPTIONS_TABLE) - - # **************************************************************************** # Copyright (C) 2009 Emily Kirkman # 2009 Robert L. Miller @@ -193,34 +124,135 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** + +from collections import defaultdict +from math import sqrt, cos, sin, acos, atan, pi from sage.structure.sage_object import SageObject -from sage.plot.all import Graphics, scatter_plot, bezier_path, line, arrow, text, circle -from math import sqrt, cos, sin, atan, pi +from sage.plot.all import ( + Graphics, scatter_plot, bezier_path, line, arrow, text, arc, circle) + +layout_options = { + 'layout': + 'A layout algorithm -- one of : "acyclic", "circular" (plots the ' + 'graph with vertices evenly distributed on a circle), "ranked", ' + '"graphviz", "planar", "spring" (traditional spring layout, using ' + 'the graph\'s current positions as initial positions), or "tree" ' + '(the tree will be plotted in levels, depending on minimum distance ' + 'for the root).', + 'iterations': + 'The number of times to execute the spring layout algorithm.', + 'heights': + 'A dictionary mapping heights to the list of vertices at this height.', + 'spring': + 'Use spring layout to finalize the current layout.', + 'tree_root': + 'A vertex designation for drawing trees. A vertex of the tree to ' + 'be used as the root for the ``layout=\'tree\'`` option. If no root ' + 'is specified, then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'tree\'``.', + 'forest_roots': + 'An iterable specifying which vertices to use as roots for the ' + '``layout=\'forest\'`` option. If no root is specified for a tree, ' + 'then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'forest\'``.', + 'tree_orientation': + 'The direction of tree branches -- \'up\', \'down\', ' + '\'left\' or \'right\'.', + 'save_pos': + 'Whether or not to save the computed position for the graph.', + 'dim': + 'The dimension of the layout -- 2 or 3.', + 'prog': + 'Which graphviz layout program to use -- one of ' + '"circo", "dot", "fdp", "neato", or "twopi".', + 'by_component': + 'Whether to do the spring layout by connected component -- a boolean.', + } + +graphplot_options = layout_options.copy() + +graphplot_options.update({ + 'pos': + 'The position dictionary of vertices.', + 'vertex_labels': + 'Whether or not to draw vertex labels.', + 'vertex_color': + 'Default color for vertices not listed ' + 'in vertex_colors dictionary.', + 'vertex_colors': + 'A dictionary specifying vertex colors: ' + 'each key is a color recognizable by matplotlib, ' + 'and each corresponding value is a list of vertices.', + 'vertex_size': + 'The size to draw the vertices.', + 'vertex_shape': + 'The shape to draw the vertices. ' + 'Currently unavailable for Multi-edged DiGraphs.', + 'edge_labels': + 'Whether or not to draw edge labels.', + 'edge_style': + 'The linestyle of the edges. It should be ' + 'one of "solid", "dashed", "dotted", dashdot", ' + 'or "-", "--", ":", "-.", respectively. ', + 'edge_thickness': + 'The thickness of the edges.', + 'edge_color': + 'The default color for edges not listed in edge_colors.', + 'edge_colors': + 'A dictionary specifying edge colors: ' + 'each key is a color recognized by matplotlib, ' + 'and each corresponding value is a list of edges.', + 'color_by_label': + 'Whether to color the edges according to their labels. This also ' + 'accepts a function or dictionary mapping labels to colors.', + 'partition': + 'A partition of the vertex set. If specified, plot will show each ' + 'cell in a different color; vertex_colors takes precedence.', + 'loop_size': + 'The radius of the smallest loop.', + 'dist': + 'The distance between multiedges.', + 'max_dist': + 'The max distance range to allow multiedges.', + 'talk': + 'Whether to display the vertices in talk mode (larger and white).', + 'graph_border': + 'Whether or not to draw a frame around the graph.', + 'edge_labels_background': + 'The color of the background of the edge labels.', + }) + +_PLOT_OPTIONS_TABLE = "" + +for key, value in graphplot_options.items(): + _PLOT_OPTIONS_TABLE += f" ``{key}`` | {value}\n" + +__doc__ = __doc__.format(PLOT_OPTIONS_TABLE=_PLOT_OPTIONS_TABLE) DEFAULT_SHOW_OPTIONS = { - "figsize" : [4,4] + 'figsize' : (4, 4) } DEFAULT_PLOT_OPTIONS = { - "vertex_size" : 200, - "vertex_labels" : True, - "layout" : None, - "edge_style" : 'solid', - "edge_thickness" : 1, - "edge_color" : 'black', - "edge_colors" : None, - "edge_labels" : False, - "iterations" : 50, - "tree_orientation" : 'down', - "heights" : None, - "graph_border" : False, - "talk" : False, - "color_by_label" : False, - "partition" : None, - "dist" : .075, - "max_dist" : 1.5, - "loop_size" : .075, - "edge_labels_background" : "white" + 'vertex_size' : 200, + 'vertex_labels' : True, + 'layout' : None, + 'edge_style' : 'solid', + 'edge_thickness' : 1, + 'edge_color' : 'black', + 'edge_colors' : None, + 'edge_labels' : False, + 'iterations' : 50, + 'tree_orientation' : 'down', + 'heights' : None, + 'graph_border' : False, + 'talk' : False, + 'color_by_label' : False, + 'partition' : None, + 'dist' : .075, + 'max_dist' : 1.5, + 'loop_size' : .075, + 'edge_labels_background' : 'white' } class GraphPlot(SageObject): @@ -237,27 +269,26 @@ def __init__(self, graph, options): sage: from sage.graphs.graph_plot import GraphPlot sage: options = { - ....: 'vertex_size': 200, - ....: 'vertex_labels': True, - ....: 'layout': None, - ....: 'edge_style': 'solid', - ....: 'edge_color': 'black', - ....: 'edge_colors': None, - ....: 'edge_labels': False, - ....: 'iterations': 50, - ....: 'tree_orientation': 'down', - ....: 'heights': None, - ....: 'graph_border': False, - ....: 'talk': False, - ....: 'color_by_label': False, - ....: 'partition': None, - ....: 'dist': .075, - ....: 'max_dist': 1.5, - ....: 'loop_size': .075, - ....: 'edge_labels_background': 'transparent'} - sage: g = Graph({0:[1, 2], 2:[3], 4:[0, 1]}) + ....: 'vertex_size': 200, + ....: 'vertex_labels': True, + ....: 'layout': None, + ....: 'edge_style': 'solid', + ....: 'edge_color': 'black', + ....: 'edge_colors': None, + ....: 'edge_labels': False, + ....: 'iterations': 50, + ....: 'tree_orientation': 'down', + ....: 'heights': None, + ....: 'graph_border': False, + ....: 'talk': False, + ....: 'color_by_label': False, + ....: 'partition': None, + ....: 'dist': .075, + ....: 'max_dist': 1.5, + ....: 'loop_size': .075, + ....: 'edge_labels_background': 'transparent'} + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) sage: GP = GraphPlot(g, options) - """ # Setting the default values if needed for k, value in DEFAULT_PLOT_OPTIONS.items(): @@ -267,11 +298,11 @@ def __init__(self, graph, options): self._nodelist = list(graph) self._graph = graph self._options = options # contains both plot and show options - self.set_pos() self._arcs = self._graph.has_multiple_edges(to_undirected=True) self._loops = self._graph.has_loops() self._arcdigraph = self._graph.is_directed() and self._arcs + self.set_pos() self.set_vertices() self.set_edges() @@ -283,11 +314,11 @@ def _repr_(self): This function is called implicitly by the code below:: - sage: g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - sage: g.graphplot() # indirect doctest + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + sage: g.graphplot() # indirect doctest GraphPlot object for Graph on 5 vertices """ - return "GraphPlot object for %s"%self._graph + return f"GraphPlot object for {self._graph}" def set_pos(self): """ @@ -297,8 +328,8 @@ def set_pos(self): This function is called implicitly by the code below:: - sage: g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - sage: g.graphplot(save_pos=True, layout='circular') # indirect doctest + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + sage: g.graphplot(save_pos=True, layout='circular') # indirect doctest GraphPlot object for Graph on 5 vertices The following illustrates the format of a position dictionary, but due @@ -315,24 +346,24 @@ def set_pos(self): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) Graphics object consisting of 14 graphics primitives .. PLOT:: - g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - g.graphplot(save_pos=True, layout='circular') # indirect doctest + g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + g.graphplot(save_pos=True, layout='circular') # indirect doctest T = list(graphs.trees(7)) t = T[3] - P = t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) + P = t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) sphinx_plot(P) TESTS: - Make sure that vertex locations are floats. Not being floats isn't a - bug in itself but makes it too easy to accidentally introduce a bug + Make sure that vertex locations are floats. Not being floats isn't + a bug in itself but made it too easy to accidentally introduce a bug elsewhere, such as in :meth:`set_edges` (:trac:`10124`), via silent - truncating division of integers:: + truncating division of Python 2 integers:: sage: g = graphs.FruchtGraph() sage: gp = g.graphplot() @@ -349,7 +380,7 @@ def set_pos(self): Graphics object consisting of 6 graphics primitives """ self._pos = self._graph.layout(**self._options) - # make sure the positions are floats (trac #10124) + # Make sure the positions are floats (trac #10124) self._pos = {k: (float(v[0]), float(v[1])) for k, v in self._pos.items()} @@ -357,17 +388,18 @@ def set_vertices(self, **vertex_options): """ Set the vertex plotting parameters for this ``GraphPlot``. - This function is called by the constructor but can also be called to - make updates to the vertex options of an existing ``GraphPlot`` object. - Note that the changes are cumulative. + This function is called by the constructor but can also be + called to make updates to the vertex options of an existing + ``GraphPlot`` object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_vertices(talk=True) sage: GP.plot() Graphics object consisting of 22 graphics primitives @@ -377,25 +409,26 @@ def set_vertices(self, **vertex_options): .. PLOT:: - g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), - (0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') - GP.set_vertices(talk=True) - sphinx_plot(GP) + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_vertices(talk=True) + sphinx_plot(GP) .. PLOT:: - g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), - (0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') - GP.set_vertices(talk=True) - GP.set_vertices(vertex_color='green', vertex_shape='^') - sphinx_plot(GP) - + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_vertices(talk=True) + GP.set_vertices(vertex_color='green', vertex_shape='^') + sphinx_plot(GP) """ # Handle base vertex options voptions = {} @@ -412,12 +445,14 @@ def set_vertices(self, **vertex_options): else: voptions['markersize'] = self._options['vertex_size'] - if 'vertex_color' not in self._options or self._options['vertex_color'] is None: + if ('vertex_color' not in self._options + or self._options['vertex_color'] is None): vertex_color = '#fec7b8' else: vertex_color = self._options['vertex_color'] - if 'vertex_colors' not in self._options or self._options['vertex_colors'] is None: + if ('vertex_colors' not in self._options + or self._options['vertex_colors'] is None): if self._options['partition'] is not None: from sage.plot.colors import rainbow partition = self._options['partition'] @@ -442,67 +477,61 @@ def set_vertices(self, **vertex_options): if not isinstance(vertex_colors, dict): voptions['facecolor'] = vertex_colors + pos = list(self._pos.values()) if self._arcdigraph: - self._plot_components['vertices'] = [circle(center, - self._vertex_radius, - fill=True, - facecolor=vertex_colors, - edgecolor='black', - clip=False) - for center in self._pos.values()] + self._plot_components['vertices'] = [ + circle(p, self._vertex_radius, fill=True, clip=False, + edgecolor='black', facecolor=vertex_colors) + for p in pos] else: - self._plot_components['vertices'] = scatter_plot(list(self._pos.values()), - clip=False, **voptions) + self._plot_components['vertices'] = ( + scatter_plot(pos, clip=False, **voptions)) else: # Color list must be ordered: pos = [] colors = [] for i in vertex_colors: - pos += [self._pos[j] for j in vertex_colors[i]] - colors += [i] * len(vertex_colors[i]) + pos.extend([self._pos[j] for j in vertex_colors[i]]) + colors.extend([i] * len(vertex_colors[i])) # If all the vertices have not been assigned a color if len(self._pos) != len(pos): leftovers = [j for j in self._pos.values() if j not in pos] - pos += leftovers - colors += [vertex_color] * len(leftovers) + pos.extend(leftovers) + colors.extend([vertex_color] * len(leftovers)) if self._arcdigraph: - self._plot_components['vertices'] = [circle(pos[i], - self._vertex_radius, - fill=True, - facecolor=colors[i], - edgecolor='black', - clip=False) - for i in range(len(pos))] + self._plot_components['vertices'] = [ + circle(p, self._vertex_radius, fill=True, clip=False, + facecolor=colors[i], edgecolor='black') + for i, p in enumerate(pos)] else: - self._plot_components['vertices'] = scatter_plot(pos, - facecolor=colors, - clip=False, **voptions) + self._plot_components['vertices'] = scatter_plot( + pos, facecolor=colors, clip=False, **voptions) if self._options['vertex_labels']: self._plot_components['vertex_labels'] = [] # TODO: allow text options for v in self._nodelist: - self._plot_components['vertex_labels'].append(text(str(v), - self._pos[v], rgbcolor=(0,0,0), zorder=8)) + self._plot_components['vertex_labels'].append( + text(str(v), self._pos[v], rgbcolor=(0, 0, 0), zorder=8)) def set_edges(self, **edge_options): """ - Set the edge (or arrow) plotting parameters for the ``GraphPlot`` - object. + Set edge plotting parameters for the ``GraphPlot`` object. This function is called by the constructor but can also be called to - make updates to the vertex options of an existing ``GraphPlot`` object. + update the vertex options of an existing ``GraphPlot`` object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph(loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 22 graphics primitives @@ -510,10 +539,11 @@ def set_edges(self, **edge_options): .. PLOT:: g = Graph(loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) GP = g.graphplot(vertex_size=100, edge_labels=True, - color_by_label=True, edge_style='dashed') + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') sphinx_plot(GP) @@ -526,10 +556,11 @@ def set_edges(self, **edge_options): .. PLOT:: g = Graph(loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) GP = g.graphplot(vertex_size=100, edge_labels=True, - color_by_label=True, edge_style='dashed') + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') GP.set_edges(edge_color='black') sphinx_plot(GP) @@ -537,10 +568,11 @@ def set_edges(self, **edge_options): :: sage: d = DiGraph(loops=True, multiedges=True, sparse=True) - sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 24 graphics primitives @@ -548,10 +580,11 @@ def set_edges(self, **edge_options): .. PLOT:: d = DiGraph(loops=True, multiedges=True, sparse=True) - d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') + d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') sphinx_plot(GP) @@ -564,10 +597,11 @@ def set_edges(self, **edge_options): .. PLOT:: d = DiGraph(loops=True, multiedges=True, sparse=True) - d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') + d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') GP.set_edges(edge_color='black') sphinx_plot(GP) @@ -575,10 +609,11 @@ def set_edges(self, **edge_options): TESTS:: sage: G = Graph("Fooba") - sage: G.show(edge_colors={'red':[(3,6),(2,5)]}) + sage: G.show(edge_colors={'red':[(3, 6), (2, 5)]}) - Verify that default edge labels are pretty close to being between the vertices - in some cases where they weren't due to truncating division (:trac:`10124`):: + Check default edge labels are pretty close to halfway between + the vertices in some cases where they weren't due to Python 2 + truncating division (:trac:`10124`):: sage: test_graphs = graphs.FruchtGraph(), graphs.BullGraph() sage: tol = 0.001 @@ -593,8 +628,8 @@ def set_edges(self, **edge_options): ....: textobj = elab[0] ....: x, y, s = textobj.x, textobj.y, textobj.string ....: v0, v1 = map(int, s.split()) - ....: vn = vector(((x-(vx[v0]+vx[v1])/2.), y-(vy[v0]+vy[v1])/2.)).norm() - ....: assert vn < tol + ....: m = sum(vector((vx[v], vy[v])) for v in (v0, v1))/2 + ....: assert (vector((x, y)) - m).norm() < tol Ticket :trac:`24051` is fixed:: @@ -604,11 +639,11 @@ def set_edges(self, **edge_options): Ticket :trac:`31542` is fixed:: - sage: stnc = 'ABCCCCDABCDABCDA' + sage: s = 'ABCCCCDABCDABCDA' sage: g = DiGraph({}, loops=True, multiedges=True) - sage: for a, b in [(stnc[i], stnc[i + 1]) for i in range(len(stnc) - 1)]: + sage: for a, b in [(s[i], u) for i, u in enumerate(s[1:])]: ....: g.add_edge(a, b, b) - sage: g.plot(color_by_label=True, edge_style='solid', layout='circular') + sage: g.plot(color_by_label=True, layout='circular') Graphics object consisting of 23 graphics primitives """ for arg in edge_options: @@ -635,69 +670,68 @@ def set_edges(self, **edge_options): self._plot_components['edge_labels'] = [] # Make dict collection of all edges (keep label and edge color) - edges_to_draw = {} + edges_to_draw = defaultdict(list) - def append_or_set(key, label, color, head): - if key in edges_to_draw: - edges_to_draw[key].append((label, color, head)) - else: - edges_to_draw[key] = [(label, color, head)] + def update(key, label, color, head): + edges_to_draw[key].append((label, color, head)) v_to_int = {v: i for i, v in enumerate(self._graph)} - if self._options['color_by_label'] or isinstance(self._options['edge_colors'], dict): + if (self._options['color_by_label'] + or isinstance(self._options['edge_colors'], dict)): if self._options['color_by_label']: - edge_colors = self._graph._color_by_label(format=self._options['color_by_label']) + edge_colors = self._graph._color_by_label( + format=self._options['color_by_label']) else: edge_colors = self._options['edge_colors'] edges_drawn = [] for color in edge_colors: for edge in edge_colors[color]: - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + a, b = edge[0], edge[1] + if v_to_int[a] < v_to_int[b]: + key = a, b head = 1 else: - key = (edge[1], edge[0]) + key = b, a head = 0 if len(edge) < 3: - label = self._graph.edge_label(edge[0], edge[1]) + label = self._graph.edge_label(a, b) if isinstance(label, list): - append_or_set(key, label[-1], color, head) - edges_drawn.append((edge[0], edge[1], label[-1])) - for i in range(len(label) - 1): - edges_to_draw[key].append((label[i], color, head)) - edges_drawn.append((edge[0], edge[1], label[i])) + update(key, label[-1], color, head) + edges_drawn.append((a, b, label[-1])) + for lab in label[:-1]: + edges_to_draw[key].append((lab, color, head)) + edges_drawn.append((a, b, lab)) else: - append_or_set(key, label, color, head) - edges_drawn.append((edge[0], edge[1], label)) + update(key, label, color, head) + edges_drawn.append((a, b, label)) else: label = edge[2] - append_or_set(key, label, color, head) - edges_drawn.append((edge[0], edge[1], label)) + update(key, label, color, head) + edges_drawn.append((a, b, label)) # Add unspecified edges (default color black set in DEFAULT_PLOT_OPTIONS) - for edge in self._graph.edge_iterator(): - if ((edge[0], edge[1], edge[2]) not in edges_drawn and - (self._graph.is_directed() or - (edge[1], edge[0], edge[2]) not in edges_drawn - )): - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + for a, b, c in self._graph.edge_iterator(): + if ((a, b, c) not in edges_drawn + and (self._graph.is_directed() + or (b, a, c) not in edges_drawn)): + if v_to_int[a] < v_to_int[b]: + key = (a, b) head = 1 else: - key = (edge[1], edge[0]) + key = (b, a) head = 0 - append_or_set(key, edge[2], self._options['edge_color'], head) + update(key, c, self._options['edge_color'], head) else: - for edge in self._graph.edge_iterator(): - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + for a, b, c in self._graph.edge_iterator(): + if v_to_int[a] < v_to_int[b]: + key = a, b head = 1 else: - key = (edge[1], edge[0]) + key = b, a head = 0 - append_or_set(key, edge[2], self._options['edge_color'], head) + update(key, c, self._options['edge_color'], head) if edges_to_draw: self._plot_components['edges'] = [] @@ -707,7 +741,7 @@ def append_or_set(key, label, color, head): # Check for multi-edges or loops if self._arcs or self._loops: tmp = edges_to_draw.copy() - dist = self._options['dist'] * 2. + dist = self._options['dist'] * 2 loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt @@ -719,50 +753,55 @@ def append_or_set(key, label, color, head): len_local_labels = len(local_labels) if len_local_labels * dist > max_dist: distance = float(max_dist) / len_local_labels - curr_loop_size = loop_size - for i in range(len_local_labels): - self._plot_components['edges'].append(circle((self._pos[a][0], - self._pos[a][1]-curr_loop_size), curr_loop_size, - rgbcolor=local_labels[i][1], **eoptions)) + r = loop_size # current loop size + for lab, col, _ in local_labels: + c = circle((self._pos[a][0], self._pos[a][1] - r), r, + rgbcolor=col, **eoptions) + self._plot_components['edges'].append(c) if labels: - self._plot_components['edge_labels'].append(text(local_labels[i][0], - (self._pos[a][0], self._pos[a][1] - 2 * curr_loop_size), - background_color=self._options['edge_labels_background'])) - curr_loop_size += distance / 4 - elif len(edges_to_draw[a,b]) > 1: + bg = self._options['edge_labels_background'] + t = text(lab, + (self._pos[a][0], self._pos[a][1] - 2 * r), + background_color=bg) + self._plot_components['edge_labels'].append(t) + r += distance / 4 + elif len(edges_to_draw[a, b]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a,b)) # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] - M = ((p1[0]+p2[0])/2., (p1[1]+p2[1])/2.) # midpoint + p12 = (float(p2[0] - p1[0]), float(p2[1] - p1[1])) + m = ((p1[0] + p2[0])/2., (p1[1] + p2[1])/2.) # midpoint if not p1[1] == p2[1]: - S = float(p1[0]-p2[0]) / (p2[1]-p1[1]) # perp slope - y = lambda x: S*x-S*M[0]+M[1] # perp bisector line + s = -p12[0]/p12[1] # perp slope + y = lambda x: s*(x - m[0]) + m[1] # perp bisector line - # f,g are functions of distance d to determine x values - # on line y at d from point M - f = lambda d: sqrt(d**2/(1.+S**2)) + M[0] - g = lambda d: -sqrt(d**2/(1.+S**2)) + M[0] + # f, g are functions to determine x-values of point + # on line y at distance d from point m (on each side) + f = lambda d: sqrt(d**2/(1. + s**2)) + m[0] + g = lambda d: -sqrt(d**2/(1. + s**2)) + m[0] odd_x = f even_x = g if p1[0] == p2[0]: - odd_y = lambda d: M[1] + odd_y = lambda d: m[1] even_y = odd_y else: odd_y = lambda x: y(f(x)) even_y = lambda x: y(g(x)) else: - odd_x = lambda d: M[0] + odd_x = lambda d: m[0] even_x = odd_x - odd_y = lambda d: M[1] + d - even_y = lambda d: M[1] - d + odd_y = lambda d: m[1] + d + even_y = lambda d: m[1] - d + odd_xy = lambda d: (odd_x(d), odd_y(d)) + even_xy = lambda d: (even_x(d), even_y(d)) - # We now have the control points for each bezier curve + # We now have the control points for each Bezier curve # in terms of distance parameter d. - # Also note that the label for each edge should be drawn at d/2. + # Also note each edge label should be drawn at d/2. # (This is because we're using the perp bisectors). distance = dist len_local_labels = len(local_labels) @@ -771,75 +810,95 @@ def append_or_set(key, label, color, head): for i in range(len_local_labels // 2): k = (i + 1.0) * distance if self._arcdigraph: - odd_start = self._polar_hack_for_multidigraph(p1, - [odd_x(k), odd_y(k)], self._vertex_radius)[0] - odd_end = self._polar_hack_for_multidigraph([odd_x(k), odd_y(k)], - p2, self._vertex_radius)[1] - even_start = self._polar_hack_for_multidigraph(p1, - [even_x(k), even_y(k)], self._vertex_radius)[0] - even_end = self._polar_hack_for_multidigraph([even_x(k), even_y(k)], - p2, self._vertex_radius)[1] - self._plot_components['edges'].append(arrow(path=[[odd_start, - [odd_x(k), odd_y(k)], odd_end]], head=local_labels[2*i][2], - zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(arrow(path=[[even_start, - [even_x(k), even_y(k)], even_end]], head=local_labels[2*i+1][2], - zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) + vr = self._vertex_radius + ph = self._polar_hack_for_multidigraph + odd_start = ph(p1, odd_xy(k), vr)[0] + odd_end = ph(odd_xy(k), p2, vr)[1] + even_start = ph(p1, even_xy(k), vr)[0] + even_end = ph(even_xy(k), p2, vr)[1] + self._plot_components['edges'].append( + arrow(path=[[odd_start, odd_xy(k), odd_end]], + head=local_labels[2*i][2], zorder=1, + rgbcolor=local_labels[2*i][1], + **eoptions)) + self._plot_components['edges'].append( + arrow(path=[[even_start, even_xy(k), even_end]], + head=local_labels[2*i + 1][2], zorder=1, + rgbcolor=local_labels[2*i + 1][1], + **eoptions)) else: - self._plot_components['edges'].append(bezier_path([[p1, - [odd_x(k), odd_y(k)], p2]], zorder=1, - rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(bezier_path([[p1, - [even_x(k), even_y(k)], p2]], zorder=1, - rgbcolor=local_labels[2*i+1][1], **eoptions)) + self._plot_components['edges'].append( + bezier_path([[p1, odd_xy(k), p2]], zorder=1, + rgbcolor=local_labels[2*i][1], + **eoptions)) + self._plot_components['edges'].append( + bezier_path([[p1, even_xy(k), p2]], zorder=1, + rgbcolor=local_labels[2*i + 1][1], + **eoptions)) if labels: j = k / 2.0 - self._plot_components['edge_labels'].append(text(local_labels[2*i][0], - [odd_x(j), odd_y(j)], background_color=self._options['edge_labels_background'])) - self._plot_components['edge_labels'].append(text(local_labels[2*i+1][0], - [even_x(j), even_y(j)], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(local_labels[2*i][0], odd_xy(j), + background_color=bg)) + self._plot_components['edge_labels'].append( + text(local_labels[2*i + 1][0], even_xy(j), + background_color=bg)) if len_local_labels % 2: - edges_to_draw[a,b] = [local_labels[-1]] # draw line for last odd + # draw line for last odd + edges_to_draw[a, b] = [local_labels[-1]] is_directed = self._graph.is_directed() for a,b in edges_to_draw: if self._arcdigraph: - C, D = self._polar_hack_for_multidigraph(self._pos[a], self._pos[b], self._vertex_radius) - self._plot_components['edges'].append(arrow(C, D, - rgbcolor=edges_to_draw[a,b][0][1], head=edges_to_draw[a,b][0][2], - **eoptions)) + ph = self._polar_hack_for_multidigraph + C, D = ph(self._pos[a], self._pos[b], self._vertex_radius) + self._plot_components['edges'].append( + arrow(C, D, + rgbcolor=edges_to_draw[a, b][0][1], + head=edges_to_draw[a, b][0][2], + **eoptions)) if labels: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[a,b][0][0]), - [(C[0]+D[0])/2., (C[1]+D[1])/2.], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(str(edges_to_draw[a, b][0][0]), + [(C[0] + D[0])/2., (C[1] + D[1])/2.], + background_color=bg)) elif is_directed: - self._plot_components['edges'].append(arrow(self._pos[a], self._pos[b], - rgbcolor=edges_to_draw[a,b][0][1], arrowshorten=self._arrowshorten, - head=edges_to_draw[a,b][0][2], **eoptions)) + self._plot_components['edges'].append( + arrow(self._pos[a], self._pos[b], + rgbcolor=edges_to_draw[a, b][0][1], + arrowshorten=self._arrowshorten, + head=edges_to_draw[a, b][0][2], + **eoptions)) else: - self._plot_components['edges'].append(line([self._pos[a], self._pos[b]], - rgbcolor=edges_to_draw[a,b][0][1], **eoptions)) + self._plot_components['edges'].append( + line([self._pos[a], self._pos[b]], + rgbcolor=edges_to_draw[a, b][0][1], + **eoptions)) if labels and not self._arcdigraph: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[a,b][0][0]), - [(self._pos[a][0] + self._pos[b][0])/2., - (self._pos[a][1] + self._pos[b][1])/2.], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(str(edges_to_draw[a, b][0][0]), + [(self._pos[a][0] + self._pos[b][0])/2., + (self._pos[a][1] + self._pos[b][1])/2.], + background_color=bg)) def _polar_hack_for_multidigraph(self, A, B, VR): """ - Helper function to quickly compute the two points of intersection of a - line segment from A to B (xy tuples) and circles centered at A and B, - both with radius VR. Returns a tuple of xy tuples representing the two - points. + Helper function to quickly compute the two points of intersection + of a line segment from ``A`` to ``B`` (provided as xy pairs) and + circles centered at ``A`` and ``B``, both with radius ``VR``. + Returns a pair of xy pairs representing the two points. EXAMPLES:: sage: d = DiGraph(loops=True, multiedges=True, sparse=True) - sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP._polar_hack_for_multidigraph((0, 1), (1, 1), .1) ([0.10..., 1.00...], [0.90..., 1.00...]) @@ -850,7 +909,8 @@ def _polar_hack_for_multidigraph(self, A, B, VR): sage: GP = DiGraph().graphplot() sage: GP._polar_hack_for_multidigraph((0, 1), (2, 2), .1) ([0.08..., 1.04...], [1.91..., 1.95...]) - sage: GP._polar_hack_for_multidigraph((int(0), int(1)), (int(2), int(2)), .1) + sage: GP._polar_hack_for_multidigraph((int(0), int(1)), + ....: (int(2), int(2)), .1) ([0.08..., 1.04...], [1.91..., 1.95...]) """ @@ -872,7 +932,7 @@ def _polar_hack_for_multidigraph(self, A, B, VR): def show(self, **kwds): """ - Show the (Di)Graph associated with this ``GraphPlot`` object. + Show the (di)graph associated with this ``GraphPlot`` object. INPUT: @@ -881,8 +941,8 @@ def show(self, **kwds): .. NOTE:: - - See :mod:`the module's documentation ` for - information on default values of this method. + - See :mod:`the module's documentation ` + for information on default values of this method. - Any options not used by plot will be passed on to the :meth:`~sage.plot.graphics.Graphics.show` method. @@ -890,13 +950,15 @@ def show(self, **kwds): EXAMPLES:: sage: C = graphs.CubeGraph(8) - sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sage: P = C.graphplot(vertex_labels=False, vertex_size=0, + ....: graph_border=True) sage: P.show() .. PLOT:: C = graphs.CubeGraph(8) - P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + P = C.graphplot(vertex_labels=False, vertex_size=0, + graph_border=True) sphinx_plot(P) """ @@ -913,9 +975,9 @@ def plot(self, **kwds): INPUT: - The options accepted by this method are to be found in the documentation - of the :mod:`sage.graphs.graph_plot` module, and the - :meth:`~sage.plot.graphics.Graphics.show` method. + The options accepted by this method are to be found in the + documentation of the :mod:`sage.graphs.graph_plot` module, + and the :meth:`~sage.plot.graphics.Graphics.show` method. .. NOTE:: @@ -926,15 +988,15 @@ def plot(self, **kwds): sage: from math import sin, cos, pi sage: P = graphs.PetersenGraph() - sage: d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], - ....: '#0000FF':[4,9]} + sage: d = {'#FF0000': [0, 5], '#FF9900': [1, 6], '#FFFF00': [2, 7], + ....: '#00FF00': [3, 8], '#0000FF': [4,9]} sage: pos_dict = {} sage: for i in range(5): ....: x = float(cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] ... - sage: for i in range(5,10): + sage: for i in range(5, 10): ....: x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] @@ -946,15 +1008,15 @@ def plot(self, **kwds): from math import sin, cos, pi P = graphs.PetersenGraph() - d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], - '#0000FF':[4,9]} + d = {'#FF0000': [0, 5], '#FF9900': [1, 6], '#FFFF00': [2, 7], + '#00FF00': [3, 8], '#0000FF': [4,9]} pos_dict = {} for i in range(5): x = float(cos(pi/2 + ((2*pi)/5)*i)) y = float(sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] - for i in range(5,10): + for i in range(5, 10): x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] @@ -965,47 +1027,53 @@ def plot(self, **kwds): Here are some more common graphs with typical options:: sage: C = graphs.CubeGraph(8) - sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sage: P = C.graphplot(vertex_labels=False, vertex_size=0, + ....: graph_border=True) sage: P.show() .. PLOT:: C = graphs.CubeGraph(8) - P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + P = C.graphplot(vertex_labels=False, vertex_size=0, + graph_border=True) sphinx_plot(P) :: sage: G = graphs.HeawoodGraph().copy(sparse=True) - sage: for u,v,l in G.edges(): - ....: G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sage: for u, v, l in G.edges(): + ....: G.set_edge_label(u, v, f'({u},{v})') sage: G.graphplot(edge_labels=True).show() .. PLOT:: G = graphs.HeawoodGraph().copy(sparse=True) - for u,v,l in G.edges(): - G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + for u, v, l in G.edges(): + G.set_edge_label(u, v, f'({u},{v})') sphinx_plot(G.graphplot(edge_labels=True)) The options for plotting also work with directed graphs:: - sage: D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], - ....: 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], - ....: 10: [11], 11: [12, 18], 12: [16, 13], 13: [14], 14: [15], 15: [16], - ....: 16: [17], 17: [18], 18: [19], 19: []}) - sage: for u,v,l in D.edges(): - ....: D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sage: D = DiGraph({ + ....: 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + ....: 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], + ....: 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], + ....: 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], + ....: 18: [19], 19: []}) + sage: for u, v, l in D.edges(): + ....: D.set_edge_label(u, v, f'({u},{v})') sage: D.graphplot(edge_labels=True, layout='circular').show() .. PLOT:: - D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], 4: [17, 5], - 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], 10: [11], - 11: [12, 18],12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], - 17: [18], 18: [19], 19: []}) - for u,v,l in D.edges(): - D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + D = DiGraph({ + 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], + 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], + 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], + 18: [19], 19: []}) + for u, v, l in D.edges(): + D.set_edge_label(u, v, f'({u},{v})') sphinx_plot(D.graphplot(edge_labels=True, layout='circular')) This example shows off the coloring of edges:: @@ -1015,12 +1083,13 @@ def plot(self, **kwds): sage: R = rainbow(5) sage: edge_colors = {} sage: for i in range(5): - ....: edge_colors[R[i]] = [] - sage: for u,v,l in C.edges(): - ....: for i in range(5): - ....: if u[i] != v[i]: - ....: edge_colors[R[i]].append((u,v,l)) - sage: C.graphplot(vertex_labels=False, vertex_size=0, edge_colors=edge_colors).show() + ....: edge_colors[R[i]] = [] + sage: for u, v, l in C.edges(): + ....: for i in range(5): + ....: if u[i] != v[i]: + ....: edge_colors[R[i]].append((u, v, l)) + sage: C.graphplot(vertex_labels=False, vertex_size=0, + ....: edge_colors=edge_colors).show() .. PLOT:: @@ -1030,24 +1099,26 @@ def plot(self, **kwds): edge_colors = {} for i in range(5): edge_colors[R[i]] = [] - for u,v,l in C.edges(): + for u, v, l in C.edges(): for i in range(5): if u[i] != v[i]: - edge_colors[R[i]].append((u,v,l)) + edge_colors[R[i]].append((u, v, l)) sphinx_plot(C.graphplot(vertex_labels=False, vertex_size=0, - edge_colors=edge_colors)) + edge_colors=edge_colors)) With the ``partition`` option, we can separate out same-color groups of vertices:: sage: D = graphs.DodecahedralGraph() - sage: Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] + sage: Pi = [[6, 5, 15, 14, 7], [16, 13, 8, 2, 4], + ....: [12, 17, 9, 3, 1], [0, 19, 18, 10, 11]] sage: D.show(partition=Pi) .. PLOT:: D = graphs.DodecahedralGraph() - Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] + Pi = [[6, 5, 15, 14, 7], [16, 13, 8, 2, 4], + [12, 17, 9, 3, 1], [0, 19, 18, 10, 11]] sphinx_plot(D.plot(partition=Pi)) Loops are also plotted correctly:: @@ -1068,27 +1139,29 @@ def plot(self, **kwds): sage: D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) sage: D.show() - sage: D.show(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], + ....: (0, 0, 0): [(2, 3, None)]}) .. PLOT:: D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) - P = D.plot(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + P = D.plot(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], + (0, 0, 0): [(2, 3, None)]}) sphinx_plot(P) More options:: - sage: pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], - ....: 3:[0.6, -0.8], 4:[0.8, 0.3]} - sage: g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) + sage: pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], + ....: 3:[0.6, -0.8], 4:[0.8, 0.3]} + sage: g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) sage: g.graphplot(pos=pos, layout='spring', iterations=0).plot() Graphics object consisting of 11 graphics primitives .. PLOT:: - pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], - 3:[0.6, -0.8], 4:[0.8, 0.3]} - g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) + pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], + 3: [0.6, -0.8], 4:[0.8, 0.3]} + g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) P = g.graphplot(pos=pos, layout='spring', iterations=0).plot() sphinx_plot(P) @@ -1107,52 +1180,63 @@ def plot(self, **kwds): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]} + ....: ).plot() Graphics object consisting of 14 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]})) :: sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]} + ....: ).plot() Graphics object consisting of 14 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]})) :: - sage: t.set_edge_label(0,1,-7) - sage: t.set_edge_label(0,5,3) - sage: t.set_edge_label(0,5,99) - sage: t.set_edge_label(1,2,1000) - sage: t.set_edge_label(3,2,'spam') - sage: t.set_edge_label(2,6,3/2) - sage: t.set_edge_label(0,4,66) - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True).plot() + sage: t.set_edge_label(0, 1, -7) + sage: t.set_edge_label(0, 5, 3) + sage: t.set_edge_label(0, 5, 99) + sage: t.set_edge_label(1, 2, 1000) + sage: t.set_edge_label(3, 2, 'spam') + sage: t.set_edge_label(2, 6, 3/2) + sage: t.set_edge_label(0, 4, 66) + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]}, + ....: edge_labels=True + ....: ).plot() Graphics object consisting of 20 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - t.set_edge_label(0,1,-7) - t.set_edge_label(0,5,3) - t.set_edge_label(0,5,99) - t.set_edge_label(1,2,1000) - t.set_edge_label(3,2,'spam') - t.set_edge_label(2,6,3/2) - t.set_edge_label(0,4,66) - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True)) + t.set_edge_label(0, 1, -7) + t.set_edge_label(0, 5, 3) + t.set_edge_label(0, 5, 99) + t.set_edge_label(1, 2, 1000) + t.set_edge_label(3, 2, 'spam') + t.set_edge_label(2, 6, 3/2) + t.set_edge_label(0, 4, 66) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]}, + edge_labels=True)) :: @@ -1169,12 +1253,15 @@ def plot(self, **kwds): The tree layout is also useful:: sage: t = DiGraph('JCC???@A??GO??CO??GO??') - sage: t.graphplot(layout='tree', tree_root=0, tree_orientation="up").show() + sage: t.graphplot(layout='tree', tree_root=0, + ....: tree_orientation="up" + ....: ).show() .. PLOT:: t = DiGraph('JCC???@A??GO??CO??GO??') - sphinx_plot(t.graphplot(layout='tree', tree_root=0, tree_orientation="up")) + sphinx_plot(t.graphplot(layout='tree', tree_root=0, + tree_orientation="up")) More examples:: @@ -1190,37 +1277,50 @@ def plot(self, **kwds): sage: D = DiGraph(multiedges=True, sparse=True) sage: for i in range(5): - ....: D.add_edge((i,i+1,'a')) - ....: D.add_edge((i,i-1,'b')) - sage: D.graphplot(edge_labels=True,edge_colors=D._color_by_label()).plot() + ....: D.add_edge((i, i + 1, 'a')) + ....: D.add_edge((i, i - 1, 'b')) + sage: D.graphplot(edge_labels=True, + ....: edge_colors=D._color_by_label() + ....: ).plot() Graphics object consisting of 34 graphics primitives .. PLOT:: D = DiGraph(multiedges=True, sparse=True) for i in range(5): - D.add_edge((i,i+1,'a')) - D.add_edge((i,i-1,'b')) - sphinx_plot(D.graphplot(edge_labels=True,edge_colors=D._color_by_label())) + D.add_edge((i, i + 1, 'a')) + D.add_edge((i, i - 1, 'b')) + sphinx_plot(D.graphplot(edge_labels=True, + edge_colors=D._color_by_label())) :: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed').plot() + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: g.graphplot(edge_labels=True, + ....: color_by_label=True, + ....: edge_style='dashed' + ....: ).plot() Graphics object consisting of 22 graphics primitives .. PLOT:: g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sphinx_plot(g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed')) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sphinx_plot(g.graphplot(edge_labels=True, + color_by_label=True, + edge_style='dashed')) The ``edge_style`` option may be provided in the short format too:: - sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='--').plot() + sage: g.graphplot(edge_labels=True, + ....: color_by_label=True, + ....: edge_style='--' + ....: ).plot() Graphics object consisting of 22 graphics primitives TESTS: @@ -1240,16 +1340,18 @@ def plot(self, **kwds): Make sure that no graphics primitive is clipped:: - sage: tadpole = Graph({0:[0,1]}).plot() + sage: tadpole = Graph({0: [0, 1]}).plot() sage: bbox = tadpole.get_minmax_data() sage: for part in tadpole: ....: part_bbox = part.get_minmax_data() - ....: assert bbox['xmin'] <= part_bbox['xmin'] <= part_bbox['xmax'] <= bbox['xmax'] - ....: assert bbox['ymin'] <= part_bbox['ymin'] <= part_bbox['ymax'] <= bbox['ymax'] + ....: assert (bbox['xmin'] <= part_bbox['xmin'] + ....: <= part_bbox['xmax'] <= bbox['xmax']) + ....: assert (bbox['ymin'] <= part_bbox['ymin'] + ....: <= part_bbox['ymax'] <= bbox['ymax']) Check that one can plot immutable graphs (:trac:`17340`):: - sage: Graph({0:[0]},immutable=True).plot() + sage: Graph({0: [0]}, immutable=True).plot() Graphics object consisting of 3 graphics primitives """ G = Graphics() @@ -1306,17 +1408,18 @@ def layout_tree(self, root, orientation): sage: G = graphs.HoffmanSingletonGraph() sage: T = Graph() sage: T.add_edges(G.min_spanning_tree(starting_vertex=0)) - sage: T.show(layout='tree', tree_root=0) # indirect doctest + sage: T.show(layout='tree', tree_root=0) # indirect doctest """ T = self._graph if not self._graph.is_tree(): - raise RuntimeError("Cannot use tree layout on this graph: self.is_tree() returns False.") + raise RuntimeError("Cannot use tree layout on this graph: " + "self.is_tree() returns False.") children = {root: T.neighbors(root)} - #always make a copy of the children because they get eaten + # Always make a copy of the children because they get eaten stack = [[u for u in children[root]]] stick = [root] parent = {u: root for u in children[root]} @@ -1333,7 +1436,6 @@ def slide(v, dx): Precondition: v and its descendents have already had their positions computed. - """ level = [v] while level: @@ -1344,7 +1446,6 @@ def slide(v, dx): obstruction[y] = max(x + 1, obstruction[y]) pos[u] = x, y nextlevel += children[u] - level = nextlevel while stack: From dbaface15f586e947680c1008fc72559273e441f Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 31 May 2021 16:42:03 +0200 Subject: [PATCH 460/706] Trac 31875: fix exponentiation of p-adics when exponent is exact zero --- src/sage/rings/padics/CA_template.pxi | 10 +++++++++- src/sage/rings/padics/CR_template.pxi | 12 +++++++++++- src/sage/rings/padics/FP_template.pxi | 12 +++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 38901797076..809b24116f5 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -449,6 +449,14 @@ cdef class CAElement(pAdicTemplateElement): 1 + 14*19^2 + 11*19^3 + 13*19^4 + O(19^5) sage: (a.log() * 19/7).exp() 1 + 14*19^2 + 11*19^3 + 13*19^4 + O(19^5) + + Check that :trac:`31875` is fixed:: + + sage: R(1)^R(0) + 1 + O(19^5) + sage: S. = ZqCA(4) + sage: S(1)^S(0) + 1 + O(2^20) """ cdef long relprec, val, rval cdef mpz_t tmp @@ -464,7 +472,7 @@ cdef class CAElement(pAdicTemplateElement): elif self.parent() is _right.parent(): ## For extension elements, we need to switch to the ## fraction field sometimes in highly ramified extensions. - exact_exp = False + exact_exp = (_right)._is_exact_zero() pright = _right else: self, _right = canonical_coercion(self, _right) diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index fc4d46cf0ff..5424be0f500 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -654,6 +654,16 @@ cdef class CRElement(pAdicTemplateElement): 1 + 14*19^2 + 11*19^3 + 13*19^4 + O(19^5) sage: (a.log() * 19/7).exp() 1 + 14*19^2 + 11*19^3 + 13*19^4 + O(19^5) + + TESTS: + + Check that :trac:`31875` is fixed:: + + sage: R(1)^R(0) + 1 + O(19^5) + sage: S. = ZqCR(4) + sage: S(1)^S(0) + 1 + O(2^20) """ cdef long base_level, exp_prec cdef mpz_t tmp @@ -668,7 +678,7 @@ cdef class CRElement(pAdicTemplateElement): elif self.parent() is _right.parent(): ## For extension elements, we need to switch to the ## fraction field sometimes in highly ramified extensions. - exact_exp = False + exact_exp = (_right)._is_exact_zero() pright = _right else: self, _right = canonical_coercion(self, _right) diff --git a/src/sage/rings/padics/FP_template.pxi b/src/sage/rings/padics/FP_template.pxi index ec8bf5ce594..c7ce3937c0d 100644 --- a/src/sage/rings/padics/FP_template.pxi +++ b/src/sage/rings/padics/FP_template.pxi @@ -600,6 +600,16 @@ cdef class FPElement(pAdicTemplateElement): 1 + 4*11^2 + 3*11^3 + 7*11^4 sage: R(11)^-1 11^-1 + + TESTS: + + Check that :trac:`31875` is fixed:: + + sage: R(1)^R(0) + 1 + sage: S. = ZqFP(4) + sage: S(1)^S(0) + 1 """ cdef long dummyL cdef mpz_t tmp @@ -614,7 +624,7 @@ cdef class FPElement(pAdicTemplateElement): elif self.parent() is _right.parent(): ## For extension elements, we need to switch to the ## fraction field sometimes in highly ramified extensions. - exact_exp = False + exact_exp = (_right)._is_exact_zero() pright = _right else: self, _right = canonical_coercion(self, _right) From 1624c4524e64159b8b5a9d3ce788bf587fc41f3d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 31 May 2021 21:28:24 +0200 Subject: [PATCH 461/706] adapt to new parent of I --- src/sage/symbolic/expression_conversions.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 60281f617cc..199ee3870a2 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -947,6 +947,15 @@ def __init__(self): def pyobject(self, ex, obj): """ + Return a string which, when evaluated by FriCAS, returns the + object as an expression. + + We explicitly add the coercion to the FriCAS domains + `Expression Integer` and `Expression Complex Integer` to make + sure that elements of the symbolic ring are translated to + these. In particular, this is needed for integration, see + :trac:`28641` and :trac:`28647`. + EXAMPLES:: sage: 2._fricas_().domainOf() # optional - fricas @@ -983,14 +992,15 @@ def pyobject(self, ex, obj): try: result = getattr(obj, self.name_init)() if (isinstance(obj, NumberFieldElement_quadratic) and - obj.parent() == GaussianField): + obj.parent() is GaussianField()): return "((%s)::EXPR COMPLEX INT)" % result except AttributeError: result = repr(obj) return "((%s)::EXPR INT)" % result def symbol(self, ex): - """Convert the argument, which is a symbol, to FriCAS. + """ + Convert the argument, which is a symbol, to FriCAS. In this case, we do not return an `Expression Integer`, because FriCAS frequently requires elements of domain From 0ead4585cdb61bf9e361a833bd5472f5dd18a781 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 1 Jun 2021 08:21:28 +0200 Subject: [PATCH 462/706] simplify test for complex i --- src/sage/symbolic/expression_conversions.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 199ee3870a2..3f58ccfec31 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -21,8 +21,7 @@ from sage.symbolic.constants import I from sage.functions.all import exp from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg -from sage.rings.number_field.number_field import GaussianField -from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic +from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField from functools import reduce @@ -448,8 +447,7 @@ def pyobject(self, ex, obj): 'Pi' """ if (self.interface.name() in ['pari','gp'] and - isinstance(obj, NumberFieldElement_quadratic) and - obj.parent() is GaussianField()): + isinstance(obj, NumberFieldElement_gaussian)): return repr(obj) try: return getattr(obj, self.name_init)() @@ -976,6 +974,12 @@ def pyobject(self, ex, obj): sage: asin(pi)._fricas_() # optional - fricas asin(%pi) + sage: I._fricas_().domainOf() # optional - fricas + Complex(Integer()) + + sage: SR(I)._fricas_().domainOf() # optional - fricas + Expression(Complex(Integer())) + sage: ex = (I+sqrt(2)+2) sage: ex._fricas_().domainOf() # optional - fricas Expression(Complex(Integer())) @@ -991,8 +995,7 @@ def pyobject(self, ex, obj): """ try: result = getattr(obj, self.name_init)() - if (isinstance(obj, NumberFieldElement_quadratic) and - obj.parent() is GaussianField()): + if isinstance(obj, NumberFieldElement_gaussian): return "((%s)::EXPR COMPLEX INT)" % result except AttributeError: result = repr(obj) From c92aec8046b9060eb6c30d99520430ca2f96745e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 1 Jun 2021 08:30:23 +0200 Subject: [PATCH 463/706] remove superfluous import of I --- src/sage/symbolic/expression_conversions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 3f58ccfec31..c2b691f5e76 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -18,7 +18,6 @@ import operator as _operator from sage.rings.rational_field import QQ from sage.symbolic.ring import SR -from sage.symbolic.constants import I from sage.functions.all import exp from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian From cd3ca79ab94f48321cc9aa3f0b34c1b3db4e405e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 31 May 2021 23:57:05 -0700 Subject: [PATCH 464/706] Update doctests for refined category of ScalarField --- .../vector_calculus/vector_calc_advanced.rst | 2 +- .../thematic_tutorials/vector_calculus/vector_calc_plane.rst | 2 +- src/sage/manifolds/differentiable/scalarfield_algebra.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst index ee4e6e95d12..f828b94d602 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst @@ -311,7 +311,7 @@ The set `C^\infty(\mathbb{E}^3)` of all smooth scalar fields on sage: CE Algebra of differentiable scalar fields on the Euclidean space E^3 sage: CE.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: f in CE True diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst index 19ee71e386e..b3705c288f1 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst @@ -612,7 +612,7 @@ on `\mathbb{E}^2`, `C^\infty(\mathbb{E}^2)`:: sage: CE is XE.base_ring() True sage: CE.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: rank(XE) 2 diff --git a/src/sage/manifolds/differentiable/scalarfield_algebra.py b/src/sage/manifolds/differentiable/scalarfield_algebra.py index c0a0a3b59fa..4ea29402c18 100644 --- a/src/sage/manifolds/differentiable/scalarfield_algebra.py +++ b/src/sage/manifolds/differentiable/scalarfield_algebra.py @@ -93,11 +93,11 @@ class DiffScalarFieldAlgebra(ScalarFieldAlgebra): algebras over `\RR` (represented here by Sage's Symbolic Ring):: sage: CM.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CM.base_ring() Symbolic Ring sage: CW.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CW.base_ring() Symbolic Ring From ac1e491264e8a33d68097b585a9c3e1825a5c0de Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 1 Jun 2021 10:38:08 +0200 Subject: [PATCH 465/706] make symbolic ready for random seeds --- src/sage/symbolic/expression.pyx | 13 ++-- src/sage/symbolic/expression_conversions.py | 1 + src/sage/symbolic/random_tests.py | 85 ++++++++++++++------- 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f81c4ff9717..a78e27eb454 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -10013,8 +10013,8 @@ cdef class Expression(CommutativeRingElement): sage: a = RR.random_element() sage: b = RR.random_element() sage: f = SR(a + b*I) - sage: bool(f.rectform() == a + b*I) - True + sage: abs(f.rectform() - (a + b*I)) # abs tol 1e-16 + 0.0 If we decompose a complex number into its real and imaginary parts, they should correspond to the real and imaginary terms @@ -10023,9 +10023,8 @@ cdef class Expression(CommutativeRingElement): sage: z = CC.random_element() sage: a = z.real_part() sage: b = z.imag_part() - sage: bool(SR(z).rectform() == a + b*I) - True - + sage: abs(SR(z).rectform() - (a + b*I)) # abs tol 1e-16 + 0.0 """ return self.maxima_methods().rectform() @@ -10227,10 +10226,10 @@ cdef class Expression(CommutativeRingElement): 1/3*x*hypergeometric((), (2, 3), x) + hypergeometric((), (1, 2), x) sage: (2*hypergeometric((), (), x)).simplify_hypergeometric() 2*e^x - sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) + sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) # not tested, unstable ....: .simplify_hypergeometric()) laguerre(-laguerre(-e^x, x), x) - sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) + sage: (nest(lambda y: hypergeometric([y], [1], x), 3, 1) # not tested, unstable ....: .simplify_hypergeometric(algorithm='sage')) hypergeometric((hypergeometric((e^x,), (1,), x),), (1,), x) sage: hypergeometric_M(1, 3, x).simplify_hypergeometric() diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 9cf695317fc..f3a18dc13b5 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -2095,6 +2095,7 @@ def __init__(self, ex): sage: s = ExpressionTreeWalker(ex) sage: bool(s() == ex) True + sage: set_random_seed(0) # random_expr is unstable sage: foo = random_expr(20, nvars=2) sage: s = ExpressionTreeWalker(foo) sage: bool(s() == foo) diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index 81ac9310f40..0c03da8d409 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -141,16 +141,19 @@ def choose_from_prob_list(lst): sage: from sage.symbolic.random_tests import * sage: v = [(0.1, False), (0.9, True)] - sage: choose_from_prob_list(v) + sage: choose_from_prob_list(v) # random (0.900000000000000, True) sage: true_count = 0 - sage: for _ in range(10000): - ....: if choose_from_prob_list(v)[1]: - ....: true_count += 1 - sage: true_count - 9033 - sage: true_count - (10000 * 9/10) - 33 + sage: total_count = 0 + sage: def more_samples(): + ....: global true_count, total_count + ....: for _ in range(10000): + ....: total_count += 1.0 + ....: if choose_from_prob_list(v)[1]: + ....: true_count += 1.0 + sage: more_samples() + sage: while abs(true_count/total_count - 0.9) > 0.01: + ....: more_samples() """ r = random() for i in range(len(lst)-1): @@ -168,22 +171,39 @@ def random_integer_vector(n, length): That gives values uniformly at random, but might be slow; this routine is not uniform, but should always be fast. - (This routine is uniform if *length* is 1 or 2; for longer vectors, + (This routine is uniform if ``length`` is 1 or 2; for longer vectors, we prefer approximately balanced vectors, where all the values are around `n/{length}`.) EXAMPLES:: sage: from sage.symbolic.random_tests import * - sage: random_integer_vector(100, 2) + sage: a = random_integer_vector(100, 2); a # random [11, 89] - sage: random_integer_vector(100, 2) - [51, 49] - sage: random_integer_vector(100, 2) - [4, 96] - sage: random_integer_vector(10000, 20) - [332, 529, 185, 738, 82, 964, 596, 892, 732, 134, - 834, 765, 398, 608, 358, 300, 652, 249, 586, 66] + sage: len(a) + 2 + sage: sum(a) + 100 + + sage: b = random_integer_vector(10000, 20) + sage: len(b) + 20 + sage: sum(b) + 10000 + + The routine is uniform if ``length`` is 2:: + + sage: true_count = 0 + sage: total_count = 0 + sage: def more_samples(): + ....: global true_count, total_count + ....: for _ in range(1000): + ....: total_count += 1.0 + ....: if a == random_integer_vector(100, 2): + ....: true_count += 1.0 + sage: more_samples() + sage: while abs(true_count/total_count - 0.01) > 0.01: + ....: more_samples() """ if length == 0: return [] @@ -206,15 +226,26 @@ def random_expr_helper(n_nodes, internal, leaves, verbose): EXAMPLES:: sage: from sage.symbolic.random_tests import * - sage: random_expr_helper(9, [(0.5, operator.add, 2), + sage: a = random_expr_helper(9, [(0.5, operator.add, 2), ....: (0.5, operator.neg, 1)], [(0.5, 1), (0.5, x)], True) - About to apply to [1, x] - About to apply to [x, x + 1] - About to apply to [1] - About to apply to [-1] - About to apply to [1] - About to apply to [2*x + 1, -1] - 2*x + About to apply to [31] About to apply sgn to [v1] About to apply to [1/31, sgn(v1)] From 72a435d36bbd04e0dd46ab9e5f11cc27510d58a1 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 1 Jun 2021 10:45:33 +0200 Subject: [PATCH 466/706] make finance ready for fuzzing doctests --- src/sage/finance/markov_multifractal.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/finance/markov_multifractal.py b/src/sage/finance/markov_multifractal.py index 30c8b0d950a..7ff249c4e4e 100644 --- a/src/sage/finance/markov_multifractal.py +++ b/src/sage/finance/markov_multifractal.py @@ -222,10 +222,14 @@ def simulation(self, n): EXAMPLES:: sage: msm = finance.MarkovSwitchingMultifractal(8,1.4,1.0,0.95,3) - sage: msm.simulation(5) + sage: m = msm.simulation(5); m # random [0.0059, -0.0097, -0.0101, -0.0110, -0.0067] - sage: msm.simulation(3) + sage: len(m) + 5 + sage: m = msm.simulation(3); m # random [0.0055, -0.0084, 0.0141] + sage: len(m) + 3 """ return self.simulations(n, 1)[0] From 0e062d5987166d71ce925d19aba21026fa9191ab Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 1 Jun 2021 11:57:33 +0200 Subject: [PATCH 467/706] make documentation ready for random seeds --- src/doc/en/constructions/groups.rst | 4 ++-- src/doc/en/prep/Quickstarts/Linear-Algebra.rst | 10 +++++----- src/doc/en/reference/sat/index.rst | 7 ++++++- .../elliptic_curves.rst | 2 +- src/doc/ja/tutorial/tour_groups.rst | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/doc/en/constructions/groups.rst b/src/doc/en/constructions/groups.rst index b2ff0e16a4c..042d37088b1 100644 --- a/src/doc/en/constructions/groups.rst +++ b/src/doc/en/constructions/groups.rst @@ -185,7 +185,7 @@ Here's another way, working more directly with GAP:: sage: print(gap.eval("G := SymmetricGroup( 4 )")) Sym( [ 1 .. 4 ] ) sage: print(gap.eval("normal := NormalSubgroups( G );")) - [ Sym( [ 1 .. 4 ] ), Alt( [ 1 .. 4 ] ), Group([ (1,4)(2,3), (1,2)(3,4) ]), + [ Sym( [ 1 .. 4 ] ), Alt( [ 1 .. 4 ] ), Group([ (1,4)(2,3), ... ]), Group(()) ] .. index:: @@ -252,7 +252,7 @@ Another example of using the small groups database: ``group_id`` gap> G:=Group((4,6,5)(7,8,9),(1,7,2,4,6,9,5,3)); Group([ (4,6,5)(7,8,9), (1,7,2,4,6,9,5,3) ]) gap> StructureDescription(G); - "(C3 x C3) : GL(2,3)" + "(C3 x C3) : GL(2,3)" Construction instructions for every group of order less than 32 =============================================================== diff --git a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst index 3f6b6feb3c0..666e4b48765 100644 --- a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst +++ b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst @@ -330,17 +330,17 @@ We can easily solve linear equations using the backslash, like in Matlab. :: - sage: A=random_matrix(QQ,3) # random - sage: v=vector([2,3,1]) - sage: A,v # random + sage: A = random_matrix(QQ, 3, algorithm='unimodular') + sage: v = vector([2,3,1]) + sage: A,v # random ( [ 0 -1 1] [-1 -1 -1] [ 0 2 2], (2, 3, 1) ) - sage: x=A\v; x # random + sage: x=A\v; x # random (-7/2, -3/4, 5/4) - sage: A*x # random + sage: A*x # random (2, 3, 1) For *lots* more (concise) information, see the Sage `Linear Algebra diff --git a/src/doc/en/reference/sat/index.rst b/src/doc/en/reference/sat/index.rst index 03f26030a0c..c7a3ba619dd 100644 --- a/src/doc/en/reference/sat/index.rst +++ b/src/doc/en/reference/sat/index.rst @@ -124,7 +124,12 @@ Sage provides various highlevel functions which make working with Boolean polyno construct a very small-scale AES system of equations and pass it to a SAT solver:: sage: sr = mq.SR(1,1,1,4,gf2=True,polybori=True) - sage: F,s = sr.polynomial_system() + sage: while True: + ....: try: + ....: F,s = sr.polynomial_system() + ....: break + ....: except ZeroDivisionError: + ....: pass sage: from sage.sat.boolean_polynomials import solve as solve_sat # optional - cryptominisat sage: s = solve_sat(F) # optional - cryptominisat sage: F.subs(s[0]) # optional - cryptominisat diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst index 034e52699de..d1a2bda863b 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/elliptic_curves.rst @@ -108,7 +108,7 @@ compute its cardinality, which behind the scenes uses SEA. :: sage: E = EllipticCurve_from_j(k.random_element()) - sage: E.cardinality() # less than a second + sage: E.cardinality() # random, less than a second 99999999999371984255 To see how Sage chooses when to use SEA versus other methods, type diff --git a/src/doc/ja/tutorial/tour_groups.rst b/src/doc/ja/tutorial/tour_groups.rst index a2518600d34..1b067df06c0 100644 --- a/src/doc/ja/tutorial/tour_groups.rst +++ b/src/doc/ja/tutorial/tour_groups.rst @@ -21,7 +21,7 @@ Sageでは,置換群,有限古典群(例えば :math:`SU(n,q)`),有限行 False sage: G.derived_series() # 結果は変化しがち [Subgroup generated by [(3,4), (1,2,3)(4,5)] of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]), - Subgroup generated by [(1,3,5), (1,5)(3,4), (1,5)(2,4)] of (Permutation Group with generators [(3,4), (1,2,3)(4,5)])] + Subgroup generated by [...] of (Permutation Group with generators [(3,4), (1,2,3)(4,5)])] sage: G.center() Subgroup generated by [()] of (Permutation Group with generators [(3,4), (1,2,3)(4,5)]) sage: G.random_element() # random 出力は変化する From 95e3da9e57add7608f05d871534a127e18d631ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Tue, 1 Jun 2021 12:10:59 +0200 Subject: [PATCH 468/706] 31886: Address reviewer comments --- src/sage/graphs/graph_plot.py | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index b107ab586cc..558eb4f22c8 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -672,9 +672,6 @@ def set_edges(self, **edge_options): # Make dict collection of all edges (keep label and edge color) edges_to_draw = defaultdict(list) - def update(key, label, color, head): - edges_to_draw[key].append((label, color, head)) - v_to_int = {v: i for i, v in enumerate(self._graph)} if (self._options['color_by_label'] @@ -689,25 +686,25 @@ def update(key, label, color, head): for edge in edge_colors[color]: a, b = edge[0], edge[1] if v_to_int[a] < v_to_int[b]: - key = a, b + key = (a, b) head = 1 else: - key = b, a + key = (b, a) head = 0 if len(edge) < 3: label = self._graph.edge_label(a, b) if isinstance(label, list): - update(key, label[-1], color, head) + edges_to_draw[key].append((label[-1], color, head)) edges_drawn.append((a, b, label[-1])) for lab in label[:-1]: edges_to_draw[key].append((lab, color, head)) edges_drawn.append((a, b, lab)) else: - update(key, label, color, head) + edges_to_draw[key].append((label, color, head)) edges_drawn.append((a, b, label)) else: label = edge[2] - update(key, label, color, head) + edges_to_draw[key].append((label, color, head)) edges_drawn.append((a, b, label)) # Add unspecified edges (default color black set in DEFAULT_PLOT_OPTIONS) @@ -721,17 +718,17 @@ def update(key, label, color, head): else: key = (b, a) head = 0 - update(key, c, self._options['edge_color'], head) + edges_to_draw[key].append((c, self._options['edge_color'], head)) else: for a, b, c in self._graph.edge_iterator(): if v_to_int[a] < v_to_int[b]: - key = a, b + key = (a, b) head = 1 else: - key = b, a + key = (b, a) head = 0 - update(key, c, self._options['edge_color'], head) + edges_to_draw[key].append((c, self._options['edge_color'], head)) if edges_to_draw: self._plot_components['edges'] = [] @@ -742,29 +739,31 @@ def update(key, label, color, head): if self._arcs or self._loops: tmp = edges_to_draw.copy() dist = self._options['dist'] * 2 - loop_size = self._options['loop_size'] + min_loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt for a, b in tmp: if a == b: - # Loops + # Multiple loops need varying loop radius starting at + # minimum loop size and respecting other distances + loop_size = min_loop_size # current loop radius distance = dist local_labels = edges_to_draw.pop((a, b)) len_local_labels = len(local_labels) if len_local_labels * dist > max_dist: distance = float(max_dist) / len_local_labels - r = loop_size # current loop size + loop_size_increment = distance / 4 + # Now add all the loops at this vertex, varying their size for lab, col, _ in local_labels: - c = circle((self._pos[a][0], self._pos[a][1] - r), r, - rgbcolor=col, **eoptions) + x, y = self._pos[a][0], self._pos[a][1] - loop_size + c = circle((x, y), loop_size, rgbcolor=col, **eoptions) self._plot_components['edges'].append(c) if labels: bg = self._options['edge_labels_background'] - t = text(lab, - (self._pos[a][0], self._pos[a][1] - 2 * r), - background_color=bg) + y -= loop_size # place label at bottom of loop + t = text(lab, (x, y), background_color=bg) self._plot_components['edge_labels'].append(t) - r += distance / 4 + loop_size += loop_size_increment elif len(edges_to_draw[a, b]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a,b)) @@ -772,10 +771,9 @@ def update(key, label, color, head): # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] - p12 = (float(p2[0] - p1[0]), float(p2[1] - p1[1])) m = ((p1[0] + p2[0])/2., (p1[1] + p2[1])/2.) # midpoint if not p1[1] == p2[1]: - s = -p12[0]/p12[1] # perp slope + s = (p1[0] - p2[0])/(p2[1] - p1[1]) # perp slope y = lambda x: s*(x - m[0]) + m[1] # perp bisector line # f, g are functions to determine x-values of point From 99b1ca136e43f5a2e06d073ab82425b2fd62d7ed Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 1 Jun 2021 12:53:37 +0200 Subject: [PATCH 469/706] make arith ready for random doctesting --- src/sage/arith/misc.py | 41 ++++++++++++------ src/sage/arith/multi_modular.pyx | 73 ++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 173baa3b793..58654a0db93 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1337,21 +1337,30 @@ def random_prime(n, proof=None, lbound=2): EXAMPLES:: - sage: random_prime(100000) - 30029 + sage: p = random_prime(100000) + sage: p.is_prime() + True + sage: p <= 100000 + True sage: random_prime(2) 2 Here we generate a random prime between 100 and 200:: - sage: random_prime(200, lbound=100) - 167 + sage: p = random_prime(200, lbound=100) + sage: p.is_prime() + True + sage: 100 <= p <= 200 + True If all we care about is finding a pseudo prime, then we can pass in ``proof=False`` :: - sage: random_prime(200, proof=False, lbound=100) - 197 + sage: p = random_prime(200, proof=False, lbound=100) + sage: p.is_pseudoprime() + True + sage: 100 <= p <= 200 + True TESTS:: @@ -5613,11 +5622,12 @@ def sort_complex_numbers_for_display(nums): r""" Given a list of complex numbers (or a list of tuples, where the first element of each tuple is a complex number), we sort the list - in a "pretty" order. First come the real numbers (with zero - imaginary part), then the complex numbers sorted according to - their real part. If two complex numbers have the same real part, - then they are sorted according to their - imaginary part. + in a "pretty" order. + + Real numbers (with a zero imaginary part) come before complex numbers, + and are sorted. Complex numbers are sorted by their real part + unless their real parts are quite close, in which case they are + sorted by their imaginary part. This is not a useful function mathematically (not least because there is no principled way to determine whether the real components @@ -5639,8 +5649,13 @@ def sort_complex_numbers_for_display(nums): ....: nums.append(CDF(i + RDF.random_element(-3e-11, 3e-11), ....: RDF.random_element())) sage: shuffle(nums) - sage: sort_c(nums) - [0.0, 1.0, 2.0, -2.862406201002009e-11 - 0.7088740263015161*I, 2.2108362706985576e-11 - 0.43681052967509904*I, 1.0000000000138833 - 0.7587654737635712*I, 0.9999999999760288 - 0.7238965893336062*I, 1.9999999999874383 - 0.4560801012073723*I, 1.9999999999869107 + 0.6090836283134269*I] + sage: nums = sort_c(nums) + sage: nums[:3] + [0.0, 1.0, 2.0] + sage: for i in range(3, len(nums)-1): + ....: assert nums[i].real() <= nums[i+1].real() + 1e-10 + ....: if abs(nums[i].real() - nums[i+1].real()) < 1e-10: + ....: assert nums[i].imag() <= nums[i+1].imag() + 1e-10 """ if not nums: return nums diff --git a/src/sage/arith/multi_modular.pyx b/src/sage/arith/multi_modular.pyx index e84720a7891..cdde67af2d0 100644 --- a/src/sage/arith/multi_modular.pyx +++ b/src/sage/arith/multi_modular.pyx @@ -55,14 +55,9 @@ cdef class MultiModularBasis_base(object): sage: height = 52348798724 sage: mm = MultiModularBasis_base(height); mm - MultiModularBasis with moduli [31051, 16981, 6007] - sage: mm = MultiModularBasis_base(height); mm - MultiModularBasis with moduli [21419, 13751, 15901] - sage: mm = MultiModularBasis_base(height); mm - MultiModularBasis with moduli [14369, 31379, 10067] - - sage: mm.prod()//height - 86 + MultiModularBasis with moduli [...] + sage: mm.prod() >= 2*height + True TESTS:: @@ -116,7 +111,7 @@ cdef class MultiModularBasis_base(object): sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base(1099511627791); mm - MultiModularBasis with moduli [31051, 16981, 6007] + MultiModularBasis with moduli [...] sage: del mm """ sig_free(self.moduli) @@ -164,7 +159,7 @@ cdef class MultiModularBasis_base(object): OverflowError: given modulus 1000000000000000000000000000057 is larger than 3037000498 sage: mm = MultiModularBasis_base(0); mm - MultiModularBasis with moduli [6007] + MultiModularBasis with moduli [...] sage: mm = MultiModularBasis_base([6, 10]) Traceback (most recent call last): @@ -312,17 +307,24 @@ cdef class MultiModularBasis_base(object): sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base(0); mm - MultiModularBasis with moduli [31051] + MultiModularBasis with moduli [...] + sage: p = mm[0] sage: mm._extend_moduli_to_height(70000) sage: mm - MultiModularBasis with moduli [31051, 16981] + MultiModularBasis with moduli [...] + sage: p == mm[0] + True + sage: mm.prod() >= 2*70000 + True sage: mm = MultiModularBasis_base([46307]); mm MultiModularBasis with moduli [46307] sage: mm._extend_moduli_to_height(10^30); mm - MultiModularBasis with moduli [46307, 6007, 21419, 13751, 15901, 14369, 31379, 10067] + MultiModularBasis with moduli [...] + sage: mm.prod() >= 2*10^30 + True TESTS: @@ -408,7 +410,9 @@ cdef class MultiModularBasis_base(object): sage: mm._extend_moduli_to_count(3) 3 sage: mm - MultiModularBasis with moduli [46307, 31051, 16981] + MultiModularBasis with moduli [...] + sage: len(mm) + 3 """ if count <= self.n: return self.n @@ -433,8 +437,12 @@ cdef class MultiModularBasis_base(object): sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307]); mm MultiModularBasis with moduli [46307] - sage: mm._extend_moduli(2); mm + sage: mm._extend_moduli(2); mm # random MultiModularBasis with moduli [46307, 31051, 16981] + sage: mm[0] + 46307 + sage: all(p.is_prime() for p in mm) + True """ self._extend_moduli_to_count(self.n + count) @@ -930,11 +938,11 @@ cdef class MutableMultiModularBasis(MultiModularBasis): sage: from sage.arith.multi_modular import MutableMultiModularBasis sage: mm = MutableMultiModularBasis([10007]) - sage: mm.next_prime() - 31051 # 64-bit - 31051L # 32-bit - sage: mm - MultiModularBasis with moduli [10007, 31051] + sage: p = mm.next_prime() + sage: p > 10007 + True + sage: mm.list() == [10007, p] + True """ self._extend_moduli(1) return self.moduli[self.n-1] @@ -956,23 +964,24 @@ cdef class MutableMultiModularBasis(MultiModularBasis): sage: mm = MutableMultiModularBasis([10007, 10009, 10037, 10039]) sage: mm MultiModularBasis with moduli [10007, 10009, 10037, 10039] - sage: mm.prod() + sage: prev_prod = mm.prod(); prev_prod 10092272478850909 sage: mm.precomputation_list() [1, 5004, 6536, 6060] sage: mm.partial_product(2) 1005306552331 - sage: mm.replace_prime(1) - 31051 # 64-bit - 31051L # 32-bit - sage: mm - MultiModularBasis with moduli [10007, 31051, 10037, 10039] - sage: mm.prod() - 31309336870896151 - sage: mm.precomputation_list() - [1, 17274, 1770, 2170] - sage: mm.partial_product(2) - 3118770482209 + sage: p = mm.replace_prime(1) + sage: mm.list() == [10007, p, 10037, 10039] + True + sage: mm.prod()*10009 == prev_prod*p + True + sage: precomputed = mm.precomputation_list() + sage: precomputed == [prod(Integers(mm[i])(1 / mm[j]) + ....: for j in range(i)) + ....: for i in range(4)] + True + sage: mm.partial_product(2) == prod(mm.list()[:3]) + True """ cdef mod_int new_p From f0dbc77d0b5ec3c2f0c58d30e2e2d323402e51ee Mon Sep 17 00:00:00 2001 From: sheerluck Date: Tue, 1 Jun 2021 16:28:50 +0300 Subject: [PATCH 470/706] fix "sageinspect.py: Timed out" --- src/sage/misc/sageinspect.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 17ab74a0374..35125721f27 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2276,11 +2276,10 @@ def sage_getsourcelines(obj): ([...'class MPolynomialIdeal( MPolynomialIdeal_singular_repr, \\\n', ...) sage: x = var('x') - sage: sage_getsourcelines(x) - (['cdef class Expression(CommutativeRingElement):\n', - ' cpdef object pyobject(self):\n', - ...) - sage: sage_getsourcelines(x)[0][-1] # last line + sage: lines, lineno = sage_getsourcelines(x); lines[0:2] + ['cdef class Expression(CommutativeRingElement):\n', + ' cpdef object pyobject(self):\n'] + sage: lines[-1] # last line ' return S\n' We show some enhancements provided by :trac:`11768`. First, we From 1d4fbe1c2c69229fde789b4e24c20a9b529056c1 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 1 Jun 2021 16:07:45 +0200 Subject: [PATCH 471/706] make schemes ready for fuzzed doctests --- .../elliptic_curves/ell_finite_field.py | 20 +++++------ .../elliptic_curves/ell_rational_field.py | 8 ++--- .../schemes/hyperelliptic_curves/mestre.py | 6 ++-- src/sage/schemes/plane_conics/con_field.py | 33 +++++++++++-------- src/sage/schemes/toric/sheaf/klyachko.py | 4 +-- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 2aca9491afb..5115910686f 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -294,7 +294,7 @@ def random_element(self): sage: k = GF(next_prime(7^5)) sage: E = EllipticCurve(k,[2,4]) - sage: P = E.random_element(); P # random + sage: P = E.random_element(); P # random (16740 : 12486 : 1) sage: type(P) @@ -305,7 +305,7 @@ def random_element(self): sage: k. = GF(7^5) sage: E = EllipticCurve(k,[2,4]) - sage: P = E.random_element(); P + sage: P = E.random_element(); P # random (5*a^4 + 3*a^3 + 2*a^2 + a + 4 : 2*a^4 + 3*a^3 + 4*a^2 + a + 5 : 1) sage: type(P) @@ -316,7 +316,7 @@ def random_element(self): sage: k. = GF(2^5) sage: E = EllipticCurve(k,[a^2,a,1,a+1,1]) - sage: P = E.random_element(); P + sage: P = E.random_element();P # random (a^4 + a : a^4 + a^3 + a^2 : 1) sage: type(P) @@ -326,10 +326,9 @@ def random_element(self): Ensure that the entire point set is reachable:: sage: E = EllipticCurve(GF(11), [2,1]) - sage: len(set(E.random_element() for _ in range(100))) - 16 - sage: E.cardinality() - 16 + sage: S = set() + sage: while len(S) < E.cardinality(): + ....: S.add(E.random_element()) TESTS: @@ -755,10 +754,9 @@ def gens(self): sage: E.abelian_group() Additive abelian group isomorphic to Z/22 + Z/2 embedded in Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 2*x + 5 over Finite Field of size 41 - sage: E.abelian_group().gens() - ((30 : 13 : 1), (23 : 0 : 1)) - sage: E.gens() - ((30 : 13 : 1), (23 : 0 : 1)) + sage: ab_gens = E.abelian_group().gens() + sage: ab_gens == E.gens() + True sage: E.gens()[0].order() 22 sage: E.gens()[1].order() diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 1ce8323e1b0..88c79f9cec1 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -3347,10 +3347,10 @@ def elliptic_exponential(self, z, embedding=None): Observe that this is a group homomorphism (modulo rounding error):: sage: z = CC.random_element() - sage: 2 * E.elliptic_exponential(z) - (-1.52184235874404 - 0.0581413944316544*I : 0.948655866506124 - 0.0381469928565030*I : 1.00000000000000) - sage: E.elliptic_exponential(2 * z) - (-1.52184235874404 - 0.0581413944316562*I : 0.948655866506128 - 0.0381469928565034*I : 1.00000000000000) + sage: v = 2 * E.elliptic_exponential(z) + sage: w = E.elliptic_exponential(2 * z) + sage: abs(v[0] - w[0]) + abs(v[1] - w[1]) # abs tol 1e-13 + 0.0 """ return self.period_lattice().elliptic_exponential(z) diff --git a/src/sage/schemes/hyperelliptic_curves/mestre.py b/src/sage/schemes/hyperelliptic_curves/mestre.py index 8ee60d478d1..577888dc0c4 100644 --- a/src/sage/schemes/hyperelliptic_curves/mestre.py +++ b/src/sage/schemes/hyperelliptic_curves/mestre.py @@ -77,8 +77,10 @@ def HyperellipticCurve_from_invariants(i, reduced=True, precision=None, An example over a finite field:: - sage: HyperellipticCurve_from_invariants([GF(13)(1),3,7,5]) - Hyperelliptic Curve over Finite Field of size 13 defined by y^2 = 8*x^5 + 5*x^4 + 5*x^2 + 9*x + 3 + sage: H = HyperellipticCurve_from_invariants([GF(13)(1),3,7,5]); H + Hyperelliptic Curve over Finite Field of size 13 defined by ... + sage: H.igusa_clebsch_invariants() + (4, 9, 6, 11) An example over a number field:: diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 8de20e8ee71..823595850b5 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -331,11 +331,11 @@ def diagonalization(self, names=None): Traceback (most recent call last): ... ValueError: The conic self (= Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z) has no symmetric matrix because the base field has characteristic 2 - + An example over a global function field: - + :: - + sage: K = FractionField(PolynomialRing(GF(7), 't')) sage: (t,) = K.gens() sage: C = Conic(K, [t/2,0, 1, 2, 0, 3]) @@ -659,11 +659,11 @@ def hom(self, x, Y=None): ValueError: The matrix x (= [ 0 0 1/2] [ 0 1 0] [ 1 0 0]) does not define a map from self (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2) to Y (= Projective Conic Curve over Rational Field defined by -x^2 + y^2 + z^2) - + The identity map between two representations of the same conic: - + :: - + sage: C = Conic([1,2,3,4,5,6]) sage: D = Conic([2,4,6,8,10,12]) sage: C.hom(identity_matrix(3), D) @@ -674,9 +674,9 @@ def hom(self, x, Y=None): (x : y : z) An example not over the rational numbers: - + :: - + sage: P. = QQ[] sage: C = Conic([1,0,0,t,0,1/t]) sage: D = Conic([1/t^2, 0, -2/t^2, t, 0, (t + 1)/t^2]) @@ -818,6 +818,11 @@ def parametrization(self, point=None, morphism=True): Return a parametrization `f` of ``self`` together with the inverse of `f`. + .. warning:: + + The second map is currently broken and neither the inverse nor + well-defined. + If ``point`` is specified, then that point is used for the parametrization. Otherwise, use ``self.rational_point()`` to find a point. @@ -831,19 +836,21 @@ def parametrization(self, point=None, morphism=True): An example over a finite field :: sage: c = Conic(GF(2), [1,1,1,1,1,0]) - sage: c.parametrization() + sage: f, g = c.parametrization(); f, g (Scheme morphism: From: Projective Space of dimension 1 over Finite Field of size 2 To: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z - Defn: Defined on coordinates by sending (x : y) to - (x*y + y^2 : x^2 + x*y : x^2 + x*y + y^2), + Defn: Defined on coordinates by sending (x : y) to ..., Scheme morphism: From: Projective Conic Curve over Finite Field of size 2 defined by x^2 + x*y + y^2 + x*z + y*z To: Projective Space of dimension 1 over Finite Field of size 2 - Defn: Defined on coordinates by sending (x : y : z) to - (y : x)) + Defn: Defined on coordinates by sending (x : y : z) to ...) + sage: set(f(p) for p in f.domain()) + {(0 : 0 : 1), (0 : 1 : 1), (1 : 0 : 1)} + sage: (g*f).is_one() # known bug (see :trac:`31892`) + True An example with ``morphism = False`` :: diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py index 35cc2d678f6..8927bdde20a 100644 --- a/src/sage/schemes/toric/sheaf/klyachko.py +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -946,8 +946,8 @@ def random_deformation(self, epsilon=None): sage: V = P1.sheaves.line_bundle(H) + P1.sheaves.line_bundle(-H) sage: V.cohomology(dim=True, weight=(0,)) (1, 0) - sage: Vtilde = V.random_deformation() - sage: Vtilde.cohomology(dim=True, weight=(0,)) + sage: Vtilde = V.random_deformation() # not tested, known bug + sage: Vtilde.cohomology(dim=True, weight=(0,)) # not tested, known bug (1, 0) """ filt = self._filt.random_deformation(epsilon) From 8f1759867db6a4664759548fe21af046c55bbd86 Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 1 Jun 2021 11:26:26 -0700 Subject: [PATCH 472/706] added a test for the fix --- src/sage/modules/free_module_homspace.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index f8b1aa230e9..d093cfd64cb 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -171,6 +171,23 @@ def __call__(self, A, check=True): Codomain: Free module of degree 3 and rank 3 over Integer Ring Echelon ... + The following tests the bug fixed in :trac:`31818`. If there is no + coercion between base rings, one can only define the zero morphism, + as morphism of additive groups. Before one could for example use an + integer matrix to define a morphism from the rational nombers to the + integers. + + sage: V = QQ^2; W = ZZ^2; m = identity_matrix(2) + sage: H = V.Hom(W); H(m) + Traceback (most recent call last): + ... + TypeError: Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain + sage: n = zero_matrix(2); H(n) + Free module morphism defined by the matrix + [0 0] + [0 0] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring """ from . import free_module_morphism if not is_Matrix(A): From fa796d189bee0e33b43a904f312155c5b194532d Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 1 Jun 2021 11:39:00 -0700 Subject: [PATCH 473/706] fixed a typo --- src/sage/modules/free_module_homspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index d093cfd64cb..3a01055ae52 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -174,7 +174,7 @@ def __call__(self, A, check=True): The following tests the bug fixed in :trac:`31818`. If there is no coercion between base rings, one can only define the zero morphism, as morphism of additive groups. Before one could for example use an - integer matrix to define a morphism from the rational nombers to the + integer matrix to define a morphism from the rational numbers to the integers. sage: V = QQ^2; W = ZZ^2; m = identity_matrix(2) From dd528edebfdaf577a9ae8ce0726725433125b839 Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 1 Jun 2021 11:42:43 -0700 Subject: [PATCH 474/706] completed a test --- src/sage/modules/free_module_homspace.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 3a01055ae52..13c9292260d 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -182,12 +182,15 @@ def __call__(self, A, check=True): Traceback (most recent call last): ... TypeError: Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain - sage: n = zero_matrix(2); H(n) + sage: n = zero_matrix(2); + sage: h = H(n); h Free module morphism defined by the matrix [0 0] [0 0] Domain: Vector space of dimension 2 over Rational Field Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + sage: [h(v) for v in V.gens()] + [(0, 0), (0, 0)] """ from . import free_module_morphism if not is_Matrix(A): From 986ba2536251495396892f4089ded1e6a7f0575d Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 1 Jun 2021 21:24:31 +0100 Subject: [PATCH 475/706] macos11 fricas fix --- build/pkgs/fricas/patches/extdecls.patch | 12 +++++++++ build/pkgs/fricas/patches/macos_lisp.patch | 31 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 build/pkgs/fricas/patches/extdecls.patch create mode 100644 build/pkgs/fricas/patches/macos_lisp.patch diff --git a/build/pkgs/fricas/patches/extdecls.patch b/build/pkgs/fricas/patches/extdecls.patch new file mode 100644 index 00000000000..15772be6715 --- /dev/null +++ b/build/pkgs/fricas/patches/extdecls.patch @@ -0,0 +1,12 @@ +diff --git a/src/include/cfuns-c.H1 b/src/include/cfuns-c.H1 +index 643a532..da22a8d 100644 +--- a/src/include/cfuns-c.H1 ++++ b/src/include/cfuns-c.H1 +@@ -3,5 +3,7 @@ extern int directoryp(char *); + extern int make_path_from_file(char *, char *); + extern int writeablep(char *); + extern int readablep(char *); ++extern int remove_directory(char *); ++extern int makedir(char *); + extern long findString(char *, char *); + extern int copyEnvValue(char *, char *); diff --git a/build/pkgs/fricas/patches/macos_lisp.patch b/build/pkgs/fricas/patches/macos_lisp.patch new file mode 100644 index 00000000000..c1be0ac46fd --- /dev/null +++ b/build/pkgs/fricas/patches/macos_lisp.patch @@ -0,0 +1,31 @@ +diff --git a/src/lisp/Makefile.in b/src/lisp/Makefile.in +index 30f615096..f0b3f0bd4 100644 +--- a/src/lisp/Makefile.in ++++ b/src/lisp/Makefile.in +@@ -118,6 +118,8 @@ do_it.ecl: fricas-lisp.lisp fricas-package.lisp fricas-config.lisp \ + fricas-lisp.o primitives.o) ")))" \ + >> fricas-ecl.lisp + echo "(defvar *fricas-initial-lisp-forms* nil)" >> fricas-ecl.lisp ++ echo "(require :cmp)" ++ echo "(setf c::*user-cc-flags* (concatenate 'string c::*user-cc-flags* \" -I$(BASE)/$(fricas_src_srcdir)/include/ -I$(BASE)/$(fricas_src_srcdir)/../config/\"))" >> fricas-ecl.lisp + echo '(load "fricas-package.lisp")' \ + '(load "fricas-config.lisp")' \ + '(load "fricas-ecl.lisp")' \ +diff --git a/src/lisp/fricas-lisp.lisp b/src/lisp/fricas-lisp.lisp +index d6c7484df..f99e2e75b 100644 +--- a/src/lisp/fricas-lisp.lisp ++++ b/src/lisp/fricas-lisp.lisp +@@ -609,6 +609,13 @@ with this hack and will try to convince the GCL crowd to fix this. + #+(and :clisp :ffi) `(defun clisp-init-foreign-calls () ,@arguments) + ) + ++#+:ecl ++(ext:with-backend :c/c++ ++ (ffi:clines ++ "#include " ++ "#include " ++ "#include ")) ++ + (foreign-defs + + (fricas-foreign-call |writeablep| "writeablep" int From a2dc07f50f42477838b1c3712e79042684cf28ad Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 1 Jun 2021 21:25:41 +0100 Subject: [PATCH 476/706] fricas version bump --- build/pkgs/fricas/package-version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/fricas/package-version.txt b/build/pkgs/fricas/package-version.txt index 95b25aee259..0bfdb062251 100644 --- a/build/pkgs/fricas/package-version.txt +++ b/build/pkgs/fricas/package-version.txt @@ -1 +1 @@ -1.3.6 +1.3.6.p1 From 642b7c2adc0d2a54c1097eb1cf923bb2e648df0f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 2 Jun 2021 11:35:25 +1000 Subject: [PATCH 477/706] Fixing pyflakes warning. --- src/sage/rings/qqbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 7bfc1d85f89..a7cb6e757dc 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -573,7 +573,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.number_field.number_field import NumberField, GaussianField, CyclotomicField -from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic, NumberFieldElement_gaussian +from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian from sage.arith.all import factor from . import infinity from sage.categories.action import Action From 0cc4d56233bff37aab318b36b148ce883c93533a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 2 Jun 2021 16:20:03 +0200 Subject: [PATCH 478/706] make some parts of matrix ready for random seeds --- src/sage/matrix/matrix_cyclo_dense.pyx | 31 +- src/sage/matrix/matrix_generic_dense.pyx | 6 +- src/sage/matrix/matrix_gf2e_dense.pyx | 579 +++++++--------- src/sage/matrix/matrix_integer_dense.pyx | 29 +- .../matrix/matrix_integer_dense_saturation.py | 16 +- src/sage/matrix/matrix_modn_dense_double.pyx | 98 +-- src/sage/matrix/matrix_modn_dense_float.pyx | 101 ++- .../matrix/matrix_modn_dense_template.pxi | 651 +++++------------- 8 files changed, 541 insertions(+), 970 deletions(-) diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index bcd549b7057..49b439f92c2 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1010,23 +1010,30 @@ cdef class Matrix_cyclo_dense(Matrix_dense): The following doctests are all indirect:: sage: MS = MatrixSpace(CyclotomicField(10), 4, 4) - sage: A = MS.random_element(); A + sage: A = MS.random_element(); A # random [ -2*zeta10^3 + 2*zeta10^2 - zeta10 zeta10^3 + 2*zeta10^2 - zeta10 + 1 0 -2*zeta10^3 + zeta10^2 - 2*zeta10 + 2] [ 0 -zeta10^3 + 2*zeta10^2 - zeta10 -zeta10^3 + 1 zeta10^3 + zeta10] [ 1/2*zeta10^2 -2*zeta10^2 + 2 -1/2*zeta10^3 + 1/2*zeta10^2 + 2 2*zeta10^3 - zeta10^2 - 2] [ 1 zeta10^2 + 2 2*zeta10^2 2*zeta10 - 2] + sage: A.parent() is MS + True + + :: + sage: B = MS.random_element(density=0.5) - sage: B._rational_matrix() - [ 0 0 0 0 1 0 0 2 0 2 0 0 0 0 0 0] - [ 0 0 0 0 0 0 0 0 -1 -2 0 0 0 0 0 2] - [ 0 -1 0 0 -1 0 0 0 0 0 0 0 0 0 -2 -1] - [ 0 0 0 0 0 0 0 2 -1/2 1/2 0 0 0 0 -1 0] + sage: all(a in (-2, -1, -1/2, 0, 1/2, 1, 2) for a in B._rational_matrix().list()) + True + sage: while set(B._rational_matrix().list()) != set((-2, -1, -1/2, 0, 1/2, 1, 2)): + ....: B = MS.random_element(density=0.5) + + :: + sage: C = MS.random_element(density=0.5, num_bound=20, den_bound=20) - sage: C._rational_matrix() - [ 0 0 8/11 -10/3 -11/7 8 1 -3 0 0 1 0 0 0 0 0] - [ 0 0 -11/17 -3/13 -5/6 17/3 -19/17 -4/5 0 0 9 0 0 0 0 0] - [ 0 0 -11 -3/2 -5/12 8/11 0 -3/19 0 0 -5/6 0 0 0 0 0] - [ 0 0 0 5/8 -5/11 -5/4 6/11 2/3 0 0 -16/11 0 0 0 0 0] + sage: all(abs(a.denominator()) <= 20 and abs(a.numerator()) <= 20 for a in C._rational_matrix().list()) + True + sage: while not (any(abs(a.denominator()) == 20 for a in C._rational_matrix().list()) + ....: and any(abs(a.numerator()) == 20 for a in C._rational_matrix().list())): + ....: C = MS.random_element(density=0.5, num_bound=20, den_bound=20) """ cdef Py_ssize_t i cdef Matrix_rational_dense mat = self._matrix @@ -1671,7 +1678,7 @@ cdef class Matrix_cyclo_dense(Matrix_dense): cdef int i cdef Matrix_cyclo_dense res cdef bint is_square - + verbose("entering _echelon_form_multimodular", level=echelon_verbose_level) diff --git a/src/sage/matrix/matrix_generic_dense.pyx b/src/sage/matrix/matrix_generic_dense.pyx index ad75514bc58..2bf35d1a79f 100644 --- a/src/sage/matrix/matrix_generic_dense.pyx +++ b/src/sage/matrix/matrix_generic_dense.pyx @@ -25,16 +25,14 @@ cdef class Matrix_generic_dense(matrix_dense.Matrix_dense): EXAMPLES:: - sage: A = random_matrix(Integers(25)['x'],2); A - [ 0 8*x + 1] - [17*x + 4 0] + sage: A = random_matrix(Integers(25)['x'], 2) sage: type(A) sage: TestSuite(A).run() Test comparisons:: - sage: A = random_matrix(Integers(25)['x'],2) + sage: A = random_matrix(Integers(25)['x'], 2) sage: A == A True sage: A < A + 1 diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index 63dc9e3ce67..1d6bbbc22b2 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -39,15 +39,11 @@ EXAMPLES:: sage: K. = GF(2^8) sage: A = random_matrix(K, 3,4) - sage: A - [ a^6 + a^5 + a^4 + a^2 a^6 + a^3 + a + 1 a^5 + a^3 + a^2 + a + 1 a^7 + a^6 + a + 1] - [ a^7 + a^6 + a^3 a^7 + a^6 + a^5 + 1 a^5 + a^4 + a^3 + a + 1 a^6 + a^5 + a^4 + a^3 + a^2 + 1] - [ a^6 + a^5 + a + 1 a^7 + a^3 + 1 a^7 + a^3 + a + 1 a^7 + a^6 + a^3 + a^2 + a + 1] - - sage: A.echelon_form() - [ 1 0 0 a^6 + a^5 + a^4 + a^2] - [ 0 1 0 a^7 + a^5 + a^3 + a + 1] - [ 0 0 1 a^6 + a^4 + a^3 + a^2 + 1] + sage: E = A.echelon_form() + sage: A.row_space() == E.row_space() + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r) + True AUTHOR: @@ -151,10 +147,8 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): [0 0 0 0] [0 0 0 0] - sage: A.randomize(); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] + sage: A.randomize() + sage: TestSuite(A).run() sage: K. = GF(2^3) sage: A = Matrix(K,3,4); A @@ -162,10 +156,8 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): [0 0 0 0] [0 0 0 0] - sage: A.randomize(); A - [ a^2 + a a^2 + 1 a^2 + a a^2 + a] - [ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2] - [ a^2 + a a^2 + 1 a^2 + a + 1 a + 1] + sage: A.randomize() + sage: TestSuite(A).run() """ cdef M4RIE_finite_field FF @@ -221,19 +213,14 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: l = [K.random_element() for _ in range(3*4)]; l - [a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a] - - sage: A = Matrix(K, 3, 4, l); A - [ a^2 + 1 a^3 + 1 0 0] - [ a a^3 + a + 1 a + 1 a + 1] - [ a^2 a^3 + a + 1 a^3 + a a^3 + a] + sage: l = [K.random_element() for _ in range(3*4)] - sage: A.list() - [a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a] + sage: A = Matrix(K, 3, 4, l) + sage: l == A.list() + True - sage: l[0], A[0,0] - (a^2 + 1, a^2 + 1) + sage: l[0] == A[0,0] + True sage: A = Matrix(K, 3, 3, a); A [a 0 0] @@ -257,16 +244,18 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = Matrix(K,3,4,[K.random_element() for _ in range(3*4)]); A - [ a^2 + 1 a^3 + 1 0 0] - [ a a^3 + a + 1 a + 1 a + 1] - [ a^2 a^3 + a + 1 a^3 + a a^3 + a] + sage: l = [K.random_element() for _ in range(3*4)] + sage: A = Matrix(K, 3, 4, l) - sage: A[0,0] = a # indirect doctest - sage: A - [ a a^3 + 1 0 0] - [ a a^3 + a + 1 a + 1 a + 1] - [ a^2 a^3 + a + 1 a^3 + a a^3 + a] + sage: i = randrange(3) + sage: j = randrange(4) + sage: A[i,j] = a # indirect doctest + sage: A[i,j] == a == A.list()[j + 4*i] + True + sage: A.list()[:j + 4*i] == l[:j + 4*i] + True + sage: A.list()[j + 4*i + 1:] == l[j + 4*i + 1:] + True """ mzed_write_elem(self._entries, i, j, poly_to_word(value)) @@ -281,15 +270,15 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = random_matrix(K,3,4) - sage: A[2,3] # indirect doctest - a^3 + a^2 + a + 1 + sage: l = [K.random_element() for _ in range(3*4)] + sage: A = Matrix(K, 3, 4, l) + sage: A[2,3] == l[3 + 4*2] # indirect doctest + True sage: K. = GF(2^3) - sage: m,n = 3, 4 - sage: A = random_matrix(K,3,4); A - [ a^2 + a a^2 + 1 a^2 + a a^2 + a] - [ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2] - [ a^2 + a a^2 + 1 a^2 + a + 1 a + 1] + sage: l = [K.random_element() for _ in range(3*4)] + sage: A = Matrix(K, 3, 4, l) + sage: A.list() == l + True """ cdef int r = mzed_read_elem(self._entries, i, j) cdef Cache_base cache = self._base_ring._cache @@ -320,20 +309,12 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = random_matrix(K,3,4); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - - sage: B = random_matrix(K,3,4); B - [ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a] - [ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2] - [ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1] + sage: A = random_matrix(K,3,4) + sage: B = random_matrix(K,3,4) - sage: C = A + B; C # indirect doctest - [ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1] - [a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1] - [a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2] + sage: C = A + B # indirect doctest + sage: all(C.list()[i] == A.list()[i] + B.list()[i] for i in range(12)) + True """ cdef Matrix_gf2e_dense A A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0, alloc=False) @@ -349,22 +330,11 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: from sage.matrix.matrix_gf2e_dense import Matrix_gf2e_dense sage: K. = GF(2^4) - sage: m,n = 3, 4 - sage: MS = MatrixSpace(K,m,n) - sage: A = random_matrix(K,3,4); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - - sage: B = random_matrix(K,3,4); B - [ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a] - [ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2] - [ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1] - - sage: C = A - B; C # indirect doctest - [ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1] - [a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1] - [a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2] + sage: A = random_matrix(K,3,4) + sage: B = random_matrix(K,3,4) + sage: C = A - B # indirect doctest + sage: all(C.list()[i] == A.list()[i] - B.list()[i] for i in range(12)) + True """ return self._add_(right) @@ -634,29 +604,9 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(4) sage: A = random_matrix(K,10,10) - sage: A - [ 0 a + 1 a + 1 a + 1 0 1 a + 1 1 a + 1 1] - [a + 1 a + 1 a 1 a a 1 a + 1 1 0] - [ a 1 a + 1 a + 1 0 a + 1 a 1 a a] - [a + 1 a 0 0 1 a + 1 a + 1 0 a + 1 1] - [ a 0 a + 1 a a 0 a + 1 a 1 a + 1] - [ a 0 a a + 1 a 1 a + 1 a a a] - [a + 1 a 0 1 0 a + 1 a + 1 a 0 a + 1] - [a + 1 a + 1 0 a + 1 a 1 a + 1 a + 1 a + 1 0] - [ 0 0 0 a + 1 1 a + 1 0 a + 1 1 0] - [ 1 a + 1 0 1 a 0 0 a a + 1 a] - - sage: a*A # indirect doctest - [ 0 1 1 1 0 a 1 a 1 a] - [ 1 1 a + 1 a a + 1 a + 1 a 1 a 0] - [a + 1 a 1 1 0 1 a + 1 a a + 1 a + 1] - [ 1 a + 1 0 0 a 1 1 0 1 a] - [a + 1 0 1 a + 1 a + 1 0 1 a + 1 a 1] - [a + 1 0 a + 1 1 a + 1 a 1 a + 1 a + 1 a + 1] - [ 1 a + 1 0 a 0 1 1 a + 1 0 1] - [ 1 1 0 1 a + 1 a 1 1 1 0] - [ 0 0 0 1 a 1 0 1 a 0] - [ a 1 0 a a + 1 0 0 a + 1 1 a + 1] + sage: B = a*A # indirect doctest + sage: all(B.list()[i] == a*A.list()[i] for i in range(100)) + True """ cdef m4ri_word a = poly_to_word(right) cdef Matrix_gf2e_dense C = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0) @@ -668,15 +618,10 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = random_matrix(K, 3, 4); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - - sage: -A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] + sage: A = random_matrix(K, 3, 4) + sage: B = -A + sage: all(B.list()[i] == -A.list()[i] for i in range(12)) + True """ return self.__copy__() @@ -704,19 +649,14 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^4) sage: m,n = 3, 4 - sage: A = random_matrix(K,3,4); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - - sage: A2 = copy(A); A2 - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] + sage: A = random_matrix(K,3,4) + sage: A2 = copy(A) + sage: A2.list() == A.list() + True - sage: A[0,0] = 1 - sage: A2[0,0] - a^2 + sage: A[0,0] = 1 if A[0,0] != 1 else 0 + sage: A2[0,0] == A[0,0] + False """ cdef Matrix_gf2e_dense A A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0) @@ -749,14 +689,10 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: m,n = 3, 4 - sage: A = random_matrix(K,3,4); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1] - [ a^3 1 a^3 + a + 1 a^3 + a^2 + 1] - [ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - - sage: A.list() # indirect doctest - [a^2, a^3 + a + 1, a^3 + a^2 + a + 1, a + 1, a^3, 1, a^3 + a + 1, a^3 + a^2 + 1, a + 1, a^3 + 1, a^3 + a + 1, a^3 + a^2 + a + 1] + sage: l = [K.random_element() for _ in range(3*4)] + sage: A = Matrix(K, 3, 4, l) + sage: A.list() == l # indirect doctest + True """ cdef int i,j l = [] @@ -784,37 +720,78 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = Matrix(K,3,3) - - sage: A.randomize(); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1] - [ a + 1 a^3 1] - [ a^3 + a + 1 a^3 + a^2 + 1 a + 1] - - sage: K. = GF(2^4) - sage: A = random_matrix(K,1000,1000,density=0.1) - sage: float(A.density()) - 0.0999... - - sage: A = random_matrix(K,1000,1000,density=1.0) - sage: float(A.density()) - 1.0 - - sage: A = random_matrix(K,1000,1000,density=0.5) - sage: float(A.density()) - 0.4996... + sage: total_count = 0 + sage: from collections import defaultdict + sage: dic = defaultdict(Integer) + sage: def add_samples(): + ....: global dic, total_count + ....: for _ in range(100): + ....: A = Matrix(K,3,3) + ....: A.randomize() + ....: for a in A.list(): + ....: dic[a] += 1 + ....: total_count += 1.0 + sage: add_samples() + sage: while not all(abs(dic[a]/total_count - 1/16) < 0.01 for a in dic): + ....: add_samples() + + sage: def add_sample(density): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: density_sum += random_matrix(K, 1000, 1000, density=density).density() + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.1) + sage: while abs(density_sum/total_count - 0.1) > 0.001: + ....: add_sample(0.1) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(1.0) + sage: while abs(density_sum/total_count - 1.0) > 0.001: + ....: add_sample(1.0) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.5) + sage: while abs(density_sum/total_count - 0.5) > 0.001: + ....: add_sample(0.5) Note, that the matrix is updated and not zero-ed out before being randomized:: - sage: A = matrix(K,1000,1000) - sage: A.randomize(nonzero=False, density=0.1) - sage: float(A.density()) - 0.0936... - - sage: A.randomize(nonzero=False, density=0.05) - sage: float(A.density()) - 0.135854 + sage: def add_sample(density, nonzero): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: A = matrix(K, 1000, 1000) + ....: A.randomize(nonzero=nonzero, density=density) + ....: A.randomize(nonzero=nonzero, density=density) + ....: density_sum += A.density() + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.1, True) + sage: while abs(density_sum/total_count - (1 - 0.9^2)) > 0.001: + ....: add_sample(0.1, True) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.1, False) + sage: while abs(density_sum/total_count - (1 - 0.9^2)*15/16) > 0.001: + ....: add_sample(0.1, False) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.05, True) + sage: while abs(density_sum/total_count - (1 - 0.95^2)) > 0.001: + ....: add_sample(0.05, True) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.5, True) + sage: while abs(density_sum/total_count - (1 - 0.5^2)) > 0.001: + ....: add_sample(0.5, True) """ if self._ncols == 0 or self._nrows == 0: return @@ -894,35 +871,25 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^4) sage: m,n = 3, 5 - sage: A = random_matrix(K, 3, 5); A - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1 a^3] - [ 1 a^3 + a + 1 a^3 + a^2 + 1 a + 1 a^3 + 1] - [ a^3 + a + 1 a^3 + a^2 + a + 1 a^2 + a a^2 + 1 a^2 + a] - - sage: A.echelonize(); A - [ 1 0 0 a + 1 a^2 + 1] - [ 0 1 0 a^2 a + 1] - [ 0 0 1 a^3 + a^2 + a a^3] + sage: A = random_matrix(K, 3, 5) + sage: R = A.row_space() + sage: A.echelonize() + sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r) + True + sage: A.row_space() == R + True sage: K. = GF(2^3) sage: m,n = 3, 5 sage: MS = MatrixSpace(K,m,n) sage: A = random_matrix(K, 3, 5) - - sage: copy(A).echelon_form('newton_john') - [ 1 0 a + 1 0 a^2 + 1] - [ 0 1 a^2 + a + 1 0 a] - [ 0 0 0 1 a^2 + a + 1] - - sage: copy(A).echelon_form('naive') - [ 1 0 a + 1 0 a^2 + 1] - [ 0 1 a^2 + a + 1 0 a] - [ 0 0 0 1 a^2 + a + 1] - - sage: copy(A).echelon_form('builtin') - [ 1 0 a + 1 0 a^2 + 1] - [ 0 1 a^2 + a + 1 0 a] - [ 0 0 0 1 a^2 + a + 1] + sage: B = copy(A).echelon_form('newton_john') + sage: C = copy(A).echelon_form('naive') + sage: D = copy(A).echelon_form('builtin') + sage: B == C == D + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in B.rows() if r) + True """ if self._nrows == 0 or self._ncols == 0: self.cache('in_echelon_form',True) @@ -1000,20 +967,15 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^3) - sage: A = random_matrix(K,3,3); A - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] - - sage: B = ~A; B - [a^2 + a + 1 a^2 a^2] - [ a + 1 a^2 + a + 1 a + 1] - [ a a^2 + a a^2 + a + 1] - + sage: A = random_matrix(K, 3, 3) + sage: while A.rank() != 3: + ....: A = random_matrix(K, 3, 3) + sage: B = ~A sage: A*B [1 0 0] [0 1 0] [0 0 1] + """ cdef Matrix_gf2e_dense A A = Matrix_gf2e_dense.__new__(Matrix_gf2e_dense, self._parent, 0, 0, 0) @@ -1041,20 +1003,29 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^3) - sage: A = random_matrix(K,3,3); A - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] + sage: A = random_matrix(K,3,3) + sage: B = copy(A) + sage: B.rescale_row(0, a, 0) + sage: B[0] == a*A[0] + True + sage: B[1:] == A[1:] + True - sage: A.rescale_row(0, a , 0); A - [ a + 1 a^2 + a a^2 + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] + sage: B = copy(A) + sage: B.rescale_row(1, 0, 0) + sage: B[0] == A[0] + True + sage: B[2] == A[2] + True + sage: B[1].is_zero() + True - sage: A.rescale_row(0,0,0); A - [ 0 0 0] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] + sage: B = copy(A) + sage: B.rescale_row(2, a^2, 2) + sage: B.list()[:-1] == A.list()[:-1] + True + sage: B[2,2] == a^2*A[2,2] + True """ cdef m4ri_word x = poly_to_word(multiple) mzed_rescale_row(self._entries, row, start_col, x) @@ -1074,15 +1045,20 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^3) - sage: A = random_matrix(K,3,3); A - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] + sage: A = random_matrix(K,3,3) + sage: B = copy(A) + sage: B.add_multiple_of_row(0,1,a,0) + sage: B[1:] == A[1:] + True + sage: B[0] == A[0] + a*A[1] + True - sage: A.add_multiple_of_row(0,1,a,0); A - [ a a + 1 a^2 + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] + sage: B = copy(A) + sage: B.add_multiple_of_row(2,1,a,2) + sage: B.list()[:-1] == A.list()[:-1] + True + sage: B[2,2] == A[2,2] + a*A[1,2] + True """ cdef m4ri_word x = poly_to_word(multiple) @@ -1102,16 +1078,14 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^3) sage: A = random_matrix(K,3,3) - sage: A - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] - - sage: A.swap_rows(0,1); A - [ a + 1 0 1] - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 a^2 + 1 a + 1] - + sage: B = copy(A) + sage: B.swap_rows(0,1) + sage: B[0] == A[1] + True + sage: B[1] == A[0] + True + sage: B[2] == A[2] + True """ mzed_row_swap(self._entries, row1, row2) @@ -1128,18 +1102,16 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^3) sage: A = random_matrix(K,3,3) - sage: A - [ a^2 a + 1 a^2 + a + 1] - [ a + 1 0 1] - [ a + 1 a^2 + 1 a + 1] - - sage: A.swap_columns(0,1); A - [ a + 1 a^2 a^2 + a + 1] - [ 0 a + 1 1] - [ a^2 + 1 a + 1 a + 1] + sage: B = copy(A) + sage: B.swap_columns(0,1) + sage: B.column(0) == A.column(1) + True + sage: B.column(1) == A.column(0) + True + sage: B.column(2) == A.column(2) + True sage: A = random_matrix(K,4,16) - sage: B = copy(A) sage: B.swap_columns(0,1) sage: B.swap_columns(0,1) @@ -1168,24 +1140,11 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^4) sage: MS = MatrixSpace(K,3,3) sage: A = random_matrix(K,3,3) - sage: B = A.augment(MS(1)); B - [ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 1 0 0] - [ a + 1 a^3 1 0 1 0] - [ a^3 + a + 1 a^3 + a^2 + 1 a + 1 0 0 1] - - sage: B.echelonize(); B - [ 1 0 0 a^2 + a a^3 + 1 a^3 + a] - [ 0 1 0 a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a] - [ 0 0 1 a + 1 a^3 a^3] - - sage: C = B.matrix_from_columns([3,4,5]); C - [ a^2 + a a^3 + 1 a^3 + a] - [ a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a] - [ a + 1 a^3 a^3] - - sage: C == ~A + sage: B = A.augment(MS(1)) + sage: B.echelonize() + sage: C = B.matrix_from_columns([3,4,5]) + sage: A.rank() < 3 or C == ~A True - sage: C*A == MS(1) True @@ -1194,13 +1153,11 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^4) sage: A = random_matrix(K,2,3) sage: B = random_matrix(K,2,0) - sage: A.augment(B) - [ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - [ a^2 + a a^2 + 1 a^2 + a] + sage: A.augment(B) == A + True - sage: B.augment(A) - [ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1] - [ a^2 + a a^2 + 1 a^2 + a] + sage: B.augment(A) == A + True sage: M = Matrix(K, 0, 0, 0) sage: N = Matrix(K, 0, 19, 0) @@ -1240,39 +1197,27 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^4) - sage: A = random_matrix(K,2,2); A - [ a^2 a^3 + a + 1] - [a^3 + a^2 + a + 1 a + 1] - - sage: B = random_matrix(K,2,2); B - [ a^3 1] - [ a^3 + a + 1 a^3 + a^2 + 1] - - sage: A.stack(B) - [ a^2 a^3 + a + 1] - [a^3 + a^2 + a + 1 a + 1] - [ a^3 1] - [ a^3 + a + 1 a^3 + a^2 + 1] - - sage: B.stack(A) - [ a^3 1] - [ a^3 + a + 1 a^3 + a^2 + 1] - [ a^2 a^3 + a + 1] - [a^3 + a^2 + a + 1 a + 1] + sage: A = random_matrix(K,2,2) + sage: B = random_matrix(K,2,2) + sage: C = A.stack(B) + sage: C[:2] == A + True + sage: C[2:] == B + True + sage: D = B.stack(A) + sage: D[:2] == B + True + sage: D[2:] == A + True TESTS:: sage: A = random_matrix(K,0,3) sage: B = random_matrix(K,3,3) - sage: A.stack(B) - [ a + 1 a^3 + 1 a^3 + a + 1] - [a^3 + a^2 + a + 1 a^2 + a a^2 + 1] - [ a^2 + a a^3 + a^2 + a a^2 + 1] - - sage: B.stack(A) - [ a + 1 a^3 + 1 a^3 + a + 1] - [a^3 + a^2 + a + 1 a^2 + a a^2 + 1] - [ a^2 + a a^3 + a^2 + a a^2 + 1] + sage: A.stack(B) == B + True + sage: B.stack(A) == B + True sage: M = Matrix(K, 0, 0, 0) sage: N = Matrix(K, 19, 0, 0) @@ -1437,51 +1382,21 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): EXAMPLES:: sage: K. = GF(2^2) - sage: A = random_matrix(K, 5, 5); A - [ 0 a + 1 a + 1 a + 1 0] - [ 1 a + 1 1 a + 1 1] - [a + 1 a + 1 a 1 a] - [ a 1 a + 1 1 0] - [ a 1 a + 1 a + 1 0] - - sage: A1,A0 = A.slice() - sage: A0 - [0 1 1 1 0] - [0 1 0 1 0] - [1 1 1 0 1] - [1 0 1 0 0] - [1 0 1 1 0] - - sage: A1 - [0 1 1 1 0] - [1 1 1 1 1] - [1 1 0 1 0] - [0 1 1 1 0] - [0 1 1 1 0] - - sage: A0[2,4]*a + A1[2,4], A[2,4] - (a, a) + sage: A = random_matrix(K, 5, 5) + sage: A0, A1 = A.slice() + sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25)) + True sage: K. = GF(2^3) - sage: A = random_matrix(K, 5, 5); A - [ a + 1 a^2 + a 1 a a^2 + a] - [ a + 1 a^2 + a a^2 a^2 a^2 + 1] - [a^2 + a + 1 a^2 + a + 1 0 a^2 + a + 1 a^2 + 1] - [ a^2 + a 0 a^2 + a + 1 a a] - [ a^2 a + 1 a a^2 + 1 a^2 + a + 1] - - sage: A0,A1,A2 = A.slice() - sage: A0 - [1 0 1 0 0] - [1 0 0 0 1] - [1 1 0 1 1] - [0 0 1 0 0] - [0 1 0 1 1] + sage: A = random_matrix(K, 5, 5) + sage: A0, A1, A2 = A.slice() + sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] + a^2*A2.list()[i] for i in range(25)) + True Slicing and clinging are inverse operations:: sage: B = matrix(K, 5, 5) - sage: B.cling(A0,A1,A2) + sage: B.cling(A0, A1, A2) sage: B == A True """ @@ -1530,33 +1445,15 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^2) sage: A = matrix(K, 5, 5) - sage: A0 = random_matrix(GF(2), 5, 5); A0 - [0 1 0 1 1] - [0 1 1 1 0] - [0 0 0 1 0] - [0 1 1 0 0] - [0 0 0 1 1] - - sage: A1 = random_matrix(GF(2), 5, 5); A1 - [0 0 1 1 1] - [1 1 1 1 0] - [0 0 0 1 1] - [1 0 0 0 1] - [1 0 0 1 1] - - sage: A.cling(A1, A0); A - [ 0 a 1 a + 1 a + 1] - [ 1 a + 1 a + 1 a + 1 0] - [ 0 0 0 a + 1 1] - [ 1 a a 0 1] - [ 1 0 0 a + 1 a + 1] - - sage: A0[0,3]*a + A1[0,3], A[0,3] - (a + 1, a + 1) + sage: A0 = random_matrix(GF(2), 5, 5) + sage: A1 = random_matrix(GF(2), 5, 5) + sage: A.cling(A0, A1) + sage: all(A.list()[i] == A0.list()[i] + a*A1.list()[i] for i in range(25)) + True Slicing and clinging are inverse operations:: - sage: B1, B0 = A.slice() + sage: B0, B1 = A.slice() sage: B0 == A0 and B1 == A1 True diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 5c2d94bab22..a896995a058 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1998,8 +1998,8 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: m = random_matrix(ZZ, 100, 100, x=-1000, y=1000, density=.1) sage: m.parent() Full MatrixSpace of 100 by 100 dense matrices over Integer Ring - sage: H, U = m.hermite_form(algorithm="flint", transformation=True) - sage: H == U*m + sage: H, U = m.hermite_form(algorithm="flint", transformation=True) # long time + sage: H == U*m # long time True """ key = 'hnf-%s-%s'%(include_zero_rows,transformation) @@ -3433,17 +3433,18 @@ cdef class Matrix_integer_dense(Matrix_dense): EXAMPLES:: - sage: A = matrix(ZZ, 2,3, [1..6]); A - [1 2 3] - [4 5 6] - sage: A.randomize() - sage: A - [-8 2 0] - [ 0 1 -1] - sage: A.randomize(x=-30,y=30) - sage: A - [ 5 -19 24] - [ 24 23 -9] + sage: A = matrix(ZZ, 2,3, [1..6]) + sage: ranks = [True, True, True] + sage: while any(ranks): + ....: A.randomize() + ....: ranks[A.rank()] = False + + sage: mini = 0 + sage: maxi = 0 + sage: while mini != -30 and maxi != 30: + ....: A.randomize(x=-30, y=30) + ....: mini = min(min(A.list()), mini) + ....: maxi = min(min(A.list()), maxi) """ density = float(density) if density <= 0: @@ -5530,7 +5531,7 @@ cdef class Matrix_integer_dense(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(ZZ,3,3) + sage: A = matrix(ZZ, 3, 3, [-8, 2, 0, 0, 1, -1, 2, 1, -95]) sage: As = singular(A); As -8 2 0 0 1 -1 diff --git a/src/sage/matrix/matrix_integer_dense_saturation.py b/src/sage/matrix/matrix_integer_dense_saturation.py index e256849c1c9..87b55b6d7e6 100644 --- a/src/sage/matrix/matrix_integer_dense_saturation.py +++ b/src/sage/matrix/matrix_integer_dense_saturation.py @@ -81,10 +81,18 @@ def random_sublist_of_size(k, n): EXAMPLES:: sage: import sage.matrix.matrix_integer_dense_saturation as s - sage: s.random_sublist_of_size(10,3) - [0, 1, 5] - sage: s.random_sublist_of_size(10,7) - [0, 1, 3, 4, 5, 7, 8] + sage: l = s.random_sublist_of_size(10, 3) + sage: len(l) + 3 + sage: l_check = [-1] + l + [10] + sage: all(l_check[i] < l_check[i+1] for i in range(4)) + True + sage: l = s.random_sublist_of_size(10, 7) + sage: len(l) + 7 + sage: l_check = [-1] + l + [10] + sage: all(l_check[i] < l_check[i+1] for i in range(8)) + True """ if n > k: raise ValueError("n must be <= len(v)") diff --git a/src/sage/matrix/matrix_modn_dense_double.pyx b/src/sage/matrix/matrix_modn_dense_double.pyx index b10d2e9a71d..cb8f2a09e24 100644 --- a/src/sage/matrix/matrix_modn_dense_double.pyx +++ b/src/sage/matrix/matrix_modn_dense_double.pyx @@ -80,31 +80,21 @@ cdef class Matrix_modn_dense_double(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(GF(3016963), 4, 4); A - [ 220081 2824836 765701 2282256] - [1795330 767112 2967421 1373921] - [2757699 1142917 2720973 2877160] - [1674049 1341486 2641133 2173280] - sage: A[0,0] = 220082r; A - [ 220082 2824836 765701 2282256] - [1795330 767112 2967421 1373921] - [2757699 1142917 2720973 2877160] - [1674049 1341486 2641133 2173280] + sage: A = random_matrix(GF(3016963), 4, 4) + sage: l = A.list() + sage: A[0,0] = 220082r + sage: A.list()[1:] == l[1:] + True sage: a = A[0,0]; a 220082 sage: ~a 2859358 - sage: A = random_matrix(Integers(5099106), 4, 4); A - [2629491 1237101 2033003 3788106] - [4649912 1157595 4928315 4382585] - [4252686 978867 2601478 1759921] - [1303120 1860486 3405811 2203284] - sage: A[0,0] = 220082r; A - [ 220082 1237101 2033003 3788106] - [4649912 1157595 4928315 4382585] - [4252686 978867 2601478 1759921] - [1303120 1860486 3405811 2203284] + sage: A = random_matrix(Integers(5099106), 4, 4) + sage: l = A.list() + sage: A[0,0] = 220082r + sage: A.list()[1:] == l[1:] + True sage: a = A[0,0]; a 220082 sage: a*a @@ -120,29 +110,23 @@ cdef class Matrix_modn_dense_double(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(GF(3016963), 4, 4); A - [ 220081 2824836 765701 2282256] - [1795330 767112 2967421 1373921] - [2757699 1142917 2720973 2877160] - [1674049 1341486 2641133 2173280] + sage: A = random_matrix(GF(3016963), 4, 4) sage: K = A.base_ring() - sage: A[0,0] = K(220082); A - [ 220082 2824836 765701 2282256] - [1795330 767112 2967421 1373921] - [2757699 1142917 2720973 2877160] - [1674049 1341486 2641133 2173280] + sage: l = A.list() + sage: A[0,0] = K(220082) + sage: A.list()[1:] == l[1:] + True sage: a = A[0,0]; a 220082 sage: ~a 2859358 - sage: A = random_matrix(Integers(5099106), 4, 4); A - [2629491 1237101 2033003 3788106] - [4649912 1157595 4928315 4382585] - [4252686 978867 2601478 1759921] - [1303120 1860486 3405811 2203284] + sage: A = random_matrix(Integers(5099106), 4, 4) sage: K = A.base_ring() + sage: l = A.list() sage: A[0,0] = K(220081) + sage: A.list()[1:] == l[1:] + True sage: a = A[0,0]; a 220081 sage: a*a @@ -166,31 +150,25 @@ cdef class Matrix_modn_dense_double(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(GF(3016963), 4, 4); A - [ 220081 2824836 765701 2282256] - [1795330 767112 2967421 1373921] - [2757699 1142917 2720973 2877160] - [1674049 1341486 2641133 2173280] - sage: a = A[0,0]; a - 220081 - sage: ~a - 697224 - sage: K = A.base_ring() - sage: ~K(220081) - 697224 - - sage: A = random_matrix(Integers(5099106), 4, 4); A - [2629491 1237101 2033003 3788106] - [4649912 1157595 4928315 4382585] - [4252686 978867 2601478 1759921] - [1303120 1860486 3405811 2203284] - sage: a = A[0,1]; a - 1237101 - sage: a*a - 3803997 - sage: K = A.base_ring() - sage: K(1237101)^2 - 3803997 + sage: K = GF(3016963) + sage: l = [K.random_element() for _ in range(4*4)] + sage: A = matrix(K, 4, 4, l) + sage: a = A[0,0] + sage: a == l[0] + True + sage: a == 0 or ~a*a == 1 + True + sage: a.parent() is K + True + + sage: K = Integers(5099106) + sage: l = [K.random_element() for _ in range(4*4)] + sage: A = matrix(Integers(5099106), 4, 4, l) + sage: a = A[0,1] + sage: a == l[1] + True + sage: a*a == K(Integer(l[1]))^2 + True """ cdef Matrix_modn_dense_double _self = self cdef double result = (self)._matrix[i][j] diff --git a/src/sage/matrix/matrix_modn_dense_float.pyx b/src/sage/matrix/matrix_modn_dense_float.pyx index b23389a83b9..8a215de6f66 100644 --- a/src/sage/matrix/matrix_modn_dense_float.pyx +++ b/src/sage/matrix/matrix_modn_dense_float.pyx @@ -73,27 +73,21 @@ cdef class Matrix_modn_dense_float(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(GF(7), 4, 4); A - [3 1 6 6] - [4 4 2 2] - [3 5 4 5] - [6 2 2 1] - sage: A[0,0] = 12; A - [5 1 6 6] - [4 4 2 2] - [3 5 4 5] - [6 2 2 1] - - sage: B = random_matrix(Integers(100), 4, 4); B - [13 95 1 16] - [18 33 7 31] - [92 19 18 93] - [82 42 15 38] - sage: B[0,0] = 422; B - [22 95 1 16] - [18 33 7 31] - [92 19 18 93] - [82 42 15 38] + sage: A = random_matrix(GF(7), 4, 4) + sage: l = A.list() + sage: A[0,0] = 12 + sage: A.list()[0] == 12 + True + sage: l[1:] == A.list()[1:] + True + + sage: B = random_matrix(Integers(100), 4, 4) + sage: l = B.list() + sage: B[0,0] = 422 + sage: B.list()[0] == 22 + True + sage: l[1:] == B.list()[1:] + True """ self._matrix[i][j] = value @@ -105,31 +99,25 @@ cdef class Matrix_modn_dense_float(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(GF(13), 4, 4); A - [ 0 0 2 9] - [10 6 11 8] - [10 12 8 8] - [ 3 6 8 0] + sage: A = random_matrix(GF(13), 4, 4) + sage: l = A.list() sage: K = A.base_ring() sage: x = K(27) - sage: A[0,0] = x; A - [ 1 0 2 9] - [10 6 11 8] - [10 12 8 8] - [ 3 6 8 0] - - sage: B = random_matrix(Integers(200), 4, 4); B - [ 13 95 101 116] - [118 133 7 131] - [192 19 118 193] - [ 82 142 115 38] + sage: A[0,0] = x + sage: A[0,0] == x + True + sage: l[1:] == A.list()[1:] + True + + sage: B = random_matrix(Integers(200), 4, 4) + sage: l = B.list() sage: R = B.base_ring() sage: x = R(311) - sage: B[0,0] = x; B - [111 95 101 116] - [118 133 7 131] - [192 19 118 193] - [ 82 142 115 38] + sage: B[0,0] = x + sage: B.list()[0] == x + True + sage: l[1:] == B.list()[1:] + True """ self._matrix[i][j] = (x).ivalue @@ -144,24 +132,21 @@ cdef class Matrix_modn_dense_float(Matrix_modn_dense_template): EXAMPLES:: - sage: A = random_matrix(Integers(100), 4, 4); A - [ 4 95 83 47] - [44 57 91 53] - [75 53 15 39] - [26 25 10 74] - sage: a = A[0,0]; a - 4 - sage: a in A.base_ring() + sage: R = Integers(100) + sage: l = [R.random_element() for _ in range(4*4)] + sage: A = matrix(Integers(100), 4, 4, l) + sage: a = A[0,0] + sage: a == l[0] + True + sage: a in R True - sage: B = random_matrix(Integers(100), 4, 4); B - [13 95 1 16] - [18 33 7 31] - [92 19 18 93] - [82 42 15 38] - sage: b = B[0,0]; b - 13 - sage: b in B.base_ring() + sage: l = [R.random_element() for _ in range(4*4)] + sage: B = matrix(Integers(100), 4, 4, l) + sage: b = B[0,0] + sage: b == l[0] + True + sage: b in R True """ cdef float result = (self)._matrix[i][j] diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index c0e6d2e7047..f0ef0d7ac1c 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -389,34 +389,10 @@ cpdef __matrix_from_rows_of_matrices(X): EXAMPLES:: - sage: X = [random_matrix(GF(17), 4, 4) for _ in range(10)]; X - [ - [ 2 14 0 15] [12 14 3 13] [ 9 15 8 1] [ 2 12 6 10] - [11 10 16 2] [10 1 14 6] [ 5 8 10 11] [12 0 6 9] - [ 9 4 10 14] [ 2 14 13 7] [ 5 12 4 9] [ 7 7 3 8] - [ 1 14 3 14], [ 6 14 10 3], [15 2 6 11], [ 2 9 1 5], - - [12 13 7 16] [ 5 3 16 2] [14 15 16 4] [ 1 15 11 0] - [ 7 11 11 1] [11 10 12 14] [14 1 12 13] [16 13 8 14] - [ 0 2 0 4] [ 0 7 16 4] [ 5 5 16 13] [13 14 16 4] - [ 7 9 8 15], [ 6 5 2 3], [10 12 1 7], [15 6 6 6], - - [ 4 10 11 15] [13 12 5 1] - [11 2 9 14] [16 13 16 7] - [12 5 4 4] [12 2 0 11] - [ 2 0 12 8], [13 11 6 15] - ] - sage: X[0]._matrix_from_rows_of_matrices(X) # indirect doctest - [ 2 14 0 15 11 10 16 2 9 4 10 14 1 14 3 14] - [12 14 3 13 10 1 14 6 2 14 13 7 6 14 10 3] - [ 9 15 8 1 5 8 10 11 5 12 4 9 15 2 6 11] - [ 2 12 6 10 12 0 6 9 7 7 3 8 2 9 1 5] - [12 13 7 16 7 11 11 1 0 2 0 4 7 9 8 15] - [ 5 3 16 2 11 10 12 14 0 7 16 4 6 5 2 3] - [14 15 16 4 14 1 12 13 5 5 16 13 10 12 1 7] - [ 1 15 11 0 16 13 8 14 13 14 16 4 15 6 6 6] - [ 4 10 11 15 11 2 9 14 12 5 4 4 2 0 12 8] - [13 12 5 1 16 13 16 7 12 2 0 11 13 11 6 15] + sage: X = [random_matrix(GF(17), 4, 4) for _ in range(10)] + sage: Y = X[0]._matrix_from_rows_of_matrices(X) # indirect doctest + sage: all(list(Y[i]) == X[i].list() for i in range(10)) + True OUTPUT: A single matrix mod ``p`` whose ``i``-th row is ``X[i].list()``. @@ -551,16 +527,11 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sage: B = random_matrix(GF(127),3,3) sage: B.set_immutable() - sage: {B:0} # indirect doctest - {[ 9 75 94] - [ 4 57 112] - [ 59 85 45]: 0} + sage: _ = {B:0} # indirect doctest sage: M = random_matrix(GF(7), 10, 10) sage: M.set_immutable() - sage: hash(M) - -5724333594806680561 # 64-bit - -1581874161 # 32-bit + sage: _ = hash(M) sage: MZ = M.change_ring(ZZ) sage: MZ.set_immutable() sage: hash(MZ) == hash(M) @@ -1003,26 +974,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(7),2,2); A - [3 1] - [6 6] - - sage: B = random_matrix(GF(7),2,2); B - [4 4] - [2 2] - - sage: A*B - [0 0] - [1 1] - - sage: 3*A - [2 3] - [4 4] + sage: A = random_matrix(GF(7),2,2) + sage: B = random_matrix(GF(7),2,2) + sage: C = A*B + sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2)) + True sage: MS = parent(A) - sage: MS(3) * A - [2 3] - [4 4] + sage: MS(3) * A == 3*A + True :: @@ -1048,26 +1008,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): :: - sage: A = random_matrix(Integers(8),2,2); A - [0 5] - [6 4] - - sage: B = random_matrix(Integers(8),2,2); B - [4 4] - [5 6] - - sage: A*B - [1 6] - [4 0] - - sage: 3*A - [0 7] - [2 4] + sage: A = random_matrix(Integers(8),2,2) + sage: B = random_matrix(Integers(8),2,2) + sage: C = A*B + sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2)) + True sage: MS = parent(A) - sage: MS(3) * A - [0 7] - [2 4] + sage: MS(3) * A == 3*A + True :: @@ -1093,26 +1042,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): :: - sage: A = random_matrix(GF(16007),2,2); A - [ 6194 13327] - [ 5985 5926] - - sage: B = random_matrix(GF(16007),2,2); B - [ 6901 1242] - [13032 859] - - sage: A*B - [ 7618 12476] - [14289 6330] - - sage: 3*A - [2575 7967] - [1948 1771] + sage: A = random_matrix(GF(16007),2,2) + sage: B = random_matrix(GF(16007),2,2) + sage: C = A*B + sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2)) + True sage: MS = parent(A) - sage: MS(3) * A - [2575 7967] - [1948 1771] + sage: MS(3) * A == 3*A + True :: @@ -1140,26 +1078,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): :: - sage: A = random_matrix(Integers(1008),2,2); A - [ 41 973] - [851 876] - - sage: B = random_matrix(Integers(1008),2,2); B - [180 234] - [680 640] - - sage: A*B - [716 298] - [924 750] - - sage: 3*A - [123 903] - [537 612] + sage: A = random_matrix(Integers(1008),2,2) + sage: B = random_matrix(Integers(1008),2,2) + sage: C = A*B + sage: all(C[i, j] == sum(A[i, k]*B[k, j] for k in range(2)) for i in range(2) for j in range(2)) + True sage: MS = parent(A) - sage: MS(3) * A - [123 903] - [537 612] + sage: MS(3) * A == 3*A + True :: @@ -1329,52 +1256,29 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(19), 10, 10); A - [ 3 1 8 10 5 16 18 9 6 1] - [ 5 14 4 4 14 15 5 11 3 0] - [ 4 1 0 7 11 6 17 8 5 6] - [ 4 6 9 4 8 1 18 17 8 18] - [11 2 0 6 13 7 4 11 16 10] - [12 6 12 3 15 10 5 11 3 8] - [15 1 16 2 18 15 14 7 2 11] - [16 16 17 7 14 12 7 7 0 5] - [13 15 9 2 12 16 1 15 18 7] - [10 8 16 18 9 18 2 13 5 10] - + sage: A = random_matrix(GF(19), 10, 10) sage: B = copy(A) - sage: char_p = A.characteristic_polynomial(); char_p - x^10 + 2*x^9 + 18*x^8 + 4*x^7 + 13*x^6 + 11*x^5 + 2*x^4 + 5*x^3 + 7*x^2 + 16*x + 6 + sage: char_p = A.characteristic_polynomial() sage: char_p(A) == 0 True sage: B == A # A is not modified True - sage: min_p = A.minimal_polynomial(proof=True); min_p - x^10 + 2*x^9 + 18*x^8 + 4*x^7 + 13*x^6 + 11*x^5 + 2*x^4 + 5*x^3 + 7*x^2 + 16*x + 6 + sage: min_p = A.minimal_polynomial(proof=True) sage: min_p.divides(char_p) True :: - sage: A = random_matrix(GF(2916337), 7, 7); A - [ 514193 1196222 1242955 1040744 99523 2447069 40527] - [ 930282 2685786 2892660 1347146 1126775 2131459 869381] - [1853546 2266414 2897342 1342067 1054026 373002 84731] - [1270068 2421818 569466 537440 572533 297105 1415002] - [2079710 355705 2546914 2299052 2883413 1558788 1494309] - [1027319 1572148 250822 522367 2516720 585897 2296292] - [1797050 2128203 1161160 562535 2875615 1165768 286972] - + sage: A = random_matrix(GF(2916337), 7, 7) sage: B = copy(A) - sage: char_p = A.characteristic_polynomial(); char_p - x^7 + 1274305*x^6 + 1497602*x^5 + 12362*x^4 + 875330*x^3 + 31311*x^2 + 1858466*x + 700510 + sage: char_p = A.characteristic_polynomial() sage: char_p(A) == 0 True sage: B == A # A is not modified True - sage: min_p = A.minimal_polynomial(proof=True); min_p - x^7 + 1274305*x^6 + 1497602*x^5 + 12362*x^4 + 875330*x^3 + 31311*x^2 + 1858466*x + 700510 + sage: min_p = A.minimal_polynomial(proof=True) sage: min_p.divides(char_p) True @@ -1506,57 +1410,29 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(17), 10, 10); A - [ 2 14 0 15 11 10 16 2 9 4] - [10 14 1 14 3 14 12 14 3 13] - [10 1 14 6 2 14 13 7 6 14] - [10 3 9 15 8 1 5 8 10 11] - [ 5 12 4 9 15 2 6 11 2 12] - [ 6 10 12 0 6 9 7 7 3 8] - [ 2 9 1 5 12 13 7 16 7 11] - [11 1 0 2 0 4 7 9 8 15] - [ 5 3 16 2 11 10 12 14 0 7] - [16 4 6 5 2 3 14 15 16 4] - + sage: A = random_matrix(GF(17), 10, 10) sage: B = copy(A) - sage: min_p = A.minimal_polynomial(proof=True); min_p - x^10 + 13*x^9 + 10*x^8 + 9*x^7 + 10*x^6 + 4*x^5 + 10*x^4 + 10*x^3 + 12*x^2 + 14*x + 7 + sage: min_p = A.minimal_polynomial(proof=True) sage: min_p(A) == 0 True sage: B == A True - sage: char_p = A.characteristic_polynomial(); char_p - x^10 + 13*x^9 + 10*x^8 + 9*x^7 + 10*x^6 + 4*x^5 + 10*x^4 + 10*x^3 + 12*x^2 + 14*x + 7 + sage: char_p = A.characteristic_polynomial() sage: min_p.divides(char_p) True :: - sage: A = random_matrix(GF(1214471), 10, 10); A - [ 160562 831940 65852 173001 515930 714380 778254 844537 584888 392730] - [ 502193 959391 614352 775603 240043 1156372 104118 1175992 612032 1049083] - [ 660489 1066446 809624 15010 1002045 470722 314480 1155149 1173111 14213] - [1190467 1079166 786442 429883 563611 625490 1015074 888047 1090092 892387] - [ 4724 244901 696350 384684 254561 898612 44844 83752 1091581 349242] - [ 130212 580087 253296 472569 913613 919150 38603 710029 438461 736442] - [ 943501 792110 110470 850040 713428 668799 1122064 325250 1084368 520553] - [1179743 791517 34060 1183757 1118938 642169 47513 73428 1076788 216479] - [ 626571 105273 400489 1041378 1186801 158611 888598 1138220 1089631 56266] - [1092400 890773 1060810 211135 719636 1011640 631366 427711 547497 1084281] - + sage: A = random_matrix(GF(1214471), 10, 10) sage: B = copy(A) - sage: min_p = A.minimal_polynomial(proof=True); min_p - x^10 + 384251*x^9 + 702437*x^8 + 960299*x^7 + 202699*x^6 + 409368*x^5 + 1109249*x^4 + 1163061*x^3 + 333802*x^2 + 273775*x + 55190 - + sage: min_p = A.minimal_polynomial(proof=True) sage: min_p(A) == 0 True sage: B == A True - sage: char_p = A.characteristic_polynomial(); char_p - x^10 + 384251*x^9 + 702437*x^8 + 960299*x^7 + 202699*x^6 + 409368*x^5 + 1109249*x^4 + 1163061*x^3 + 333802*x^2 + 273775*x + 55190 - + sage: char_p = A.characteristic_polynomial() sage: min_p.divides(char_p) True @@ -1656,28 +1532,15 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(19), 10, 10); A - [ 3 1 8 10 5 16 18 9 6 1] - [ 5 14 4 4 14 15 5 11 3 0] - [ 4 1 0 7 11 6 17 8 5 6] - [ 4 6 9 4 8 1 18 17 8 18] - [11 2 0 6 13 7 4 11 16 10] - [12 6 12 3 15 10 5 11 3 8] - [15 1 16 2 18 15 14 7 2 11] - [16 16 17 7 14 12 7 7 0 5] - [13 15 9 2 12 16 1 15 18 7] - [10 8 16 18 9 18 2 13 5 10] - + sage: A = random_matrix(GF(19), 10, 10) sage: B = copy(A) - sage: char_p = A._charpoly_linbox(); char_p - x^10 + 2*x^9 + 18*x^8 + 4*x^7 + 13*x^6 + 11*x^5 + 2*x^4 + 5*x^3 + 7*x^2 + 16*x + 6 + sage: char_p = A._charpoly_linbox() sage: char_p(A) == 0 True sage: B == A # A is not modified True - sage: min_p = A.minimal_polynomial(proof=True); min_p - x^10 + 2*x^9 + 18*x^8 + 4*x^7 + 13*x^6 + 11*x^5 + 2*x^4 + 5*x^3 + 7*x^2 + 16*x + 6 + sage: min_p = A.minimal_polynomial(proof=True) sage: min_p.divides(char_p) True """ @@ -1726,61 +1589,24 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(7), 10, 20); A - [3 1 6 6 4 4 2 2 3 5 4 5 6 2 2 1 2 5 0 5] - [3 2 0 5 0 1 5 4 2 3 6 4 5 0 2 4 2 0 6 3] - [2 2 4 2 4 5 3 4 4 4 2 5 2 5 4 5 1 1 1 1] - [0 6 3 4 2 2 3 5 1 1 4 2 6 5 6 3 4 5 5 3] - [5 2 4 3 6 2 3 6 2 1 3 3 5 3 4 2 2 1 6 2] - [0 5 6 3 2 5 6 6 3 2 1 4 5 0 2 6 5 2 5 1] - [4 0 4 2 6 3 3 5 3 0 0 1 2 5 5 1 6 0 0 3] - [2 0 1 0 0 3 0 2 4 2 2 4 4 4 5 4 1 2 3 4] - [2 4 1 4 3 0 6 2 2 5 2 5 3 6 4 2 2 6 4 4] - [0 0 2 2 1 6 2 0 5 0 4 3 1 6 0 6 0 4 6 5] - - sage: A.echelon_form() - [1 0 0 0 0 0 0 0 0 0 6 2 6 0 1 1 2 5 6 2] - [0 1 0 0 0 0 0 0 0 0 0 4 5 4 3 4 2 5 1 2] - [0 0 1 0 0 0 0 0 0 0 6 3 4 6 1 0 3 6 5 6] - [0 0 0 1 0 0 0 0 0 0 0 3 5 2 3 4 0 6 5 3] - [0 0 0 0 1 0 0 0 0 0 0 6 3 4 5 3 0 4 3 2] - [0 0 0 0 0 1 0 0 0 0 1 1 0 2 4 2 5 5 5 0] - [0 0 0 0 0 0 1 0 0 0 1 0 1 3 2 0 0 0 5 3] - [0 0 0 0 0 0 0 1 0 0 4 4 2 6 5 4 3 4 1 0] - [0 0 0 0 0 0 0 0 1 0 1 0 4 2 3 5 4 6 4 0] - [0 0 0 0 0 0 0 0 0 1 2 0 5 0 5 5 3 1 1 4] + sage: A = random_matrix(GF(7), 10, 20) + sage: E = A.echelon_form() + sage: A.row_space() == E.row_space() + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r) + True :: - sage: A = random_matrix(GF(13), 10, 10); A - [ 8 3 11 11 9 4 8 7 9 9] - [ 2 9 6 5 7 12 3 4 11 5] - [12 6 11 12 4 3 3 8 9 5] - [ 4 2 10 5 10 1 1 1 6 9] - [12 8 5 5 11 4 1 2 8 11] - [ 2 6 9 11 4 7 1 0 12 2] - [ 8 9 0 7 7 7 10 4 1 4] - [ 0 8 2 6 7 5 7 12 2 3] - [ 2 11 12 3 4 7 2 9 6 1] - [ 0 11 5 9 4 5 5 8 7 10] - + sage: A = random_matrix(GF(13), 10, 10) + sage: while A.rank() != 10: + ....: A = random_matrix(GF(13), 10, 10) sage: MS = parent(A) sage: B = A.augment(MS(1)) sage: B.echelonize() sage: A.rank() 10 - sage: C = B.submatrix(0,10,10,10); C - [ 4 9 4 4 0 4 7 11 9 11] - [11 7 6 8 2 8 6 11 9 5] - [ 3 9 9 2 4 8 9 2 9 4] - [ 7 0 11 4 0 9 6 11 8 1] - [12 12 4 12 3 12 6 1 7 12] - [12 2 11 6 6 6 7 0 10 6] - [ 0 7 3 4 7 11 10 12 4 6] - [ 5 11 0 5 3 11 4 12 5 12] - [ 6 7 3 5 1 4 11 7 4 1] - [ 4 9 6 7 11 1 2 12 6 7] - + sage: C = B.submatrix(0,10,10,10) sage: ~A == C True @@ -1794,28 +1620,12 @@ cdef class Matrix_modn_dense_template(Matrix_dense): :: - sage: A = random_matrix(GF(16007), 10, 20); A - [15455 1177 10072 4693 3887 4102 10746 15265 6684 14559 4535 13921 9757 9525 9301 8566 2460 9609 3887 6205] - [ 8602 10035 1242 9776 162 7893 12619 6660 13250 1988 14263 11377 2216 1247 7261 8446 15081 14412 7371 7948] - [12634 7602 905 9617 13557 2694 13039 4936 12208 15480 3787 11229 593 12462 5123 14167 6460 3649 5821 6736] - [10554 2511 11685 12325 12287 6534 11636 5004 6468 3180 3607 11627 13436 5106 3138 13376 8641 9093 2297 5893] - [ 1025 11376 10288 609 12330 3021 908 13012 2112 11505 56 5971 338 2317 2396 8561 5593 3782 7986 13173] - [ 7607 588 6099 12749 10378 111 2852 10375 8996 7969 774 13498 12720 4378 6817 6707 5299 9406 13318 2863] - [15545 538 4840 1885 8471 1303 11086 14168 1853 14263 3995 12104 1294 7184 1188 11901 15971 2899 4632 711] - [ 584 11745 7540 15826 15027 5953 7097 14329 10889 12532 13309 15041 6211 1749 10481 9999 2751 11068 21 2795] - [ 761 11453 3435 10596 2173 7752 15941 14610 1072 8012 9458 5440 612 10581 10400 101 11472 13068 7758 7898] - [10658 4035 6662 655 7546 4107 6987 1877 4072 4221 7679 14579 2474 8693 8127 12999 11141 605 9404 10003] - sage: A.echelon_form() - [ 1 0 0 0 0 0 0 0 0 0 8416 8364 10318 1782 13872 4566 14855 7678 11899 2652] - [ 0 1 0 0 0 0 0 0 0 0 4782 15571 3133 10964 5581 10435 9989 14303 5951 8048] - [ 0 0 1 0 0 0 0 0 0 0 15688 6716 13819 4144 257 5743 14865 15680 4179 10478] - [ 0 0 0 1 0 0 0 0 0 0 4307 9488 2992 9925 13984 15754 8185 11598 14701 10784] - [ 0 0 0 0 1 0 0 0 0 0 927 3404 15076 1040 2827 9317 14041 10566 5117 7452] - [ 0 0 0 0 0 1 0 0 0 0 1144 10861 5241 6288 9282 5748 3715 13482 7258 9401] - [ 0 0 0 0 0 0 1 0 0 0 769 1804 1879 4624 6170 7500 11883 9047 874 597] - [ 0 0 0 0 0 0 0 1 0 0 15591 13686 5729 11259 10219 13222 15177 15727 5082 11211] - [ 0 0 0 0 0 0 0 0 1 0 8375 14939 13471 12221 8103 4212 11744 10182 2492 11068] - [ 0 0 0 0 0 0 0 0 0 1 6534 396 6780 14734 1206 3848 7712 9770 10755 410] + sage: A = random_matrix(GF(16007), 10, 20) + sage: E = A.echelon_form() + sage: A.row_space() == E.row_space() + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in E.rows() if r) + True :: @@ -1936,29 +1746,13 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(7), 10, 20); A - [3 1 6 6 4 4 2 2 3 5 4 5 6 2 2 1 2 5 0 5] - [3 2 0 5 0 1 5 4 2 3 6 4 5 0 2 4 2 0 6 3] - [2 2 4 2 4 5 3 4 4 4 2 5 2 5 4 5 1 1 1 1] - [0 6 3 4 2 2 3 5 1 1 4 2 6 5 6 3 4 5 5 3] - [5 2 4 3 6 2 3 6 2 1 3 3 5 3 4 2 2 1 6 2] - [0 5 6 3 2 5 6 6 3 2 1 4 5 0 2 6 5 2 5 1] - [4 0 4 2 6 3 3 5 3 0 0 1 2 5 5 1 6 0 0 3] - [2 0 1 0 0 3 0 2 4 2 2 4 4 4 5 4 1 2 3 4] - [2 4 1 4 3 0 6 2 2 5 2 5 3 6 4 2 2 6 4 4] - [0 0 2 2 1 6 2 0 5 0 4 3 1 6 0 6 0 4 6 5] - - sage: A._echelonize_linbox(); A - [1 0 0 0 0 0 0 0 0 0 6 2 6 0 1 1 2 5 6 2] - [0 1 0 0 0 0 0 0 0 0 0 4 5 4 3 4 2 5 1 2] - [0 0 1 0 0 0 0 0 0 0 6 3 4 6 1 0 3 6 5 6] - [0 0 0 1 0 0 0 0 0 0 0 3 5 2 3 4 0 6 5 3] - [0 0 0 0 1 0 0 0 0 0 0 6 3 4 5 3 0 4 3 2] - [0 0 0 0 0 1 0 0 0 0 1 1 0 2 4 2 5 5 5 0] - [0 0 0 0 0 0 1 0 0 0 1 0 1 3 2 0 0 0 5 3] - [0 0 0 0 0 0 0 1 0 0 4 4 2 6 5 4 3 4 1 0] - [0 0 0 0 0 0 0 0 1 0 1 0 4 2 3 5 4 6 4 0] - [0 0 0 0 0 0 0 0 0 1 2 0 5 0 5 5 3 1 1 4] + sage: A = random_matrix(GF(7), 10, 20) + sage: B = copy(A) + sage: A._echelonize_linbox() + sage: A.row_space() == B.row_space() + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r) + True """ self.check_mutability() self.clear_cache() @@ -1982,29 +1776,13 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(7), 10, 20); A - [3 1 6 6 4 4 2 2 3 5 4 5 6 2 2 1 2 5 0 5] - [3 2 0 5 0 1 5 4 2 3 6 4 5 0 2 4 2 0 6 3] - [2 2 4 2 4 5 3 4 4 4 2 5 2 5 4 5 1 1 1 1] - [0 6 3 4 2 2 3 5 1 1 4 2 6 5 6 3 4 5 5 3] - [5 2 4 3 6 2 3 6 2 1 3 3 5 3 4 2 2 1 6 2] - [0 5 6 3 2 5 6 6 3 2 1 4 5 0 2 6 5 2 5 1] - [4 0 4 2 6 3 3 5 3 0 0 1 2 5 5 1 6 0 0 3] - [2 0 1 0 0 3 0 2 4 2 2 4 4 4 5 4 1 2 3 4] - [2 4 1 4 3 0 6 2 2 5 2 5 3 6 4 2 2 6 4 4] - [0 0 2 2 1 6 2 0 5 0 4 3 1 6 0 6 0 4 6 5] - - sage: A._echelon_in_place_classical(); A - [1 0 0 0 0 0 0 0 0 0 6 2 6 0 1 1 2 5 6 2] - [0 1 0 0 0 0 0 0 0 0 0 4 5 4 3 4 2 5 1 2] - [0 0 1 0 0 0 0 0 0 0 6 3 4 6 1 0 3 6 5 6] - [0 0 0 1 0 0 0 0 0 0 0 3 5 2 3 4 0 6 5 3] - [0 0 0 0 1 0 0 0 0 0 0 6 3 4 5 3 0 4 3 2] - [0 0 0 0 0 1 0 0 0 0 1 1 0 2 4 2 5 5 5 0] - [0 0 0 0 0 0 1 0 0 0 1 0 1 3 2 0 0 0 5 3] - [0 0 0 0 0 0 0 1 0 0 4 4 2 6 5 4 3 4 1 0] - [0 0 0 0 0 0 0 0 1 0 1 0 4 2 3 5 4 6 4 0] - [0 0 0 0 0 0 0 0 0 1 2 0 5 0 5 5 3 1 1 4] + sage: A = random_matrix(GF(7), 10, 20) + sage: B = copy(A) + sage: A._echelon_in_place_classical() + sage: A.row_space() == B.row_space() + True + sage: all(r[r.nonzero_positions()[0]] == 1 for r in A.rows() if r) + True """ self.check_mutability() self.clear_cache() @@ -2153,28 +1931,13 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(17), 10, 10, density=0.1); A - [ 0 0 0 0 12 0 0 0 0 0] - [ 0 0 0 4 0 0 0 0 0 0] - [ 0 0 0 0 2 0 0 0 0 0] - [ 0 14 0 0 0 0 0 0 0 0] - [ 0 0 0 0 0 10 0 0 0 0] - [ 0 0 0 0 0 16 0 0 0 0] - [ 0 0 0 0 0 0 6 0 0 0] - [15 0 0 0 0 0 0 0 0 0] - [ 0 0 0 16 0 0 0 0 0 0] - [ 0 5 0 0 0 0 0 0 0 0] - sage: A.hessenbergize(); A - [ 0 0 0 0 0 0 0 12 0 0] - [15 0 0 0 0 0 0 0 0 0] - [ 0 0 0 0 0 0 0 2 0 0] - [ 0 0 0 0 14 0 0 0 0 0] - [ 0 0 0 4 0 0 0 0 0 0] - [ 0 0 0 0 5 0 0 0 0 0] - [ 0 0 0 0 0 0 6 0 0 0] - [ 0 0 0 0 0 0 0 0 0 10] - [ 0 0 0 0 0 0 0 0 0 0] - [ 0 0 0 0 0 0 0 0 0 16] + sage: A = random_matrix(GF(17), 10, 10, density=0.1) + sage: B = copy(A) + sage: A.hessenbergize() + sage: all(A[i,j] == 0 for j in range(10) for i in range(j+2, 10)) + True + sage: A.charpoly() == B.charpoly() + True """ self.check_mutability() x = self.fetch('in_hessenberg_form') @@ -2250,22 +2013,11 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(17), 10, 10, density=0.1); A - [ 0 0 0 0 12 0 0 0 0 0] - [ 0 0 0 4 0 0 0 0 0 0] - [ 0 0 0 0 2 0 0 0 0 0] - [ 0 14 0 0 0 0 0 0 0 0] - [ 0 0 0 0 0 10 0 0 0 0] - [ 0 0 0 0 0 16 0 0 0 0] - [ 0 0 0 0 0 0 6 0 0 0] - [15 0 0 0 0 0 0 0 0 0] - [ 0 0 0 16 0 0 0 0 0 0] - [ 0 5 0 0 0 0 0 0 0 0] - sage: A.characteristic_polynomial() - x^10 + 12*x^9 + 6*x^8 + 8*x^7 + 13*x^6 + sage: A = random_matrix(GF(17), 10, 10, density=0.1) + sage: B = copy(A) sage: P. = GF(17)[] - sage: A._charpoly_hessenberg('x') - x^10 + 12*x^9 + 6*x^8 + 8*x^7 + 13*x^6 + sage: A._charpoly_hessenberg('x') == B.charpoly() + True """ if self._nrows != self._ncols: raise ArithmeticError("charpoly not defined for non-square matrix.") @@ -2325,14 +2077,13 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sage: A = random_matrix(GF(3), 100, 100) sage: B = copy(A) - sage: A.rank() - 99 + sage: _ = A.rank() sage: B == A True sage: A = random_matrix(GF(3), 100, 100, density=0.01) - sage: A.rank() - 63 + sage: A.transpose().rank() == A.rank() + True sage: A = matrix(GF(3), 100, 100) sage: A.rank() @@ -2399,68 +2150,38 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(7), 10, 10); A - [3 1 6 6 4 4 2 2 3 5] - [4 5 6 2 2 1 2 5 0 5] - [3 2 0 5 0 1 5 4 2 3] - [6 4 5 0 2 4 2 0 6 3] - [2 2 4 2 4 5 3 4 4 4] - [2 5 2 5 4 5 1 1 1 1] - [0 6 3 4 2 2 3 5 1 1] - [4 2 6 5 6 3 4 5 5 3] - [5 2 4 3 6 2 3 6 2 1] - [3 3 5 3 4 2 2 1 6 2] - - sage: A.determinant() - 6 + sage: s = set() + sage: while s != set(GF(7)): + ....: A = random_matrix(GF(7), 10, 10) + ....: s.add(A.determinant()) :: sage: A = random_matrix(GF(7), 100, 100) - sage: A.determinant() - 2 - - sage: A.transpose().determinant() - 2 + sage: A.determinant() == A.transpose().determinant() + True sage: B = random_matrix(GF(7), 100, 100) - sage: B.determinant() - 4 - sage: (A*B).determinant() == A.determinant() * B.determinant() True :: - sage: A = random_matrix(GF(16007), 10, 10); A - [ 5037 2388 4150 1400 345 5945 4240 14022 10514 700] - [15552 8539 1927 3870 9867 3263 11637 609 15424 2443] - [ 3761 15836 12246 15577 10178 13602 13183 15918 13942 2958] - [ 4526 10817 6887 6678 1764 9964 6107 1705 5619 5811] - [13537 15004 8307 11846 14779 550 14113 5477 7271 7091] - [13338 4927 11406 13065 5437 12431 6318 5119 14198 496] - [ 1044 179 12881 353 12975 12567 1092 10433 12304 954] - [10072 8821 14118 13895 6543 13484 10685 14363 2612 11070] - [15113 237 2612 14127 11589 5808 117 9656 15957 14118] - [15233 11080 5716 9029 11402 9380 13045 13986 14544 5771] - - sage: A.determinant() - 10207 + sage: A = random_matrix(GF(16007), 10, 10) + sage: A.determinant().parent() is GF(16007) + True :: sage: A = random_matrix(GF(16007), 100, 100) - sage: A.determinant() - 3576 + sage: A.determinant().parent() is GF(16007) + True - sage: A.transpose().determinant() - 3576 + sage: A.determinant() == A.transpose().determinant() + True sage: B = random_matrix(GF(16007), 100, 100) - sage: B.determinant() - 4075 - sage: (A*B).determinant() == A.determinant() * B.determinant() True @@ -2669,46 +2390,22 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(37), 10, 10); A - [24 15 7 27 32 34 16 32 25 23] - [11 3 22 13 35 33 1 10 33 25] - [33 9 25 3 15 27 30 30 7 12] - [10 0 35 4 12 34 32 16 19 17] - [36 4 21 17 3 34 11 10 10 17] - [32 15 23 2 23 32 5 8 18 11] - [24 5 28 13 21 22 29 18 33 30] - [26 18 10 26 17 31 35 18 25 30] - [21 1 4 14 11 17 29 16 18 12] - [34 19 14 11 35 30 35 34 25 33] - - sage: A[2] + 10*A[3] - (22, 9, 5, 6, 24, 34, 17, 5, 12, 34) + sage: A = random_matrix(GF(37), 10, 10) + sage: B = copy(A) sage: A.add_multiple_of_row(2, 3, 10) - sage: A - [24 15 7 27 32 34 16 32 25 23] - [11 3 22 13 35 33 1 10 33 25] - [22 9 5 6 24 34 17 5 12 34] - [10 0 35 4 12 34 32 16 19 17] - [36 4 21 17 3 34 11 10 10 17] - [32 15 23 2 23 32 5 8 18 11] - [24 5 28 13 21 22 29 18 33 30] - [26 18 10 26 17 31 35 18 25 30] - [21 1 4 14 11 17 29 16 18 12] - [34 19 14 11 35 30 35 34 25 33] + sage: all(A[i] == B[i] for i in range(10) if not i == 2) + True + sage: A[2] == B[2] + 10*B[3] + True sage: A.add_multiple_of_row(2, 3, 10, 4) - sage: A - [24 15 7 27 32 34 16 32 25 23] - [11 3 22 13 35 33 1 10 33 25] - [22 9 5 6 33 4 4 17 17 19] - [10 0 35 4 12 34 32 16 19 17] - [36 4 21 17 3 34 11 10 10 17] - [32 15 23 2 23 32 5 8 18 11] - [24 5 28 13 21 22 29 18 33 30] - [26 18 10 26 17 31 35 18 25 30] - [21 1 4 14 11 17 29 16 18 12] - [34 19 14 11 35 30 35 34 25 33] + sage: all(A[i] == B[i] for i in range(10) if not i == 2) + True + sage: A[2][:4] == B[2][:4] + 10*B[3][:4] + True + sage: A[2][4:] == B[2][4:] + 20*B[3][4:] + True """ cdef celement p cdef celement *v_from @@ -2730,46 +2427,22 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: - sage: A = random_matrix(GF(37), 10, 10); A - [24 15 7 27 32 34 16 32 25 23] - [11 3 22 13 35 33 1 10 33 25] - [33 9 25 3 15 27 30 30 7 12] - [10 0 35 4 12 34 32 16 19 17] - [36 4 21 17 3 34 11 10 10 17] - [32 15 23 2 23 32 5 8 18 11] - [24 5 28 13 21 22 29 18 33 30] - [26 18 10 26 17 31 35 18 25 30] - [21 1 4 14 11 17 29 16 18 12] - [34 19 14 11 35 30 35 34 25 33] - - sage: A.column(2) + 10*A.column(3) - (18, 4, 18, 1, 6, 6, 10, 11, 33, 13) + sage: A = random_matrix(GF(37), 10, 10) + sage: B = copy(A) sage: A.add_multiple_of_column(2, 3, 10) - sage: A - [24 15 18 27 32 34 16 32 25 23] - [11 3 4 13 35 33 1 10 33 25] - [33 9 18 3 15 27 30 30 7 12] - [10 0 1 4 12 34 32 16 19 17] - [36 4 6 17 3 34 11 10 10 17] - [32 15 6 2 23 32 5 8 18 11] - [24 5 10 13 21 22 29 18 33 30] - [26 18 11 26 17 31 35 18 25 30] - [21 1 33 14 11 17 29 16 18 12] - [34 19 13 11 35 30 35 34 25 33] + sage: all(A.column(i) == B.column(i) for i in range(10) if not i == 2) + True + sage: A.column(2) == B.column(2) + 10*B.column(3) + True sage: A.add_multiple_of_column(2, 3, 10, 4) - sage: A - [24 15 18 27 32 34 16 32 25 23] - [11 3 4 13 35 33 1 10 33 25] - [33 9 18 3 15 27 30 30 7 12] - [10 0 1 4 12 34 32 16 19 17] - [36 4 28 17 3 34 11 10 10 17] - [32 15 26 2 23 32 5 8 18 11] - [24 5 29 13 21 22 29 18 33 30] - [26 18 12 26 17 31 35 18 25 30] - [21 1 25 14 11 17 29 16 18 12] - [34 19 12 11 35 30 35 34 25 33] + sage: all(A.column(i) == B.column(i) for i in range(10) if not i == 2) + True + sage: A.column(2)[:4] == B.column(2)[:4] + 10*B.column(3)[:4] + True + sage: A.column(2)[4:] == B.column(2)[4:] + 20*B.column(3)[4:] + True """ cdef celement p cdef celement **m @@ -2839,29 +2512,53 @@ cdef class Matrix_modn_dense_template(Matrix_dense): EXAMPLES:: sage: A = matrix(GF(5), 5, 5, 0) - sage: A.randomize(0.5); A - [0 0 0 2 0] - [0 3 0 0 2] - [4 0 0 0 0] - [4 0 0 0 0] - [0 1 0 0 0] - - sage: A.randomize(); A - [3 3 2 1 2] - [4 3 3 2 2] - [0 3 3 3 3] - [3 3 2 2 4] - [2 2 2 1 4] + sage: total_count = 0 + sage: from collections import defaultdict + sage: dic = defaultdict(Integer) + sage: def add_samples(density): + ....: global dic, total_count + ....: for _ in range(100): + ....: A = Matrix(GF(5), 5, 5, 0) + ....: A.randomize(density) + ....: for a in A.list(): + ....: dic[a] += 1 + ....: total_count += 1.0 + + sage: add_samples(1.0) + sage: while not all(abs(dic[a]/total_count - 1/5) < 0.01 for a in dic): + ....: add_samples(1.0) + + sage: def add_sample(density): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: density_sum += random_matrix(GF(5), 1000, 1000, density=density).density() + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.5) + sage: while abs(density_sum/total_count - 0.394) > 0.01: # TODO + ....: add_sample(0.5) The matrix is updated instead of overwritten:: - sage: A = random_matrix(GF(5), 100, 100, density=0.1) - sage: A.density() - 961/10000 - - sage: A.randomize(density=0.1) - sage: A.density() - 801/5000 + sage: def add_sample(density): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: A += random_matrix(GF(5), 1000, 1000, density=density) + ....: A.randomize(density=density) + ....: density_sum += A.density() + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.5) + sage: while abs(density_sum/total_count - (1 - 0.5^2)*4/5) > 0.001: + ....: add_sample(0.5) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(0.1) + sage: while abs(density_sum/total_count - (1 - 0.1^2)*4/5) > 0.001: + ....: add_sample(0.1) """ density = float(density) if density <= 0: From dde45e2f063a163af0db11757944de1d63dad8b7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 2 Jun 2021 16:34:01 +0200 Subject: [PATCH 479/706] small fixes in build files --- build/pkgs/memory_allocator/checksums.ini | 1 + build/pkgs/memory_allocator/install_requires.txt | 1 + build/pkgs/memory_allocator/spkg-install.in | 4 +++- build/pkgs/sagelib/dependencies | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 build/pkgs/memory_allocator/install_requires.txt diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini index 14e6cf2a2b4..fb9511de83c 100644 --- a/build/pkgs/memory_allocator/checksums.ini +++ b/build/pkgs/memory_allocator/checksums.ini @@ -1,3 +1,4 @@ +tarball=memory_allocator--VERSION.tar.gz sha1=d0eeb3be4462a41b316086a33f4dfbca08b887b4 md5=74c8a21dd6e79452bae99881d40ab2f9 cksum=3681970071 diff --git a/build/pkgs/memory_allocator/install_requires.txt b/build/pkgs/memory_allocator/install_requires.txt new file mode 100644 index 00000000000..fad07b22ebe --- /dev/null +++ b/build/pkgs/memory_allocator/install_requires.txt @@ -0,0 +1 @@ +memory_allocator diff --git a/build/pkgs/memory_allocator/spkg-install.in b/build/pkgs/memory_allocator/spkg-install.in index deba1bb42bb..058b1344dc2 100644 --- a/build/pkgs/memory_allocator/spkg-install.in +++ b/build/pkgs/memory_allocator/spkg-install.in @@ -1 +1,3 @@ -cd src && sdh_pip_install . +cd src + +sdh_pip_install . diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index 770025fb5b2..62aa32fca69 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_conf singular symmetrica zn_poly $(PCFILES) +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy pycygwin pynac $(PYTHON) ratpoints readline rw sage_conf singular symmetrica zn_poly $(PCFILES) ---------- All lines of this file are ignored except the first. From b9909c061046a204354341833e3b3fcb4c6d7341 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 2 Jun 2021 09:58:10 -0700 Subject: [PATCH 480/706] ManifoldSubsetClosure: Add examples, fix docstring markup --- src/sage/manifolds/subsets/closure.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index f66f5dec589..aa536bfafc9 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -20,17 +20,34 @@ class ManifoldSubsetClosure(ManifoldSubset): - """ + r""" Topological closure of a manifold subset in the topology of the manifold. INPUT: - ``subset`` -- a :class:`ManifoldSubset` - ``name`` -- (default: computed from the name of the subset) - string; name (symbol) given to the closure + string; name (symbol) given to the closure - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the subset; if none is provided, it is set to ``name`` + EXAMPLES:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = ManifoldSubsetClosure(D) + + The closure of the subset `D` is a subset of every closed superset + of `D`:: + + sage: S = D.superset('S') + sage: S.declare_closed() + sage: cl_D.is_subset(S) + True + """ def __init__(self, subset, name=None, latex_name=None): @@ -87,7 +104,7 @@ def _repr_(self): return "Topological closure {} of the {}".format(self._name, self._subset) def is_closed(self): - """ + r""" Return if ``self`` is a closed set. This implementation of the method always returns ``True``. From db8330c24af995f9497f0931f5555e3892340676 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 2 Jun 2021 22:01:08 +0200 Subject: [PATCH 481/706] make a release on pypi --- build/pkgs/memory_allocator/checksums.ini | 10 +++++----- build/pkgs/memory_allocator/package-version.txt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini index fb9511de83c..dc635557a88 100644 --- a/build/pkgs/memory_allocator/checksums.ini +++ b/build/pkgs/memory_allocator/checksums.ini @@ -1,5 +1,5 @@ -tarball=memory_allocator--VERSION.tar.gz -sha1=d0eeb3be4462a41b316086a33f4dfbca08b887b4 -md5=74c8a21dd6e79452bae99881d40ab2f9 -cksum=3681970071 -upstream_url=https://github.com/sagemath/memory_allocator/archive/refs/tags/vVERSION.tar.gz +tarball=memory_allocator-VERSION.tar.gz +sha1=0e9256d307957e84eaba408f190cafc04d5c246a +md5=1ce1fb6dc5972017a26ee82c4371b180 +cksum=2493862442 +upstream_url=https://files.pythonhosted.org/packages/26/38/ca4e812a90dd396808fd6bf5c234ae813635f7c08d6297efbfae57333450/memory_allocator-VERSION.tar.gz diff --git a/build/pkgs/memory_allocator/package-version.txt b/build/pkgs/memory_allocator/package-version.txt index 4ab2ff64ead..6e8bf73aa55 100644 --- a/build/pkgs/memory_allocator/package-version.txt +++ b/build/pkgs/memory_allocator/package-version.txt @@ -1 +1 @@ -0.1.0-beta.1 +0.1.0 From c08aa848e0241618ba009db6ebad20427f46945a Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Wed, 2 Jun 2021 15:57:36 -0700 Subject: [PATCH 482/706] fixed typos --- src/sage/modules/free_module_homspace.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 13c9292260d..81f7a7a6023 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -175,7 +175,7 @@ def __call__(self, A, check=True): coercion between base rings, one can only define the zero morphism, as morphism of additive groups. Before one could for example use an integer matrix to define a morphism from the rational numbers to the - integers. + integers. :: sage: V = QQ^2; W = ZZ^2; m = identity_matrix(2) sage: H = V.Hom(W); H(m) @@ -207,8 +207,8 @@ def __call__(self, A, check=True): # Let us hope that FreeModuleMorphism knows to handle # that case pass - if not(self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring())) and not(A.is_zero()): - raise TypeError("Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") + if not self.codomain().base_ring().has_coerce_map_from(self.domain().base_ring()) and not A.is_zero(): + raise TypeError("nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain") return free_module_morphism.FreeModuleMorphism(self, A) @cached_method From 1d84a20f691f55ea79a66793852add6709a417d7 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 3 Jun 2021 07:28:23 +0200 Subject: [PATCH 483/706] update upstream url to be more generic --- build/pkgs/memory_allocator/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/memory_allocator/checksums.ini b/build/pkgs/memory_allocator/checksums.ini index dc635557a88..f7efcd9b150 100644 --- a/build/pkgs/memory_allocator/checksums.ini +++ b/build/pkgs/memory_allocator/checksums.ini @@ -2,4 +2,4 @@ tarball=memory_allocator-VERSION.tar.gz sha1=0e9256d307957e84eaba408f190cafc04d5c246a md5=1ce1fb6dc5972017a26ee82c4371b180 cksum=2493862442 -upstream_url=https://files.pythonhosted.org/packages/26/38/ca4e812a90dd396808fd6bf5c234ae813635f7c08d6297efbfae57333450/memory_allocator-VERSION.tar.gz +upstream_url=https://pypi.io/packages/source/m/memory_allocator/memory_allocator-VERSION.tar.gz From 16c6a7219848d2045fe92b1ca50eb0195a05d91f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 3 Jun 2021 11:03:22 -0700 Subject: [PATCH 484/706] ManifoldSubsetClosure, ManifoldSubset.closure: Improve documentation --- src/sage/manifolds/subset.py | 15 +++++++++++++++ src/sage/manifolds/subsets/closure.py | 12 ++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 3872def36d5..dfac550f302 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2742,6 +2742,21 @@ def closure(self, name=None, latex_name=None): r""" Return the topological closure of ``self`` as a subset of the manifold. + INPUT: + + - ``name`` -- (default: ``None``) name given to the difference in the + case the latter has to be created; the default prepends ``cl_`` + to ``self._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + difference in the case the latter has to be created; the default + is built upon the operator `\mathrm{cl}` + + OUTPUT: + + - if ``self`` is already known to be closed (see :meth:`is_closed`), + ``self``; otherwise, an instance of + :class:`~sage.manifolds.subsets.closure.ManifoldSubsetClosure` + EXAMPLES:: sage: M = Manifold(2, 'R^2', structure='topological') diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index aa536bfafc9..ec60272cb8e 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -33,12 +33,20 @@ class ManifoldSubsetClosure(ManifoldSubset): EXAMPLES:: - sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure sage: M = Manifold(2, 'R^2', structure='topological') sage: c_cart. = M.chart() # Cartesian coordinates on R^2 sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D Open subset D of the 2-dimensional topological manifold R^2 - sage: cl_D = ManifoldSubsetClosure(D) + sage: cl_D = D.closure() + sage: cl_D + Topological closure cl_D of the Open subset D of the 2-dimensional + topological manifold R^2 + sage: latex(cl_D) + \mathop{\mathrm{cl}}(D) + sage: type(cl_D) + + sage: cl_D.category() + Category of subobjects of sets The closure of the subset `D` is a subset of every closed superset of `D`:: From 908d7f70e95267607a3d26004528e7278e6d65a7 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 3 Jun 2021 15:17:18 -0500 Subject: [PATCH 485/706] Fix doctest on line 811 --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index c5ecba6d070..9de38a3ad83 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -817,11 +817,11 @@ def intersection_poset(self, element_class="int"): p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [1 0], Affine space p + W where: + [0 1], Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [0 1], Affine space p + W where: + [1 0], Affine space p + W where: p = (0, 0) W = Vector space of dimension 2 over Rational Field] """ From d204defba7a3519f487c0ba599fb28907005f4d1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 4 Jun 2021 17:14:58 +1000 Subject: [PATCH 486/706] Build the digraph as a dict during the construction of the intersection poset. --- .../hyperplane_arrangement/arrangement.py | 97 +++++++++++-------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 9de38a3ad83..28cb717328e 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -741,14 +741,18 @@ def cone(self, variable='t'): return H(*hyperplanes, backend=self._backend) @cached_method - def intersection_poset(self, element_class="int"): + def intersection_poset(self, element_label="int"): r""" Return the intersection poset of the hyperplane arrangement. INPUT: - - ``element_class`` -- either "subspace", "subset", "int" (default) - specifying how an intersection should be represented. + - ``element_label`` -- (default: ``"int"``) specify how an + intersection should be represented; must be one of the following: + + * ``"subspace"`` - as a subspace + * ``"subset"`` - as a subset of the defining hyperplanes + * ``"int"`` - as an integer OUTPUT: @@ -761,7 +765,7 @@ def intersection_poset(self, element_class="int"): By default, the elements of the poset are the integers from `0` through the cardinality of the poset *minus one*. The element labelled `0` always corresponds to the ambient vector space, and the hyperplanes - themselves are labelled `1`, `2`, `\dots`, `n`, where `n` is the number + themselves are labelled `1, 2, \ldots, n`, where `n` is the number of hyperplanes of the arrangement. :: sage: A = hyperplane_arrangements.coordinate(2) @@ -782,13 +786,13 @@ def intersection_poset(self, element_class="int"): sage: [sorted(level_set) for level_set in L.level_sets()] [[0], [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]] - By passing the argument ``element_class="subset"``, each element of the + By passing the argument ``element_label="subset"``, each element of the intesection poset is labelled by the set of indices of the hyperplanes whose intersection is said element. The index of a hyperplane is its index in ``self.hyperplanes()``. :: sage: A = hyperplane_arrangements.semiorder(3) - sage: L = A.intersection_poset(element_class='subset') + sage: L = A.intersection_poset(element_label='subset') sage: [sorted(level, key=sorted) for level in L.level_sets()] [[{}], [{0}, {1}, {2}, {3}, {4}, {5}], @@ -798,7 +802,7 @@ def intersection_poset(self, element_class="int"): sage: H. = HyperplaneArrangements(QQ) sage: A = H((y , (y-1) , (y+1) , (x - y) , (x + y))) - sage: L = A.intersection_poset(element_class='subset') + sage: L = A.intersection_poset(element_label='subset') sage: sorted(L, key=sorted) [{}, {0}, {0, 3}, {0, 4}, {1}, {1, 3, 4}, {2}, {2, 3}, {2, 4}, {3}, {4}] @@ -806,7 +810,7 @@ def intersection_poset(self, element_class="int"): which is what is used to compute the poset in the first place:: sage: A = hyperplane_arrangements.coordinate(2) - sage: L = A.intersection_poset(element_class='subspace'); L + sage: L = A.intersection_poset(element_label='subspace'); L Finite poset containing 4 elements sage: sorted(L) [Affine space p + W where: @@ -817,56 +821,65 @@ def intersection_poset(self, element_class="int"): p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [0 1], Affine space p + W where: + [1 0], Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [1 0], Affine space p + W where: + [0 1], Affine space p + W where: p = (0, 0) W = Vector space of dimension 2 over Rational Field] """ + if element_label == "int": + def update(mapping, val, I): + mapping[val] = len(mapping) + elif element_label == "subset": + from sage.sets.set import Set + def update(mapping, val, I): + mapping[val] = Set(val) + elif element_label == "subspace": + def update(mapping, val, I): + mapping[val] = I + else: + raise ValueError("invalid element label type") + K = self.base_ring() from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace whole_space = AffineSubspace(0, VectorSpace(K, self.dimension())) - L = [[whole_space]] - hyperplanes = [H._affine_subspace() for H in self.hyperplanes()] - L.append(hyperplanes) - - active = True - codim = 1 - while active: - active = False - new_level = [] - for T in L[codim]: - for H in hyperplanes: - I = H.intersection(T) - if I is not None and I != T and I not in new_level: - new_level.append(I) - active = True - if active: - L.append(new_level) - codim += 1 - L = [U for level in L for U in level] + mapping = {} + update(mapping, frozenset(), whole_space) + for i, H in enumerate(hyperplanes): + update(mapping, frozenset([i]), H) - if element_class == "int": - elements = dict(enumerate(L)) - cmp_fn = lambda p, q: elements[q] < elements[p] + hasse = {frozenset(): [frozenset([i]) for i in range(len(hyperplanes))]} + cur_level = [(frozenset([i]), H) for i, H in enumerate(hyperplanes)] - elif element_class == "subset": - from sage.sets.set import Set - hyperplanes_dict = dict(enumerate(hyperplanes)) - elements = [Set([i for i in hyperplanes_dict if U <= hyperplanes_dict[i]]) for U in L] - cmp_fn = lambda U, V: U.issubset(V) - - else: # element_class == "subspace" - elements = L - cmp_fn = lambda U, V: U < V + while cur_level: + new_level = {} + for label, T in cur_level: + edges = [] + for i, H in enumerate(hyperplanes): + I = H.intersection(T) + if I is not None and I != T: + try: + target = new_level[I] + except KeyError: + target = set(label) + new_level[I] = target + target.add(i) + edges.append(target) + hasse[label] = edges + for label, T in cur_level: + # Freeze them in place now + hasse[label] = [frozenset(X) for X in hasse[label]] + cur_level = [(frozenset(X), T) for T, X in new_level.items()] + for label, T in cur_level: + update(mapping, label, T) from sage.combinat.posets.posets import Poset - return Poset((elements, cmp_fn)) + return Poset({mapping[i]: [mapping[j] for j in val] for i, val in hasse.items()}) def _slow_characteristic_polynomial(self): """ From 8455aab6a2b5193539aea0a0c783aa02c43e6bfd Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Fri, 4 Jun 2021 10:56:24 +0200 Subject: [PATCH 487/706] Fix bug #31904 in pullback --- src/sage/manifolds/differentiable/diff_map.py | 174 ++++++++++-------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 8caab15c093..bc772ca1f13 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -13,6 +13,7 @@ AUTHORS: - Eric Gourgoulhon, Michal Bejger (2013-2015): initial version +- Marco Mancini (2018): pullback parallelization REFERENCES: @@ -22,8 +23,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015-2021 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger +# Copyright (C) 2018 Marco Mancini # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -917,13 +919,27 @@ def pullback(self, tensor): sage: pa.display() # should be zero (as any 3-form on a 2-dimensional manifold) Phi^*(A) = 0 + TESTS: + + Check that :trac:`31904` is fixed:: + + sage: E. = EuclideanSpace() + sage: polar. = E.polar_coordinates() + sage: g = E.metric() + sage: M = Manifold(1, 'M') + sage: Ct. = M.chart() + sage: F = M.diff_map(E, coord_functions={(Ct, polar): (1 + cos(t), t)}) + sage: gM = F.pullback(g) + sage: gM.display() + (2*cos(t) + 2) dt*dt + """ from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal from sage.manifolds.differentiable.vectorframe import CoordFrame from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) - def _pullback_chart(diff_map, tensor): + def _pullback_chart(diff_map, tensor, chart1, chart2): r""" Helper function performing the pullback on chart domains only. @@ -931,9 +947,13 @@ def _pullback_chart(diff_map, tensor): INPUT: - ``diff_map`` -- a restriction of ``self``, whose both - domain and codomain are chart domains + domain and codomain are chart domains, corresponding + respectively to ``chart1`` and ``chart2`` - ``tensor`` -- a covariant tensor field, whose domain is - the codomain of ``diff_map`` + the codomain of ``diff_map`` and whose components are known + in ``chart2.frame()`` + - ``chart1`` -- chart on the domain of ``diff_map`` + - ``chart2`` -- chart on the codomain of ``diff_map`` OUTPUT: @@ -963,82 +983,72 @@ def _pullback_chart(diff_map, tensor): nproc = Parallelism().get('tensor') ind_old_list = list(dom2.manifold().index_generator(ncov)) - for frame2 in tensor._components: - if isinstance(frame2, CoordFrame): - chart2 = frame2._chart - for chart1 in dom1._atlas: - if (chart1._domain is dom1 and (chart1, chart2) in - diff_map._coord_expression): - # Computation at the component level: - frame1 = chart1._frame - tcomp = tensor._components[frame2] - if isinstance(tcomp, CompFullySym): - ptcomp = CompFullySym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - elif isinstance(tcomp, CompFullyAntiSym): - ptcomp = CompFullyAntiSym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - elif isinstance(tcomp, CompWithSym): - ptcomp = CompWithSym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1, - sym=tcomp.sym, - antisym=tcomp.antisym) - else: - ptcomp = Components(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - phi = diff_map._coord_expression[(chart1, chart2)] - jacob = phi.jacobian() - # X2 coordinates expressed in terms of - # X1 ones via the diff. map: - coord2_1 = phi(*(chart1._xx)) - - if nproc != 1: - # Parallel computation - lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] - ind_list = [ind for ind in ptcomp.non_redundant_index_generator()] - ind_step = max(1, int(len(ind_list)/nproc/2)) - local_list = lol(ind_list, ind_step) - # list of input parameters - listParalInput = [(tcomp,chart1,chart2,coord2_1,jacob, - ind_old_list,si1,si2,ncov,ind_part) for ind_part in local_list] - - @parallel(p_iter='multiprocessing', ncpus=nproc) - def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, - ind_old_list,si1,si2,ncov,local_list_ind): - partial = [] - for ind_new in local_list_ind: - res = 0 - for ind_old in ind_old_list: - ff = tcomp[[ind_old]].coord_function(chart2) - t = chart1.function(ff(*coord2_1)) - for i in range(ncov): - t *= jacob[ind_old[i]-si2, ind_new[i]-si1] - res += t - partial.append([ind_new, res]) - return partial - - for ii, val in paral_comp(listParalInput): - for jj in val: - ptcomp[[jj[0]]] = jj[1] - - else: - # Sequential computation - for ind_new in ptcomp.non_redundant_index_generator(): - res = 0 - for ind_old in ind_old_list: - ff = tcomp[[ind_old]].coord_function(chart2) - t = chart1.function(ff(*coord2_1)) - for i in range(ncov): - t *= jacob[ind_old[i]-si2, ind_new[i]-si1] - res += t - ptcomp[ind_new] = res - - resu._components[frame1] = ptcomp - return resu + frame1 = chart1.frame() + frame2 = chart2.frame() + + tcomp = tensor._components[frame2] + if isinstance(tcomp, CompFullySym): + ptcomp = CompFullySym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + elif isinstance(tcomp, CompFullyAntiSym): + ptcomp = CompFullyAntiSym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + elif isinstance(tcomp, CompWithSym): + ptcomp = CompWithSym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1, sym=tcomp.sym, + antisym=tcomp.antisym) + else: + ptcomp = Components(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + phi = diff_map._coord_expression[(chart1, chart2)] + jacob = phi.jacobian() + # X2 coordinates expressed in terms of X1 ones via the diff. map: + coord2_1 = phi(*(chart1._xx)) + + if nproc != 1: + # Parallel computation + lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] + ind_list = [ind for ind in ptcomp.non_redundant_index_generator()] + ind_step = max(1, int(len(ind_list)/nproc/2)) + local_list = lol(ind_list, ind_step) + # list of input parameters + listParalInput = [(tcomp, chart1, chart2, coord2_1, jacob, + ind_old_list, si1, si2, ncov, ind_part) + for ind_part in local_list] + + @parallel(p_iter='multiprocessing', ncpus=nproc) + def paral_comp(tcomp, chart1, chart2, coord2_1, jacob, + ind_old_list, si1, si2, ncov, local_list_ind): + partial = [] + for ind_new in local_list_ind: + res = 0 + for ind_old in ind_old_list: + ff = tcomp[[ind_old]].coord_function(chart2) + t = chart1.function(ff(*coord2_1)) + for i in range(ncov): + t *= jacob[ind_old[i]-si2, ind_new[i]-si1] + res += t + partial.append([ind_new, res]) + return partial + + for ii, val in paral_comp(listParalInput): + for jj in val: + ptcomp[[jj[0]]] = jj[1] + + else: + # Sequential computation + for ind_new in ptcomp.non_redundant_index_generator(): + res = 0 + for ind_old in ind_old_list: + ff = tcomp[[ind_old]].coord_function(chart2) + t = chart1.function(ff(*coord2_1)) + for i in range(ncov): + t *= jacob[ind_old[i]-si2, ind_new[i]-si1] + res += t + ptcomp[ind_new] = res + + resu._components[frame1] = ptcomp + return resu # End of function _pullback_chart # Special case of the identity map: @@ -1091,7 +1101,9 @@ def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, if ch2dom.is_subset(tdom): self_r = self.restrict(chart1._domain, subcodomain=ch2dom) tensor_r = tensor.restrict(ch2dom) - resu_rst.append(_pullback_chart(self_r, tensor_r)) + if chart2.frame() in tensor_r._components: + resu_rst.append(_pullback_chart(self_r, tensor_r, + chart1, chart2)) dom_resu = resu_rst[0]._domain for rst in resu_rst[1:]: dom_resu = dom_resu.union(rst._domain) From c1c315cf300a55f18b18008502b8deb9018b8411 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 4 Jun 2021 15:55:00 +0200 Subject: [PATCH 488/706] make other part of matrix ready for random seeds --- src/sage/matrix/matrix1.pyx | 7 +- src/sage/matrix/matrix2.pyx | 46 +-- src/sage/matrix/matrix_generic_dense.pyx | 4 +- src/sage/matrix/matrix_gf2e_dense.pyx | 7 +- src/sage/matrix/matrix_mod2_dense.pyx | 186 ++++----- .../matrix/matrix_modn_dense_template.pxi | 19 +- src/sage/matrix/matrix_modn_sparse.pyx | 27 +- src/sage/matrix/matrix_mpolynomial_dense.pyx | 6 +- src/sage/matrix/matrix_rational_dense.pyx | 115 +++++- src/sage/matrix/matrix_space.py | 41 +- src/sage/matrix/matrix_sparse.pyx | 10 +- src/sage/matrix/special.py | 389 ++++++++---------- 12 files changed, 450 insertions(+), 407 deletions(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index f3007973a9d..6444dde05d9 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -534,9 +534,10 @@ cdef class Matrix(Matrix0): sage: sage_input(matrix(QQ, 3, 3, [5..13])/7, verify=True) # Verified matrix(QQ, [[5/7, 6/7, 1], [8/7, 9/7, 10/7], [11/7, 12/7, 13/7]]) - sage: sage_input(MatrixSpace(GF(5), 50, 50, sparse=True).random_element(density=0.002), verify=True) - # Verified - matrix(GF(5), 50, 50, {(4,44):2, (5,25):1, (26,9):3, (43,24):3, (44,38):4}) + sage: M = MatrixSpace(GF(5), 50, 50, sparse=True).random_element(density=0.002) + sage: input = sage_input(M, verify=True) + sage: sage_eval(input) == M + True sage: from sage.misc.sage_input import SageInputBuilder sage: matrix(RDF, [[3, 1], [4, 1]])._sage_input_(SageInputBuilder(), False) {call: {atomic:matrix}({atomic:RDF}, {list: ({list: ({atomic:3}, {atomic:1})}, {list: ({atomic:4}, {atomic:1})})})} diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 1925fd80a0e..b5f6439af0e 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -2386,14 +2386,14 @@ cdef class Matrix(Matrix1): r""" Computes the Pfaffian of ``self`` using the Baer-Faddeev-LeVerrier algorithm. - + .. WARNING:: - + This method assumes that the base ring is an `\QQ`-algebra. OUTPUT: - - an element (possibly coerced) originated from the base ring of + - an element (possibly coerced) originated from the base ring of ``self`` representing the Pfaffian EXAMPLES: @@ -2409,7 +2409,7 @@ cdef class Matrix(Matrix1): ....: (1/2, -3/2, -1, -5/2, -1/2, 0)]) sage: A.pfaffian(algorithm='bfl') -1/2 - + TESTS:: sage: A = random_matrix(ZZ[x], 6) @@ -3224,10 +3224,8 @@ cdef class Matrix(Matrix1): sage: M = random_matrix(ZZ, 10, 20) sage: N = random_matrix(ZZ, 20, 10) - sage: M.trace_of_product(N) - -1629 - sage: (M*N).trace() - -1629 + sage: M.trace_of_product(N) == (M*N).trace() + True """ if self._nrows != other._ncols or other._nrows != self._ncols: raise ArithmeticError("incompatible dimensions") @@ -7164,10 +7162,9 @@ cdef class Matrix(Matrix1): [ 0 1 2] [ 0 0 0] - sage: B=random_matrix(QQ,3,num_bound=10); B - [ -4 -3 6] - [ 5 -5 9/2] - [3/2 -4 -7] + sage: B = random_matrix(QQ, 3, num_bound=10) + sage: while B.rank() != 3: + ....: B = random_matrix(QQ, 3, num_bound=10) sage: B.rref() [1 0 0] [0 1 0] @@ -9055,18 +9052,14 @@ cdef class Matrix(Matrix1): We then randomize roughly half the entries:: sage: a.randomize(0.5) - sage: a - [ 0 0 0] - [ 0 0 1/2*x^2 - x - 12] - [1/2*x^2 - 1/95*x - 1/2 0 0] + sage: a.density() <= 0.5 + True Now we randomize all the entries of the resulting matrix:: - sage: a.randomize() - sage: a - [ 0 -5/2*x^2 + 2/3*x - 1/4 -x^2 + 2/3*x] - [ 1 x^2 + 1/3*x - 1 -1] - [ -1 -x^2 - 1/4*x + 1 -1/14] + sage: while a.density() < 0.9: + ....: a = matrix(QQ['x'], 3) + ....: a.randomize() We create the zero matrix over the integers:: @@ -9081,9 +9074,10 @@ cdef class Matrix(Matrix1): :: sage: a.randomize(x=-2^64, y=2^64) - sage: a - [-3789934696463997112 -3775200185786627805] - [-8014573080042851913 7914454492632997238] + sage: while all(abs(b) < 2^63 for b in a.list()): + ....: a.randomize(x=-2^64, y=2^64) + sage: all(abs(b) < 2^64 for b in a.list()) + True """ randint = current_randstate().python_random().randint @@ -9609,8 +9603,8 @@ cdef class Matrix(Matrix1): :: sage: A = random_matrix(GF(127),200,200,density=0.3) - sage: A.density() - 5211/20000 + sage: A.density() <= 0.3 + True :: diff --git a/src/sage/matrix/matrix_generic_dense.pyx b/src/sage/matrix/matrix_generic_dense.pyx index 2bf35d1a79f..3f01179dd14 100644 --- a/src/sage/matrix/matrix_generic_dense.pyx +++ b/src/sage/matrix/matrix_generic_dense.pyx @@ -35,9 +35,9 @@ cdef class Matrix_generic_dense(matrix_dense.Matrix_dense): sage: A = random_matrix(Integers(25)['x'], 2) sage: A == A True - sage: A < A + 1 + sage: A < A + 1 or A[0, 0].coefficients()[0] == 24 True - sage: A+1 < A + sage: A+1 < A and A[0, 0].coefficients()[0] != 24 False Test hashing:: diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index 1d6bbbc22b2..9efc6065b89 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -631,6 +631,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^4) sage: A = random_matrix(K,3,4) + sage: A[0,0] = 0 sage: B = copy(A) sage: A == B True @@ -943,7 +944,9 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: K. = GF(2^8) sage: A = random_matrix(K, 15, 15) - sage: A.pivots() # indirect doctest + sage: while A.rank() != 15: + ....: A = random_matrix(K, 15, 15) + sage: A.pivots() # indirect doctest (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) """ if not self.fetch('in_echelon_form'): @@ -1145,7 +1148,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: C = B.matrix_from_columns([3,4,5]) sage: A.rank() < 3 or C == ~A True - sage: C*A == MS(1) + sage: A.rank() < 3 or C*A == MS(1) True TESTS:: diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 540cd66e28f..8f1374eb39f 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -277,10 +277,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: B = random_matrix(GF(2),3,3) sage: B.set_immutable() - sage: {B:0} # indirect doctest - {[0 1 0] - [0 1 1] - [0 0 0]: 0} + sage: _ = {B:0} # indirect doctest sage: M = random_matrix(GF(2), 123, 321) sage: M.set_immutable() sage: MZ = M.change_ring(ZZ) @@ -396,8 +393,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse EXAMPLES:: - sage: B = random_matrix(GF(2),3,3) - sage: B # indirect doctest + sage: B = matrix(GF(2), 3, 3, [0, 1, 0, 0, 1, 1, 0, 0, 0]) + sage: B # indirect doctest [0 1 0] [0 1 1] [0 0 0] @@ -488,26 +485,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse EXAMPLES:: - sage: A = random_matrix(GF(2),10,10); A - [0 1 0 1 1 0 0 0 1 1] - [0 1 1 1 0 1 1 0 0 1] - [0 0 0 1 0 1 0 0 1 0] - [0 1 1 0 0 1 0 1 1 0] - [0 0 0 1 1 1 1 0 1 1] - [0 0 1 1 1 1 0 0 0 0] - [1 1 1 1 0 1 0 1 1 0] - [0 0 0 1 1 0 0 0 1 1] - [1 0 0 0 1 1 1 0 1 1] - [1 0 0 1 1 0 1 0 0 0] - - sage: A.row(0) - (0, 1, 0, 1, 1, 0, 0, 0, 1, 1) - - sage: A.row(-1) - (1, 0, 0, 1, 1, 0, 1, 0, 0, 0) + sage: l = [GF(2).random_element() for _ in range(100)] + sage: A = matrix(GF(2), 10, 10 , l) + sage: list(A.row(0)) == l[:10] + True + sage: list(A.row(-1)) == l[-10:] + True - sage: A.row(2,from_list=True) - (0, 0, 0, 1, 0, 1, 0, 0, 1, 0) + sage: list(A.row(2, from_list=True)) == l[20:30] + True sage: A = Matrix(GF(2),1,0) sage: A.row(0) @@ -1175,18 +1161,19 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse EXAMPLES:: sage: A = matrix(GF(2), 5, 5, 0) - sage: A.randomize(0.5); A - [0 0 0 1 1] - [0 1 0 0 1] - [1 0 0 0 0] - [0 1 0 0 0] - [0 0 0 1 0] - sage: A.randomize(); A - [0 0 1 1 0] - [1 1 0 0 1] - [1 1 1 1 0] - [1 1 1 1 1] - [0 0 1 1 0] + sage: A.randomize(0.5) + sage: A.density() < 0.5 + True + sage: expected = 0.5 + sage: A = matrix(GF(2), 5, 5, 0) + sage: A.randomize() + sage: density_sum = float(A.density()) + sage: total = 1 + sage: while abs(density_sum/total - expected) > 0.001: + ....: A = matrix(GF(2), 5, 5, 0) + ....: A.randomize() + ....: density_sum += float(A.density()) + ....: total += 1 TESTS: @@ -1196,8 +1183,10 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse problem is gone, with Mersenne Twister:: sage: MS2 = MatrixSpace(GF(2), 1000) - sage: [MS2.random_element().rank() for i in range(5)] - [999, 998, 1000, 999, 999] + sage: from collections import defaultdict + sage: found = defaultdict(bool) + sage: while not all(found[i] for i in range(997, 1001)): + ....: found[MS2.random_element().rank()] = True Testing corner case:: @@ -1259,15 +1248,10 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse """ EXAMPLES:: - sage: A = random_matrix(GF(2),3,3); A - [0 1 0] - [0 1 1] - [0 0 0] - - sage: A.rescale_row(0,0,0); A - [0 0 0] - [0 1 1] - [0 0 0] + sage: A = random_matrix(GF(2),3,3) + sage: A.rescale_row(0,0,0) + sage: A.row(0) + (0, 0, 0) """ if (int(multiple)%2) == 0: mzd_row_clear_offset(self._entries, row, start_col); @@ -1277,14 +1261,11 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse """ EXAMPLES:: - sage: A = random_matrix(GF(2),3,3); A - [0 1 0] - [0 1 1] - [0 0 0] - sage: A.add_multiple_of_row(0,1,1,0); A - [0 0 1] - [0 1 1] - [0 0 0] + sage: A = random_matrix(GF(2),3,3) + sage: B = copy(A) + sage: A.add_multiple_of_row(0,1,1,0) + sage: A.row(0) == B.row(0) + B.row(1) + True """ if (int(multiple)%2) != 0: mzd_row_add_offset(self._entries, row_to, row_from, start_col) @@ -1293,14 +1274,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse """ EXAMPLES:: - sage: A = random_matrix(GF(2),3,3); A - [0 1 0] - [0 1 1] - [0 0 0] - sage: A.swap_rows(0,1); A - [0 1 1] - [0 1 0] - [0 0 0] + sage: A = random_matrix(GF(2),3,3) + sage: B = copy(A) + sage: A.swap_rows(0,1) + sage: A.row(0) == B.row(1) + True + sage: A.row(1) == B.row(0) + True + sage: A.row(2) == B.row(2) + True """ mzd_row_swap(self._entries, row1, row2) @@ -1308,15 +1290,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse """ EXAMPLES:: - sage: A = random_matrix(GF(2),3,3); A - [0 1 0] - [0 1 1] - [0 0 0] - - sage: A.swap_columns(0,1); A - [1 0 0] - [1 0 1] - [0 0 0] + sage: A = random_matrix(GF(2),3,3) + sage: B = copy(A) + sage: A.swap_columns(0,1) + sage: A.column(0) == B.column(1) + True + sage: A.column(1) == B.column(0) + True + sage: A.column(2) == B.column(2) + True sage: A = random_matrix(GF(2),3,65) @@ -1519,13 +1501,11 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A = random_matrix(GF(2),2,3) sage: B = random_matrix(GF(2),2,0) - sage: A.augment(B) - [0 1 0] - [0 1 1] + sage: A.augment(B) == A + True - sage: B.augment(A) - [0 1 0] - [0 1 1] + sage: B.augment(A) == A + True sage: M = Matrix(GF(2), 0, 0, 0) sage: N = Matrix(GF(2), 0, 19, 0) @@ -1630,15 +1610,11 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A = random_matrix(GF(2),0,3) sage: B = random_matrix(GF(2),3,3) - sage: A.stack(B) - [0 1 0] - [0 1 1] - [0 0 0] + sage: A.stack(B) == B + True - sage: B.stack(A) - [0 1 0] - [0 1 1] - [0 0 0] + sage: B.stack(A) == B + True sage: M = Matrix(GF(2), 0, 0, 0) sage: N = Matrix(GF(2), 19, 0, 0) @@ -1823,18 +1799,19 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse EXAMPLES:: - sage: A = random_matrix(GF(2),1000,1000) - sage: d = A.density(); d - 62483/125000 - - sage: float(d) - 0.499864 - - sage: A.density(approx=True) - 0.499864000... + sage: A = random_matrix(GF(2), 1000, 1000) + sage: d = A.density() + sage: float(d) == A.density(approx=True) + True + sage: len(A.nonzero_positions())/1000^2 == d + True - sage: float(len(A.nonzero_positions())/1000^2) - 0.499864 + sage: total = 1.0 + sage: density_sum = A.density() + sage: while abs(density_sum/total - 0.5) > 0.001: + ....: A = random_matrix(GF(2), 1000, 1000) + ....: total += 1 + ....: density_sum += A.density() """ if approx: from sage.rings.real_mpfr import create_RealNumber @@ -1857,9 +1834,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse EXAMPLES:: - sage: A = random_matrix(GF(2), 1000, 1000) - sage: A.rank() - 999 + sage: while random_matrix(GF(2), 1000, 1000).rank() != 999: + ....: pass sage: A = matrix(GF(2),10, 0) sage: A.rank() @@ -2201,12 +2177,12 @@ def pluq(Matrix_mod2_dense A, algorithm="standard", int param=0): EXAMPLES:: sage: from sage.matrix.matrix_mod2_dense import pluq - sage: A = random_matrix(GF(2),4,4); A + sage: A = matrix(GF(2), 4, 4, [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0]) + sage: A [0 1 0 1] [0 1 1 1] [0 0 0 1] [0 1 1 0] - sage: LU, P, Q = pluq(A) sage: LU [1 0 1 0] @@ -2264,7 +2240,9 @@ def ple(Matrix_mod2_dense A, algorithm="standard", int param=0): EXAMPLES:: sage: from sage.matrix.matrix_mod2_dense import ple - sage: A = random_matrix(GF(2),4,4); A + + sage: A = matrix(GF(2), 4, 4, [0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0]) + sage: A [0 1 0 1] [0 1 1 1] [0 0 0 1] diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index f0ef0d7ac1c..534cb7f0fc8 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -2536,7 +2536,10 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sage: density_sum = 0.0 sage: total_count = 0.0 sage: add_sample(0.5) - sage: while abs(density_sum/total_count - 0.394) > 0.01: # TODO + sage: expected_density = 1.0 - (999/1000)^500 + sage: expected_density + 0.3936... + sage: while abs(density_sum/total_count - expected_density) > 0.001: ....: add_sample(0.5) The matrix is updated instead of overwritten:: @@ -2544,20 +2547,26 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sage: def add_sample(density): ....: global density_sum, total_count ....: total_count += 1.0 - ....: A += random_matrix(GF(5), 1000, 1000, density=density) - ....: A.randomize(density=density) + ....: A = random_matrix(GF(5), 1000, 1000, density=density) + ....: A.randomize(density=density, nonzero=True) ....: density_sum += A.density() sage: density_sum = 0.0 sage: total_count = 0.0 sage: add_sample(0.5) - sage: while abs(density_sum/total_count - (1 - 0.5^2)*4/5) > 0.001: + sage: expected_density = 1.0 - (999/1000)^1000 + sage: expected_density + 0.6323... + sage: while abs(density_sum/total_count - expected_density) > 0.001: ....: add_sample(0.5) sage: density_sum = 0.0 sage: total_count = 0.0 sage: add_sample(0.1) - sage: while abs(density_sum/total_count - (1 - 0.1^2)*4/5) > 0.001: + sage: expected_density = 1.0 - (999/1000)^200 + sage: expected_density + 0.1813... + sage: while abs(density_sum/total_count - expected_density) > 0.001: ....: add_sample(0.1) """ density = float(density) diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 8ea6db2190e..9762f857e7c 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -215,13 +215,19 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): EXAMPLES:: sage: MS = MatrixSpace(GF(13), 50, 50, sparse=True) - sage: m = MS.random_element(density=0.002) - sage: m._dict() - {(4, 44): 7, (5, 25): 4, (26, 9): 9, (43, 43): 6, (44, 38): 1} + sage: m = MS._random_nonzero_element(density=0.002) + sage: d = m._dict() + sage: for i in range(50): + ....: for j in range(50): + ....: if m[i, j] != 0: + ....: assert m[i, j] == d[i, j] + ....: else: + ....: assert (i, j) not in d TESTS:: - sage: parent(m._dict()[26,9]) + sage: [i, j] = list(d.keys())[0] + sage: parent(m._dict()[i, j]) Finite Field of size 13 """ d = self.fetch('dict') @@ -526,9 +532,16 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): :: - sage: A = random_matrix(GF(127),200,200,density=0.3, sparse=True) - sage: A.density() - 2073/8000 + sage: A = random_matrix(GF(127), 200, 200, density=0.3, sparse=True) + sage: density_sum = float(A.density()) + sage: total = 1 + sage: expected_density = 1.0 - (199/200)^60 + sage: expected_density + 0.2597... + sage: while abs(density_sum/total - expected_density) > 0.001: + ....: A = random_matrix(GF(127), 200, 200, density=0.3, sparse=True) + ....: density_sum += float(A.density()) + ....: total += 1 """ cdef Py_ssize_t i, nonzero_entries diff --git a/src/sage/matrix/matrix_mpolynomial_dense.pyx b/src/sage/matrix/matrix_mpolynomial_dense.pyx index 607a0a49333..d6645c04dec 100644 --- a/src/sage/matrix/matrix_mpolynomial_dense.pyx +++ b/src/sage/matrix/matrix_mpolynomial_dense.pyx @@ -447,10 +447,12 @@ cdef class Matrix_mpolynomial_dense(Matrix_generic_dense): sage: R. = QQ[] sage: C = random_matrix(R, 2, 2, terms=2) + sage: while C.rank() != 2: + ....: C = random_matrix(R, 2, 2, terms=2) sage: C.swapped_columns() sage: E = C.echelon_form('bareiss') - sage: E.swapped_columns() - (0, 1) + sage: sorted(E.swapped_columns()) + [0, 1] """ return self.fetch('swapped_columns') diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 549c5c46305..5dab0da8769 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -2243,20 +2243,109 @@ cdef class Matrix_rational_dense(Matrix_dense): - None, the matrix is modified in-space - EXAMPLES:: + EXAMPLES: - sage: a = matrix(QQ,2,4); a.randomize(); a - [ 0 -1 2 -2] - [ 1 -1 2 1] - sage: a = matrix(QQ,2,4); a.randomize(density=0.5); a - [ -1 -2 0 0] - [ 0 0 1/2 0] - sage: a = matrix(QQ,2,4); a.randomize(num_bound=100, den_bound=100); a - [ 14/27 21/25 43/42 -48/67] - [-19/55 64/67 -11/51 76] - sage: a = matrix(QQ,2,4); a.randomize(distribution='1/n'); a - [ 3 1/9 1/2 1/4] - [ 1 1/39 2 -1955/2] + The default distribution:: + + sage: from collections import defaultdict + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: def add_samples(distribution=None): + ....: global dic, total_count + ....: for _ in range(100): + ....: A = Matrix(QQ, 2, 4, 0) + ....: A.randomize(distribution=distribution) + ....: for a in A.list(): + ....: dic[a] += 1 + ....: total_count += 1.0 + + sage: expected = {-2: 1/9, -1: 3/18, -1/2: 1/18, 0: 3/9, + ....: 1/2: 1/18, 1: 3/18, 2: 1/9} + sage: add_samples() + sage: while not all(abs(dic[a]/total_count - expected[a]) < 0.001 for a in dic): + ....: add_samples() + + The distribution ``'1/n'``:: + + sage: def mpq_randomize_entry_recip_uniform(): + ....: r = 2*random() - 1 + ....: if r == 0: r = 1 + ....: num = int(4/(5*r)) + ....: r = random() + ....: if r == 0: r = 1 + ....: den = int(1/random()) + ....: return Integer(num)/Integer(den) + + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: dic2 = defaultdict(Integer) + sage: add_samples('1/n') + sage: for _ in range(8): + ....: dic2[mpq_randomize_entry_recip_uniform()] += 1 + sage: while not all(abs(dic[a] - dic2[a])/total_count < 0.005 for a in dic): + ....: add_samples('1/n') + ....: for _ in range(800): + ....: dic2[mpq_randomize_entry_recip_uniform()] += 1 + + The default can be used to obtain matrices of different rank:: + + sage: ranks = [False]*11 + sage: while not all(ranks): + ....: for dens in (0.05, 0.1, 0.2, 0.5): + ....: A = Matrix(QQ, 10, 10, 0) + ....: A.randomize(dens) + ....: ranks[A.rank()] = True + + The default density is ``6/9`:: + + sage: def add_sample(density, num_rows, num_cols): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: A = Matrix(QQ, num_rows, num_cols, 0) + ....: A.randomize(density) + ....: density_sum += float(A.density()) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: expected_density = 6/9 + sage: add_sample(1.0, 100, 100) + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(1.0, 100, 100) + + The modified density depends on the number of columns:: + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: expected_density = 6/9*0.5 + sage: add_sample(0.5, 100, 2) + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(0.5, 100, 2) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: expected_density = 6/9*(1.0 - (99/100)^50) + sage: expected_density + 0.263... + + sage: add_sample(0.5, 100, 100) + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(0.5, 100, 100) + + Modifying the bounds for numerator and denominator:: + + sage: num_dic = defaultdict(Integer) + sage: den_dic = defaultdict(Integer) + sage: while not (all(num_dic[i] for i in range(-200, 201)) + ....: and all(den_dic[i] for i in range(1, 101))): + ....: a = matrix(QQ, 2, 4) + ....: a.randomize(num_bound=200, den_bound=100) + ....: for q in a.list(): + ....: num_dic[q.numerator()] += 1 + ....: den_dic[q.denominator()] += 1 + sage: len(num_dic) + 401 + sage: len(den_dic) + 100 TESTS: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index f1b265162fa..223988344b3 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -2022,20 +2022,17 @@ def random_element(self, density=None, *args, **kwds): EXAMPLES:: - sage: Mat(ZZ,2,5).random_element() - [ -8 2 0 0 1] - [ -1 2 1 -95 -1] - sage: Mat(QQ,2,5).random_element(density=0.5) - [ 2 0 0 0 1] - [ 0 0 0 1/2 0] - sage: Mat(QQ,3,sparse=True).random_element() - [ -1 1/3 1] - [ 0 -1 0] - [ -1 1 -1/4] - sage: Mat(GF(9,'a'),3,sparse=True).random_element() - [ 1 2 1] - [ a + 2 2*a 2] - [ 2 2*a + 2 1] + sage: M = Mat(ZZ, 2, 5).random_element() + sage: TestSuite(M).run() + + sage: M = Mat(QQ, 2, 5).random_element(density=0.5) + sage: TestSuite(M).run() + + sage: M = Mat(QQ, 3, sparse=True).random_element() + sage: TestSuite(M).run() + + sage: M = Mat(GF(9,'a'), 3, sparse=True).random_element() + sage: TestSuite(M).run() """ Z = self.zero_matrix().__copy__() if density is None: @@ -2198,12 +2195,18 @@ def _random_nonzero_element(self, *args, **kwds): EXAMPLES:: sage: M = MatrixSpace(ZZ, 4) - sage: M._random_nonzero_element() - [ -8 2 0 0] - [ 1 -1 2 1] - [-95 -1 -2 -12] - [ 0 0 1 -1] + sage: A = M._random_nonzero_element() + sage: A.is_zero() + False + + sage: M = MatrixSpace(ZZ, 0) + sage: A = M._random_nonzero_element() + Traceback (most recent call last): + ... + ValueError: Full MatrixSpace of 0 by 0 dense matrices over Integer Ring only has zero elements """ + if 0 in self.dims(): + raise ValueError("{} only has zero elements".format(self)) rand_matrix = self.random_element(*args, **kwds) while rand_matrix.is_zero(): rand_matrix = self.random_element(*args, **kwds) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index e8cd45ad86c..ac795c9b71e 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -888,9 +888,13 @@ cdef class Matrix_sparse(matrix.Matrix): sage: A = random_matrix(ZZ, 100000, density=.00005, sparse=True) # long time (4s on sage.math, 2012) sage: B = A[50000:,:50000] # long time - sage: len(B.nonzero_positions()) # long time - 17550 # 32-bit - 125449 # 64-bit + sage: count = 0 + sage: for i, j in A.nonzero_positions(): # long time + ....: if i >= 50000 and j < 50000: + ....: assert B[i-50000, j] == A[i, j] + ....: count += 1 + sage: count == sum(1 for _ in B.nonzero_positions()) # long time + True We must pass in a list of indices:: diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 1f249b4da5f..dbac2df7a9e 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -282,68 +282,102 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation Random integer matrices. With no arguments, the majority of the entries are zero, -1, and 1, and rarely "large." :: - sage: random_matrix(ZZ, 5, 5) - [ -8 2 0 0 1] - [ -1 2 1 -95 -1] - [ -2 -12 0 0 1] - [ -1 1 -1 -2 -1] - [ 4 -4 -6 5 0] + sage: from collections import defaultdict + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: def add_samples(*args, **kwds): + ....: global dic, total_count + ....: for _ in range(100): + ....: A = random_matrix(*args, **kwds) + ....: for a in A.list(): + ....: dic[a] += 1 + ....: total_count += 1.0 + + sage: expected = lambda n : 2 / (5*abs(n)*(abs(n) + 1)) if n != 0 else 1/5 + sage: expected(0) + 1/5 + sage: expected(0) == expected(1) == expected(-1) + True + sage: expected(100) + 1/25250 + sage: add_samples(ZZ, 5, 5) + sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): + ....: add_samples(ZZ, 5, 5) The ``distribution`` keyword set to ``uniform`` will limit values between -2 and 2. :: - sage: random_matrix(ZZ, 5, 5, distribution='uniform') - [ 1 0 -2 1 1] - [ 1 0 0 0 2] - [-1 -2 0 2 -2] - [-1 -1 1 1 2] - [ 0 -2 -1 0 0] + sage: expected = lambda n : 1/5 if n in range(-2, 3) else 0 + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: add_samples(ZZ, 5, 5, distribution='uniform') + sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): + ....: add_samples(ZZ, 5, 5, distribution='uniform') The ``x`` and ``y`` keywords can be used to distribute entries uniformly. When both are used ``x`` is the minimum and ``y`` is one greater than the maximum. :: - sage: random_matrix(ZZ, 4, 8, x=70, y=100) - [81 82 70 81 78 71 79 94] - [80 98 89 87 91 94 94 77] - [86 89 85 92 95 94 72 89] - [78 80 89 82 94 72 90 92] + sage: expected = lambda n : 1/30 if n in range(70, 100) else 0 + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: add_samples(ZZ, 4, 8, x=70, y=100) + sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): + ....: add_samples(ZZ, 4, 8, x=70, y=100) - sage: random_matrix(ZZ, 3, 7, x=-5, y=5) - [-3 3 1 -5 3 1 2] - [ 3 3 0 3 -5 -2 1] - [ 0 -2 -2 2 -3 -4 -2] + sage: expected = lambda n : 1/10 if n in range(-5, 5) else 0 + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: add_samples(ZZ, 3, 7, x=-5, y=5) + sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): + ....: add_samples(ZZ, 3, 7, x=-5, y=5) If only ``x`` is given, then it is used as the upper bound of a range starting at 0. :: - sage: random_matrix(ZZ, 5, 5, x=25) - [20 16 8 3 8] - [ 8 2 2 14 5] - [18 18 10 20 11] - [19 16 17 15 7] - [ 0 24 3 17 24] + sage: expected = lambda n : 1/25 if n in range(25) else 0 + sage: total_count = 0 + sage: dic = defaultdict(Integer) + sage: add_samples(ZZ, 5, 5, x=25) + sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): + ....: add_samples(ZZ, 5, 5, x=25) To control the number of nonzero entries, use the ``density`` keyword at a value strictly below the default of 1.0. The ``density`` keyword - is used to compute the number of entries that will be nonzero, but the + is used to compute the number of entries per row that will be nonzero, but the same entry may be selected more than once. So the value provided will be an upper bound for the density of the created matrix. Note that for a square matrix it is only necessary to set a single dimension. :: - sage: random_matrix(ZZ, 5, x=-10, y=10, density=0.75) - [-6 1 0 0 0] - [ 9 0 0 4 1] - [-6 0 0 -8 0] - [ 0 4 0 6 0] - [ 1 -9 0 0 -8] - - sage: random_matrix(ZZ, 5, x=20, y=30, density=0.75) - [ 0 28 0 27 0] - [25 28 20 0 0] - [ 0 21 0 21 0] - [ 0 28 22 0 0] - [ 0 0 0 26 24] + sage: def add_sample(*args, **kwds): + ....: global density_sum, total_count + ....: total_count += 1.0 + ....: A = random_matrix(*args, **kwds) + ....: density_sum += float(A.density()) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(ZZ, 5, x=-10, y=10, density=0.75) + sage: expected_density = (1 - (4/5)^3) + sage: float(expected_density) + 0.488 + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(ZZ, 5, x=-10, y=10, density=0.75) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(ZZ, 5, x=20, y=30, density=0.75) + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(ZZ, 5, x=20, y=30, density=0.75) + + sage: density_sum = 0.0 + sage: total_count = 0.0 + sage: add_sample(ZZ, 100, x=20, y=30, density=0.75) + sage: expected_density = (1 - (99/100)^75) + sage: float(expected_density) + 0.529... + sage: while abs(density_sum/total_count - expected_density) > 0.001: + ....: add_sample(ZZ, 100, x=20, y=30, density=0.75) For a matrix with low density it may be advisable to insist on a sparse representation, as this representation is not selected automatically. :: @@ -355,13 +389,6 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation sage: A.is_sparse() True - sage: random_matrix(ZZ, 5, 5, density=0.3, sparse=True) - [ 4 0 0 0 -1] - [ 0 0 0 0 -7] - [ 0 0 2 0 0] - [ 0 0 1 0 -4] - [ 0 0 0 0 0] - For algorithm testing you might want to control the number of bits, say 10,000 entries, each limited to 16 bits. :: @@ -381,31 +408,45 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation numerators and denominators (respectively). Entries will be positive and negative (map the absolute value function through the entries to get all positive values). If either the numerator or denominator bound (or both) - is not used, then the values default to the distribution for ``ZZ`` - described above. :: - - sage: random_matrix(QQ, 2, 8, num_bound=20, den_bound=4) - [ -1/4 5 5 -9/2 5/3 19 15/2 19/2] - [ 20/3 -13/4 0 16 -5 -20 -11 -7/3] + is not used, then the values default to ``2``:: + + sage: A = random_matrix(QQ, 2, 8, num_bound=20, den_bound=4) + sage: A.dimensions() + (2, 8) + sage: type(A) + + sage: all(a.numerator() in range(-20, 21) and + ....: a.denominator() in range(1, 5) + ....: for a in A.list()) + True - sage: random_matrix(QQ, 4, density = 0.5, sparse=True) - [ 0 1 0 -1] - [ 0 0 0 0] - [ 6 0 3 0] - [ 1 1/3 0 0] + sage: A = random_matrix(QQ, 4, density = 0.5, sparse=True) + sage: type(A) + + sage: A.density() <= 0.5 + True sage: A = random_matrix(QQ, 3, 10, num_bound = 99, den_bound = 99) sage: positives = list(map(abs, A.list())) - sage: matrix(QQ, 3, 10, positives) - [ 2/45 40/21 45/46 17/22 1 70/79 97/71 7/24 12/5 13/8] - [ 8/25 1/3 61/14 92/45 4/85 3/38 95/16 82/71 1/5 41/16] - [55/76 19 28/41 52/51 14/3 43 76/13 8/77 13/38 37/21] + sage: A1 = matrix(QQ, 3, 10, positives) + sage: all(abs(A.list()[i]) == A1.list()[i] for i in range(30)) + True + sage: all(a.numerator() in range(100) and + ....: a.denominator() in range(1, 100) + ....: for a in A1.list()) + True + + sage: A = random_matrix(QQ, 4, 10, den_bound = 10) + sage: all(a.numerator() in range(-2, 3) and + ....: a.denominator() in range(1, 11) + ....: for a in A.list()) + True - sage: random_matrix(QQ, 4, 10, den_bound = 10) - [ 1/9 1/5 -1 2/9 1/4 -1/7 1/8 -1/9 0 2] - [ 2/3 2 1/8 -2 0 0 -2 2 0 -1/2] - [ 0 2 1 -2/3 0 0 1/6 0 -1/3 -2/9] - [ 0 0 2/5 1/9 0 0 1/6 1/10 0 1] + sage: A = random_matrix(QQ, 4, 10) + sage: all(a.numerator() in range(-2, 3) and + ....: a.denominator() in range(1, 3) + ....: for a in A.list()) + True Random matrices over other rings. Several classes of matrices have specialized ``randomize()`` methods. You can locate these with the Sage command:: @@ -419,32 +460,35 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation that we use the default implementation in this test:: sage: K.=FiniteField(3^2) - sage: random_matrix(K, 2, 5, implementation='generic') - [ a + 1 a + 1 0 2*a + 2 a + 1] - [ a + 2 a + 1 2 0 0] - - sage: random_matrix(RR, 3, 4, density=0.66) - [ 0.000000000000000 0.0869697644118808 -0.232952499486647 0.000000000000000] - [-0.793158962467820 0.000000000000000 0.318853016385637 0.000000000000000] - [-0.220342454156035 0.000000000000000 0.000000000000000 0.914890766754157] - - sage: A = random_matrix(ComplexField(32), 3, density=0.8, sparse=True); A - [ 0.000000000 -0.443499553 - 0.406854867*I 0.000000000] - [ 0.171578609 + 0.644048756*I 0.518523841 + 0.794429291*I -0.341030168 - 0.507791873*I] - [ 0.000000000 0.000000000 0.782759943 + 0.236288982*I] + sage: A = random_matrix(K, 2, 5, implementation='generic') + sage: type(A) + + sage: A.base_ring() is K + True + sage: TestSuite(A).run() + + sage: A = random_matrix(RR, 3, 4, density=0.66) + sage: type(A) + + sage: A.base_ring() is RR + True + sage: TestSuite(A).run() + + sage: A = random_matrix(ComplexField(32), 3, density=0.8, sparse=True) sage: A.is_sparse() True + sage: type(A) + + sage: A.base_ring() is ComplexField(32) + True + sage: TestSuite(A).run() Random matrices in echelon form. The ``algorithm='echelon_form'`` keyword, along with a requested number of non-zero rows (``num_pivots``) will return a random matrix in echelon form. When the base ring is ``QQ`` the result has integer entries. Other exact rings may be also specified. :: - sage: A = random_matrix(QQ, 4, 8, algorithm='echelon_form', num_pivots=3); A # random - [ 1 -5 0 -2 0 1 1 -2] - [ 0 0 1 -5 0 -3 -1 0] - [ 0 0 0 0 1 2 -2 1] - [ 0 0 0 0 0 0 0 0] + sage: A = random_matrix(QQ, 4, 8, algorithm='echelon_form', num_pivots=3) sage: A.base_ring() Rational Field sage: (A.nrows(), A.ncols()) @@ -472,7 +516,7 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation of full rank generated by this function always have determinant one, and can be constructed with the ``unimodular`` keyword. :: - sage: A = random_matrix(QQ, 4, 8, algorithm='echelonizable', rank=3, upper_bound=60); A # random + sage: A = random_matrix(QQ, 4, 8, algorithm='echelonizable', rank=3, upper_bound=60) sage: A.base_ring() Rational Field sage: (A.nrows(), A.ncols()) @@ -2546,12 +2590,7 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): sage: from sage.matrix.constructor import random_echelonizable_matrix sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 5, 6) - sage: A = random_echelonizable_matrix(matrix_space, rank=4, upper_bound=40); A - [ 3 4 12 39 18 22] - [ -1 -3 -9 -27 -16 -19] - [ 1 3 10 31 18 21] - [ -1 0 0 -2 2 2] - [ 0 1 2 8 4 5] + sage: A = random_echelonizable_matrix(matrix_space, rank=4, upper_bound=40) sage: A.rank() 4 sage: max(map(abs,A.list()))<40 @@ -2561,13 +2600,7 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): An example with default settings (i.e. no entry size control). :: - sage: C=random_matrix(QQ, 6, 7, algorithm='echelonizable', rank=5); C - [ 1 -5 -8 16 6 65 30] - [ 3 -14 -22 42 17 178 84] - [ -5 24 39 -79 -31 -320 -148] - [ 4 -15 -26 55 27 224 106] - [ -1 0 -6 29 8 65 17] - [ 3 -20 -32 72 14 250 107] + sage: C=random_matrix(QQ, 6, 7, algorithm='echelonizable', rank=5) sage: C.rank() 5 sage: C.rref() == C.rref().change_ring(ZZ) @@ -2575,7 +2608,7 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): A matrix without size control may have very large entry sizes. :: - sage: D=random_matrix(ZZ, 7, 8, algorithm='echelonizable', rank=6); D + sage: D=random_matrix(ZZ, 7, 8, algorithm='echelonizable', rank=6); D # random [ 1 2 8 -35 -178 -239 -284 778] [ 4 9 37 -163 -827 -1111 -1324 3624] [ 5 6 21 -88 -454 -607 -708 1951] @@ -2587,24 +2620,18 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100): Matrices can be generated over any exact ring. :: sage: F.=GF(2^3) - sage: B = random_matrix(F, 4, 5, algorithm='echelonizable', rank=4, upper_bound=None); B - [ 1 a + 1 0 a^2 + a + 1 1] - [ a a^2 + a + 1 a^2 + 1 a^2 + a 0] - [ a^2 + a 1 1 a^2 + a a + 1] - [a^2 + a + 1 a^2 + a + 1 a^2 0 a^2 + a] + sage: B = random_matrix(F, 4, 5, algorithm='echelonizable', rank=4, upper_bound=None) sage: B.rank() 4 + sage: B.base_ring() is F + True Square matrices over ZZ or QQ with full rank are always unimodular. :: - sage: E=random_matrix(QQ, 7, 7, algorithm='echelonizable', rank=7); E - [ 1 1 7 -29 139 206 413] - [ -2 -1 -10 41 -197 -292 -584] - [ 2 5 27 -113 541 803 1618] - [ 4 0 14 -55 268 399 798] - [ 3 1 8 -32 152 218 412] - [ -3 -2 -18 70 -343 -506 -1001] - [ 1 -2 -1 1 -2 9 52] + sage: E = random_matrix(QQ, 7, 7, algorithm='echelonizable', rank=7) + sage: det(E) + 1 + sage: E = random_matrix(ZZ, 7, 7, algorithm='echelonizable', rank=7) sage: det(E) 1 @@ -2765,13 +2792,7 @@ def random_subspaces_matrix(parent, rank=None): sage: from sage.matrix.constructor import random_subspaces_matrix sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 6, 8) - sage: B = random_subspaces_matrix(matrix_space, rank=3); B - [ -15 -4 83 35 -24 47 -74 50] - [ -16 -7 94 34 -25 38 -75 50] - [ 89 34 -513 -196 141 -235 426 -285] - [ 17 6 -97 -38 27 -47 82 -55] - [ 7 3 -41 -15 11 -17 33 -22] - [ -5 -2 29 11 -8 13 -24 16] + sage: B = random_subspaces_matrix(matrix_space, rank=3) sage: B.rank() 3 sage: B.nullity() @@ -2783,13 +2804,6 @@ def random_subspaces_matrix(parent, rank=None): sage: B_expanded = B.augment(identity_matrix(6)).rref() sage: all(x in ZZ for x in B_expanded.list()) True - sage: B_expanded - [ 1 0 -5 0 -1 1 0 -1 0 0 0 3 10 24] - [ 0 1 -2 0 1 2 1 0 0 0 0 -2 -3 -11] - [ 0 0 0 1 -1 2 -2 1 0 0 0 1 4 9] - [ 0 0 0 0 0 0 0 0 1 0 0 2 -2 1] - [ 0 0 0 0 0 0 0 0 0 1 0 0 3 1] - [ 0 0 0 0 0 0 0 0 0 0 1 -3 -4 2] Check that we fixed :trac:`10543` (echelon forms should be immutable):: @@ -2799,24 +2813,9 @@ def random_subspaces_matrix(parent, rank=None): We want to modify B_expanded, so replace it with a copy:: sage: B_expanded = copy(B_expanded) - sage: B_expanded.subdivide(B.nrows()-B.nullity(),B.ncols());B_expanded - [ 1 0 -5 0 -1 1 0 -1| 0 0 0 3 10 24] - [ 0 1 -2 0 1 2 1 0| 0 0 0 -2 -3 -11] - [ 0 0 0 1 -1 2 -2 1| 0 0 0 1 4 9] - [-------------------------------+-----------------------] - [ 0 0 0 0 0 0 0 0| 1 0 0 2 -2 1] - [ 0 0 0 0 0 0 0 0| 0 1 0 0 3 1] - [ 0 0 0 0 0 0 0 0| 0 0 1 -3 -4 2] - sage: C=B_expanded.subdivision(0,0) - sage: C - [ 1 0 -5 0 -1 1 0 -1] - [ 0 1 -2 0 1 2 1 0] - [ 0 0 0 1 -1 2 -2 1] - sage: L=B_expanded.subdivision(1,1) - sage: L - [ 1 0 0 2 -2 1] - [ 0 1 0 0 3 1] - [ 0 0 1 -3 -4 2] + sage: B_expanded.subdivide(B.nrows()-B.nullity(), B.ncols()) + sage: C = B_expanded.subdivision(0, 0) + sage: L = B_expanded.subdivision(1, 1) sage: B.right_kernel() == C.right_kernel() True sage: B.row_space() == C.row_space() @@ -2828,29 +2827,16 @@ def random_subspaces_matrix(parent, rank=None): A matrix to show that the null space of the L matrix is the column space of the starting matrix. :: - sage: A = random_matrix(QQ, 5, 7, algorithm='subspaces', rank=None); A - [ -63 13 -71 29 -163 150 -268] - [ 24 -5 27 -11 62 -57 102] - [ 14 -3 16 -7 37 -34 60] - [ -4 1 -4 1 -9 8 -16] - [ 9 -2 10 -4 23 -21 38] + sage: A = random_matrix(QQ, 5, 7, algorithm='subspaces', rank=None) sage: (A.nrows(), A.ncols()) (5, 7) sage: all(x in ZZ for x in A.list()) True - sage: A.nullity() - 2 sage: A_expanded=A.augment(identity_matrix(5)).rref() - sage: A_expanded - [ 1 0 0 2 -1 1 2 0 2 0 -4 -7] - [ 0 1 0 1 -1 0 0 0 4 0 -3 -12] - [ 0 0 1 -2 3 -3 2 0 -1 0 3 4] - [ 0 0 0 0 0 0 0 1 3 0 0 -1] - [ 0 0 0 0 0 0 0 0 0 1 -1 -2] sage: all(x in ZZ for x in A_expanded.list()) True - sage: C=A_expanded.submatrix(0,0,A.nrows()-A.nullity(),A.ncols()) - sage: L=A_expanded.submatrix(A.nrows()-A.nullity(),A.ncols()) + sage: C = A_expanded.submatrix(0,0,A.nrows()-A.nullity(), A.ncols()) + sage: L = A_expanded.submatrix(A.nrows()-A.nullity(), A.ncols()) sage: A.right_kernel() == C.right_kernel() True sage: A.row_space() == C.row_space() @@ -2963,38 +2949,27 @@ def random_unimodular_matrix(parent, upper_bound=None, max_tries=100): sage: from sage.matrix.constructor import random_unimodular_matrix sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 5) - sage: A = random_unimodular_matrix(matrix_space); A - [ 0 3 8 -30 -30] - [ 0 1 4 -18 -13] - [ -1 0 0 3 0] - [ 4 16 71 -334 -222] - [ -1 -1 -9 50 24] + sage: A = random_unimodular_matrix(matrix_space) sage: det(A) 1 A matrix size 6 with entries no larger than 50. :: - sage: B = random_matrix(ZZ, 7, algorithm='unimodular', upper_bound=50);B - [-14 17 14 -31 43 24 46] - [ -5 6 5 -11 15 9 18] - [ -2 5 3 -7 15 -3 -16] - [ 1 -2 -3 4 -3 -7 -21] - [ -1 4 1 -4 14 -10 -37] - [ 3 -3 -1 6 -12 4 25] - [ 4 -4 -2 7 -13 -2 1] + sage: B = random_matrix(ZZ, 7, algorithm='unimodular', upper_bound=50) sage: det(B) 1 + sage: all(abs(b) < 50 for b in B.list()) + True A matrix over the number Field in `y` with defining polynomial `y^2-2y-2`. :: sage: y = var('y') - sage: K=NumberField(y^2-2*y-2,'y') - sage: C=random_matrix(K, 3, algorithm='unimodular');C - [ -5*y + 11 10*y - 30 -695*y + 2366] - [ 5 5*y - 9 -535*y + 588] - [ y - 1 3*y - 1 -35*y - 273] + sage: K = NumberField(y^2-2*y-2, 'y') + sage: C = random_matrix(K, 3, algorithm='unimodular') sage: det(C) 1 + sage: C.base_ring() is K + True TESTS: @@ -3071,57 +3046,29 @@ def random_diagonalizable_matrix(parent,eigenvalues=None,dimensions=None): sage: from sage.matrix.constructor import random_diagonalizable_matrix sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 5) - sage: A = random_diagonalizable_matrix(matrix_space); A - [ 90 -80 56 -448 -588] - [ 60 0 28 -324 -204] - [ 60 -72 32 -264 -432] - [ 30 -16 16 -152 -156] - [ -10 -8 -4 60 8] - sage: sorted(A.eigenvalues()) - [-10, -8, -4, 0, 0] - sage: S=A.right_eigenmatrix()[1]; S - [ 1 1 1 1 0] - [ 1/2 0 2/3 0 1] - [ 4/7 9/10 2/3 6/7 -3/7] - [ 2/7 1/5 1/3 3/14 1/7] - [-1/14 1/10 -1/9 1/14 -2/7] - sage: S_inverse=S.inverse(); S_inverse - [ 0 0 -14 42 42] - [ 0 10 0 -10 30] - [ -9 0 0 36 18] - [ 10 -10 14 -68 -90] - [ 6 1 7 -45 -33] - sage: S_inverse*A*S - [ -4 0 0 0 0] - [ 0 -8 0 0 0] - [ 0 0 -10 0 0] - [ 0 0 0 0 0] - [ 0 0 0 0 0] + sage: A = random_diagonalizable_matrix(matrix_space) + sage: eigenvalues = A.eigenvalues() + sage: S = A.right_eigenmatrix()[1] + sage: eigenvalues2 = (S.inverse()*A*S).diagonal() + sage: sorted(eigenvalues) == sorted(eigenvalues2) + True A diagonalizable matrix with eigenvalues and dimensions designated, with a check that if eigenvectors were calculated by hand entries would all be integers. :: - sage: B = random_matrix(QQ, 6, algorithm='diagonalizable', eigenvalues=[-12,4,6],dimensions=[2,3,1]); B - [ 2 -64 16 206 56 -142] - [ 14 -28 -64 46 40 -14] - [ -4 -16 4 44 32 -28] - [ 6 0 -32 -22 8 26] - [ 0 -16 0 48 20 -32] - [ 2 0 -16 -14 8 18] + sage: eigenvalues = [ZZ.random_element() for _ in range(3)] + sage: B = random_matrix(QQ, 6, algorithm='diagonalizable', eigenvalues=eigenvalues, dimensions=[2,3,1]) sage: all(x in ZZ for x in (B-(-12*identity_matrix(6))).rref().list()) True sage: all(x in ZZ for x in (B-(4*identity_matrix(6))).rref().list()) True sage: all(x in ZZ for x in (B-(6*identity_matrix(6))).rref().list()) True - sage: S=B.right_eigenmatrix()[1]; S_inverse=S.inverse(); S_inverse*B*S - [ 6 0 0 0 0 0] - [ 0 -12 0 0 0 0] - [ 0 0 -12 0 0 0] - [ 0 0 0 4 0 0] - [ 0 0 0 0 4 0] - [ 0 0 0 0 0 4] + sage: S = B.right_eigenmatrix()[1] + sage: eigenvalues2 = (S.inverse()*B*S).diagonal() + sage: all(e in eigenvalues for e in eigenvalues2) + True TESTS: From 77c8601f6e7eea47925851626af5463d223855af Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 4 Jun 2021 16:00:03 +0200 Subject: [PATCH 489/706] make dynamics ready for random seeds --- src/sage/dynamics/arithmetic_dynamics/wehlerK3.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 34ee7c1248e..a385b8a54b9 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -97,13 +97,9 @@ def random_WehlerK3Surface(PP): EXAMPLES:: sage: PP. = ProductProjectiveSpaces([2, 2], GF(3)) - sage: random_WehlerK3Surface(PP) - Closed subscheme of Product of projective spaces P^2 x P^2 over Finite Field of size 3 defined by: - x0*y0 + x1*y1 + x2*y2, - -x1^2*y0^2 - x2^2*y0^2 + x0^2*y0*y1 - x0*x1*y0*y1 - x1^2*y0*y1 - + x1*x2*y0*y1 + x0^2*y1^2 + x0*x1*y1^2 - x1^2*y1^2 + x0*x2*y1^2 - - x0^2*y0*y2 - x0*x1*y0*y2 + x0*x2*y0*y2 + x1*x2*y0*y2 + x0*x1*y1*y2 - - x1^2*y1*y2 - x1*x2*y1*y2 - x0^2*y2^2 + x0*x1*y2^2 - x1^2*y2^2 - x0*x2*y2^2 + sage: w = random_WehlerK3Surface(PP) + sage: type(w) + """ CR = PP.coordinate_ring() From 5bf10ddda635e5cec09d539938a7b950befb5234 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 4 Jun 2021 20:18:46 +0200 Subject: [PATCH 490/706] Update docutils to 0.17.1 --- build/pkgs/docutils/checksums.ini | 7 ++++--- build/pkgs/docutils/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/docutils/checksums.ini b/build/pkgs/docutils/checksums.ini index 79c8595503c..7eb275b5d93 100644 --- a/build/pkgs/docutils/checksums.ini +++ b/build/pkgs/docutils/checksums.ini @@ -1,4 +1,5 @@ tarball=docutils-VERSION.tar.gz -sha1=32cefb69ac3dab5b04c4d150776f35419cc4c863 -md5=c53768d63db3873b7d452833553469de -cksum=2981697109 +sha1=f423535c12fcd2a68d4fc52525fbe36020a58ab5 +md5=ed810564c25063e9dac10dd0893ead47 +cksum=3160620183 +upstream_url=https://pypi.io/packages/source/d/docutils/docutils-VERSION.tar.gz diff --git a/build/pkgs/docutils/package-version.txt b/build/pkgs/docutils/package-version.txt index 948a5472708..7cca7711a0d 100644 --- a/build/pkgs/docutils/package-version.txt +++ b/build/pkgs/docutils/package-version.txt @@ -1 +1 @@ -0.14 +0.17.1 From bd9a33c55b3d711de607701c6196fb03271c18ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Fri, 4 Jun 2021 23:51:26 +0200 Subject: [PATCH 491/706] 31909: Update distro prereq links in README --- README.md | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e48e6f1730b..020bfba5a66 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ https://www.sagemath.org -The Sage Library is GPLv2+, and included packages have [compatible OSS -licenses](./COPYING.txt). [Over 400 people](https://www.sagemath.org/development-map.html) -have contributed code to Sage. In many cases, documentation for modules -and functions list the authors. +The Sage Library is GPLv2+, and included packages have +[compatible OSS licenses](./COPYING.txt). +[Over 400 people](https://www.sagemath.org/development-map.html) +have contributed code to Sage. In many cases, documentation +for modules and functions list the authors. Getting Started --------------- @@ -22,7 +23,7 @@ If you downloaded a [binary](https://www.sagemath.org/download.html) Sage is ready to start -- just open a terminal in the directory where you extracted the binary archive and type: - ./sage + $ ./sage (Note that the first run will take more time, as Sage needs to get itself ready.) @@ -118,9 +119,9 @@ virtual appliance](https://wiki.sagemath.org/SageAppliance). ------------------------------ Make sure you have installed the most current version of Xcode -supported on your version of macOS. If you don't, go to +supported on your version of macOS. If you don't, either go to https://developer.apple.com/, sign up, and download the free Xcode -package. +package, or get it from Apple's app store. You also need to install the "command line tools": After installing Xcode, run `xcode-select --install` from a terminal window; then click @@ -131,7 +132,7 @@ and then "Install" the Command Line Tools.) Optionally, you can consider installing Homebrew ("the missing package manager for macOS") from https://brew.sh/, which can provide libraries -such gfortran, gmp, etc. +such as gfortran, gmp, etc. Instructions to Build from Source --------------------------------- @@ -173,11 +174,12 @@ Guide](https://doc.sagemath.org/html/en/installation). - [Git] Alternatively, clone the Sage git repository: - $ git clone -c core.symlinks=true --branch master https://github.com/sagemath/sage.git + $ ORIG=https://github.com/sagemath/sage.git + $ git clone -c core.symlinks=true --branch master $ORIG - This will create the subdirectory `sage`. `cd sage/` and pick the branch you need - by doing `git checkout` - typically you want the latest development branch, thus do - `git checkout develop`. + This will create the subdirectory `sage`. `cd sage/` and pick + the branch you need by doing `git checkout` - typically you want + the latest development branch, thus do `git checkout develop`. - [Windows] The Sage source tree contains symbolic links, and the build will not work if Windows line endings rather than UNIX @@ -186,8 +188,8 @@ Guide](https://doc.sagemath.org/html/en/installation). Therefore it is crucial that you unpack the source tree from the Cygwin (or WSL) `bash` using the Cygwin (or WSL) `tar` utility and not using other Windows tools (including mingw). Likewise, - when using `git`, it is recommended (but not necessary) to use the Cygwin (or WSL) - version of `git`. + when using `git`, it is recommended (but not necessary) to use + the Cygwin (or WSL) version of `git`. 3. `cd` into the source/build directory: @@ -224,11 +226,16 @@ Guide](https://doc.sagemath.org/html/en/installation). avoid having to build Sage's own copy of Python 3. We have collected lists of system packages that provide these build - prerequisites. See [build/pkgs/arch.txt](build/pkgs/arch.txt), - [cygwin.txt](build/pkgs/cygwin.txt), - [debian.txt](build/pkgs/debian.txt) (also for Ubuntu, Linux Mint, - etc.), [fedora.txt](build/pkgs/fedora.txt) (also for Red Hat, - CentOS), and [slackware.txt](build/pkgs/slackware.txt). + prerequisites. See, in the folder + [build/pkgs/_prereq/distros](build/pkgs/_prereq/distros), + the files + [arch.txt](build/pkgs/_prereq/distros/arch.txt), + [cygwin.txt](build/pkgs/_prereq/distros/cygwin.txt), + [debian.txt](build/pkgs/_prereq/distros/debian.txt) + (also for Ubuntu, Linux Mint, etc.), + [fedora.txt](build/pkgs/_prereq/distros/fedora.txt) + (also for Red Hat, CentOS), and + [slackware.txt](build/pkgs/_prereq/distros/slackware.txt). 7. Optional, but highly recommended: Make sure your system has an SSL library and its development files installed. @@ -245,7 +252,7 @@ Guide](https://doc.sagemath.org/html/en/installation). 9. Optionally, review the configuration options, which includes many optional packages: - ./configure --help + $ ./configure --help 10. Optional, but highly recommended: Set some environment variables to customize the build. @@ -470,7 +477,7 @@ do. 2. (**Obsolete, probably broken**) To make your own source tarball of Sage, type: - sage --sdist + $ sage --sdist The result is placed in the directory `dist/`. From f6383fa0766b7ed2e7e9bd0650343282bec37307 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 4 Jun 2021 20:53:38 -0700 Subject: [PATCH 492/706] src/sage/functions/min_max.py: Remove py2-ish special casing of None --- src/sage/functions/min_max.py | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 771ec204020..4d9901d03d2 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -64,23 +64,10 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): symb_args.append(x) else: num_non_symbolic_args += 1 - if res is None: - # Any argument is greater or less than None - res = x - else: - res = builtin_f(res, x) + res = builtin_f(res, x) # if no symbolic arguments, return the result if len(symb_args) == 0: - if res is None: - # this is a hack to get the function to return None to the user - # the convention to leave a given symbolic function unevaluated - # is to return None from the _eval_ function, so we need - # a trick to indicate that the return value of the function is - # really None - # this is caught in the __call__ method, which knows to return - # None in this case - raise ValueError("return None") return res # if all arguments were symbolic return @@ -119,17 +106,6 @@ def __call__(self, *args, **kwds): ... ValueError: number of arguments must be > 0 - Check if we return None, when the builtin function would:: - - sage: max_symbolic([None]) is None # py2 on Python 3 None is not ordered - True - sage: max_symbolic([None, None]) is None # py2 - True - sage: min_symbolic([None]) is None # py2 - True - sage: min_symbolic([None, None]) is None # py2 - True - Check if a single argument which is not iterable works:: sage: max_symbolic(None) @@ -164,9 +140,7 @@ def __call__(self, *args, **kwds): try: return BuiltinFunction.__call__(self, *args, **kwds) except ValueError as e: - if e.args[0] == "return None": - return None - + pass class MaxSymbolic(MinMax_base): def __init__(self): From 86605538360f5505ed99c78e8f9bc857076b6db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 5 Jun 2021 10:42:15 +0200 Subject: [PATCH 493/706] various lgtm fixes about unused variables --- .../orthogonal_arrays_build_recursive.py | 6 +-- src/sage/combinat/ribbon_tableau.py | 11 +++-- src/sage/crypto/mq/sr.py | 10 ++--- src/sage/databases/stein_watkins.py | 3 -- .../quadratic_forms/genera/normal_form.py | 4 +- .../quadratic_form__split_local_covering.py | 43 +++---------------- .../projective/projective_rational_point.py | 8 ++-- 7 files changed, 21 insertions(+), 64 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 64bb91d61ca..443202c7487 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -1075,12 +1075,10 @@ def product_with_parallel_classes(OA1,k,g1,g2,g1_parall,parall,check=True): # Check our stuff before we return it if check: - profile = [i for i in range(g2*g1) for _ in range(g1)] for classs in new_g1_parallel_classes: - assert_c_partition(classs,k,g2*g1,g1) - profile = list(range(g2*g1)) + assert_c_partition(classs, k, g2 * g1, g1) for classs in new_parallel_classes: - assert_c_partition(classs,k,g2*g1,1) + assert_c_partition(classs, k, g2 * g1, 1) return new_g1_parallel_classes, new_parallel_classes diff --git a/src/sage/combinat/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 0212acd458f..f164a886729 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -1110,14 +1110,13 @@ def __iter__(self): parts = self._shape mu = self._weight - #Splitting the partition - s = [ p.size() for p in parts ] + # Splitting the partition + s = [p.size() for p in parts] parts = [p.to_list() for p in parts] - #Gluing the partitions + # Gluing the partitions parttmp = parts[0] - i = 1 - for i in range(1,len(parts)): + for i in range(1, len(parts)): trans = parttmp[0][0] current_part = parts[i] current_part[1] += [0]*(len(current_part[0])-len(current_part[1])) @@ -1125,7 +1124,7 @@ def __iter__(self): outer_current = [ trans + j for j in current_part[0] ] parttmp = [ outer_current + parttmp[0], inner_current + parttmp[1] ] - #List the corresponding skew tableaux + # List the corresponding skew tableaux l = [ st.to_word() for st in SemistandardSkewTableaux(parttmp, mu) ] S = SkewTableaux() diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index ab047b5f6cb..0b390e7d3cf 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1315,11 +1315,9 @@ def __call__(self, P, K): R[10].output 3925841D02DC09FBDC118597196A0B32 sage: set_verbose(0) """ - r,c,e = self.r,self.c,self.e + r, c, e = self.r, self.c, self.e F = self.base_ring() - _type = self.state_array - if isinstance(P, str): P = self.state_array([F.fetch_int(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) if isinstance(K, str): @@ -2831,14 +2829,12 @@ def _mul_matrix(self, x): sage: (a^2 + 1)*(a+1) a^3 + a^2 + a + 1 """ - a = self.k.gen() k = self.k e = self.e a = k.gen() - columns = [] - for i in reversed(range(e)): - columns.append( list(reversed((x * a**i)._vector_())) ) + columns = [list(reversed((x * a**i)._vector_())) + for i in reversed(range(e))] return Matrix(GF(2), e, e, columns).transpose() def _square_matrix(self): diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index d8fe4c73351..f326925ac95 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -346,7 +346,6 @@ def ecdb_num_curves(max_level=200000): 0, 8, 0, 6, 11, 4] """ i = 0 - N = 1 d = SteinWatkinsAllData(i) v = [int(0) for _ in range(max_level + 1)] while True: @@ -361,5 +360,3 @@ def ecdb_num_curves(max_level=200000): break v[N] += len(C.curves) return v - - diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index 394835cc53e..db46afe0d76 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -259,14 +259,12 @@ def p_adic_normal_form(G, p, precision=None, partial=False, debug=False): precision = G.det().valuation(p) + 4 R = Zp(p, prec=precision, type='fixed-mod') G = G.change_ring(R) - G.set_immutable() # is not changed during computation - D = copy(G) # is transformed into jordan form + G.set_immutable() # is not changed during computation n = G.ncols() # The trivial case if n == 0: return G.parent().zero(), G.parent().zero() # the transformation matrix is called B - B = Matrix.identity(R, n) if p == 2: D, B = _jordan_2_adic(G) else: diff --git a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py index 92102fcbd1e..764f68c2273 100644 --- a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py +++ b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py @@ -186,11 +186,11 @@ def vectors_by_length(self, bound): ## (So theta_vec[i] will have all vectors v with Q(v) = i.) theta_vec = [[] for i in range(bound + 1)] - ## Initialize Q with zeros and Copy the Cholesky array into Q + # Initialize Q with zeros and Copy the Cholesky array into Q Q = self.cholesky_decomposition() - ## 1. Initialize + # 1. Initialize T = n * [RDF(0)] ## Note: We index the entries as 0 --> n-1 U = n * [RDF(0)] i = n-1 @@ -199,36 +199,21 @@ def vectors_by_length(self, bound): L = n * [0] x = n * [0] - Z = RDF(0) - ## 2. Compute bounds + # 2. Compute bounds Z = (T[i] / Q[i][i]).sqrt(extend=False) L[i] = ( Z - U[i]).floor() x[i] = (-Z - U[i]).ceil() done_flag = False - Q_val_double = RDF(0) Q_val = 0 ## WARNING: Still need a good way of checking overflow for this value... - ## Big loop which runs through all vectors + # Big loop which runs through all vectors while not done_flag: ## 3b. Main loop -- try to generate a complete vector x (when i=0) while (i > 0): - #print " i = ", i - #print " T[i] = ", T[i] - #print " Q[i][i] = ", Q[i][i] - #print " x[i] = ", x[i] - #print " U[i] = ", U[i] - #print " x[i] + U[i] = ", (x[i] + U[i]) - #print " T[i-1] = ", T[i-1] - T[i-1] = T[i] - Q[i][i] * (x[i] + U[i]) * (x[i] + U[i]) - - #print " T[i-1] = ", T[i-1] - #print " x = ", x - #print - i = i - 1 U[i] = 0 for j in range(i+1, n): @@ -247,29 +232,20 @@ def vectors_by_length(self, bound): i += 1 x[i] += 1 - ## 4. Solution found (This happens when i = 0) - #print "-- Solution found! --" - #print " x = ", x - #print " Q_val = Q(x) = ", Q_val + # 4. Solution found (This happens when i = 0) Q_val_double = Theta_Precision - T[0] + Q[0][0] * (x[0] + U[0]) * (x[0] + U[0]) Q_val = Q_val_double.round() - ## SANITY CHECK: Roundoff Error is < 0.001 + # SANITY CHECK: Roundoff Error is < 0.001 if abs(Q_val_double - Q_val) > 0.001: print(" x = ", x) print(" Float = ", Q_val_double, " Long = ", Q_val) raise RuntimeError("The roundoff error is bigger than 0.001, so we should use more precision somewhere...") - #print " Float = ", Q_val_double, " Long = ", Q_val, " XX " - #print " The float value is ", Q_val_double - #print " The associated long value is ", Q_val - if (Q_val <= bound): - #print " Have vector ", x, " with value ", Q_val theta_vec[Q_val].append(deepcopy(x)) - - ## 5. Check if x = 0, for exit condition. =) + # 5. Check if x = 0, for exit condition. =) j = 0 done_flag = True while (j < n): @@ -277,20 +253,15 @@ def vectors_by_length(self, bound): done_flag = False j += 1 - ## 3a. Increment (and carry if we go out of bounds) x[i] += 1 while (x[i] > L[i]) and (i < n-1): i += 1 x[i] += 1 - - #print " Leaving ThetaVectors()" return theta_vec - - def complementary_subform_to_vector(self, v): """ Finds the `(n-1)`-dim'l quadratic form orthogonal to the vector `v`. diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index a75958eae6a..6196eaa1021 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -343,7 +343,7 @@ def sieve(X, bound): sage: from sage.schemes.projective.projective_rational_point import sieve sage: P.=ProjectiveSpace(QQ,3) sage: Y=P.subscheme([x^2-3^2*y^2+z*q,x+z+4*q]) - sage: sorted(sieve(Y, 12)) + sage: sorted(sieve(Y, 12)) # long time [(-4 : -4/3 : 0 : 1), (-4 : 4/3 : 0 : 1), (-1 : -1/3 : 1 : 0), (-1 : 1/3 : 1 : 0)] @@ -351,7 +351,7 @@ def sieve(X, bound): sage: from sage.schemes.projective.projective_rational_point import sieve sage: E = EllipticCurve('37a') - sage: sorted(sieve(E, 14)) + sage: sorted(sieve(E, 14)) # long time [(-1 : -1 : 1), (-1 : 0 : 1), (0 : -1 : 1), (0 : 0 : 1), (0 : 1 : 0), (1/4 : -5/8 : 1), (1/4 : -3/8 : 1), (1 : -1 : 1), (1 : 0 : 1), @@ -539,12 +539,11 @@ def lift_all_points(): primes_list = good_primes(B.ceil()) modulo_points = points_modulo_primes(X, primes_list) - len_modulo_points = [len(_) for _ in modulo_points] + len_modulo_points = [len(pt) for pt in modulo_points] len_primes = len(primes_list) prod_primes = prod(primes_list) # stores final result - rat_points = set() for i in range(N + 1): w = [0 for _ in range(N + 1)] @@ -554,4 +553,3 @@ def lift_all_points(): rat_points = lift_all_points() return sorted(rat_points) - From aea4554d90de3a3cb39c0e3704edc0807d8b4109 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Sat, 5 Jun 2021 19:11:51 +0200 Subject: [PATCH 494/706] #31904: Fix indentation in _pullback_chart --- src/sage/manifolds/differentiable/diff_map.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index bc772ca1f13..ad9a2a94f95 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -935,7 +935,6 @@ def pullback(self, tensor): """ from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal - from sage.manifolds.differentiable.vectorframe import CoordFrame from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) @@ -1029,7 +1028,7 @@ def paral_comp(tcomp, chart1, chart2, coord2_1, jacob, t *= jacob[ind_old[i]-si2, ind_new[i]-si1] res += t partial.append([ind_new, res]) - return partial + return partial for ii, val in paral_comp(listParalInput): for jj in val: From 6489ba720aca1c9ab7b130eb549b910943e6206d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sat, 5 Jun 2021 19:44:35 +0200 Subject: [PATCH 495/706] 31915: Add xz dependency for symmetrica --- build/pkgs/symmetrica/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/symmetrica/dependencies b/build/pkgs/symmetrica/dependencies index 7a7b9cf8a80..0fef19aa992 100644 --- a/build/pkgs/symmetrica/dependencies +++ b/build/pkgs/symmetrica/dependencies @@ -1,4 +1,4 @@ -| xz +xz xz is needed for unpacking the tarball when sage-bootstrap-python is ancient From 5ae501f6773263ff67a1f290b7fd32f214f5659d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 5 Jun 2021 20:08:08 +0200 Subject: [PATCH 496/706] remove unused import --- src/sage/combinat/designs/orthogonal_arrays_build_recursive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 443202c7487..7a63bac3c11 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -783,7 +783,6 @@ def thwart_lemma_4_1(k,n,m,explain_construction=False): T. G. Ostrom and F. A. Sherk. Canad. Math. Bull vol7 num.4 (1964) """ - from sage.combinat.designs.designs_pyx import is_orthogonal_array from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.arith.all import is_prime_power from .block_design import DesarguesianProjectivePlaneDesign From 695dd5e892b80814540fcec9e33a8dda52d5db38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sat, 5 Jun 2021 20:29:13 +0200 Subject: [PATCH 497/706] 31910: Improve code style compliance --- src/sage/functions/min_max.py | 67 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 4d9901d03d2..f38c991d405 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -7,22 +7,22 @@ Here you can see some differences:: - sage: max(x,x^2) + sage: max(x, x^2) x - sage: max_symbolic(x,x^2) + sage: max_symbolic(x, x^2) max(x, x^2) - sage: f(x) = max_symbolic(x,x^2); f(1/2) + sage: f(x) = max_symbolic(x, x^2); f(1/2) 1/2 This works as expected for more than two entries:: - sage: max(3,5,x) + sage: max(3, 5, x) 5 - sage: min(3,5,x) + sage: min(3, 5, x) 3 - sage: max_symbolic(3,5,x) + sage: max_symbolic(3, 5, x) max(x, 5) - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) """ @@ -45,9 +45,9 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): """ EXAMPLES:: - sage: max_symbolic(3,5,x) # indirect doctest + sage: max_symbolic(3, 5, x) # indirect doctest max(x, 5) - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) """ # __call__ ensures that if args is a singleton, the element is iterable @@ -74,27 +74,28 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): if num_non_symbolic_args <= 1 and not arg_is_iter: return None - if res is not None: symb_args.append(res) + if res is not None: + symb_args.append(res) return this_f(*symb_args) def __call__(self, *args, **kwds): """ EXAMPLES:: - sage: max_symbolic(3,5,x) + sage: max_symbolic(3, 5, x) max(x, 5) - sage: max_symbolic(3,5,x, hold=True) + sage: max_symbolic(3, 5, x, hold=True) max(3, 5, x) - sage: max_symbolic([3,5,x]) + sage: max_symbolic([3, 5, x]) max(x, 5) :: - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) - sage: min_symbolic(3,5,x, hold=True) + sage: min_symbolic(3, 5, x, hold=True) min(3, 5, x) - sage: min_symbolic([3,5,x]) + sage: min_symbolic([3, 5, x]) min(x, 3) TESTS: @@ -134,12 +135,12 @@ def __call__(self, *args, **kwds): if len(args) == 1: try: args = (SR._force_pyobject(iter(args[0])),) - except TypeError as e: - raise e + except TypeError: + raise try: return BuiltinFunction.__call__(self, *args, **kwds) - except ValueError as e: + except ValueError: pass class MaxSymbolic(MinMax_base): @@ -159,14 +160,14 @@ def __init__(self): 5 sage: max_symbolic(3, 5, x) max(x, 5) - sage: max_symbolic([3,5,x]) + sage: max_symbolic([3, 5, x]) max(x, 5) TESTS:: - sage: loads(dumps(max_symbolic(x,5))) + sage: loads(dumps(max_symbolic(x, 5))) max(x, 5) - sage: latex(max_symbolic(x,5)) + sage: latex(max_symbolic(x, 5)) \max\left(x, 5\right) sage: max_symbolic(x, 5)._sympy_() Max(5, x) @@ -180,17 +181,17 @@ def _eval_(self, *args): sage: t = max_symbolic(x, 5); t max(x, 5) - sage: t.subs(x=3) # indirect doctest + sage: t.subs(x=3) # indirect doctest 5 - sage: max_symbolic(5,3) + sage: max_symbolic(5, 3) 5 - sage: u = max_symbolic(*(list(range(10))+[x])); u + sage: u = max_symbolic(*(list(range(10)) + [x])); u max(x, 9) sage: u.subs(x=-1) 9 sage: u.subs(x=10) 10 - sage: max_symbolic([0,x]) + sage: max_symbolic([0, x]) max(x, 0) TESTS:: @@ -250,14 +251,14 @@ def __init__(self): 3 sage: min_symbolic(3, 5, x) min(x, 3) - sage: min_symbolic([3,5,x]) + sage: min_symbolic([3, 5, x]) min(x, 3) TESTS:: - sage: loads(dumps(min_symbolic(x,5))) + sage: loads(dumps(min_symbolic(x, 5))) min(x, 5) - sage: latex(min_symbolic(x,5)) + sage: latex(min_symbolic(x, 5)) \min\left(x, 5\right) sage: min_symbolic(x, 5)._sympy_() Min(5, x) @@ -271,17 +272,17 @@ def _eval_(self, *args): sage: t = min_symbolic(x, 5); t min(x, 5) - sage: t.subs(x=3) # indirect doctest + sage: t.subs(x=3) # indirect doctest 3 - sage: min_symbolic(5,3) + sage: min_symbolic(5, 3) 3 - sage: u = min_symbolic(*(list(range(10))+[x])); u + sage: u = min_symbolic(*(list(range(10)) + [x])); u min(x, 0) sage: u.subs(x=-1) -1 sage: u.subs(x=10) 0 - sage: min_symbolic([3,x]) + sage: min_symbolic([3, x]) min(x, 3) TESTS:: From 3459eccd99670a176eb8df0f201ec6016dc6122b Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sat, 5 Jun 2021 15:29:01 -0700 Subject: [PATCH 498/706] Add function is_easy_sn_an --- .../polynomial/polynomial_rational_flint.pyx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 2dfe570bd12..f9724ea41d3 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2513,4 +2513,42 @@ cdef class Polynomial_rational_flint(Polynomial): # Alias for discriminant disc = discriminant + def is_easy_sn_an(self, num_trials=50, assume_irreducible=False): + """ + Use the Davenport-Smith test to attempt to certify that `f` has Galois group A_n or S_n. + + Return 1 if the Galois group is certified as S_n, 2 if A_n, or 0 if no conclusion is reached. + + By default, we first check that `f` is irreducible. For extra efficiency, one can override this + by specifying `assume_irreducible=True`; this yields undefined results if `f` is not irreducible. + + EXAMPLES:: + + sage: P. = QQ[] + sage: u = x^7 + x + 1 + sage: u.is_easy_sn_an() + 1 + sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 + sage: u.is_easy_sn_an() + 2 + sage: u = x^7 - 2 + sage: u.is_easy_sn_an() + 0 + + """ + from sage.arith.misc import primes_first_n + from sage.rings.finite_rings.integer_mod_ring import IntegerModRing + if not assume_irreducible and not self.is_irreducible(): + return 0 + d = self.degree() + for p in primes_first_n(num_trials): + fp = self.change_ring(IntegerModRing(p)) + g = fp.factor()[-1][0] + d1 = g.degree() + # Here we use the fact that a transitive permutation representation with a long prime cycle + # must have image at least as big as A_n. + if (d1 <= 7 and (d,d1) in ((1,1),(2,2),(3,2),(3,3),(4,3),(5,3),(5,4),(6,5),(7,5))) or\ + (d1 > d/2 and d1 < d-2 and d1.is_prime()): + return (2 if self.disc().is_square() else 1) + return 0 From e9c670c00a492f8f50f3e21e55895a0d3c1d55f2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 5 Jun 2021 19:05:53 -0700 Subject: [PATCH 499/706] sage.geometry.polyhedron.relint, Polyhedron_base.relative_interior, Polyhedron_base.interior: New --- src/sage/geometry/polyhedron/base.py | 54 ++++++++++++++++++++++++++ src/sage/geometry/polyhedron/relint.py | 36 +++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/sage/geometry/polyhedron/relint.py diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..8e36e1dda63 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,6 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron +from .relint import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8394,6 +8395,38 @@ def contains(self, point): __contains__ = contains + @cached_method + def interior(self): + """ + The interior of ``self``. + + OUTPUT: + + - either an empty polyhedron or an instance of + :class:`~sage.geometry.polyhedra.relint.RelativeInterior` + + EXAMPLES: + + If the polyhedron is full-dimensional, the result is the + same as that of :meth:`relative_interior`:: + + sage: P_full = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) + sage: P_full.interior() + Relative interior of + a 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices + + If the polyhedron is of strictly smaller dimension than the + ambient space, its interior is empty:: + + sage: P_lower = Polyhedron(vertices=[[0,1], [0,-1]]) + sage: P_lower.interior() + The empty polyhedron in ZZ^2 + + """ + if not self.is_full_dimensional(): + return self.parent().element_class(self.parent(), None, None) + return self.relative_interior() + def interior_contains(self, point): """ Test whether the interior of the polyhedron contains the @@ -8451,6 +8484,27 @@ def interior_contains(self, point): return False return True + @cached_method + def relative_interior(self): + """ + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) + sage: ri_P = P.relative_interior(); ri_P + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: (0, 0) in ri_P + True + sage: (1, 0) in ri_P + False + + """ + if self.is_empty() or self.is_universe(): + return self + return RelativeInterior(self) + def relative_interior_contains(self, point): """ Test whether the relative interior of the polyhedron diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/polyhedron/relint.py new file mode 100644 index 00000000000..6075ba970ac --- /dev/null +++ b/src/sage/geometry/polyhedron/relint.py @@ -0,0 +1,36 @@ +r""" +Relative interiors of polyhedra +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject + +class RelativeInterior(SageObject): + + """ + The relative interior of a polyhedron + """ + + def __init__(self, polyhedron): + self._polyhedron = polyhedron + + def __contains__(self, point): + return self._polyhedron.relative_interior_contains(point) + + def closure(self): + return self._polyhedron + + def _repr_(self): + repr_P = repr(self._polyhedron) + if repr_P.startswith('A '): + repr_P = 'a ' + repr_P[2:] + return 'Relative interior of ' + repr_P From 795c5b5fe9eac929030f72acb6c5589755bad1ab Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 6 Jun 2021 08:24:37 +0200 Subject: [PATCH 500/706] initialize fmpz_zero --- src/sage/libs/linkages/padics/relaxed/flint_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/linkages/padics/relaxed/flint_helper.c b/src/sage/libs/linkages/padics/relaxed/flint_helper.c index f8221519d29..aaa7e1a657f 100644 --- a/src/sage/libs/linkages/padics/relaxed/flint_helper.c +++ b/src/sage/libs/linkages/padics/relaxed/flint_helper.c @@ -26,7 +26,7 @@ fmpz* get_coeff (fmpz_poly_t poly, slong i) if (zero == NULL) { zero = malloc(sizeof(fmpz)); - fmpz_zero(zero); + fmpz_init(zero); } return zero; } From 68796e16d1ce83ff7551c0729a74244050bff084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sun, 6 Jun 2021 12:45:54 +0200 Subject: [PATCH 501/706] 31917: fix typo prescripted -> prescribed --- src/sage/categories/pushout.py | 2 +- src/sage/rings/ring.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 7120901bfd2..7a15885b609 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3055,7 +3055,7 @@ def __init__(self, polys, names, embeddings=None, structures=None, sage: F2(GF(5)) Traceback (most recent call last): ... - NotImplementedError: ring extension with prescripted embedding is not implemented + NotImplementedError: ring extension with prescribed embedding is not implemented When applying a number field constructor to the ring of integers, an order (not necessarily maximal) of that field is diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 0408a5bcabe..5de398d0895 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1515,9 +1515,9 @@ cdef class CommutativeRing(Ring): name = str(poly.parent().gen(0)) for key, val in kwds.items(): if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_name', 'latex_names']: - raise TypeError("extension() got an unexpected keyword argument '%s'"%key) + raise TypeError("extension() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): - raise NotImplementedError("ring extension with prescripted %s is not implemented"%key) + raise NotImplementedError("ring extension with prescribed %s is not implemented" % key) R = self[name] I = R.ideal(R(poly.list())) return R.quotient(I, name) From ee487beff2037c2f235ce514a95b7389066bdc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 6 Jun 2021 14:20:36 +0200 Subject: [PATCH 502/706] various details about combinat (range and isinstance) --- src/sage/combinat/composition.py | 10 +++++----- src/sage/combinat/crystals/alcove_path.py | 5 ++--- src/sage/combinat/designs/steiner_quadruple_systems.py | 4 ++-- src/sage/combinat/integer_list_old.py | 2 +- src/sage/combinat/k_tableau.py | 2 +- src/sage/combinat/matrices/latin.py | 10 +++++----- src/sage/combinat/ncsf_qsym/qsym.py | 4 ++-- src/sage/combinat/partition_tuple.py | 2 +- src/sage/combinat/root_system/ambient_space.py | 4 ++-- src/sage/combinat/root_system/branching_rules.py | 2 +- src/sage/combinat/similarity_class_type.py | 2 +- src/sage/combinat/tableau_tuple.py | 7 +++---- src/sage/combinat/words/finite_word.py | 4 ++-- 13 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 05833955854..cfdce06dabc 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -251,7 +251,7 @@ def conjugate(self): cocjg = [] for i in range(n-1): - cocjg += [i+1 for _ in range(0, (coofcp[n-i-1]-coofcp[n-i-2]))] + cocjg += [i + 1 for _ in range(coofcp[n-i-1]-coofcp[n-i-2])] cocjg += [n for j in range(coofcp[0])] return self.parent()([cocjg[0]] + [cocjg[i]-cocjg[i-1]+1 for i in range(1,len(cocjg))]) @@ -854,11 +854,11 @@ def fatten(self, grouping): sage: c.fatten(Composition([3,1,1])).__class__ == c.__class__ True """ - result = [None] * len(grouping) + result = [] j = 0 - for i in range(len(grouping)): - result[i] = sum(self[j:j+grouping[i]]) - j += grouping[i] + for i, gi in enumerate(grouping): + result.append(sum(self[j:j + gi])) + j += gi return Compositions()(result) def fatter(self): diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 1a9c5f0a127..6ef9d20720d 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -250,9 +250,8 @@ def __classcall_private__(cls, starting_weight, cartan_type=None, """ if isinstance(cartan_type, bool): # new style signature, optional arguments leak over highest_weight_crystal = cartan_type - - elif isinstance(cartan_type, list) or isinstance(cartan_type, tuple): #old style signature - #switch positional arguments + elif isinstance(cartan_type, (list, tuple)): # old style signature + # switch positional arguments cartan_type, starting_weight = CartanType(starting_weight), cartan_type if highest_weight_crystal is False: diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 35834d93e50..5f49cdd3daf 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -637,7 +637,7 @@ def barP_system(m): # pairs. Those are added to 'last', a new list of pairs last = [] - for n in range(0, (m-3)//2+1): + for n in range((m - 3) // 2 + 1): pairs.append([p for p in P(2*n,m) if not isequal(p,(2*n,(4*n+1)%(2*m)))]) last.append((2*n,(4*n+1)%(2*m))) pairs.append([p for p in P(2*n+1,m) if not isequal(p,(2*m-2-2*n,2*m-3-4*n))]) @@ -659,7 +659,7 @@ def barP_system(m): # Now the points must be relabeled relabel = {} - for n in range(0, (m-3)//2+1): + for n in range((m - 3) // 2 + 1): relabel[2*n] = (4*n)%(2*m) relabel[4*n+1] = (4*n+1)%(2*m) relabel[2*m-2-2*n] = (4*n+2)%(2*m) diff --git a/src/sage/combinat/integer_list_old.py b/src/sage/combinat/integer_list_old.py index 99c6dcfa3e7..cdd420bb79e 100644 --- a/src/sage/combinat/integer_list_old.py +++ b/src/sage/combinat/integer_list_old.py @@ -1209,6 +1209,6 @@ def __contains__(self, v): sage: all(v in C for v in C) True """ - if isinstance(v, self.element_class) or isinstance(v, list): + if isinstance(v, (self.element_class, list)): return is_a(v, *(self.build_args())) and sum(v) in self.n_range return False diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 490f212d415..005f3504456 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -1818,7 +1818,7 @@ def straighten_input(t, k): """ W = WeylGroup(['A', k, 1], prefix='s') if len(t) > 0: - if isinstance(t[0], list) or isinstance(t[0], tuple): + if isinstance(t[0], (list, tuple)): w_tuple = tuple(W.from_reduced_word(p) for p in t) else: w_tuple = tuple(W(r) for r in t) diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 4183a520b26..765b2065cae 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -177,11 +177,11 @@ def __init__(self, *args): [0 1] [2 3] """ - - if len(args) == 1 and (isinstance(args[0], Integer) or isinstance(args[0], int)): + if len(args) == 1 and isinstance(args[0], (Integer, int)): self.square = matrix(ZZ, args[0], args[0]) self.clear_cells() - elif len(args) == 2 and (isinstance(args[0], Integer) or isinstance(args[0], int)) and (isinstance(args[1], Integer) or isinstance(args[1], int)): + elif len(args) == 2 and all(isinstance(a, (Integer, int)) + for a in args): self.square = matrix(ZZ, args[0], args[1]) self.clear_cells() elif len(args) == 1 and isinstance(args[0], Matrix_integer_dense): @@ -1513,8 +1513,8 @@ def isotopism(p): """ # Identity isotopism on p points: - if isinstance(p, Integer) or isinstance(p, int): - return Permutation(range(1, p+1)) + if isinstance(p, (Integer, int)): + return Permutation(range(1, p + 1)) if isinstance(p, PermutationGroupElement): # fixme Ask the Sage mailing list about the tuple/list issue! diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 44aae642397..af6efe920f4 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -1806,7 +1806,7 @@ def coproduct_on_basis(self, compo): """ return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), self._indices(compo[i:])) - for i in range(0,len(compo)+1)) + for i in range(len(compo)+1)) def lambda_of_monomial(self, I, n): r""" @@ -2758,7 +2758,7 @@ def coproduct_on_basis(self, compo): """ return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), self._indices(compo[i:])) - for i in range(0,len(compo)+1)) + for i in range(len(compo)+1)) def product_on_basis(self, I, J): r""" diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 54d5784eb3e..34b5a9e46ff 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -1971,7 +1971,7 @@ def __contains__(self, mu): sage: 1 in PartitionTuples() False """ - if isinstance(mu, PartitionTuple) or isinstance(mu, Partition): + if isinstance(mu, (PartitionTuple, Partition)): return True if isinstance(mu, (tuple, list)): if not mu: diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 67088b3197b..e1d4adea0ea 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -85,11 +85,11 @@ def __init__(self, root_system, base_ring, index_set=None): """ self.root_system = root_system if index_set is None: - index_set = tuple(range(0, self.dimension())) + index_set = tuple(range(self.dimension())) CombinatorialFreeModule.__init__(self, base_ring, index_set, prefix='e', - category = WeightLatticeRealizations(base_ring)) + category=WeightLatticeRealizations(base_ring)) coroot_lattice = self.root_system.coroot_lattice() coroot_lattice.module_morphism(self.simple_coroot, codomain=self).register_as_coercion() diff --git a/src/sage/combinat/root_system/branching_rules.py b/src/sage/combinat/root_system/branching_rules.py index 1a2d6cbd8b0..ded9b53460b 100644 --- a/src/sage/combinat/root_system/branching_rules.py +++ b/src/sage/combinat/root_system/branching_rules.py @@ -979,7 +979,7 @@ def rule(x): sage: A3(0,1,0).branch(C2,rule=br) C2(0,0) + C2(0,1) """ - if isinstance(rule, str) or isinstance(rule, list): + if isinstance(rule, (str, list)): rule = branching_rule(R._cartan_type, S._cartan_type, rule) if hasattr(rule, "_S"): if rule._S != S.cartan_type(): diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 857c52c4fe0..e500595082f 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -308,7 +308,7 @@ def centralizer_algebra_dim(la): If it is a list, ``la`` is expected to be sorted in decreasing order. """ - return sum([(2*i + 1)*la[i] for i in range(0, len(la))]) + return sum([(2 * i + 1) * la[i] for i in range(len(la))]) @cached_function diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index f2d5b91a660..9e8e689ac0f 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -1080,16 +1080,15 @@ def row_stabilizer(self): sage: rs.one().domain() [1, 2, 3, 4, 5, 6, 7, 8, 9] """ - # Ensure that the permutations involve all elements of the # tableau, by including the identity permutation on the set [1..n]. n = max(self.entries()) gens = [list(range(1, n + 1))] for t in self: for i in range(len(t)): - for j in range(0, len(t[i])-1): - gens.append( (t[i][j], t[i][j+1]) ) - return PermutationGroup( gens ) + for j in range(len(t[i]) - 1): + gens.append((t[i][j], t[i][j + 1])) + return PermutationGroup(gens) def column_stabilizer(self): """ diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index aec56d7fbd4..b546105c7c7 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -7047,8 +7047,8 @@ def is_cube_free(self): L = self.length() if L < 3: return True - for start in range(0, L - 2): - for end in range(start+3, L+1, 3): + for start in range(L - 2): + for end in range(start + 3, L + 1, 3): if self[start:end].is_cube(): return False return True From 2967d9f99397c6e2fa213dc68ff440d874e8f284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 6 Jun 2021 14:26:50 +0200 Subject: [PATCH 503/706] one detail --- src/sage/combinat/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index cfdce06dabc..a1b2f97d396 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -856,7 +856,7 @@ def fatten(self, grouping): """ result = [] j = 0 - for i, gi in enumerate(grouping): + for gi in grouping: result.append(sum(self[j:j + gi])) j += gi return Compositions()(result) From e53845eb6e396c68cb51118ab3093712affef1b3 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:01:43 +0200 Subject: [PATCH 504/706] Don't pollute the global namespace in sagelib code --- src/sage/coding/grs_code.py | 4 ++-- src/sage/combinat/multiset_partition_into_sets_ordered.py | 4 ++-- src/sage/combinat/sine_gordon.py | 4 ++-- src/sage/rings/number_field/S_unit_solver.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 9bc6248c4a9..803cdb0943d 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -65,7 +65,7 @@ from copy import copy from sage.functions.other import binomial, floor -from sage.calculus.var import var +from sage.symbolic.ring import SR from .linear_code import AbstractLinearCode from .encoder import Encoder @@ -534,7 +534,7 @@ def weight_distribution(self): d = self.minimum_distance() n = self.length() q = self.base_ring().order() - s = var('s') + s = SR.var('s') wd = [1] + [0] * (d - 1) for i in range(d, n+1): tmp = binomial(n, i) * (q - 1) diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 42aeb20da66..1d6eb8b37f6 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -81,7 +81,7 @@ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.functions.other import binomial -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.combinat.subset import Subsets_sk from sage.combinat.composition import Composition, Compositions, composition_iterator_fast @@ -2067,7 +2067,7 @@ def cardinality(self): # # The 2-regular partitions have a nice generating function (see OEIS:A000009). # Below, we take (products of) coefficients of polynomials to compute cardinality. - t = var('t') + t = SR.var('t') partspoly = prod(1+t**k for k in range(1,self._n+1)).coefficients() deg = 0 for alpha in composition_iterator_fast(self._n): diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index 309c07fcd23..a3c63e7b2d8 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -58,7 +58,7 @@ from sage.functions.log import exp from sage.functions.other import ceil from sage.misc.flatten import flatten -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.functions.other import real_part, imag_part from sage.misc.cachefunc import cached_method @@ -516,7 +516,7 @@ def plot_arc(radius, p, q, **opts): # plot the arc from p to q differently depending on the type of self p = ZZ(p) q = ZZ(q) - t = var('t') + t = SR.var('t') if p - q in [1, -1]: def f(t): return (radius * cos(t), radius * sin(t)) diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 5c0a33eb74f..e19a9b5580d 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -55,7 +55,7 @@ from sage.rings.all import Infinity -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RealField, RR @@ -675,7 +675,7 @@ def Yu_bound(SUK, v, prec=106): else: # K and v don't satisfy the theorem hypotheses, and we must move to a quadratic extension L. # For justification of this next bound, see [AKMRVW]. - x = var('x') + x = SR.var('x') if p == 2: L_over_K = K.extension(x**2 + x + 1, 'xi0') else: From 9df21042f5cb76869b14d54c501bda4c7ef2cbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:07 -0700 Subject: [PATCH 505/706] sage.geometry.relative_interior: Move here from sage.geometry.polyhedron.relint --- src/sage/geometry/polyhedron/base.py | 4 ++-- .../relint.py => relative_interior.py} | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) rename src/sage/geometry/{polyhedron/relint.py => relative_interior.py} (65%) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e36e1dda63..1175d21925f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,7 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron -from .relint import RelativeInterior +from sage.geometry.relative_interior import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8403,7 +8403,7 @@ def interior(self): OUTPUT: - either an empty polyhedron or an instance of - :class:`~sage.geometry.polyhedra.relint.RelativeInterior` + :class:`~sage.geometry.relative_interior.RelativeInterior` EXAMPLES: diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/relative_interior.py similarity index 65% rename from src/sage/geometry/polyhedron/relint.py rename to src/sage/geometry/relative_interior.py index 6075ba970ac..3c20f90d1dd 100644 --- a/src/sage/geometry/polyhedron/relint.py +++ b/src/sage/geometry/relative_interior.py @@ -1,5 +1,5 @@ r""" -Relative interiors of polyhedra +Relative Interiors of Polyhedra and Cones """ # **************************************************************************** @@ -17,7 +17,7 @@ class RelativeInterior(SageObject): """ - The relative interior of a polyhedron + The relative interior of a polyhedron or cone """ def __init__(self, polyhedron): @@ -30,6 +30,18 @@ def closure(self): return self._polyhedron def _repr_(self): + """ + Return a description of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]]) + sage: P.relative_interior()._repr_() + 'Relative interior of a 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices' + sage: P.rename('A') + sage: P.relative_interior()._repr_() + 'Relative interior of A' + """ repr_P = repr(self._polyhedron) if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] From b8bfe200f6e698dec855394af9adaaa63c282abd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:57 -0700 Subject: [PATCH 506/706] ConvexRationalPolyhedralCone: Add methods interior, relative_interior --- src/sage/geometry/cone.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..e7813040521 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -205,6 +205,7 @@ from sage.geometry.toric_lattice import (ToricLattice, is_ToricLattice, is_ToricLatticeQuotient) from sage.geometry.toric_plotter import ToricPlotter, label_list +from sage.geometry.relative_interior import RelativeInterior from sage.graphs.digraph import DiGraph from sage.matrix.all import column_matrix, matrix, MatrixSpace from sage.misc.all import cached_method, flatten, latex @@ -1711,6 +1712,14 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + def interior(self): + r""" + Return the interior of ``self``. + """ + if self.is_solid(): + return self.relative_interior() + return Polyhedron(ambient_dim=self.lattice_dim()) + def relative_interior_contains(self, *args): r""" Check if a given point is contained in the relative interior of ``self``. @@ -1752,6 +1761,14 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + def relative_interior(self): + r""" + Return the relative interior of ``self``. + """ + if self.is_full_space(): + return self + return RelativeInterior(self) + def cartesian_product(self, other, lattice=None): r""" Return the Cartesian product of ``self`` with ``other``. From 6869673199fb80af0a09caf039873e27d50d2741 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:45:02 -0700 Subject: [PATCH 507/706] relative_interior: Fix for dimension 0 --- src/sage/geometry/cone.py | 4 +++- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index e7813040521..a4e11c5a9d3 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1712,6 +1712,7 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + @cached_method def interior(self): r""" Return the interior of ``self``. @@ -1761,11 +1762,12 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + @cached_method def relative_interior(self): r""" Return the relative interior of ``self``. """ - if self.is_full_space(): + if self.is_trivial() or self.is_full_space(): return self return RelativeInterior(self) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1175d21925f..985a9f5c6c5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8500,8 +8500,15 @@ def relative_interior(self): sage: (1, 0) in ri_P False + sage: P0 = Polyhedron(vertices=[[1, 2]]) + sage: P0.relative_interior() is P0 + True + + sage: Empty = Polyhedron(ambient_dim=2) + sage: Empty.relative_interior() is Empty + True """ - if self.is_empty() or self.is_universe(): + if self.dim() <= 0 or self.is_universe(): return self return RelativeInterior(self) From 021d073e758d37f0abdb2466e3dc59e3383ed05b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 12:40:46 -0700 Subject: [PATCH 508/706] RelativeInterior: Add documentation, tests, comparison methods, method relative_interior --- src/sage/geometry/cone.py | 15 ++- src/sage/geometry/relative_interior.py | 127 ++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index a4e11c5a9d3..29007febb11 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -181,9 +181,18 @@ """ # **************************************************************************** -# Copyright (C) 2010 Volker Braun -# Copyright (C) 2012 Andrey Novoseltsev -# Copyright (C) 2010 William Stein +# Copyright (C) 2010-2014 Volker Braun +# Copyright (C) 2010-2018 Andrey Novoseltsev +# Copyright (C) 2010 William Stein +# Copyright (C) 2012 Christian Stump +# Copyright (C) 2014-2018 Frédéric Chapoton +# Copyright (C) 2014 Peter Bruin +# Copyright (C) 2015-2017 Jori Mäntysalo +# Copyright (C) 2015-2020 Michael Orlitzky +# Copyright (C) 2016-2020 John H. Palmieri +# Copyright (C) 2018 David Coudert +# Copyright (C) 2019-2020 Jonathan Kliem +# Copyright (C) 2020-2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3c20f90d1dd..d715b9f708d 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -16,21 +16,96 @@ class RelativeInterior(SageObject): - """ + r""" The relative interior of a polyhedron or cone + + This class should not be used directly. Use methods + :meth:`~sage.geometry.polyhedron.Polyhedron_base.relative_interior`, + :meth:`~sage.geometry.polyhedron.Polyhedron_base.interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.relative_interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.interior` instead. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.relative_interior() + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: octant.relative_interior() + Relative interior of 3-d cone in 3-d lattice N + """ def __init__(self, polyhedron): + r""" + Initialize ``self``. + + INPUT: + + - ``polyhedron`` - an instance of :class:`Polyhedron_base` or + :class:`ConvexRationalPolyhedralCone`. + + TESTS:: + + sage: P = Polyhedron() + sage: from sage.geometry.relative_interior import RelativeInterior + sage: TestSuite(RelativeInterior(P)).run() + + """ self._polyhedron = polyhedron def __contains__(self, point): + r""" + Return whether ``self`` contains ``point``. + + EXAMPLES:: + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: (1, 1, 1) in ri_octant + True + sage: (1, 0, 0) in ri_octant + False + """ return self._polyhedron.relative_interior_contains(point) + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + As ``self`` is already relatively open, this method just returns ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.relative_interior() is ri_segment + True + """ + return self + def closure(self): + r""" + Return the topological closure of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.closure() is segment + True + + """ return self._polyhedron def _repr_(self): - """ + r""" Return a description of ``self``. EXAMPLES:: @@ -46,3 +121,51 @@ def _repr_(self): if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] return 'Relative interior of ' + repr_P + + def __eq__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment == ri_segment2 + True + + """ + return self._polyhedron == other._polyhedron + + def __ne__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + TESTS:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment != ri_segment2 + False + + """ + return self._polyhedron != other._polyhedron From 8f38e0475edfd5913bf25ede90162b04597db64c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 13:02:44 -0700 Subject: [PATCH 509/706] ConvexRationalPolyhedralCone.interior, relative_interior: Add doctests --- src/sage/geometry/cone.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 29007febb11..6d2a49eec66 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1725,6 +1725,34 @@ def interior_contains(self, *args): def interior(self): r""" Return the interior of ``self``. + + OUTPUT: + + - either ``self``, an empty polyhedron, or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.interior() + The empty polyhedron in ZZ^3 + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.interior() + The empty polyhedron in ZZ^2 + + sage: K = cones.nonnegative_orthant(2); K + 2-d cone in 2-d lattice N + sage: K.interior() + Relative interior of 2-d cone in 2-d lattice N + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.interior() is K2 + True + """ if self.is_solid(): return self.relative_interior() @@ -1775,6 +1803,29 @@ def relative_interior_contains(self, *args): def relative_interior(self): r""" Return the relative interior of ``self``. + + OUTPUT: + + - either ``self`` or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.relative_interior() + Relative interior of 2-d cone in 3-d lattice N + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.relative_interior() is origin + True + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.relative_interior() is K2 + True + """ if self.is_trivial() or self.is_full_space(): return self From 669a161dfb16a5e725183df86b30619a452f3e6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 15:36:10 -0700 Subject: [PATCH 510/706] ConvexSet_{base,closed,relatively_open}: New; make Polyhedron_base, LatticePolytopeClass, ConvexRationalPolyhedralCone subclasses --- src/sage/geometry/cone.py | 3 +- src/sage/geometry/convex_set.py | 157 ++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 5 +- src/sage/geometry/polyhedron/base.py | 3 +- 4 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/sage/geometry/convex_set.py diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..243314c173c 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -213,6 +213,7 @@ from sage.structure.all import SageObject, parent from sage.structure.richcmp import richcmp_method, richcmp from sage.geometry.integral_points import parallelotope_points +from sage.geometry.convex_set import ConvexSet_closed from sage.misc.lazy_import import lazy_import from sage.features import PythonModule @@ -1375,7 +1376,7 @@ def classify_cone_2d(ray0, ray1, check=True): # and ``ambient_ray_indices`` keyword parameters. See ``intersection`` method # for an example why this is needed. @richcmp_method -class ConvexRationalPolyhedralCone(IntegralRayCollection, Container): +class ConvexRationalPolyhedralCone(IntegralRayCollection, Container, ConvexSet_closed): r""" Create a convex rational polyhedral cone. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py new file mode 100644 index 00000000000..ed8a72ca225 --- /dev/null +++ b/src/sage/geometry/convex_set.py @@ -0,0 +1,157 @@ +r""" +Convex Sets +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.misc.abstract_method import abstract_method + +class ConvexSet_base(SageObject): + + """ + Abstract base class for convex sets. + """ + + @abstract_method + def is_empty(self): + r""" + Test whether ``self`` is the empty set + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_universe(self): + r""" + Test whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_full_dimensional(self): + r""" + Return whether ``self`` is full dimensional. + + OUTPUT: + + Boolean. Whether the polyhedron is not contained in any strict + affine subspace. + + """ + + @abstract_method + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + if self.is_open(): + return True + raise NotImplementedError + + @abstract_method + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + + def closure(self): + r""" + Return the topological closure of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + def interior(self): + r""" + Return the topological interior of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + +class ConvexSet_closed(ConvexSet_base): + + r""" + Abstract base class for closed convex sets. + """ + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + """ + return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() + + +class ConvexSet_relatively_open(ConvexSet_base): + + r""" + Abstract base class for relatively open sets. + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index cc5d4303897..0456f5cc14b 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,6 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp +from sage.geometry.convex_set import ConvexSet_closed from copy import copy from collections.abc import Hashable @@ -464,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(SageObject, Hashable): +class LatticePolytopeClass(ConvexSet_closed, Hashable): r""" Create a lattice polytope. @@ -517,6 +518,8 @@ def __init__(self, points=None, compute_vertices=None, sage: LatticePolytope([(1,2,3), (4,5,6)]) # indirect test 1-d lattice polytope in 3-d lattice M + sage: TestSuite(_).run() + """ if ambient is None: self._ambient = self diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..5cfb0278aef 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,6 +51,7 @@ from sage.functions.other import sqrt, floor, ceil from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.graphs.graph import Graph +from sage.geometry.convex_set import ConvexSet_closed from .constructor import Polyhedron from sage.categories.sets_cat import EmptySetError @@ -98,7 +99,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Element): +class Polyhedron_base(Element, ConvexSet_closed): """ Base class for Polyhedron objects From 39e1f18e81532191faf2cf48f431005f79f53fa9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 20:06:35 -0700 Subject: [PATCH 511/706] MinMax_base: Fix for numeric arguments --- src/sage/functions/min_max.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index f38c991d405..746c1067acc 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -47,8 +47,13 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): sage: max_symbolic(3, 5, x) # indirect doctest max(x, 5) + sage: max_symbolic([5.0r]) # indirect doctest + 5.0 sage: min_symbolic(3, 5, x) min(x, 3) + sage: min_symbolic([5.0r]) # indirect doctest + 5.0 + """ # __call__ ensures that if args is a singleton, the element is iterable arg_is_iter = False @@ -64,7 +69,10 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): symb_args.append(x) else: num_non_symbolic_args += 1 - res = builtin_f(res, x) + if res is None: + res = x + else: + res = builtin_f(res, x) # if no symbolic arguments, return the result if len(symb_args) == 0: From 7fe6cf9eba3c30bc6157ce3de897eb2f6363cb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 7 Jun 2021 15:59:23 +1200 Subject: [PATCH 512/706] Replace PythonModule feature by StaticFile feature. --- src/sage/features/databases.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/features/databases.py b/src/sage/features/databases.py index 82d81be8c83..c21aa40be87 100644 --- a/src/sage/features/databases.py +++ b/src/sage/features/databases.py @@ -87,7 +87,10 @@ def __init__(self): sage: isinstance(DatabaseKnotInfo(), DatabaseKnotInfo) True """ - PythonModule.__init__(self, 'sage.knots.knotinfo', spkg='database_knotinfo',) - import os from sage.env import SAGE_SHARE + StaticFile.__init__(self, "Knot info database", + filename='knotinfo/num_knots.sobj', + search_path=SAGE_SHARE, + spkg='database_knotinfo') + import os self._sobj_path = os.path.join(SAGE_SHARE, 'knotinfo') From e67f75377b140110ccd28fc9fa54b6e090b077cd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 20:33:52 -0700 Subject: [PATCH 513/706] ConvexRationalPolyhedralCone: Add method is_empty and aliases is_full_dimensional, is_universe --- src/sage/geometry/cone.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 243314c173c..8838d58b654 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3191,6 +3191,14 @@ def is_smooth(self): return False return self.rays().matrix().elementary_divisors() == [1] * self.nrays() + def is_empty(self): + """ + Return whether ``self`` is the empty set. + + Because a cone always contains the origin, this method returns ``False``. + """ + return False + def is_trivial(self): """ Checks if the cone has no rays. @@ -4448,6 +4456,8 @@ def is_solid(self): A cone is said to be solid if it has nonempty interior. That is, if its extreme rays span the entire ambient space. + An alias is :meth:`is_full_dimensional`. + OUTPUT: ``True`` if this cone is solid, and ``False`` otherwise. @@ -4487,6 +4497,8 @@ def is_solid(self): """ return (self.dim() == self.lattice_dim()) + is_full_dimensional = is_solid + def is_proper(self): r""" Check if this cone is proper. @@ -4537,6 +4549,8 @@ def is_full_space(self): r""" Check if this cone is equal to its ambient vector space. + An alias is :meth:`is_universe`. + OUTPUT: ``True`` if this cone equals its entire ambient vector @@ -4574,6 +4588,8 @@ def is_full_space(self): """ return self.linear_subspace() == self.lattice().vector_space() + is_universe = is_full_space + def lineality(self): r""" Return the lineality of this cone. From 770fdcd9125c8bb78dd45573613e6489220664a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:52:29 -0700 Subject: [PATCH 514/706] LatticePolytopeClass, IntegralRayCollection: Make ambient_dim an alias for lattice_dim --- src/sage/geometry/cone.py | 4 ++++ src/sage/geometry/convex_set.py | 5 +++++ src/sage/geometry/lattice_polytope.py | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8838d58b654..07df0fc575e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1010,6 +1010,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -1024,6 +1026,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def nrays(self): r""" Return the number of rays of ``self``. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index ed8a72ca225..197aecac9be 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -42,6 +42,11 @@ def is_universe(self): """ @abstract_method + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + """ + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 0456f5cc14b..10866bc8c31 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2599,6 +2599,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -2613,6 +2615,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. From b2ac6396c075f8ad4d71a379de367fd9cbaa3561 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:22 -0700 Subject: [PATCH 515/706] ConvexSet_base.dim: New --- src/sage/geometry/convex_set.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 197aecac9be..56189f2518c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -21,7 +21,6 @@ class ConvexSet_base(SageObject): Abstract base class for convex sets. """ - @abstract_method def is_empty(self): r""" Test whether ``self`` is the empty set @@ -30,8 +29,8 @@ def is_empty(self): Boolean. """ + return self.dim() < 0 - @abstract_method def is_universe(self): r""" Test whether ``self`` is the whole ambient space @@ -40,6 +39,15 @@ def is_universe(self): Boolean. """ + if not self.is_full_dimensional(): + return False + raise NotImplementedError + + @abstract_method + def dim(self): + r""" + Return the dimension of ``self``. + """ @abstract_method def ambient_dim(self): @@ -55,8 +63,8 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. - """ + return self.dim() == self.ambient_dim() @abstract_method def is_open(self): @@ -105,7 +113,7 @@ def interior(self): r""" Return the topological interior of ``self``. """ - if self.is_closed(): + if self.is_open(): return self raise NotImplementedError From ccc84213906ff6bf34d2e5c6b50d146a2901ec9c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:52 -0700 Subject: [PATCH 516/706] ConvexSet_base.is_compact, ConvexSet_compact: New --- src/sage/geometry/convex_set.py | 43 +++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 4 +-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 56189f2518c..37ef89ae39e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -101,6 +101,21 @@ def is_closed(self): """ + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + if not self.is_closed(): + return False + if self.dimension() < 1: + return True + raise NotImplementedError + def closure(self): r""" Return the topological closure of ``self``. @@ -152,6 +167,34 @@ def is_open(self): return self.is_empty() or self.is_universe() +class ConvexSet_compact(ConvexSet_closed): + + r""" + Abstract base class for compact convex sets. + """ + + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + return self.ambient_dim() == 0 and not self.is_empty() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + return True + + class ConvexSet_relatively_open(ConvexSet_base): r""" diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 10866bc8c31..a063657e30a 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,7 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp -from sage.geometry.convex_set import ConvexSet_closed +from sage.geometry.convex_set import ConvexSet_compact from copy import copy from collections.abc import Hashable @@ -465,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(ConvexSet_closed, Hashable): +class LatticePolytopeClass(ConvexSet_compact, Hashable): r""" Create a lattice polytope. From 0091a4e879a1c10e7eb0feb4f0408337ab7ad04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 7 Jun 2021 19:16:33 +1200 Subject: [PATCH 517/706] Use PythonModule, but this time the right one --- src/sage/features/databases.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/features/databases.py b/src/sage/features/databases.py index c21aa40be87..0dcef2751e2 100644 --- a/src/sage/features/databases.py +++ b/src/sage/features/databases.py @@ -87,10 +87,4 @@ def __init__(self): sage: isinstance(DatabaseKnotInfo(), DatabaseKnotInfo) True """ - from sage.env import SAGE_SHARE - StaticFile.__init__(self, "Knot info database", - filename='knotinfo/num_knots.sobj', - search_path=SAGE_SHARE, - spkg='database_knotinfo') - import os - self._sobj_path = os.path.join(SAGE_SHARE, 'knotinfo') + PythonModule.__init__(self, 'database_knotinfo', spkg='database_knotinfo') From 99070951e1888e5a0bafd19cd1d88215536b67e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Mon, 7 Jun 2021 19:28:15 +1200 Subject: [PATCH 518/706] Define _sobj_path in knotinfo_db.py itself --- src/sage/databases/knotinfo_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 29433f9ee55..9eb36c4d575 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -370,8 +370,9 @@ def __init__(self, install=False): self._num_knots = None from sage.features.databases import DatabaseKnotInfo + from sage.env import DOT_SAGE self._feature = DatabaseKnotInfo() - self._sobj_path = self._feature._sobj_path + self._sobj_path = os.path.join(DOT_SAGE, 'knotinfo') def reset_filecache(self): r""" From 923196adb6c2e75ef44c4e9ce4df171cf607585c Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Mon, 7 Jun 2021 16:54:18 +0200 Subject: [PATCH 519/706] #31923: initialize the inverse of the inverse to self --- src/sage/manifolds/chart.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 6fb017d0082..24f73f7a585 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -3123,7 +3123,11 @@ def __call__(self, *coords): def inverse(self): r""" - Compute the inverse coordinate transformation. + Return the inverse coordinate transformation. + + If the inverse is not already known, it is computed here. If the + computation fails, the inverse can be set by hand via the method + :meth:`set_inverse`. OUTPUT: @@ -3156,6 +3160,16 @@ def inverse(self): (Chart (M, (x, y)), Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + The result is cached:: + + sage: xy_to_uv.inverse() is uv_to_xy + True + + We have as well:: + + sage: uv_to_xy.inverse() is xy_to_uv + True + """ from sage.symbolic.relation import solve from sage.symbolic.assumptions import assumptions @@ -3236,6 +3250,7 @@ def inverse(self): "manually") x2_to_x1 = list_x2_to_x1[0] self._inverse = type(self)(self._chart2, self._chart1, *x2_to_x1) + self._inverse._inverse = self # Some cleaning: the local symbolic variables (xxxx0, xxxx1, ...) are # removed from the list of assumptions for asm in assumptions(): @@ -3338,7 +3353,14 @@ def set_inverse(self, *transformations, **kwds): u == u *passed* v == v *passed* - TESTS:: + TESTS: + + Check that :trac:`31923` is fixed:: + + sage: X1_to_X2.inverse().inverse() is X1_to_X2 + True + + Check of keyword arguments:: sage: X1_to_X2.set_inverse((u+v)/2, (u-v)/2, bla=3) Traceback (most recent call last): @@ -3353,6 +3375,7 @@ def set_inverse(self, *transformations, **kwds): "argument".format(unknown_key)) self._inverse = type(self)(self._chart2, self._chart1, *transformations) + self._inverse._inverse = self if check: infos = ["Check of the inverse coordinate transformation:"] x1 = self._chart1._xx From 5f9c8526d013234f9caf33c50b9117812fbaad55 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 11:55:20 -0700 Subject: [PATCH 520/706] RelativeInterior.interior: New --- src/sage/geometry/relative_interior.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index d715b9f708d..65fd898ffd1 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -71,6 +71,27 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def interior(self): + r""" + Return the interior of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.interior() + The empty polyhedron in ZZ^2 + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: ri_octant.interior() is ri_octant + True + """ + return self._polyhedron.interior() + def relative_interior(self): r""" Return the relative interior of ``self``. From 5c089ec4926664d426e5d46735cb8c1a9b1d1017 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:01:39 -0700 Subject: [PATCH 521/706] RelativeInterior.__eq__, __ne__: Handle comparisons with objects of other types --- src/sage/geometry/relative_interior.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 65fd898ffd1..73e65d4361e 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -149,7 +149,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object EXAMPLES:: @@ -164,7 +164,15 @@ def __eq__(self, other): sage: ri_segment == ri_segment2 True + TESTS:: + + sage: empty = Polyhedron(ambient_dim=2) + sage: ri_segment == empty + False + """ + if type(self) != type(other): + return False return self._polyhedron == other._polyhedron def __ne__(self, other): @@ -173,7 +181,7 @@ def __ne__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object TESTS:: @@ -189,4 +197,4 @@ def __ne__(self, other): False """ - return self._polyhedron != other._polyhedron + return not (self == other) From 86ce301c02936f4b56f6ab821520e8542bd5fbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:38:05 -0700 Subject: [PATCH 522/706] Polyhedron_base.is_relatively_open: New; fix relative_interior for affine subspaces --- src/sage/geometry/polyhedron/base.py | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 985a9f5c6c5..b49ab2a8d38 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8484,6 +8484,39 @@ def interior_contains(self, point): return False return True + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]); P + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: P.is_relatively_open() + False + + sage: P0 = Polyhedron(vertices=[[1, 2]]); P0 + A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P0.is_relatively_open() + True + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.is_relatively_open() + True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]); Line + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: Line.is_relatively_open() + True + + """ + return not self.inequalities() + @cached_method def relative_interior(self): """ @@ -8507,8 +8540,12 @@ def relative_interior(self): sage: Empty = Polyhedron(ambient_dim=2) sage: Empty.relative_interior() is Empty True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]) + sage: Line.relative_interior() is Line + True """ - if self.dim() <= 0 or self.is_universe(): + if self.is_relatively_open(): return self return RelativeInterior(self) From 4bac2fe2db4519478d6e5633e86b29098d9aa0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:53:33 -0700 Subject: [PATCH 523/706] RelativeInterior: Subclass ConvexSet_relatively_open, add missing methods --- src/sage/geometry/convex_set.py | 13 +++++- src/sage/geometry/relative_interior.py | 59 +++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 37ef89ae39e..5a531dd9c44 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -203,7 +203,7 @@ class ConvexSet_relatively_open(ConvexSet_base): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. OUTPUT: @@ -211,3 +211,14 @@ def is_relatively_open(self): """ return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_full_dimensional() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..c41012835eb 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -12,9 +12,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.sage_object import SageObject +from sage.geometry.convex_set import ConvexSet_relatively_open -class RelativeInterior(SageObject): +class RelativeInterior(ConvexSet_relatively_open): r""" The relative interior of a polyhedron or cone @@ -71,6 +71,40 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.ambient_dim() + 2 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_dim() + 2 + + """ + return self._polyhedron.ambient_dim() + + def dim(self): + r""" + Return the dimension of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.dim() + 1 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.dim() + 1 + + """ + return self._polyhedron.dim() + def interior(self): r""" Return the interior of ``self``. @@ -125,6 +159,27 @@ def closure(self): """ return self._polyhedron + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_closed() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_relatively_open() + return False + def _repr_(self): r""" Return a description of ``self``. From 216cb811d16bdf1bedfb2d7e04339ed4387f7212 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:01:25 -0700 Subject: [PATCH 524/706] ConvexRationalPolyhedralCone.is_relatively_open: New, fix ConvexRationalPolyhedralCone.relative_interior for linear subspaces --- src/sage/geometry/cone.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 6d2a49eec66..46b7d88b2e9 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1821,13 +1821,18 @@ def relative_interior(self): sage: origin.relative_interior() is origin True + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.relative_interior() is K1 + True + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True """ - if self.is_trivial() or self.is_full_space(): + if self.is_relatively_open(): return self return RelativeInterior(self) @@ -4724,6 +4729,29 @@ def lineality(self): """ return self.linear_subspace().dimension() + def is_relatively_open(self): + + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.is_relatively_open() + False + + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.is_relatively_open() + True + + """ + return self.lineality() == self.dim() + @cached_method def discrete_complementarity_set(self): r""" From 44cde1e3dbea9086e75f235c0f61164278e4e451 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:24:42 -0700 Subject: [PATCH 525/706] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/relative_interior --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 3d027326933..88dbc812caf 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -107,6 +107,7 @@ Miscellaneous sage/geometry/linear_expression sage/geometry/newton_polygon + sage/geometry/relative_interior sage/geometry/ribbon_graph sage/geometry/pseudolines sage/geometry/voronoi_diagram From fa4c2d238a8f16a61f02e7cfa89ba3f17d8f4980 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 15:07:47 -0700 Subject: [PATCH 526/706] Whitespace fixes --- src/sage/geometry/cone.py | 3 --- src/sage/geometry/relative_interior.py | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 46b7d88b2e9..8e3e73d75dd 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1830,7 +1830,6 @@ def relative_interior(self): 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True - """ if self.is_relatively_open(): return self @@ -4730,7 +4729,6 @@ def lineality(self): return self.linear_subspace().dimension() def is_relatively_open(self): - r""" Return whether ``self`` is relatively open. @@ -4748,7 +4746,6 @@ def is_relatively_open(self): 1-d cone in 2-d lattice N sage: K1.is_relatively_open() True - """ return self.lineality() == self.dim() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..0850ea4860c 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -14,8 +14,8 @@ from sage.structure.sage_object import SageObject -class RelativeInterior(SageObject): +class RelativeInterior(SageObject): r""" The relative interior of a polyhedron or cone @@ -34,7 +34,6 @@ class RelativeInterior(SageObject): sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) sage: octant.relative_interior() Relative interior of 3-d cone in 3-d lattice N - """ def __init__(self, polyhedron): @@ -51,7 +50,6 @@ def __init__(self, polyhedron): sage: P = Polyhedron() sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() - """ self._polyhedron = polyhedron @@ -121,7 +119,6 @@ def closure(self): a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.closure() is segment True - """ return self._polyhedron @@ -169,7 +166,6 @@ def __eq__(self, other): sage: empty = Polyhedron(ambient_dim=2) sage: ri_segment == empty False - """ if type(self) != type(other): return False @@ -195,6 +191,5 @@ def __ne__(self, other): a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices sage: ri_segment != ri_segment2 False - """ return not (self == other) From dede9de1a8e01114b289149bccb51c466223d027 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 16:47:05 -0700 Subject: [PATCH 527/706] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/convex_set --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 88dbc812caf..6975fa144a7 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -105,6 +105,7 @@ Miscellaneous .. toctree:: :maxdepth: 1 + sage/geometry/convex_set sage/geometry/linear_expression sage/geometry/newton_polygon sage/geometry/relative_interior From 5cb33ed6000ecedda11bc556820a8eb8367c7f3e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 8 Jun 2021 10:39:46 +1000 Subject: [PATCH 528/706] Fixing doctest with different ordering. --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 28cb717328e..f7b00495313 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -812,7 +812,7 @@ def intersection_poset(self, element_label="int"): sage: A = hyperplane_arrangements.coordinate(2) sage: L = A.intersection_poset(element_label='subspace'); L Finite poset containing 4 elements - sage: sorted(L) + sage: sorted(L, key=lambda S: (S.dimension(), S.linear_part().basis_matrix())) [Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 0 over Rational Field @@ -821,11 +821,11 @@ def intersection_poset(self, element_label="int"): p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [1 0], Affine space p + W where: + [0 1], Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: - [0 1], Affine space p + W where: + [1 0], Affine space p + W where: p = (0, 0) W = Vector space of dimension 2 over Rational Field] """ From e1f4093f60c7349e740fc79302e9808057b05151 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Mon, 7 Jun 2021 18:49:23 -0700 Subject: [PATCH 529/706] trac 31925: move files into sage/topology edit to reflect their new locations --- src/doc/en/reference/homology/index.rst | 24 +--- src/doc/en/reference/topology/conf.py | 1 + src/doc/en/reference/topology/index.rst | 26 ++++ .../{homology => topology}/media/klein.png | Bin .../{homology => topology}/media/rp2.png | Bin .../media/simplices.png | Bin .../{homology => topology}/media/torus.png | Bin .../media/torus_labelled.png | Bin src/sage/all.py | 1 + src/sage/categories/finite_monoids.py | 2 +- src/sage/categories/simplicial_sets.py | 22 ++-- src/sage/combinat/posets/posets.py | 6 +- src/sage/combinat/subword_complex.py | 2 +- src/sage/ext_data/kenzo/README.txt | 2 +- src/sage/geometry/polyhedron/base.py | 2 +- src/sage/geometry/triangulation/element.py | 4 +- src/sage/graphs/generators/random.py | 2 +- src/sage/graphs/graph.py | 4 +- .../homology/algebraic_topological_model.py | 4 +- src/sage/homology/all.py | 16 --- src/sage/homology/chains.py | 18 +-- src/sage/homology/homology_morphism.py | 4 +- .../homology_vector_space_with_basis.py | 16 +-- src/sage/homology/tests.py | 2 +- src/sage/interfaces/chomp.py | 12 +- src/sage/interfaces/kenzo.py | 10 +- .../differentiable/examples/sphere.py | 2 +- src/sage/matroids/matroid.pyx | 2 +- src/sage/sandpiles/sandpile.py | 2 +- src/sage/schemes/toric/divisor.py | 4 +- src/sage/topology/__init__.py | 1 + src/sage/topology/all.py | 16 +++ .../{homology => topology}/cell_complex.py | 36 +++--- .../{homology => topology}/cubical_complex.py | 52 ++++---- .../{homology => topology}/delta_complex.py | 6 +- .../simplicial_complex.py | 22 ++-- .../simplicial_complex_catalog.py} | 53 ++++---- .../simplicial_complex_examples.py} | 18 +-- .../simplicial_complex_homset.py | 6 +- .../simplicial_complex_morphism.py | 8 +- .../{homology => topology}/simplicial_set.py | 122 +++++++++--------- .../simplicial_set_catalog.py | 0 .../simplicial_set_constructions.py | 56 ++++---- .../simplicial_set_examples.py | 8 +- .../simplicial_set_morphism.py | 16 +-- 45 files changed, 312 insertions(+), 298 deletions(-) create mode 120000 src/doc/en/reference/topology/conf.py create mode 100644 src/doc/en/reference/topology/index.rst rename src/doc/en/reference/{homology => topology}/media/klein.png (100%) rename src/doc/en/reference/{homology => topology}/media/rp2.png (100%) rename src/doc/en/reference/{homology => topology}/media/simplices.png (100%) rename src/doc/en/reference/{homology => topology}/media/torus.png (100%) rename src/doc/en/reference/{homology => topology}/media/torus_labelled.png (100%) create mode 100644 src/sage/topology/__init__.py create mode 100644 src/sage/topology/all.py rename src/sage/{homology => topology}/cell_complex.py (97%) rename src/sage/{homology => topology}/cubical_complex.py (97%) rename src/sage/{homology => topology}/delta_complex.py (99%) rename src/sage/{homology => topology}/simplicial_complex.py (99%) rename src/sage/{homology/simplicial_complexes_catalog.py => topology/simplicial_complex_catalog.py} (58%) rename src/sage/{homology/examples.py => topology/simplicial_complex_examples.py} (98%) rename src/sage/{homology => topology}/simplicial_complex_homset.py (96%) rename src/sage/{homology => topology}/simplicial_complex_morphism.py (99%) rename src/sage/{homology => topology}/simplicial_set.py (97%) rename src/sage/{homology => topology}/simplicial_set_catalog.py (100%) rename src/sage/{homology => topology}/simplicial_set_constructions.py (98%) rename src/sage/{homology => topology}/simplicial_set_examples.py (99%) rename src/sage/{homology => topology}/simplicial_set_morphism.py (99%) diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index bf98e0841fd..32424286980 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -1,12 +1,8 @@ -Cell complexes and their homology -================================= +Chain complexes and homology +============================ -Sage includes some tools for algebraic topology: from the algebraic -side, chain complexes and their homology, and from the topological -side, simplicial complexes, `\Delta`-complexes, cubical complexes, and -simplicial sets. A class of generic cell complexes is also available, -mainly for developers who want to use it as a base for other types of -cell complexes. +Sage includes some tools for algebraic topology, and in particular +computing homology groups. .. toctree:: :maxdepth: 2 @@ -16,18 +12,6 @@ cell complexes. sage/homology/chain_complex_morphism sage/homology/chain_homotopy sage/homology/chain_complex_homspace - sage/homology/simplicial_complex - sage/homology/simplicial_complex_morphism - sage/homology/simplicial_complex_homset - sage/homology/examples - sage/homology/delta_complex - sage/homology/cubical_complex - sage/homology/simplicial_set - sage/homology/simplicial_set_constructions - sage/homology/simplicial_set_examples - sage/homology/simplicial_set_catalog - sage/homology/simplicial_set_morphism - sage/homology/cell_complex sage/homology/koszul_complex sage/homology/hochschild_complex sage/homology/homology_group diff --git a/src/doc/en/reference/topology/conf.py b/src/doc/en/reference/topology/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/topology/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/topology/index.rst b/src/doc/en/reference/topology/index.rst new file mode 100644 index 00000000000..bf3eff5149b --- /dev/null +++ b/src/doc/en/reference/topology/index.rst @@ -0,0 +1,26 @@ +Topology +======== + +Sage includes some tools for topology, and in particular cell +complexes: simplicial complexes, `\Delta`-complexes, cubical +complexes, and simplicial sets. A class of generic cell complexes is +also available, mainly for developers who want to use it as a base for +other types of cell complexes. + +.. toctree:: + :maxdepth: 2 + + sage/topology/simplicial_complex + sage/topology/simplicial_complex_morphism + sage/topology/simplicial_complex_homset + sage/topology/simplicial_complex_examples + sage/topology/delta_complex + sage/topology/cubical_complex + sage/topology/simplicial_set + sage/topology/simplicial_set_constructions + sage/topology/simplicial_set_examples + sage/topology/simplicial_set_catalog + sage/topology/simplicial_set_morphism + sage/topology/cell_complex + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/homology/media/klein.png b/src/doc/en/reference/topology/media/klein.png similarity index 100% rename from src/doc/en/reference/homology/media/klein.png rename to src/doc/en/reference/topology/media/klein.png diff --git a/src/doc/en/reference/homology/media/rp2.png b/src/doc/en/reference/topology/media/rp2.png similarity index 100% rename from src/doc/en/reference/homology/media/rp2.png rename to src/doc/en/reference/topology/media/rp2.png diff --git a/src/doc/en/reference/homology/media/simplices.png b/src/doc/en/reference/topology/media/simplices.png similarity index 100% rename from src/doc/en/reference/homology/media/simplices.png rename to src/doc/en/reference/topology/media/simplices.png diff --git a/src/doc/en/reference/homology/media/torus.png b/src/doc/en/reference/topology/media/torus.png similarity index 100% rename from src/doc/en/reference/homology/media/torus.png rename to src/doc/en/reference/topology/media/torus.png diff --git a/src/doc/en/reference/homology/media/torus_labelled.png b/src/doc/en/reference/topology/media/torus_labelled.png similarity index 100% rename from src/doc/en/reference/homology/media/torus_labelled.png rename to src/doc/en/reference/topology/media/torus_labelled.png diff --git a/src/sage/all.py b/src/sage/all.py index 52d59fadf2e..e3813c2adab 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -183,6 +183,7 @@ from sage.dynamics.all import * from sage.homology.all import * +from sage.topology.all import * from sage.quadratic_forms.all import * diff --git a/src/sage/categories/finite_monoids.py b/src/sage/categories/finite_monoids.py index ffc959d3b6b..69e4a6bc83e 100644 --- a/src/sage/categories/finite_monoids.py +++ b/src/sage/categories/finite_monoids.py @@ -157,7 +157,7 @@ def nerve(self): 3: Vector space of dimension 1 over Finite Field of size 5, 4: Vector space of dimension 1 over Finite Field of size 5} """ - from sage.homology.simplicial_set_examples import Nerve + from sage.topology.simplicial_set_examples import Nerve return Nerve(self) def rhodes_radical_congruence(self, base_ring=None): diff --git a/src/sage/categories/simplicial_sets.py b/src/sage/categories/simplicial_sets.py index 7f40f791097..56ca3e7b76d 100644 --- a/src/sage/categories/simplicial_sets.py +++ b/src/sage/categories/simplicial_sets.py @@ -85,7 +85,7 @@ def is_pointed(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: e = AbstractSimplex(1) @@ -109,7 +109,7 @@ def set_base_point(self, point): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v_0') sage: w = AbstractSimplex(0, name='w_0') sage: e = AbstractSimplex(1) @@ -136,7 +136,7 @@ def set_base_point(self, point): ... ValueError: the point is not a simplex in this simplicial set """ - from sage.homology.simplicial_set import SimplicialSet + from sage.topology.simplicial_set import SimplicialSet if point.dimension() != 0: raise ValueError('the "point" is not a zero-simplex') if point not in self._simplices: @@ -158,7 +158,7 @@ def one(self): Simplicial set endomorphism of Torus Defn: Identity map """ - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism return SimplicialSetMorphism(domain=self.domain(), codomain=self.codomain(), identity=True) @@ -196,7 +196,7 @@ def base_point(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='*') sage: e = AbstractSimplex(1) sage: S1 = SimplicialSet({e: (v, v)}, base_point=v) @@ -220,7 +220,7 @@ def base_point_map(self, domain=None): - ``domain`` -- optional, default ``None``. Use this to specify a particular one-point space as the domain. The default behavior is to use the - :func:`sage.homology.simplicial_set.Point` + :func:`sage.topology.simplicial_set.Point` function to use a standard one-point space. EXAMPLES:: @@ -250,7 +250,7 @@ def base_point_map(self, domain=None): To: Classifying space of Multiplicative Abelian group isomorphic to C5 Defn: Constant map at 1 """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if domain is None: domain = Point() else: @@ -491,7 +491,7 @@ def unset_base_point(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v_0') sage: w = AbstractSimplex(0, name='w_0') sage: e = AbstractSimplex(1) @@ -504,7 +504,7 @@ def unset_base_point(self): sage: Z.is_pointed() False """ - from sage.homology.simplicial_set import SimplicialSet + from sage.topology.simplicial_set import SimplicialSet return SimplicialSet(self.face_data()) def fat_wedge(self, n): @@ -530,7 +530,7 @@ def fat_wedge(self, n): sage: S1.fat_wedge(4).homology() {0: 0, 1: Z x Z x Z x Z, 2: Z^6, 3: Z x Z x Z x Z} """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if n == 0: return Point() if n == 1: @@ -561,6 +561,6 @@ def smash_product(self, *others): sage: X.homology(reduced=False) {0: Z, 1: 0, 2: Z x Z, 3: Z} """ - from sage.homology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite + from sage.topology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite return SmashProductOfSimplicialSets_finite((self,) + others) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 0a2ce11d568..024b89e97cf 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6858,7 +6858,7 @@ def order_complex(self, on_ints=False): sage: P.order_complex(on_ints=True) Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 1, 3), (0, 2, 3)} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex L = self.list() if on_ints: iso = dict([(L[i], i) for i in range(len(L))]) @@ -7052,7 +7052,7 @@ def f_polynomial(self): .. SEEALSO:: :meth:`is_bounded`, :meth:`h_polynomial`, :meth:`order_complex`, - :meth:`sage.homology.cell_complex.GenericCellComplex.f_vector` + :meth:`sage.topology.cell_complex.GenericCellComplex.f_vector` TESTS:: @@ -7120,7 +7120,7 @@ def h_polynomial(self): .. SEEALSO:: :meth:`is_bounded`, :meth:`f_polynomial`, :meth:`order_complex`, - :meth:`sage.homology.simplicial_complex.SimplicialComplex.h_vector` + :meth:`sage.topology.simplicial_complex.SimplicialComplex.h_vector` """ q = polygen(ZZ, 'q') hasse = self._hasse_diagram diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index dd4926c414e..2e447d7076a 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -116,7 +116,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation -from sage.homology.simplicial_complex import SimplicialComplex, Simplex +from sage.topology.simplicial_complex import SimplicialComplex, Simplex from sage.categories.simplicial_complexes import SimplicialComplexes from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.cone import Cone diff --git a/src/sage/ext_data/kenzo/README.txt b/src/sage/ext_data/kenzo/README.txt index 9943fbd25fb..601f7e7f523 100644 --- a/src/sage/ext_data/kenzo/README.txt +++ b/src/sage/ext_data/kenzo/README.txt @@ -10,7 +10,7 @@ Kenzo: - https://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/ - https://github.com/gheber/kenzo -The results for CP^2, CP^3, and CP^4 have been saved in the corresponding text files. The file S4.txt includes the 4-sphere, just for testing purposes. These files can be processed by the function "simplicial_data_from_kenzo_output" in sage/homology/simplicial_set.py. To get a simplicial set structure for CP^n using Kenzo in sbcl, do the following. +The results for CP^2, CP^3, and CP^4 have been saved in the corresponding text files. The file S4.txt includes the 4-sphere, just for testing purposes. These files can be processed by the function "simplicial_data_from_kenzo_output" in sage/topology/simplicial_set.py. To get a simplicial set structure for CP^n using Kenzo in sbcl, do the following. ;; ;; Start Kenzo. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..003461995db 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2764,7 +2764,7 @@ def boundary_complex(self): ... ValueError: self should be compact """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex if not self.is_compact(): raise ValueError("self should be compact") diff --git a/src/sage/geometry/triangulation/element.py b/src/sage/geometry/triangulation/element.py index fe7a6560946..b19b9170258 100644 --- a/src/sage/geometry/triangulation/element.py +++ b/src/sage/geometry/triangulation/element.py @@ -576,7 +576,7 @@ def simplicial_complex(self): OUTPUT: - A :class:`~sage.homology.simplicial_complex.SimplicialComplex`. + A :class:`~sage.topology.simplicial_complex.SimplicialComplex`. EXAMPLES:: @@ -590,7 +590,7 @@ def simplicial_complex(self): sage: sc.homology() {0: 0, 1: 0, 2: 0, 3: 0} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 31c1a8810f5..e06bc8a6d87 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1798,7 +1798,7 @@ def RandomTriangulation(n, set_position=False, k=3): .. SEEALSO:: :meth:`~sage.graphs.graph_generators.GraphGenerators.triangulations`, - :func:`~sage.homology.examples.RandomTwoSphere`. + :func:`~sage.topology.simplicial_complex_examples.RandomTwoSphere`. EXAMPLES:: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 0f1216047a5..c63df92f844 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7037,8 +7037,8 @@ def clique_complex(self): """ if self.is_directed() or self.has_loops() or self.has_multiple_edges(): raise ValueError("Self must be an undirected simple graph to have a clique complex.") - import sage.homology.simplicial_complex - C = sage.homology.simplicial_complex.SimplicialComplex(self.cliques_maximal(), maximality_check=True) + import sage.topology.simplicial_complex + C = sage.topology.simplicial_complex.SimplicialComplex(self.cliques_maximal(), maximality_check=True) C._graph = self return C diff --git a/src/sage/homology/algebraic_topological_model.py b/src/sage/homology/algebraic_topological_model.py index bfa6b18c80f..b14de9bdfa3 100644 --- a/src/sage/homology/algebraic_topological_model.py +++ b/src/sage/homology/algebraic_topological_model.py @@ -76,14 +76,14 @@ def algebraic_topological_model(K, base_ring=None): of `K`, as described in [GDR2003]_ and [PR2015]_. Implementation details: the cell complex `K` must have an - :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` + :meth:`~sage.topology.cell_complex.GenericCellComplex.n_cells` method from which we can extract a list of cells in each dimension. Combining the lists in increasing order of dimension then defines a filtration of the complex: a list of cells in which the boundary of each cell consists of cells earlier in the list. This is required by Pilarczyk and Réal's algorithm. There must also be a - :meth:`~sage.homology.cell_complex.GenericCellComplex.chain_complex` + :meth:`~sage.topology.cell_complex.GenericCellComplex.chain_complex` method, to construct the chain complex `C` associated to this chain complex. diff --git a/src/sage/homology/all.py b/src/sage/homology/all.py index 7708a0cc57c..d9306c19daa 100644 --- a/src/sage/homology/all.py +++ b/src/sage/homology/all.py @@ -1,22 +1,6 @@ - from .chain_complex import ChainComplex from .chain_complex_morphism import ChainComplexMorphism -from .simplicial_complex import SimplicialComplex, Simplex - -from .simplicial_complex_morphism import SimplicialComplexMorphism - -from .delta_complex import DeltaComplex, delta_complexes - -from .cubical_complex import CubicalComplex, cubical_complexes - from sage.misc.lazy_import import lazy_import lazy_import('sage.homology.koszul_complex', 'KoszulComplex') -lazy_import('sage.homology', 'simplicial_complexes_catalog', 'simplicial_complexes') -lazy_import('sage.homology', 'simplicial_set_catalog', 'simplicial_sets') - - -# For taking care of old pickles -from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.homology.examples', 'SimplicialSurface', SimplicialComplex) diff --git a/src/sage/homology/chains.py b/src/sage/homology/chains.py index 7d1ad790137..35df385f23d 100644 --- a/src/sage/homology/chains.py +++ b/src/sage/homology/chains.py @@ -4,7 +4,7 @@ This module implements formal linear combinations of cells of a given cell complex (:class:`Chains`) and their dual (:class:`Cochains`). It -is closely related to the :mod:`sage.homology.chain_complex` +is closely related to the :mod:`sage.topology.chain_complex` module. The main differences are that chains and cochains here are of homogeneous dimension only, and that they reference their cell complex. @@ -235,7 +235,7 @@ def to_complex(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) sage: chain.to_complex() Chain(1:(0, 0, 0, 1)) @@ -261,7 +261,7 @@ def boundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: chain -2*[0,1] x [0,0] + [1,1] x [0,1] @@ -288,7 +288,7 @@ def is_cycle(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_cycle() False @@ -315,7 +315,7 @@ def is_boundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_boundary() False @@ -476,7 +476,7 @@ def to_complex(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) sage: cochain.to_complex() Chain(1:(0, 0, 0, 1)) @@ -502,7 +502,7 @@ def coboundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: cochain -2*\chi_[0,1] x [0,0] + \chi_[1,1] x [0,1] @@ -529,7 +529,7 @@ def is_cocycle(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_cocycle() True @@ -556,7 +556,7 @@ def is_coboundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_coboundary() True diff --git a/src/sage/homology/homology_morphism.py b/src/sage/homology/homology_morphism.py index eb3b8a08ff5..7dfd5fc8b08 100644 --- a/src/sage/homology/homology_morphism.py +++ b/src/sage/homology/homology_morphism.py @@ -32,7 +32,7 @@ from sage.categories.morphism import Morphism from sage.categories.homset import Hom from sage.rings.rational_field import QQ -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex class InducedHomologyMorphism(Morphism): r""" @@ -52,7 +52,7 @@ class InducedHomologyMorphism(Morphism): This is not intended to be used directly by the user, but instead via the method - :meth:`~sage.homology.simplicial_complex_morphism.SimplicialComplexMorphism.induced_homology_morphism`. + :meth:`~sage.topology.simplicial_complex_morphism.SimplicialComplexMorphism.induced_homology_morphism`. EXAMPLES:: diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index 4282e88d273..8861ee20ad9 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -31,8 +31,8 @@ from sage.categories.modules import Modules from sage.combinat.free_module import CombinatorialFreeModule from sage.sets.family import Family -from .simplicial_complex import SimplicialComplex -from .simplicial_set import SimplicialSet_arbitrary +from sage.topology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_set import SimplicialSet_arbitrary class HomologyVectorSpaceWithBasis(CombinatorialFreeModule): r""" @@ -49,10 +49,10 @@ class HomologyVectorSpaceWithBasis(CombinatorialFreeModule): This is not intended to be created directly by the user, but instead via the methods - :meth:`~sage.homology.cell_complex.GenericCellComplex.homology_with_basis` and - :meth:`~sage.homology.cell_complex.GenericCellComplex.cohomology_ring` + :meth:`~sage.topology.cell_complex.GenericCellComplex.homology_with_basis` and + :meth:`~sage.topology.cell_complex.GenericCellComplex.cohomology_ring` for the class of :class:`cell - complexes`. + complexes`. INPUT: @@ -414,9 +414,9 @@ class CohomologyRing(HomologyVectorSpaceWithBasis): This is not intended to be created directly by the user, but instead via the - :meth:`cohomology ring` + :meth:`cohomology ring` of a :class:`cell - complex`. + complex`. INPUT: @@ -570,7 +570,7 @@ def product_on_basis(self, li, ri): and simplicial sets:: - sage: from sage.homology.simplicial_set_examples import RealProjectiveSpace + sage: from sage.topology.simplicial_set_examples import RealProjectiveSpace sage: RP5 = RealProjectiveSpace(5) sage: x = RP5.cohomology_ring(GF(2)).basis()[1,0] sage: x**4 diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index b95adea8e30..467d9f35d59 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -22,7 +22,7 @@ from sage.matrix.constructor import random_matrix from sage.homology.chain_complex import ChainComplex from sage.rings.integer_ring import ZZ -from sage.homology.examples import RandomComplex +from sage.topology.simplicial_complex_examples import RandomComplex def random_chain_complex(level=1): """ diff --git a/src/sage/interfaces/chomp.py b/src/sage/interfaces/chomp.py index 1416a79f70d..00f4b3f93b3 100644 --- a/src/sage/interfaces/chomp.py +++ b/src/sage/interfaces/chomp.py @@ -134,8 +134,8 @@ def __call__(self, program, complex, subcomplex=None, **kwds): {0: 0, 1: Z x Z, 2: Z} """ from sage.misc.temporary_file import tmp_filename - from sage.homology.all import CubicalComplex, cubical_complexes - from sage.homology.all import SimplicialComplex, Simplex + from sage.topology.cubical_complex import CubicalComplex, cubical_complexes + from sage.topology.simplicial_complex import SimplicialComplex, Simplex from sage.homology.chain_complex import HomologyGroup from subprocess import Popen, PIPE from sage.rings.all import QQ, ZZ @@ -476,7 +476,7 @@ def homsimpl(complex=None, subcomplex=None, **kwds): sage: homsimpl(S1.join(S1), generators=True, base_ring=GF(2))[3][1] # optional - CHomP [('L0', 'L1', 'R0', 'R1') + ('L0', 'L1', 'R0', 'R2') + ('L0', 'L1', 'R1', 'R2') + ('L0', 'L2', 'R0', 'R1') + ('L0', 'L2', 'R0', 'R2') + ('L0', 'L2', 'R1', 'R2') + ('L1', 'L2', 'R0', 'R1') + ('L1', 'L2', 'R0', 'R2') + ('L1', 'L2', 'R1', 'R2')] """ - from sage.homology.all import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex help = kwds.get('help', False) if help: return CHomP().help('homsimpl') @@ -529,7 +529,7 @@ def homcubes(complex=None, subcomplex=None, **kwds): sage: homcubes(cubical_complexes.Sphere(1), generators=True, base_ring=GF(2))[1][1] # optional - CHomP [[[1,1] x [0,1]] + [[0,1] x [1,1]] + [[0,1] x [0,0]] + [[0,0] x [0,1]]] """ - from sage.homology.all import CubicalComplex + from sage.topology.cubical_complex import CubicalComplex help = kwds.get('help', False) if help: return CHomP().help('homcubes') @@ -648,7 +648,7 @@ def process_generators_cubical(gen_string, dim): sage: len(process_generators_cubical(s, 1)) # only one generator 1 """ - from sage.homology.cubical_complex import Cube + from sage.topology.cubical_complex import Cube # each dim in gen_string starts with "The generator for # H_3 follows:". So search for "T" to find the # end of the current list of generators. @@ -751,7 +751,7 @@ def process_generators_simplicial(gen_string, dim, complex): sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus()) [[(-1, (1, 6)), (1, (1, 4))]] """ - from sage.homology.all import Simplex + from sage.topology.simplicial_complex import Simplex # each dim in gen_string starts with "The generator for H_3 # follows:". So search for "T" to find the end of the current # list of generators. diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index b501749cb89..e301f9b7899 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -35,7 +35,7 @@ from sage.matrix.all import matrix from sage.homology.chain_complex import ChainComplex -from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet +from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet from sage.libs.ecl import EclObject, ecl_eval, EclListIterator from sage.features.kenzo import Kenzo @@ -1190,7 +1190,7 @@ def KAbstractSimplex(simplex): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: from sage.interfaces.kenzo import KAbstractSimplex,\ ....: SAbstractSimplex # optional - kenzo sage: SAbSm = AbstractSimplex(1, (2,0,3,2,1), name = 'SAbSm') # optional - kenzo @@ -1219,7 +1219,7 @@ def KFiniteSimplicialSet(sset): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: from sage.interfaces.kenzo import KFiniteSimplicialSet # optional - kenzo sage: s0 = AbstractSimplex(0, name='s0') sage: s1 = AbstractSimplex(0, name='s1') @@ -1241,7 +1241,7 @@ def KFiniteSimplicialSet(sset): sage: KS1vS3.homology(3) # optional - kenzo Z """ - from sage.homology.simplicial_set_constructions import ProductOfSimplicialSets + from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets if isinstance(sset, ProductOfSimplicialSets): f0 = KFiniteSimplicialSet(sset.factor(0)) for f1 in sset.factors()[1:]: @@ -1287,7 +1287,7 @@ def SFiniteSimplicialSet(ksimpset, limit): EXAMPLES:: - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set import SimplicialSet sage: from sage.interfaces.kenzo import AbstractSimplex,\ ....: KFiniteSimplicialSet, SFiniteSimplicialSet, Sphere # optional - kenzo sage: s0 = AbstractSimplex(0, name='s0') # optional - kenzo diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index 67b40872c7b..5d1b9d7e72d 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -1145,7 +1145,7 @@ def minimal_triangulation(self): 2 """ - from sage.homology.examples import Sphere as SymplicialSphere + from sage.topology.simplicial_complex_examples import Sphere as SymplicialSphere return SymplicialSphere(self._dim) def center(self): diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 61693cc330a..c0fd7fc8191 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -7800,7 +7800,7 @@ cdef class Matroid(SageObject): Simplicial complex with vertex set (1, 2, 3, 4, 5) and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self.no_broken_circuits_sets(ordering)) def union(self, matroids): diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ad1cc562762..7506047d5ff 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -335,7 +335,7 @@ from sage.graphs.all import DiGraph, Graph from sage.graphs.digraph_generators import digraphs from sage.probability.probability_distribution import GeneralDiscreteDistribution -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex from sage.interfaces.singular import singular from sage.matrix.constructor import matrix, identity_matrix from sage.misc.all import prod, det, tmp_filename, exists, denominator diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index e03b724ae6f..bd4a77b4b38 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -170,7 +170,7 @@ from sage.geometry.cone import is_Cone from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.toric_lattice_element import is_ToricLatticeElement -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex from sage.matrix.constructor import matrix from sage.misc.all import cached_method, flatten, latex, prod from sage.modules.all import vector @@ -1552,7 +1552,7 @@ def _sheaf_complex(self, m): OUTPUT: - - :class:`simplicial complex `. + - :class:`simplicial complex `. EXAMPLES:: diff --git a/src/sage/topology/__init__.py b/src/sage/topology/__init__.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/sage/topology/__init__.py @@ -0,0 +1 @@ + diff --git a/src/sage/topology/all.py b/src/sage/topology/all.py new file mode 100644 index 00000000000..c4451e79f3c --- /dev/null +++ b/src/sage/topology/all.py @@ -0,0 +1,16 @@ +from .simplicial_complex import SimplicialComplex, Simplex + +from .simplicial_complex_morphism import SimplicialComplexMorphism + +from .delta_complex import DeltaComplex, delta_complexes + +from .cubical_complex import CubicalComplex, cubical_complexes + +from sage.misc.lazy_import import lazy_import +lazy_import('sage.topology', 'simplicial_complex_catalog', 'simplicial_complexes') +lazy_import('sage.topology', 'simplicial_set_catalog', 'simplicial_sets') + + +# # For taking care of old pickles +# from sage.misc.persist import register_unpickle_override +# register_unpickle_override('sage.topology.simplicial_complex_examples', 'SimplicialSurface', SimplicialComplex) diff --git a/src/sage/homology/cell_complex.py b/src/sage/topology/cell_complex.py similarity index 97% rename from src/sage/homology/cell_complex.py rename to src/sage/topology/cell_complex.py index 01e85104c45..3a518cf585e 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/topology/cell_complex.py @@ -77,7 +77,7 @@ class :class:`~sage.homology.delta_complex.DeltaComplex` It's hard to give informative examples of the base class, since essentially nothing is implemented. :: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() """ def __eq__(self,right): @@ -86,7 +86,7 @@ def __eq__(self,right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A == B # indirect doctest Traceback (most recent call last): @@ -101,7 +101,7 @@ def __ne__(self,right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A != B # indirect doctest Traceback (most recent call last): @@ -136,7 +136,7 @@ def cells(self, subcomplex=None): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.cells() Traceback (most recent call last): @@ -308,7 +308,7 @@ def product(self, right, rename_vertices=True): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.product(B) Traceback (most recent call last): @@ -327,7 +327,7 @@ def disjoint_union(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.disjoint_union(B) Traceback (most recent call last): @@ -347,7 +347,7 @@ def wedge(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.wedge(B) Traceback (most recent call last): @@ -372,7 +372,7 @@ def join(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.join(B) Traceback (most recent call last): @@ -439,7 +439,7 @@ def chain_complex(self, subcomplex=None, augmented=False, EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.chain_complex() Traceback (most recent call last): @@ -561,8 +561,8 @@ def homology(self, dim=None, base_ring=ZZ, subcomplex=None, {0: [], 1: 0, 2: [(Z, sigma_2)]} """ from sage.interfaces.chomp import have_chomp, homcubes, homsimpl - from sage.homology.cubical_complex import CubicalComplex - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.cubical_complex import CubicalComplex + from sage.topology.simplicial_complex import SimplicialComplex from sage.modules.all import VectorSpace from sage.homology.homology_group import HomologyGroup @@ -845,7 +845,7 @@ def algebraic_topological_model(self, base_ring=QQ): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.algebraic_topological_model(QQ) Traceback (most recent call last): @@ -904,7 +904,7 @@ def homology_with_basis(self, base_ring=QQ, cohomology=False): sage: list(H.basis(3)) [h^{3,0}] """ - from .homology_vector_space_with_basis import HomologyVectorSpaceWithBasis + from sage.homology.homology_vector_space_with_basis import HomologyVectorSpaceWithBasis return HomologyVectorSpaceWithBasis(base_ring, self, cohomology) def cohomology_ring(self, base_ring=QQ): @@ -1008,7 +1008,7 @@ def cohomology_ring(self, base_ring=QQ): Cohomology ring of Simplicial complex with 9 vertices and 18 facets over Rational Field """ - from .homology_vector_space_with_basis import CohomologyRing + from sage.homology.homology_vector_space_with_basis import CohomologyRing return CohomologyRing(base_ring, self) @abstract_method @@ -1053,7 +1053,7 @@ def alexander_whitney(self, cell, dim_left): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.alexander_whitney(None, 2) Traceback (most recent call last): @@ -1111,7 +1111,7 @@ def graph(self): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.graph() Traceback (most recent call last): @@ -1164,7 +1164,7 @@ def n_skeleton(self, n): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.n_skeleton(3) Traceback (most recent call last): @@ -1189,7 +1189,7 @@ def _string_constants(self): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: GenericCellComplex()._string_constants() ('Cell', 'cell', 'cells') sage: delta_complexes.Sphere(0)._string_constants() diff --git a/src/sage/homology/cubical_complex.py b/src/sage/topology/cubical_complex.py similarity index 97% rename from src/sage/homology/cubical_complex.py rename to src/sage/topology/cubical_complex.py index a1a1e32cc72..2a0721bdd5e 100644 --- a/src/sage/homology/cubical_complex.py +++ b/src/sage/topology/cubical_complex.py @@ -67,7 +67,7 @@ """ from copy import copy -from sage.homology.cell_complex import GenericCellComplex +from .cell_complex import GenericCellComplex from sage.structure.sage_object import SageObject from sage.rings.integer import Integer from sage.sets.set import Set @@ -107,7 +107,7 @@ class Cube(SageObject): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C [1,2] x [5,5] x [6,7] x [-1,0] sage: C.dimension() # number of nondegenerate intervals @@ -129,7 +129,7 @@ def __init__(self, data): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C # indirect doctest [1,2] x [5,5] x [6,7] x [-1,0] sage: C == loads(dumps(C)) @@ -166,7 +166,7 @@ def tuple(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.tuple() ((1, 2), (5, 5), (6, 7), (-1, 0)) @@ -179,7 +179,7 @@ def is_face(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C2 = Cube([[1,2], [5,], [6,], [-1, 0]]) sage: C1.is_face(C2) @@ -216,7 +216,7 @@ def _translate(self, vec): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C._translate((-12,)) [-11,-10] x [5,5] x [6,7] x [-1,0] @@ -241,7 +241,7 @@ def __getitem__(self, n): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C[2] (6, 7) @@ -256,7 +256,7 @@ def __iter__(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: [x[0] for x in C] [1, 5, 6, -1] @@ -273,7 +273,7 @@ def __add__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,]]) sage: D = Cube([[4], [0,1]]) sage: C.product(D) @@ -298,7 +298,7 @@ def nondegenerate_intervals(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.nondegenerate_intervals() [0, 2, 3] @@ -314,7 +314,7 @@ def dimension(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.dimension() 3 @@ -342,7 +342,7 @@ def face(self, n, upper=True): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C [1,2] x [5,5] x [6,7] x [-1,0] sage: C.face(0) @@ -374,7 +374,7 @@ def faces(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]) sage: C.faces() [[2,2] x [3,4], [1,2] x [4,4], [1,1] x [3,4], [1,2] x [3,3]] @@ -390,7 +390,7 @@ def faces_as_pairs(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]) sage: C.faces_as_pairs() [([2,2] x [3,4], [1,1] x [3,4]), ([1,2] x [4,4], [1,2] x [3,3])] @@ -443,7 +443,7 @@ def _compare_for_gluing(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[0,1], [3], [4], [6,7]]) sage: C2 = Cube([[2], [7,8], [9], [1,2], [0], [5]]) sage: C1._compare_for_gluing(C2) @@ -529,7 +529,7 @@ def _triangulation_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]); C [1,2] x [3,4] sage: C._triangulation_() @@ -538,7 +538,7 @@ def _triangulation_(self): sage: len(C._triangulation_()) 6 """ - from sage.homology.simplicial_complex import Simplex + from .simplicial_complex import Simplex if self.dimension() < 0: # the empty cube return [Simplex(())] # the empty simplex v = tuple([max(j) for j in self.tuple()]) @@ -576,7 +576,7 @@ def alexander_whitney(self, dim): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[0,1], [3,4]]) sage: C1.alexander_whitney(0) [(1, [0,0] x [3,3], [0,1] x [3,4])] @@ -621,7 +621,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -640,7 +640,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -660,7 +660,7 @@ def __lt__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -696,7 +696,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: hash(C1) == hash(((1,1),(2,3),(4,5))) True @@ -709,7 +709,7 @@ def _repr_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C1 [1,1] x [2,3] x [4,5] @@ -725,7 +725,7 @@ def _latex_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: latex(C1) [1,1] \times [2,3] \times [4,5] @@ -1664,7 +1664,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model + from sage.homology.algebraic_topological_model import algebraic_topological_model if base_ring is None: base_ring = QQ return algebraic_topological_model(self, base_ring) @@ -1741,7 +1741,7 @@ def _simplicial_(self): sage: SimplicialComplex(S4) # calls S4._simplicial_() Simplicial complex with 32 vertices and 240 facets """ - from sage.homology.simplicial_complex import SimplicialComplex + from .simplicial_complex import SimplicialComplex simplices = [] for C in self.maximal_cells(): simplices.extend(C._triangulation_()) diff --git a/src/sage/homology/delta_complex.py b/src/sage/topology/delta_complex.py similarity index 99% rename from src/sage/homology/delta_complex.py rename to src/sage/topology/delta_complex.py index 91c3bca27f0..9e4b08262cc 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/topology/delta_complex.py @@ -50,13 +50,13 @@ """ from copy import copy -from sage.homology.cell_complex import GenericCellComplex +from sage.topology.cell_complex import GenericCellComplex from sage.homology.chains import Chains, Cochains from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer from sage.matrix.constructor import matrix -from sage.homology.simplicial_complex import Simplex, lattice_paths, SimplicialComplex +from .simplicial_complex import Simplex, lattice_paths, SimplicialComplex from sage.homology.chain_complex import ChainComplex from sage.graphs.graph import Graph from sage.arith.all import binomial @@ -1590,7 +1590,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model_delta_complex + from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex if base_ring is None: base_ring = QQ return algebraic_topological_model_delta_complex(self, base_ring) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py similarity index 99% rename from src/sage/homology/simplicial_complex.py rename to src/sage/topology/simplicial_complex.py index 8db479fd549..6908b925904 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -69,9 +69,9 @@ .. NOTE:: This class derives from - :class:`~sage.homology.cell_complex.GenericCellComplex`, and so + :class:`~sage.topology.cell_complex.GenericCellComplex`, and so inherits its methods. Some of those methods are not listed here; - see the :mod:`Generic Cell Complex ` + see the :mod:`Generic Cell Complex ` page instead. EXAMPLES:: @@ -161,7 +161,7 @@ from copy import copy from sage.misc.lazy_import import lazy_import from sage.misc.cachefunc import cached_method -from sage.homology.cell_complex import GenericCellComplex +from .cell_complex import GenericCellComplex from sage.structure.sage_object import SageObject from sage.structure.parent import Parent from sage.rings.integer import Integer @@ -211,7 +211,7 @@ def lattice_paths(t1, t2, length=None): EXAMPLES:: - sage: from sage.homology.simplicial_complex import lattice_paths + sage: from sage.topology.simplicial_complex import lattice_paths sage: lattice_paths([0,1,2], [0,1,2]) [[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)], [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)], @@ -308,7 +308,7 @@ def rename_vertex(n, keep, left=True): EXAMPLES:: - sage: from sage.homology.simplicial_complex import rename_vertex + sage: from sage.topology.simplicial_complex import rename_vertex sage: rename_vertex(6, [5, 6, 7]) 1 sage: rename_vertex(3, [5, 6, 7, 8, 9]) @@ -413,7 +413,7 @@ def tuple(self): sage: type(Simplex(3).tuple()) <... 'tuple'> sage: type(Simplex(3)) - + """ return self.__tuple @@ -2451,7 +2451,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model + from sage.homology.algebraic_topological_model import algebraic_topological_model if base_ring is None: base_ring = QQ return algebraic_topological_model(self, base_ring) @@ -3913,7 +3913,7 @@ def _cubical_(self): sage: Tc.homology() {0: 0, 1: Z x Z, 2: Z} """ - from sage.homology.cubical_complex import CubicalComplex + from .cubical_complex import CubicalComplex V = self.vertices() embed = len(V) # dictionary to translate vertices to the numbers 1, ..., embed @@ -4309,7 +4309,7 @@ def _Hom_(self, other, category=None): """ if not category.is_subcategory(SimplicialComplexes()): raise TypeError("{} is not a subcategory of SimplicialComplexes()".format(category)) - from sage.homology.simplicial_complex_homset import SimplicialComplexHomset + from sage.topology.simplicial_complex_homset import SimplicialComplexHomset return SimplicialComplexHomset(self, other) # @cached_method when we switch to immutable SimplicialComplex @@ -4737,7 +4737,7 @@ def facets_for_RP4(): EXAMPLES:: - sage: from sage.homology.simplicial_complex import facets_for_RP4 + sage: from sage.topology.simplicial_complex import facets_for_RP4 sage: A = facets_for_RP4() # long time (1 or 2 seconds) sage: SimplicialComplex(A) == simplicial_complexes.RealProjectiveSpace(4) # long time True @@ -4775,7 +4775,7 @@ def facets_for_K3(): EXAMPLES:: - sage: from sage.homology.simplicial_complex import facets_for_K3 + sage: from sage.topology.simplicial_complex import facets_for_K3 sage: A = facets_for_K3() # long time (a few seconds) sage: SimplicialComplex(A) == simplicial_complexes.K3Surface() # long time True diff --git a/src/sage/homology/simplicial_complexes_catalog.py b/src/sage/topology/simplicial_complex_catalog.py similarity index 58% rename from src/sage/homology/simplicial_complexes_catalog.py rename to src/sage/topology/simplicial_complex_catalog.py index 955d5716703..01ade2e3667 100644 --- a/src/sage/homology/simplicial_complexes_catalog.py +++ b/src/sage/topology/simplicial_complex_catalog.py @@ -21,31 +21,31 @@ All of these examples are accessible by typing ``simplicial_complexes.NAME``, where ``NAME`` is the name of the example. -- :meth:`~sage.homology.examples.BarnetteSphere` -- :meth:`~sage.homology.examples.BrucknerGrunbaumSphere` -- :meth:`~sage.homology.examples.ChessboardComplex` -- :meth:`~sage.homology.examples.ComplexProjectivePlane` -- :meth:`~sage.homology.examples.DunceHat` -- :meth:`~sage.homology.examples.FareyMap` -- :meth:`~sage.homology.examples.K3Surface` -- :meth:`~sage.homology.examples.KleinBottle` -- :meth:`~sage.homology.examples.MatchingComplex` -- :meth:`~sage.homology.examples.MooreSpace` -- :meth:`~sage.homology.examples.NotIConnectedGraphs` -- :meth:`~sage.homology.examples.PoincareHomologyThreeSphere` -- :meth:`~sage.homology.examples.PseudoQuaternionicProjectivePlane` -- :meth:`~sage.homology.examples.RandomComplex` -- :meth:`~sage.homology.examples.RandomTwoSphere` -- :meth:`~sage.homology.examples.RealProjectivePlane` -- :meth:`~sage.homology.examples.RealProjectiveSpace` -- :meth:`~sage.homology.examples.RudinBall` -- :meth:`~sage.homology.examples.ShiftedComplex` -- :meth:`~sage.homology.examples.Simplex` -- :meth:`~sage.homology.examples.Sphere` -- :meth:`~sage.homology.examples.SumComplex` -- :meth:`~sage.homology.examples.SurfaceOfGenus` -- :meth:`~sage.homology.examples.Torus` -- :meth:`~sage.homology.examples.ZieglerBall` +- :meth:`~sage.topology.examples.BarnetteSphere` +- :meth:`~sage.topology.examples.BrucknerGrunbaumSphere` +- :meth:`~sage.topology.examples.ChessboardComplex` +- :meth:`~sage.topology.examples.ComplexProjectivePlane` +- :meth:`~sage.topology.examples.DunceHat` +- :meth:`~sage.topology.examples.FareyMap` +- :meth:`~sage.topology.examples.K3Surface` +- :meth:`~sage.topology.examples.KleinBottle` +- :meth:`~sage.topology.examples.MatchingComplex` +- :meth:`~sage.topology.examples.MooreSpace` +- :meth:`~sage.topology.examples.NotIConnectedGraphs` +- :meth:`~sage.topology.examples.PoincareHomologyThreeSphere` +- :meth:`~sage.topology.examples.PseudoQuaternionicProjectivePlane` +- :meth:`~sage.topology.examples.RandomComplex` +- :meth:`~sage.topology.examples.RandomTwoSphere` +- :meth:`~sage.topology.examples.RealProjectivePlane` +- :meth:`~sage.topology.examples.RealProjectiveSpace` +- :meth:`~sage.topology.examples.RudinBall` +- :meth:`~sage.topology.examples.ShiftedComplex` +- :meth:`~sage.topology.examples.Simplex` +- :meth:`~sage.topology.examples.Sphere` +- :meth:`~sage.topology.examples.SumComplex` +- :meth:`~sage.topology.examples.SurfaceOfGenus` +- :meth:`~sage.topology.examples.Torus` +- :meth:`~sage.topology.examples.ZieglerBall` You can also get a list by typing ``simplicial_complexes.`` and hitting the TAB key. @@ -64,7 +64,8 @@ {0: 0, 1: Z^16, 2: 0} """ -from sage.homology.examples import (Sphere, Simplex, Torus, ProjectivePlane, +from sage.topology.simplicial_complex_examples import (Sphere, Simplex, Torus, + ProjectivePlane, RealProjectivePlane, KleinBottle, FareyMap, SurfaceOfGenus, MooreSpace, ComplexProjectivePlane, PseudoQuaternionicProjectivePlane, diff --git a/src/sage/homology/examples.py b/src/sage/topology/simplicial_complex_examples.py similarity index 98% rename from src/sage/homology/examples.py rename to src/sage/topology/simplicial_complex_examples.py index 9f542ce8fe3..5b49a8de836 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -64,12 +64,12 @@ {0: 0, 1: Z^16, 2: 0} """ -from sage.homology.simplicial_complex import SimplicialComplex +from .simplicial_complex import SimplicialComplex from sage.structure.unique_representation import UniqueRepresentation # Below we define a function Simplex to construct a simplex as a # simplicial complex. We also need to use actual simplices as # simplices, hence: -from sage.homology.simplicial_complex import Simplex as TrueSimplex +from .simplicial_complex import Simplex as TrueSimplex from sage.sets.set import Set from sage.misc.functional import is_even from sage.combinat.subset import Subsets @@ -96,7 +96,7 @@ def facets_for_RP4(): EXAMPLES:: - sage: from sage.homology.examples import facets_for_RP4 + sage: from sage.topology.simplicial_complex_examples import facets_for_RP4 sage: A = facets_for_RP4() # long time (1 or 2 seconds) sage: SimplicialComplex(A) == simplicial_complexes.RealProjectiveSpace(4) # long time True @@ -134,7 +134,7 @@ def facets_for_K3(): EXAMPLES:: - sage: from sage.homology.examples import facets_for_K3 + sage: from sage.topology.simplicial_complex_examples import facets_for_K3 sage: A = facets_for_K3() # long time (a few seconds) sage: SimplicialComplex(A) == simplicial_complexes.K3Surface() # long time True @@ -160,7 +160,7 @@ def matching(A, B): EXAMPLES:: - sage: from sage.homology.examples import matching + sage: from sage.topology.simplicial_complex_examples import matching sage: matching([1, 2], [3, 4]) [{(1, 3), (2, 4)}, {(1, 4), (2, 3)}] sage: matching([0, 2], [0]) @@ -195,7 +195,7 @@ class UniqueSimplicialComplex(SimplicialComplex, UniqueRepresentation): EXAMPLES:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: SimplicialComplex([[0, 1]]) is SimplicialComplex([[0, 1]]) False sage: UniqueSimplicialComplex([[0, 1]]) is UniqueSimplicialComplex([[0, 1]]) @@ -210,7 +210,7 @@ def __classcall__(self, maximal_faces=None, name=None, **kwds): """ TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]]) is UniqueSimplicialComplex([(1, 2, 3), (0, 1, 3)]) True sage: X = UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]]) @@ -250,7 +250,7 @@ def __init__(self, maximal_faces=None, name=None, **kwds): """ TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]], is_mutable=True).is_mutable() False """ @@ -271,7 +271,7 @@ def _repr_(self): TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[0, 1]]) Simplicial complex with vertex set (0, 1) and facets {(0, 1)} sage: UniqueSimplicialComplex([[0, 1]], name='Joe') diff --git a/src/sage/homology/simplicial_complex_homset.py b/src/sage/topology/simplicial_complex_homset.py similarity index 96% rename from src/sage/homology/simplicial_complex_homset.py rename to src/sage/topology/simplicial_complex_homset.py index df181a80938..f97db05461d 100644 --- a/src/sage/homology/simplicial_complex_homset.py +++ b/src/sage/topology/simplicial_complex_homset.py @@ -26,7 +26,7 @@ False sage: x.image() Simplicial complex with vertex set (0, 1, 3) and facets {(0, 1), (0, 3), (1, 3)} - sage: from sage.homology.simplicial_complex import Simplex + sage: from sage.topology.simplicial_complex import Simplex sage: s = Simplex([1,2]) sage: x(s) (1, 3) @@ -58,7 +58,7 @@ #***************************************************************************** import sage.categories.homset -from sage.homology.simplicial_complex_morphism import SimplicialComplexMorphism +from .simplicial_complex_morphism import SimplicialComplexMorphism def is_SimplicialComplexHomset(x): """ @@ -73,7 +73,7 @@ def is_SimplicialComplexHomset(x): Set of Morphisms from Simplicial complex with vertex set () and facets {()} to Simplicial complex with vertex set () and facets {()} in Category of finite simplicial complexes - sage: from sage.homology.simplicial_complex_homset import is_SimplicialComplexHomset + sage: from sage.topology.simplicial_complex_homset import is_SimplicialComplexHomset sage: is_SimplicialComplexHomset(H) True """ diff --git a/src/sage/homology/simplicial_complex_morphism.py b/src/sage/topology/simplicial_complex_morphism.py similarity index 99% rename from src/sage/homology/simplicial_complex_morphism.py rename to src/sage/topology/simplicial_complex_morphism.py index 7d1cd628aef..272cae39f14 100644 --- a/src/sage/homology/simplicial_complex_morphism.py +++ b/src/sage/topology/simplicial_complex_morphism.py @@ -102,7 +102,7 @@ # #***************************************************************************** -from sage.homology.simplicial_complex import Simplex, SimplicialComplex +from .simplicial_complex import Simplex, SimplicialComplex from sage.matrix.constructor import matrix, zero_matrix from sage.rings.integer_ring import ZZ from sage.homology.chain_complex_morphism import ChainComplexMorphism @@ -119,7 +119,7 @@ def is_SimplicialComplexMorphism(x): EXAMPLES:: - sage: from sage.homology.simplicial_complex_morphism import is_SimplicialComplexMorphism + sage: from sage.topology.simplicial_complex_morphism import is_SimplicialComplexMorphism sage: S = SimplicialComplex([[0,1],[3,4]], is_mutable=False) sage: H = Hom(S,S) sage: f = {0:0,1:1,3:3,4:4} @@ -231,7 +231,7 @@ def __call__(self,x,orientation=False): sage: f = {0:0,1:1,2:2,3:3} sage: H = Hom(S,T) sage: x = H(f) - sage: from sage.homology.simplicial_complex import Simplex + sage: from sage.topology.simplicial_complex import Simplex sage: x(Simplex([0,2,3])) (0, 2, 3) @@ -733,7 +733,7 @@ def induced_homology_morphism(self, base_ring=None, cohomology=False): True sage: h = Hom(S,S2)({0: 0, 1:1, 2:2}).induced_homology_morphism() """ - from .homology_morphism import InducedHomologyMorphism + from sage.homology.homology_morphism import InducedHomologyMorphism return InducedHomologyMorphism(self, base_ring, cohomology) def is_contiguous_to(self, other): diff --git a/src/sage/homology/simplicial_set.py b/src/sage/topology/simplicial_set.py similarity index 97% rename from src/sage/homology/simplicial_set.py rename to src/sage/topology/simplicial_set.py index 7f6403299cb..4b58d63684c 100644 --- a/src/sage/homology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -221,7 +221,7 @@ constructed "by hand": first define some simplices, then define a simplicial set by specifying their faces:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -263,11 +263,11 @@ from sage.rings.rational_field import QQ from sage.structure.parent import Parent from sage.structure.sage_object import SageObject +from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex +from sage.homology.chain_complex import ChainComplex +from sage.homology.chains import Chains, Cochains -from .algebraic_topological_model import algebraic_topological_model_delta_complex from .cell_complex import GenericCellComplex -from .chain_complex import ChainComplex -from .chains import Chains, Cochains from .delta_complex import DeltaComplex from .simplicial_complex import SimplicialComplex @@ -312,7 +312,7 @@ def __init__(self, dim, degeneracies=(), underlying=None, name=None, TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (1,2)) s_3 s_1 Delta^3 sage: AbstractSimplex(3, None) @@ -342,7 +342,7 @@ def __init__(self, dim, degeneracies=(), underlying=None, name=None, Distinct non-degenerate simplices should never be equal, even if they have the same starting data:: - sage: from sage.homology.simplicial_set import AbstractSimplex_class + sage: from sage.topology.simplicial_set import AbstractSimplex_class sage: AbstractSimplex_class(3) == AbstractSimplex_class(3) False sage: AbstractSimplex(3) == AbstractSimplex(3) @@ -407,7 +407,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: hash(v) == hash(w) @@ -431,7 +431,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: v == w @@ -457,7 +457,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: v != w @@ -483,7 +483,7 @@ def __lt__(self, other): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) @@ -585,7 +585,7 @@ def __gt__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e > f @@ -599,7 +599,7 @@ def __le__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e <= f @@ -613,7 +613,7 @@ def __ge__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e >= f @@ -629,7 +629,7 @@ def nondegenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0, name='v') sage: sigma = v.apply_degeneracies(1, 0) sage: sigma.nondegenerate() @@ -656,7 +656,7 @@ def degeneracies(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(4, (0,0,0)).degeneracies() [2, 1, 0] sage: AbstractSimplex(4, None).degeneracies() @@ -670,7 +670,7 @@ def is_degenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).is_degenerate() True sage: AbstractSimplex(3, None).is_degenerate() @@ -684,7 +684,7 @@ def is_nondegenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).is_nondegenerate() False sage: AbstractSimplex(3, None).is_nondegenerate() @@ -700,7 +700,7 @@ def dimension(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).dimension() 5 sage: AbstractSimplex(3, None).dimension() @@ -720,7 +720,7 @@ def apply_degeneracies(self, *args): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: e = v.apply_degeneracies(0) sage: e.nondegenerate() == v @@ -766,7 +766,7 @@ def __copy__(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: copy(v) == v False @@ -809,7 +809,7 @@ def __deepcopy__(self, memo): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: import copy sage: v = AbstractSimplex(0) sage: copy.deepcopy(v) == v @@ -820,7 +820,7 @@ def __deepcopy__(self, memo): The purpose for this method is to be able to make distinct copies of simplicial sets:: - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set import SimplicialSet sage: RP3 = simplicial_sets.RealProjectiveSpace(3) sage: dict(copy.copy(RP3._data)) == dict(RP3._data) True @@ -848,7 +848,7 @@ def _repr_(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, None) Delta^3 sage: AbstractSimplex(3, (0,)) @@ -878,7 +878,7 @@ def _latex_(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: latex(AbstractSimplex(18, None)) \Delta^{18} sage: latex(AbstractSimplex(3, (0, 0,))) @@ -929,12 +929,12 @@ def __init__(self, dim, name=None, latex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0, name='v') sage: v v sage: type(v) - + Distinct non-degenerate simplices should never be equal, even if they have the same starting data. :: @@ -944,7 +944,7 @@ def __init__(self, dim, name=None, latex_name=None): sage: AbstractSimplex(3) == AbstractSimplex(3) False - sage: from sage.homology.simplicial_set import NonDegenerateSimplex + sage: from sage.topology.simplicial_set import NonDegenerateSimplex sage: x = NonDegenerateSimplex(0, name='x') sage: x == NonDegenerateSimplex(0, name='x') False @@ -992,7 +992,7 @@ def AbstractSimplex(dim, degeneracies=(), underlying=None, EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (3, 1)) s_3 s_1 Delta^3 sage: AbstractSimplex(3, None) @@ -1168,7 +1168,7 @@ def faces(self, simplex): sage: S2.faces(v_0) is None True - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: w = AbstractSimplex(0) sage: S2.faces(w) Traceback (most recent call last): @@ -1236,7 +1236,7 @@ def __contains__(self, x): sage: v0 in S1 False - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: e = AbstractSimplex(1) sage: K = SimplicialSet({e: (v, v)}) # the circle @@ -1331,7 +1331,7 @@ def nondegenerate_simplices(self, max_dim=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -1398,7 +1398,7 @@ def cells(self, subcomplex=None, max_dim=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -1500,7 +1500,7 @@ def _an_element_(self): v_0 sage: S4._an_element_() in S4 True - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: Empty()._an_element_() is None True """ @@ -1515,7 +1515,7 @@ def all_n_simplices(self, n): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: degen = v.apply_degeneracies(0) @@ -1566,7 +1566,7 @@ def _map_from_empty_set(self): To: Torus Defn: [] --> [] """ - from sage.homology.simplicial_set_examples import Empty + from sage.topology.simplicial_set_examples import Empty return Empty().Hom(self)({}) def identity(self): @@ -1635,7 +1635,7 @@ def constant_map(self, codomain=None, point=None): ... ValueError: codomain is not pointed, so specify a target for the constant map """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if codomain is None: codomain = Point() return self.Hom(codomain).constant_map(point) @@ -1749,7 +1749,7 @@ def subsimplicial_set(self, simplices): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2199,7 +2199,7 @@ def quotient(self, subcomplex, vertex_name='*'): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2309,7 +2309,7 @@ def disjoint_union(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2341,7 +2341,7 @@ def disjoint_union(self, *others): Empty factors are ignored:: - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: E = Empty() sage: K = S2.disjoint_union(S2, E, E, S2) sage: K == S2.disjoint_union(S2, S2) @@ -2438,7 +2438,7 @@ def product(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2746,7 +2746,7 @@ def wedge(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: w = AbstractSimplex(0, name='w') @@ -2822,7 +2822,7 @@ def cone(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -3012,7 +3012,7 @@ def _Hom_(self, other, category=None): Set of Morphisms from S^3 to 4-simplex in Category of finite simplicial sets """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetHomset + from sage.topology.simplicial_set_morphism import SimplicialSetHomset # Error-checking on the ``category`` argument is done when # calling Hom(X,Y), so no need to do it again here. if category is None: @@ -3039,7 +3039,7 @@ def rename_latex(self, s): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}, latex_name='*') sage: latex(X) @@ -3060,7 +3060,7 @@ def _latex_(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}, latex_name='*') sage: latex(X) @@ -3085,7 +3085,7 @@ def _repr_(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: degen = v.apply_degeneracies(0) @@ -3158,7 +3158,7 @@ class SimplicialSet_finite(SimplicialSet_arbitrary, GenericCellComplex): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: u = AbstractSimplex(0, name='u') sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') @@ -3182,7 +3182,7 @@ def __init__(self, data, base_point=None, name=None, check=True, r""" TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: e = AbstractSimplex(1) sage: SimplicialSet({e: (v, v, v)}) @@ -3364,7 +3364,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: e = AbstractSimplex(1) @@ -3393,7 +3393,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: X = SimplicialSet({v: None}) @@ -3419,7 +3419,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}) sage: degen = v.apply_degeneracies(0) @@ -3460,7 +3460,7 @@ def face_data(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -3489,7 +3489,7 @@ def n_skeleton(self, n): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: degen = v.apply_degeneracies(0) @@ -3545,7 +3545,7 @@ def f_vector(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -3617,7 +3617,7 @@ def chain_complex(self, dimensions=None, base_ring=ZZ, augmented=False, EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: degen = v.apply_degeneracies(1, 0) # s_1 s_0 applied to v sage: sigma = AbstractSimplex(3) @@ -3830,7 +3830,7 @@ def standardize_degeneracies(*L): EXAMPLES:: - sage: from sage.homology.simplicial_set import standardize_degeneracies + sage: from sage.topology.simplicial_set import standardize_degeneracies sage: standardize_degeneracies(0, 0) (1, 0) sage: standardize_degeneracies(0, 0, 0, 0) @@ -3895,7 +3895,7 @@ def all_degeneracies(n, l=1): EXAMPLES:: - sage: from sage.homology.simplicial_set import all_degeneracies + sage: from sage.topology.simplicial_set import all_degeneracies sage: all_degeneracies(0, 3) {(2, 1, 0)} sage: all_degeneracies(1, 1) @@ -3934,7 +3934,7 @@ def standardize_face_maps(*L): EXAMPLES:: - sage: from sage.homology.simplicial_set import standardize_face_maps + sage: from sage.topology.simplicial_set import standardize_face_maps sage: standardize_face_maps(0, 1) (0, 0) sage: standardize_face_maps(0, 2) @@ -3988,7 +3988,7 @@ def face_degeneracies(m, I): EXAMPLES:: - sage: from sage.homology.simplicial_set import face_degeneracies + sage: from sage.topology.simplicial_set import face_degeneracies sage: face_degeneracies(0, (1, 0)) ([0], None) sage: face_degeneracies(1, (1, 0)) @@ -4031,7 +4031,7 @@ def shrink_simplicial_complex(K): EXAMPLES:: - sage: from sage.homology.simplicial_set import shrink_simplicial_complex + sage: from sage.topology.simplicial_set import shrink_simplicial_complex sage: K = simplicial_complexes.Simplex(3) sage: X = shrink_simplicial_complex(K) sage: X.f_vector() diff --git a/src/sage/homology/simplicial_set_catalog.py b/src/sage/topology/simplicial_set_catalog.py similarity index 100% rename from src/sage/homology/simplicial_set_catalog.py rename to src/sage/topology/simplicial_set_catalog.py diff --git a/src/sage/homology/simplicial_set_constructions.py b/src/sage/topology/simplicial_set_constructions.py similarity index 98% rename from src/sage/homology/simplicial_set_constructions.py rename to src/sage/topology/simplicial_set_constructions.py index 2d7be9d16ea..ab59a2c370d 100644 --- a/src/sage/homology/simplicial_set_constructions.py +++ b/src/sage/topology/simplicial_set_constructions.py @@ -23,7 +23,7 @@ sage: eta = simplicial_sets.HopfMap() sage: CP2 = eta.mapping_cone() sage: type(CP2) - + See the main documentation for simplicial sets, as well as for the classes for pushouts, pullbacks, etc., for more details. @@ -106,7 +106,7 @@ def __classcall__(self, data, ambient=None): TESTS:: - sage: from sage.homology.simplicial_set_constructions import SubSimplicialSet + sage: from sage.topology.simplicial_set_constructions import SubSimplicialSet sage: K = simplicial_sets.Simplex(2) sage: e = K.n_cells(1)[0] sage: A = SubSimplicialSet({e: K.faces(e)}, ambient=K) @@ -232,7 +232,7 @@ def __classcall_private__(self, maps=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PullbackOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PullbackOfSimplicialSets([one, one]) == PullbackOfSimplicialSets((one, one)) @@ -290,7 +290,7 @@ def __init__(self, maps=None): [(v_0, v_0), (sigma_2, sigma_2)] """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') @@ -416,7 +416,7 @@ def __classcall_private__(self, maps=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PullbackOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets_finite sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PullbackOfSimplicialSets_finite([one, one]) == PullbackOfSimplicialSets_finite((one, one)) @@ -462,7 +462,7 @@ def __init__(self, maps=None): Defn: Identity map """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') if not maps: @@ -757,7 +757,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import ProductOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: ProductOfSimplicialSets([S2, S2]) == ProductOfSimplicialSets((S2, S2)) True @@ -800,7 +800,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -1004,7 +1004,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1) sage: X = SimplicialSet({e: (v, v)}) @@ -1059,7 +1059,7 @@ def wedge_as_subset(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: w = AbstractSimplex(0, name='w') @@ -1122,7 +1122,7 @@ def __classcall_private__(cls, maps=None, vertex_name=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PushoutOfSimplicialSets([one, one]) == PushoutOfSimplicialSets((one, one)) @@ -1171,7 +1171,7 @@ def __init__(self, maps=None, vertex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: a = AbstractSimplex(0, name='a') sage: b = AbstractSimplex(0, name='b') @@ -1249,7 +1249,7 @@ def __init__(self, maps=None, vertex_name=None): Z x Z x Z """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') Cat = SimplicialSets() @@ -1385,7 +1385,7 @@ def __classcall_private__(cls, maps=None, vertex_name=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PushoutOfSimplicialSets_finite([one, one]) == PushoutOfSimplicialSets_finite((one, one)) @@ -1409,7 +1409,7 @@ def __init__(self, maps=None, vertex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite sage: T = simplicial_sets.Torus() sage: S2 = simplicial_sets.Sphere(2) sage: PushoutOfSimplicialSets_finite([T.base_point_map(), S2.base_point_map()]).n_cells(0)[0] @@ -1418,7 +1418,7 @@ def __init__(self, maps=None, vertex_name=None): v """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') if not maps: @@ -1612,7 +1612,7 @@ def universal_property(self, *maps): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: x = AbstractSimplex(0, name='x') @@ -1882,7 +1882,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite as Smash + sage: from sage.topology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite as Smash sage: S2 = simplicial_sets.Sphere(2) sage: Smash([S2, S2]) == Smash((S2, S2)) True @@ -1958,7 +1958,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import WedgeOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: WedgeOfSimplicialSets([S2, S2]) == WedgeOfSimplicialSets((S2, S2)) True @@ -2075,7 +2075,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import WedgeOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets_finite sage: K = simplicial_sets.Simplex(3) sage: WedgeOfSimplicialSets_finite((K,K)) Traceback (most recent call last): @@ -2147,8 +2147,8 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import DisjointUnionOfSimplicialSets - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets + sage: from sage.topology.simplicial_set_examples import Empty sage: S2 = simplicial_sets.Sphere(2) sage: DisjointUnionOfSimplicialSets([S2, S2]) == DisjointUnionOfSimplicialSets((S2, S2)) True @@ -2283,8 +2283,8 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import DisjointUnionOfSimplicialSets_finite - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_examples import Empty sage: S = simplicial_sets.Sphere(4) sage: DisjointUnionOfSimplicialSets_finite((S,S,S)) Disjoint union: (S^4 u S^4 u S^4) @@ -2336,7 +2336,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2429,7 +2429,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2525,7 +2525,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2622,7 +2622,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) diff --git a/src/sage/homology/simplicial_set_examples.py b/src/sage/topology/simplicial_set_examples.py similarity index 99% rename from src/sage/homology/simplicial_set_examples.py rename to src/sage/topology/simplicial_set_examples.py index 9374ba03cae..32d392e5294 100644 --- a/src/sage/homology/simplicial_set_examples.py +++ b/src/sage/topology/simplicial_set_examples.py @@ -43,7 +43,7 @@ from .simplicial_set import AbstractSimplex, \ SimplicialSet_arbitrary, SimplicialSet_finite -import sage.homology.simplicial_complexes_catalog as simplicial_complexes +import sage.topology.simplicial_complex_catalog as simplicial_complexes from sage.misc.lazy_import import lazy_import lazy_import('sage.categories.simplicial_sets', 'SimplicialSets') @@ -447,7 +447,7 @@ def Empty(): EXAMPLES:: - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: E = Empty() sage: E Empty simplicial set @@ -639,8 +639,8 @@ def simplicial_data_from_kenzo_output(filename): EXAMPLES:: - sage: from sage.homology.simplicial_set_examples import simplicial_data_from_kenzo_output - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set_examples import simplicial_data_from_kenzo_output + sage: from sage.topology.simplicial_set import SimplicialSet sage: sphere = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'S4.txt') sage: S4 = SimplicialSet(simplicial_data_from_kenzo_output(sphere)) sage: S4.homology(reduced=False) diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/topology/simplicial_set_morphism.py similarity index 99% rename from src/sage/homology/simplicial_set_morphism.py rename to src/sage/topology/simplicial_set_morphism.py index 7e2c5046ef9..a320703001c 100644 --- a/src/sage/homology/simplicial_set_morphism.py +++ b/src/sage/topology/simplicial_set_morphism.py @@ -39,8 +39,8 @@ from sage.misc.latex import latex from sage.rings.integer_ring import ZZ -from .chain_complex_morphism import ChainComplexMorphism -from .homology_morphism import InducedHomologyMorphism +from sage.homology.chain_complex_morphism import ChainComplexMorphism +from sage.homology.homology_morphism import InducedHomologyMorphism from .simplicial_set import SimplicialSet_arbitrary class SimplicialSetHomset(Homset): @@ -55,7 +55,7 @@ class SimplicialSetHomset(Homset): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -346,7 +346,7 @@ def __init__(self, data=None, domain=None, codomain=None, EXAMPLES:: - sage: from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + sage: from sage.topology.simplicial_set_morphism import SimplicialSetMorphism sage: K = simplicial_sets.Simplex(1) sage: S1 = simplicial_sets.Sphere(1) sage: v0 = K.n_cells(0)[0] @@ -908,7 +908,7 @@ def pushout(self, *others): which must all equal that of ``self``. This returns the pushout as a simplicial set. See - :class:`sage.homology.simplicial_set_constructions.PushoutOfSimplicialSets` + :class:`sage.topology.simplicial_set_constructions.PushoutOfSimplicialSets` for more documentation and examples. EXAMPLES:: @@ -944,7 +944,7 @@ def pullback(self, *others): which must all equal that of ``self``. This returns the pullback as a simplicial set. See - :class:`sage.homology.simplicial_set_constructions.PullbackOfSimplicialSets` + :class:`sage.topology.simplicial_set_constructions.PullbackOfSimplicialSets` for more documentation and examples. EXAMPLES:: @@ -990,7 +990,7 @@ def equalizer(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: x = AbstractSimplex(0, name='x') @@ -1374,7 +1374,7 @@ def induced_homology_morphism(self, base_ring=None, cohomology=False): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') From 3e2c0417ed5a072067996fd767190f080313da28 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Mon, 7 Jun 2021 18:50:04 -0700 Subject: [PATCH 530/706] trac 31925: add deprecated versions --- src/sage/homology/cell_complex.py | 12 ++++++ src/sage/homology/cubical_complex.py | 17 ++++++++ src/sage/homology/delta_complex.py | 15 +++++++ src/sage/homology/examples.py | 41 +++++++++++++++++++ src/sage/homology/simplicial_complex.py | 16 ++++++++ .../homology/simplicial_complex_homset.py | 13 ++++++ .../homology/simplicial_complex_morphism.py | 13 ++++++ src/sage/homology/simplicial_set.py | 21 ++++++++++ .../homology/simplicial_set_constructions.py | 31 ++++++++++++++ src/sage/homology/simplicial_set_examples.py | 25 +++++++++++ src/sage/homology/simplicial_set_morphism.py | 12 ++++++ 11 files changed, 216 insertions(+) create mode 100644 src/sage/homology/cell_complex.py create mode 100644 src/sage/homology/cubical_complex.py create mode 100644 src/sage/homology/delta_complex.py create mode 100644 src/sage/homology/examples.py create mode 100644 src/sage/homology/simplicial_complex.py create mode 100644 src/sage/homology/simplicial_complex_homset.py create mode 100644 src/sage/homology/simplicial_complex_morphism.py create mode 100644 src/sage/homology/simplicial_set.py create mode 100644 src/sage/homology/simplicial_set_constructions.py create mode 100644 src/sage/homology/simplicial_set_examples.py create mode 100644 src/sage/homology/simplicial_set_morphism.py diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py new file mode 100644 index 00000000000..23292d902e3 --- /dev/null +++ b/src/sage/homology/cell_complex.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +r""" +Generic cell complexes: deprecated + +The current version is :mod:`sage.topology.cell_complexes`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.cell_complex + +GenericCellComplex = deprecated_function_alias(31925, + sage.topology.cell_complex.GenericCellComplex) diff --git a/src/sage/homology/cubical_complex.py b/src/sage/homology/cubical_complex.py new file mode 100644 index 00000000000..1149da028c0 --- /dev/null +++ b/src/sage/homology/cubical_complex.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +r""" +Finite cubical complexes: deprecated + +The current version is :mod:`sage.topology.cubical_complexes`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.cubical_complex + +Cube = deprecated_function_alias(31925, sage.topology.cubical_complex.Cube) +CubicalComplex = deprecated_function_alias(31925, + sage.topology.cubical_complex.CubicalComplex) +CubicalComplexExamples = deprecated_function_alias(31925, + sage.topology.cubical_complex.CubicalComplexExamples) +cubical_complexes = deprecated_function_alias(31925, + sage.topology.cubical_complex.cubical_complexes) diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py new file mode 100644 index 00000000000..cfd5fc6119f --- /dev/null +++ b/src/sage/homology/delta_complex.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +r""" +Finite Delta-complexes: deprecated + +The current version is :mod:`sage.topology.delta_complexes`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.delta_complex + +DeltaComplex = deprecated_function_alias(31925, + sage.topology.delta_complex.DeltaComplex) +DeltaComplexExamples = deprecated_function_alias(31925, + sage.topology.delta_complex.DeltaComplexExamples) +delta_complexes = deprecated_function_alias(31925, + sage.topology.delta_complex.delta_complexes) diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py new file mode 100644 index 00000000000..704b1219189 --- /dev/null +++ b/src/sage/homology/examples.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" +Examples of simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_examples`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_examples + +for f in ['facets_for_RP4', + 'facets_for_K3', + 'matching', + 'UniqueSimplicialComplex', + 'Sphere', + 'Simplex', + 'Torus', + 'RealProjectivePlane', + 'ProjectivePlane', + 'KleinBottle', + 'SurfaceOfGenus', + 'MooreSpace', + 'ComplexProjectivePlane', + 'PseudoQuaternionicProjectivePlane', + 'PoincareHomologyThreeSphere', + 'RealProjectiveSpace', + 'K3Surface', + 'BarnetteSphere', + 'BrucknerGrunbaumSphere', + 'NotIConnectedGraphs', + 'MatchingComplex', + 'ChessboardComplex', + 'RandomComplex', + 'SumComplex', + 'RandomTwoSphere', + 'ShiftedComplex', + 'RudinBall', + 'ZieglerBall', + 'DunceHat', + 'FareyMap']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_complex_examples.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py new file mode 100644 index 00000000000..8c8399d3b24 --- /dev/null +++ b/src/sage/homology/simplicial_complex.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +r""" +Finite simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complexes`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex + +for f in ['lattice_paths', + 'rename_vertex', + 'Simplex', + 'SimplicialComplex', + 'facets_for_RP4', + 'facets_for_K3']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_complex.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_complex_homset.py b/src/sage/homology/simplicial_complex_homset.py new file mode 100644 index 00000000000..6a9d78f69f1 --- /dev/null +++ b/src/sage/homology/simplicial_complex_homset.py @@ -0,0 +1,13 @@ +r""" +Homsets between simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_homset`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_homset + +is_SimplicialComplexHomset = deprecated_function_alias(31925, + sage.topology.simplicial_complex_homset.is_SimplicialComplexHomset) +SimplicialComplexHomset = deprecated_function_alias(31925, + sage.topology.simplicial_complex_homset.SimplicialComplexHomset) + diff --git a/src/sage/homology/simplicial_complex_morphism.py b/src/sage/homology/simplicial_complex_morphism.py new file mode 100644 index 00000000000..4673be7d414 --- /dev/null +++ b/src/sage/homology/simplicial_complex_morphism.py @@ -0,0 +1,13 @@ +r""" +Morphisms of simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_morphism`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_morphism + +is_SimplicialComplexMorphism = deprecated_function_alias(31925, + sage.topology.simplicial_complex_morphism.is_SimplicialComplexMorphism) +SimplicialComplexMorphism = deprecated_function_alias(31925, + sage.topology.simplicial_complex_morphism.SimplicialComplexMorphism) diff --git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py new file mode 100644 index 00000000000..51e1a36047a --- /dev/null +++ b/src/sage/homology/simplicial_set.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +r""" +Simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set + +for f in ['AbstractSimplex_class', + 'NonDegenerateSimplex', + 'AbstractSimplex', + 'SimplicialSet_arbitrary', + 'SimplicialSet_finite', + 'SimplicialSet', + 'standardize_degeneracies', + 'all_degeneracies', + 'standardize_face_maps', + 'face_degeneracies', + 'shrink_simplicial_complex']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_set_constructions.py b/src/sage/homology/simplicial_set_constructions.py new file mode 100644 index 00000000000..c8943f62034 --- /dev/null +++ b/src/sage/homology/simplicial_set_constructions.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +r""" +Methods of constructing simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_constructions`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_constructions + +for f in ['SubSimplicialSet', + 'PullbackOfSimplicialSets', + 'PullbackOfSimplicialSets_finite', + 'Factors', + 'ProductOfSimplicialSets', + 'ProductOfSimplicialSets_finite', + 'PushoutOfSimplicialSets', + 'PushoutOfSimplicialSets_finite', + 'QuotientOfSimplicialSet', + 'QuotientOfSimplicialSet_finite', + 'SmashProductOfSimplicialSets_finite', + 'WedgeOfSimplicialSets', + 'WedgeOfSimplicialSets_finite', + 'DisjointUnionOfSimplicialSets', + 'DisjointUnionOfSimplicialSets_finite', + 'ConeOfSimplicialSet', + 'ConeOfSimplicialSet_finite', + 'ReducedConeOfSimplicialSet', + 'ReducedConeOfSimplicialSet_finite', + 'SuspensionOfSimplicialSet', + 'SuspensionOfSimplicialSet_finite']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set_constructions.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_set_examples.py b/src/sage/homology/simplicial_set_examples.py new file mode 100644 index 00000000000..8a19cef5a86 --- /dev/null +++ b/src/sage/homology/simplicial_set_examples.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +r""" +Examples of simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_examples`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_examples + +for f in ['Nerve', + 'Sphere', + 'ClassifyingSpace', + 'RealProjectiveSpace', + 'KleinBottle', + 'Torus', + 'Simplex', + 'Empty', + 'Point', + 'Horn', + 'ComplexProjectiveSpace', + 'simplicial_data_from_kenzo_output', + 'HopfMap']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set_examples.{})'.format(f, f)) + diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/homology/simplicial_set_morphism.py new file mode 100644 index 00000000000..9df5cb9a40d --- /dev/null +++ b/src/sage/homology/simplicial_set_morphism.py @@ -0,0 +1,12 @@ +r""" +Morphisms and homsets for simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_morphism`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_morphism + +deprecated_function_alias(31925, + sage.topology.simplicial_set_morphism.SimplicialSetHomset) +deprecated_function_alias(31925, + sage.topology.simplicial_set_morphism.SimplicialSetMorphism) From 4fceeae6daae483b6b0554b9270ea75aa0801ec8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 8 Jun 2021 08:59:09 +0200 Subject: [PATCH 531/706] fix documentation build --- src/sage/matrix/matrix_rational_dense.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 5dab0da8769..d67529c0a7e 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -2296,7 +2296,7 @@ cdef class Matrix_rational_dense(Matrix_dense): ....: A.randomize(dens) ....: ranks[A.rank()] = True - The default density is ``6/9`:: + The default density is `6/9`:: sage: def add_sample(density, num_rows, num_cols): ....: global density_sum, total_count From da4004587235416144bc6bded1c6e87f166f766e Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Jun 2021 11:29:07 +0100 Subject: [PATCH 532/706] doctest fixes for #26635 on Pari 2.13 --- src/sage/modular/local_comp/local_comp.py | 26 +++++++++-------------- src/sage/modular/local_comp/smoothchar.py | 23 ++++++++++---------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index aa74ea78897..9010baa6ba8 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -73,11 +73,9 @@ def LocalComponent(f, p, twist_factor=None): Character of Q_7*, of level 0, mapping 7 |--> 1 sage: Pi.species() 'Supercuspidal' - sage: Pi.characters() - [ - Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> d, 7 |--> 1, - Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> -d, 7 |--> 1 - ] + sage: set(Pi.characters()) + {Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> -d, 7 |--> 1, + Character of unramified extension Q_7(s)* (s^2 + 6*s + 3 = 0), of level 1, mapping s |--> d, 7 |--> 1} """ p = ZZ(p) if not p.is_prime(): @@ -635,11 +633,9 @@ def characters(self): sage: f = Newform('50a') sage: Pi = LocalComponent(f, 5) - sage: chars = Pi.characters(); chars - [ - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1, - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1 - ] + sage: chars = Pi.characters(); set(chars) + {Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1, 5 |--> 1, + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 1} sage: chars[0].base_ring() Number Field in d with defining polynomial x^2 + x + 1 @@ -653,13 +649,11 @@ def characters(self): sage: f = Newforms(GammaH(25, [6]), 3, names='j')[0]; f q + j0*q^2 + 1/3*j0^3*q^3 - 1/3*j0^2*q^4 + O(q^6) sage: Pi = LocalComponent(f, 5) - sage: Pi.characters() - [ - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> d, 5 |--> 5, - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -d - 1/3*j0^3, 5 |--> 5 - ] + sage: set(Pi.characters()) + {Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> 1/3*j0^2*d - 1/3*j0^3, 5 |--> 5, + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -1/3*j0^2*d, 5 |--> 5} sage: Pi.characters()[0].base_ring() - Number Field in d with defining polynomial x^2 + 1/3*j0^3*x - 1/3*j0^2 over its base field + Number Field in d with defining polynomial x^2 - j0*x + 1/3*j0^2 over its base field .. warning:: diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index e54d5ad1d40..92135f7f71d 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -1131,9 +1131,8 @@ def discrete_log(self, level, x, gens=None): sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupRamifiedQuadratic sage: G = SmoothCharacterGroupRamifiedQuadratic(3, 1, QQ) sage: s = G.number_field().gen() - sage: G.discrete_log(4, 3 + 2*s) - [1, 2, 2, 1] - sage: gs = G.unit_gens(4); gs[0] * gs[1]^2 * gs[2]^2 * gs[3] - (3 + 2*s) in G.ideal(4) + sage: dl = G.discrete_log(4, 3 + 2*s) + sage: gs = G.unit_gens(4); gs[0]^dl[0] * gs[1]^dl[1] * gs[2]^dl[2] * gs[3]^dl[3] - (3 + 2*s) in G.ideal(4) True An example with a custom generating set:: @@ -1191,11 +1190,11 @@ def quotient_gens(self, n): sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupUnramifiedQuadratic sage: G = SmoothCharacterGroupUnramifiedQuadratic(7,QQ) sage: G.quotient_gens(1) - [s] + [2*s - 2] sage: G.quotient_gens(2) - [23*s - 16] + [15*s + 21] sage: G.quotient_gens(3) - [-124*s - 2] + [-75*s + 33] A ramified case:: @@ -1208,11 +1207,11 @@ def quotient_gens(self, n): sage: G = SmoothCharacterGroupUnramifiedQuadratic(2,QQ) sage: G.quotient_gens(1) - [s] + [s + 1] sage: G.quotient_gens(2) - [-s + 1] + [-s + 2] sage: G.quotient_gens(3) - [7*s + 5, -s + 3] + [-17*s - 14, 3*s - 2] """ # silly special case @@ -1327,18 +1326,18 @@ def extend_character(self, level, chi, vals, check=True): sage: G.extend_character(3, chi, [1]) Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 0, mapping 5 |--> 7 sage: K. = CyclotomicField(6); G.base_extend(K).extend_character(1, chi, [z]) - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> z, 5 |--> 7 + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -z + 1, 5 |--> 7 We extend the nontrivial quadratic character:: sage: chi = SmoothCharacterGroupQp(5, QQ).character(1, [-1, 7]) sage: K. = CyclotomicField(24); G.base_extend(K).extend_character(1, chi, [z^6]) - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> z^6, 5 |--> 7 + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 1, mapping s |--> -z^6, 5 |--> 7 Extensions of higher level:: sage: K. = CyclotomicField(20); rho = G.base_extend(K).extend_character(2, chi, [z]); rho - Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 2, mapping 11*s - 10 |--> -z^5, 6 |--> 1, 5*s + 1 |--> z^4, 5 |--> 7 + Character of unramified extension Q_5(s)* (s^2 + 4*s + 2 = 0), of level 2, mapping 11*s - 10 |--> z^5, 6 |--> 1, 5*s + 1 |--> z^4, 5 |--> 7 sage: rho(3) -1 From 73591c04d30aced76cf4216b87234f65b89279bb Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Jun 2021 11:32:57 +0100 Subject: [PATCH 533/706] do not test for bugs fixed in pari 2.13+ --- build/pkgs/pari/spkg-configure.m4 | 33 ------------------------------- 1 file changed, 33 deletions(-) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 4e4e1055685..4fafefb5e08 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -66,39 +66,6 @@ SAGE_SPKG_CONFIGURE([pari], [ AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) sage_spkg_install_pari=yes fi - AC_MSG_CHECKING([whether hyperellcharpoly bug is fixed]) - bug_check=`echo "hyperellcharpoly(Mod(1,3)*(x^10 + x^9 + x^8 + x))" | $GP -qf 2>> config.log` - expected="x^8 + 2*x^7 + 6*x^6 + 9*x^5 + 18*x^4 + 27*x^3 + 54*x^2 + 54*x + 81" - if test x"$bug_check" = x"$expected"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) - AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) - AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) - sage_spkg_install_pari=yes - fi - AC_MSG_CHECKING([whether bnfisunit bug of pari 2.11.3 is fixed]) - bug_check=`echo "bnf = bnfinit(y^4-y-1); bnfisunit(bnf,-y^3+2*y^2-1)==[[0,2,0]]~" | $GP -qf 2>> config.log` - expected="1" - if test x"$bug_check" = x"$expected"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) - AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) - AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) - sage_spkg_install_pari=yes - fi - AC_MSG_CHECKING([whether qfisom bug of pari 2.11.2 is fixed]) - bug_check=`echo "qfisom([[16,6;6,10]],[[4,3;3,10]])" | $GP -qf 2>> config.log` - expected="0" - if test x"$bug_check" = x"$expected"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no; cannot use system pari/GP with known bug]) - AC_MSG_NOTICE([Upgrade your system package and reconfigure.]) - AC_MSG_NOTICE([Otherwise Sage will build its own pari/GP.]) - sage_spkg_install_pari=yes - fi AC_MSG_CHECKING([whether rnfdisc bug of pari 2.13.1 is fixed]) bug_check=`echo "K = nfinit(y^4-10*y^2+1); disc = rnfdisc(K,x^2-(y^3/2+y^2-5*y/2+1)); idealnorm(K,disc)" | $GP -qf 2 >> config.log` expected="2304" From 4a9c3b68b2f82681b15b85987a7bee432b70b1e1 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Jun 2021 11:54:34 +0100 Subject: [PATCH 534/706] remove space between 2 and >> in script With it in, one gets *** error opening input file: `2'. ... skipping file '2' --- build/pkgs/pari/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 4fafefb5e08..d0fbb8f0738 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -67,7 +67,7 @@ SAGE_SPKG_CONFIGURE([pari], [ sage_spkg_install_pari=yes fi AC_MSG_CHECKING([whether rnfdisc bug of pari 2.13.1 is fixed]) - bug_check=`echo "K = nfinit(y^4-10*y^2+1); disc = rnfdisc(K,x^2-(y^3/2+y^2-5*y/2+1)); idealnorm(K,disc)" | $GP -qf 2 >> config.log` + bug_check=`echo "K = nfinit(y^4-10*y^2+1); disc = rnfdisc(K,x^2-(y^3/2+y^2-5*y/2+1)); idealnorm(K,disc)" | $GP -qf 2>> config.log` expected="2304" if test x"$bug_check" = x"$expected"; then AC_MSG_RESULT([yes]) From 96df4584db2d6d30808499689b7ddff3ca00df41 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 13 May 2021 22:50:07 +0100 Subject: [PATCH 535/706] GAP version bump to 4.11.1 --- build/pkgs/gap/SPKG.rst | 5 +---- build/pkgs/gap/checksums.ini | 10 +++++----- build/pkgs/gap/package-version.txt | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/build/pkgs/gap/SPKG.rst b/build/pkgs/gap/SPKG.rst index 148babf9e49..8dedb17dccd 100644 --- a/build/pkgs/gap/SPKG.rst +++ b/build/pkgs/gap/SPKG.rst @@ -31,7 +31,7 @@ Dependencies ------------ - Readline -- MPIR +- GMP Special Update/Build Instructions @@ -56,6 +56,3 @@ update GAP, please also update and use the spkg-src script. Patches ~~~~~~~ - -- writeandcheck.patch: fix infinite loop in writeandcheck() when - writing an error message fails. diff --git a/build/pkgs/gap/checksums.ini b/build/pkgs/gap/checksums.ini index 76035c17031..066e943308a 100644 --- a/build/pkgs/gap/checksums.ini +++ b/build/pkgs/gap/checksums.ini @@ -1,5 +1,5 @@ -tarball=gap-VERSION.tar.bz2 -sha1=0998ec7153ead8c6ccfc33e4c20156b7bb2ffb44 -md5=c5544dd73507c01760416ad04acff6f1 -cksum=290729880 -upstream_url=https://www.gap-system.org/pub/gap/gap-4.11/tar.bz2/gap-4.11.0.tar.bz2 +tarball=gap-VERSION.tar.gz +sha1=4ecdd281b8f430282fb9b12690b06e0a26abde10 +md5=85dc9e459d5b6c66fcad9f468afd3e3e +cksum=1351843158 +upstream_url=https://github.com/gap-system/gap/releases/download/vVERSION/gap-VERSION.tar.gz diff --git a/build/pkgs/gap/package-version.txt b/build/pkgs/gap/package-version.txt index 0367118566f..d782fca8f64 100644 --- a/build/pkgs/gap/package-version.txt +++ b/build/pkgs/gap/package-version.txt @@ -1 +1 @@ -4.11.0.p1 +4.11.1 From 91a695f7faf3dd34e3721d265561d647095c1987 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 14 May 2021 00:03:23 +0100 Subject: [PATCH 536/706] ctbl versions now numbered --- build/pkgs/gap/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in index f17117c847e..32d1fb982cc 100644 --- a/build/pkgs/gap/spkg-install.in +++ b/build/pkgs/gap/spkg-install.in @@ -61,7 +61,7 @@ sdh_install \ pkg/autpgrp-* \ pkg/alnuth-* \ pkg/crisp-* \ - pkg/ctbllib \ + pkg/ctbllib-* \ pkg/FactInt-* \ pkg/fga \ pkg/irredsol-* \ From 79b3bdeff23b0c9932bb748e5da6d47d92bc9898 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 14 May 2021 09:44:28 +0100 Subject: [PATCH 537/706] update libsemigroup --- build/pkgs/libsemigroups/checksums.ini | 8 ++++---- build/pkgs/libsemigroups/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/libsemigroups/checksums.ini b/build/pkgs/libsemigroups/checksums.ini index a64622471b6..62c4268515f 100644 --- a/build/pkgs/libsemigroups/checksums.ini +++ b/build/pkgs/libsemigroups/checksums.ini @@ -1,5 +1,5 @@ tarball=libsemigroups-VERSION.tar.gz -sha1=084ca07ea1101f668274a26ac61d13eab0508f71 -md5=60069d1f82d2285867fd829624f9e60d -cksum=2338845637 -upstream_url=https://github.com/libsemigroups/libsemigroups/releases/download/v1.0.9/libsemigroups-1.0.9.tar.gz +sha1=2b16c095cc5ffd3f77a71dfbf48cce188e054c03 +md5=7082cadcf7a195ccb93175cd72b6db95 +cksum=1501022358 +upstream_url=https://github.com/libsemigroups/libsemigroups/releases/download/vVERSION/libsemigroups-VERSION.tar.gz diff --git a/build/pkgs/libsemigroups/package-version.txt b/build/pkgs/libsemigroups/package-version.txt index 66c4c2263e5..9084fa2f716 100644 --- a/build/pkgs/libsemigroups/package-version.txt +++ b/build/pkgs/libsemigroups/package-version.txt @@ -1 +1 @@ -1.0.9 +1.1.0 From 1116073880233fe0b3377aaae33c85401da69ec4 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 14 May 2021 09:45:09 +0100 Subject: [PATCH 538/706] more GAP packages are deps --- build/pkgs/gap_packages/spkg-install.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/pkgs/gap_packages/spkg-install.in b/build/pkgs/gap_packages/spkg-install.in index 42870146f02..168e6b11532 100644 --- a/build/pkgs/gap_packages/spkg-install.in +++ b/build/pkgs/gap_packages/spkg-install.in @@ -23,6 +23,7 @@ sdh_install \ hap-* \ hapcryst-* \ hecke-* \ + images-* \ liealgdb-* \ liepring-* \ liering-* \ @@ -64,7 +65,7 @@ install_compiled_pkg() # # These packages have an old ./configure that take the GAP_ROOT as a positional # argument -for pkg in cohomolo-* crypting-* grape-* guava-* orb-* +for pkg in cohomolo-* crypting-* grape-* guava-* orb-* datastructures-* do echo "Building GAP package $pkg" CFLAGS="$CFLAGS -Wno-implicit-function-declaration" From 7469042d7e6fa6dda64e23a26ed571c3c38d7c9a Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Fri, 14 May 2021 10:16:06 +0100 Subject: [PATCH 539/706] modify the test so that Sage's "table" is not used see https://trac.sagemath.org/ticket/31498#comment:5 for reasoning Also, removed useless lines in a not-tested test --- src/sage/tests/gap_packages.py | 40 +++++----------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/src/sage/tests/gap_packages.py b/src/sage/tests/gap_packages.py index cf9a68cec25..0c6ef589a9f 100644 --- a/src/sage/tests/gap_packages.py +++ b/src/sage/tests/gap_packages.py @@ -5,11 +5,11 @@ sage: from sage.tests.gap_packages import all_installed_packages, test_packages sage: pkgs = all_installed_packages(ignore_dot_gap=True) - sage: test_packages(pkgs, only_failures=True) # optional - gap_packages - ... - Status Package GAP Output - +--------+---------+------------+ - + sage: libgap.SetInfoLevel(libgap.InfoWarning, 0) + sage: for p in pkgs: + ....: pkg = p.split('-')[0] + ....: if not libgap.LoadPackage(pkg): + ....: raise sage: test_packages(['atlasrep', 'tomlib']) Status Package GAP Output +--------+----------+------------+ @@ -55,36 +55,6 @@ def test_packages(packages, only_failures=False): +---------+------------+------------+ Alnuth true GAPDoc true - HAPcryst true - Hap true - QPA true - aclib true - atlasrep true - autpgrp true - cohomolo true - corelg true - crime true - cryst true - crystcat true - ctbllib true - design true - factint true - gbnp true - grape true - guava true - happrime true - hecke true - laguna true - liealgdb true - liepring true - liering true - loops true - mapclass true - polycyclic true - polymaking true - quagroup true - repsn true - sla true sonata true tomlib true toric true From 82f3fa7bcc0e73c85690a0886a67c5d8cd2e5c2b Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 2 Jun 2021 00:45:11 +0100 Subject: [PATCH 540/706] better fix for the test --- src/sage/tests/gap_packages.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/tests/gap_packages.py b/src/sage/tests/gap_packages.py index 0c6ef589a9f..2e4518ca226 100644 --- a/src/sage/tests/gap_packages.py +++ b/src/sage/tests/gap_packages.py @@ -5,11 +5,10 @@ sage: from sage.tests.gap_packages import all_installed_packages, test_packages sage: pkgs = all_installed_packages(ignore_dot_gap=True) - sage: libgap.SetInfoLevel(libgap.InfoWarning, 0) - sage: for p in pkgs: - ....: pkg = p.split('-')[0] - ....: if not libgap.LoadPackage(pkg): - ....: raise + sage: test_packages(pkgs, only_failures=True) # optional - gap_packages + Status Package GAP Output + +--------+---------+------------+ + sage: test_packages(['atlasrep', 'tomlib']) Status Package GAP Output +--------+----------+------------+ From 4143a2feec47df4aab8ab2bc177836cba12a0427 Mon Sep 17 00:00:00 2001 From: Thomas Rud Date: Tue, 8 Jun 2021 11:18:09 -0700 Subject: [PATCH 541/706] fixed typos --- src/sage/modules/free_module_homspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index 81f7a7a6023..e296c40e087 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -181,7 +181,7 @@ def __call__(self, A, check=True): sage: H = V.Hom(W); H(m) Traceback (most recent call last): ... - TypeError: Nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain + TypeError: nontrivial morphisms require a coercion map from the base ring of the domain to the base ring of the codomain sage: n = zero_matrix(2); sage: h = H(n); h Free module morphism defined by the matrix From 007e6fda9fb5dfccf109b06d15f8799f7f03ac5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 13:33:20 -0700 Subject: [PATCH 542/706] ConvexRationalPolyhedralCone.is_empty: Add doctest --- src/sage/geometry/cone.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index dc43db1a6a0..aa1acf765ac 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3283,6 +3283,13 @@ def is_empty(self): Return whether ``self`` is the empty set. Because a cone always contains the origin, this method returns ``False``. + + EXAMPLES:: + + sage: trivial_cone = cones.trivial(3) + sage: trivial_cone.is_empty() + False + """ return False From 9fcf32ea544d7e841a41deda364c179205108e9b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 17:06:15 -0700 Subject: [PATCH 543/706] Sets.CartesianProducts.ParentMethods, FreeModule_ambient, IntegerRing_class, InternalRealInterval, RealSet, NonNegativeIntegers, IntegerRing_class, PositiveIntegers, RationalField: Add _sympy_ methods --- src/sage/categories/sets_cat.py | 15 ++++++ src/sage/modules/free_module.py | 14 ++++++ src/sage/rings/integer_ring.pyx | 12 +++++ src/sage/rings/rational_field.py | 12 +++++ src/sage/sets/non_negative_integers.py | 13 ++++++ src/sage/sets/positive_integers.py | 12 +++++ src/sage/sets/real_set.py | 63 ++++++++++++++++++++++++++ 7 files changed, 141 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..6b2f57e18cf 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2438,6 +2438,21 @@ def _cartesian_product_of_elements(self, elements): (42, 47, 42) """ + def _sympy_(self): + """ + Return a SymPy ``ProductSet`` corresponding to ``self``. + + EXAMPLES:: + + sage: ZZ3 = cartesian_product([ZZ, ZZ, ZZ]) + sage: sZZ3 = ZZ3._sympy_(); sZZ3 + ProductSet(Integers, Integers, Integers) + sage: (1, 2, 3) in sZZ3 + True + """ + from sympy import ProductSet + return ProductSet(*self.cartesian_factors()) + class ElementMethods: def cartesian_projection(self, i): diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b4ce5196e48..36f668d99fe 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -5306,6 +5306,20 @@ def gen(self, i=0): v.set_immutable() return v + def _sympy_(self): + """ + Return a SymPy ``ProductSet`` corresponding to ``self``. + + EXAMPLES:: + + sage: sZZ3 = (ZZ^3)._sympy_(); sZZ3 + ProductSet(Integers, Integers, Integers) + sage: (1, 2, 3) in sZZ3 + True + """ + from sympy import ProductSet + return ProductSet(*([self.coordinate_ring()] * self.rank())) + ############################################################################### # diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index ae0b0f41327..737dda9ecb3 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1467,6 +1467,18 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ return '"Integer"' + def _sympy_(self): + r""" + Return the SymPy set ``Integers``. + + EXAMPLES:: + + sage: ZZ._sympy_() + Integers + """ + from sympy import Integers + return Integers + def _sage_input_(self, sib, coerced): r""" Produce an expression which will reproduce this value when diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index e7959e21a01..2a4237f0dfc 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1576,6 +1576,18 @@ def _polymake_init_(self): """ return '"Rational"' + def _sympy_(self): + r""" + Return the SymPy set ``Rationals``. + + EXAMPLES:: + + sage: QQ._sympy_() + Rationals + """ + from sympy import Rationals + return Rationals + def _sage_input_(self, sib, coerced): r""" Produce an expression which will reproduce this value when evaluated. diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index 9d3579a5f46..adabc89a987 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -221,3 +221,16 @@ def unrank(self, rnk): 100 """ return self.from_integer(rnk) + + def _sympy_(self): + r""" + Return the SymPy set ``Naturals0``. + + EXAMPLES:: + + sage: NN = NonNegativeIntegers() + sage: NN._sympy_() + Naturals0 + """ + from sympy import Naturals0 + return Naturals0 diff --git a/src/sage/sets/positive_integers.py b/src/sage/sets/positive_integers.py index c800d201e95..693ca04706c 100644 --- a/src/sage/sets/positive_integers.py +++ b/src/sage/sets/positive_integers.py @@ -75,3 +75,15 @@ def an_element(self): 42 """ return Integer(42) + + def _sympy_(self): + r""" + Return the SymPy set ``Naturals``. + + EXAMPLES:: + + sage: PositiveIntegers()._sympy_() + Naturals + """ + from sympy import Naturals + return Naturals diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..f2eb7d97202 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -415,6 +415,28 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition + def _sympy_(self): + r""" + Return the SymPy set corresponding to ``self``. + + EXAMPLES:: + + sage: RealSet.open_closed(0, 1)[0]._sympy_() + Interval.Lopen(0, 1) + sage: RealSet.point(0)[0]._sympy_() + FiniteSet(0) + sage: RealSet.open(0,1)[0]._sympy_() + Interval.open(0, 1) + sage: RealSet.open(-oo,1)[0]._sympy_() + Interval.open(-oo, 1) + sage: RealSet.open(0, oo)[0]._sympy_() + Interval.open(0, oo) + """ + from sympy import Interval + return Interval(self.lower(), self.upper(), + left_open=not self._lower_closed, + right_open=not self._upper_closed) + def closure(self): """ Return the closure @@ -986,6 +1008,17 @@ def is_empty(self): """ return len(self._intervals) == 0 + def is_universe(self): + """ + Return whether the set is the ambient space (the real line). + + EXAMPLES:: + + sage: RealSet().ambient().is_universe() + True + """ + return self == self.ambient() + def get_interval(self, i): """ Return the ``i``-th connected component. @@ -1811,3 +1844,33 @@ def __rmul__(self, other): [0, 1/2*pi] + [2*pi, +oo) """ return self * other + + def _sympy_(self): + r""" + Return the SymPy set corresponding to ``self``. + + EXAMPLES:: + + sage: RealSet()._sympy_() + EmptySet + sage: RealSet.point(5)._sympy_() + FiniteSet(5) + sage: (RealSet.point(1).union(RealSet.point(2)))._sympy_() + FiniteSet(1, 2) + sage: (RealSet(1, 2).union(RealSet.closed(3, 4)))._sympy_() + Union(Interval.open(1, 2), Interval(3, 4)) + sage: RealSet(-oo, oo)._sympy_() + Reals + + Infinities are not elements:: + + sage: import sympy + sage: RealSet(-oo, oo)._sympy_().contains(sympy.oo) + False + """ + from sympy import Reals, Union + if self.is_universe(): + return Reals + else: + return Union(*[interval._sympy_() + for interval in self._intervals]) From 6e5cac6491592b1d451b400de5e4f1b722db9c20 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 19:08:01 -0700 Subject: [PATCH 544/706] sage.interfaces.sympy_wrapper, Sets.ParentMethods._sympy_: New --- src/sage/categories/sets_cat.py | 26 +++++++++++++++ src/sage/interfaces/sympy_wrapper.py | 50 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/sage/interfaces/sympy_wrapper.py diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..c99f026dd0f 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1686,6 +1686,32 @@ def algebra(self, base_ring, category=None, **kwds): result.__doc__ = Sets.ParentMethods.algebra.__doc__ return result + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: F = FiniteEnumeratedSets().example(); F + An example of a finite enumerated set: {1,2,3} + sage: sF = F._sympy_(); sF + SageSet(An example of a finite enumerated set: {1,2,3}) + sage: bool(sF) + True + sage: len(sF) + 3 + sage: list(sF) + [1, 2, 3] + sage: from sympy import FiniteSet + sage: FiniteSet.fromiter(sF) + FiniteSet(1, 2, 3) + + sage: RR._sympy_().is_finite_set + False + """ + from sage.interfaces.sympy_wrapper import SageSet + return SageSet(self) + class ElementMethods: ## Should eventually contain the basic operations which are no math ## latex, hash, ... diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py new file mode 100644 index 00000000000..19e9b7ce610 --- /dev/null +++ b/src/sage/interfaces/sympy_wrapper.py @@ -0,0 +1,50 @@ +""" +Wrapper Class for Sage Sets as SymPy Sets +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sympy.core.basic import Basic +from sympy.core.decorators import sympify_method_args +from sympy.core.sympify import sympify +from sympy.sets.sets import Set + +@sympify_method_args +class SageSet(Set): + + def __new__(cls, sage_set): + return Basic.__new__(cls, sage_set) + + def _sage_(self): + return self._args[0] + + @property + def is_empty(self): + return self._sage_().is_empty() + + @property + def is_finite_set(self): + return self._sage_().is_finite() + + @property + def is_iterable(self): + from sage.categories.enumerated_sets import EnumeratedSets + return self._sage_() in EnumeratedSets() + + def __iter__(self): + for element in self._sage_(): + yield sympify(element) + + def _contains(self, other): + return other in self._sage_() + + def __len__(self): + return len(self._sage_()) From 72565f2720115475272c030db26b1144b87377bb Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 2 Jan 2018 10:15:15 +0100 Subject: [PATCH 545/706] 24171: Formal set membership function --- src/sage/functions/other.py | 55 +++++++++++++++++++++++++++++++ src/sage/libs/pynac/pynac.pyx | 2 +- src/sage/sets/real_set.py | 15 +++++++++ src/sage/symbolic/random_tests.py | 6 ++-- src/sage/symbolic/ring.pyx | 23 +++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 506cf52c218..1749c4712d9 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2285,3 +2285,58 @@ def _evalf_(self, poly, index, parent=None, algorithm=None): complex_root_of = Function_crootof() + +class Function_elementof(BuiltinFunction): + """ + Formal set membership function that is only accessible internally. + + This function is called to express a set membership statement, + usually as part of a solution set returned by ``solve()``. + See :class:`sage.sets.set.Set` and :class:`sage.sets.real_set.RealSet` + for possible set arguments. + + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: element_of(x, SR(ZZ)) + element_of(x, Integer Ring) + sage: element_of(sin(x), SR(QQ)) + element_of(sin(x), Rational Field) + sage: element_of(x, SR(RealSet.open_closed(0,1))) + element_of(x, (0, 1]) + sage: element_of(x, SR(Set([4,6,8]))) + element_of(x, {8, 4, 6}) + """ + def __init__(self): + """ + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: loads(dumps(element_of)) + element_of + """ + BuiltinFunction.__init__(self, "element_of", nargs=2) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: latex(element_of) + \in + """ + return r'\in' + + def _print_latex_(self, ex, s): + r""" + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: latex(element_of(x, SR(ZZ))) + x \in \Bold{Z} + sage: latex(element_of(x, SR(Set([4,6,8])))) + x \in \left\{8, 4, 6\right\} + """ + return r"{} \in {}".format(latex(ex), latex(s)) + +element_of = Function_elementof() diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 4c645545ac5..7dac2b54247 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -1190,7 +1190,7 @@ cdef bint py_is_real(a): return False except NotImplementedError: return False - except AttributeError: + except (TypeError, AttributeError): pass return py_imag(a) == 0 diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..e4151d625b2 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -986,6 +986,21 @@ def is_empty(self): """ return len(self._intervals) == 0 + def is_finite(self): + """ + Return whether the set is finite. + + EXAMPLES:: + + sage: RealSet().is_finite() + True + sage: RealSet(0, 0).is_finite() + True + sage: RealSet(0, 1).is_finite() + False + """ + return self.is_empty() or all(i.is_point() for i in self._intervals) + def get_interval(self, i): """ Return the ``i``-th connected component. diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index 81ac9310f40..fef6932f8e5 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -20,7 +20,7 @@ from sage.symbolic.constants import (pi, e, golden_ratio, log2, euler_gamma, catalan, khinchin, twinprime, mertens) from sage.functions.hypergeometric import hypergeometric -from sage.functions.other import cases +from sage.functions.other import (cases, element_of) from sage.symbolic.comparison import mixed_order ################################################################### @@ -48,13 +48,13 @@ def _mk_full_functions(): Note that this doctest will produce different output whenever a symbolic function is added or removed. """ + excluded = [hypergeometric, cases, element_of] items = sorted(symbol_table['functions'].items()) return [(1.0, f, f.number_of_arguments()) for (name, f) in items if hasattr(f, 'number_of_arguments') and f.number_of_arguments() > 0 and - f != hypergeometric and - f != cases] + f not in excluded] # For creating simple expressions diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 692266db00c..b5b788af6e9 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -254,6 +254,19 @@ cdef class SymbolicRing(CommutativeRing): sage: SR(complex(2,-3)) (2-3j) + Any proper subset of the complex numbers:: + + sage: SR(NN) + Non negative integer semiring + sage: SR(ZZ) + Integer Ring + sage: SR(Set([1/2, 2/3, 3/4])) + {3/4, 2/3, 1/2} + sage: SR(QQ.completion(oo, oo)) + Real Field + sage: SR(RealSet(0, 1)) + (0, 1) + TESTS:: sage: SR._coerce_(int(5)) @@ -364,6 +377,7 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.infinity import (infinity, minus_infinity, unsigned_infinity) from sage.structure.factorization import Factorization + from sage.categories.sets_cat import Sets if isinstance(x, RealNumber): if x.is_NaN(): @@ -392,6 +406,15 @@ cdef class SymbolicRing(CommutativeRing): elif isinstance(x, Factorization): from sage.misc.all import prod return prod([SR(p)**e for p,e in x], SR(x.unit())) + elif x in Sets(): + from sage.rings.all import NN, ZZ, QQ, AA + from sage.sets.real_set import RealSet + oo = infinity + if (x.is_finite() or x in (NN, ZZ, QQ, AA, QQ.completion(oo,oo)) + or isinstance(x, RealSet)): + exp = x + else: + raise TypeError(f"unable to convert {x!r} to a symbolic expression") else: raise TypeError(f"unable to convert {x!r} to a symbolic expression") From 5e623bb508d45a0ac79538c93bc115562b6269e0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:22:03 -0700 Subject: [PATCH 546/706] RealSet.is_finite: Remove, inherited from category --- src/sage/sets/real_set.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 57f6b4b73e0..5d39fbe0029 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1015,21 +1015,6 @@ def is_empty(self): """ return len(self._intervals) == 0 - def is_finite(self): - """ - Return whether the set is finite. - - EXAMPLES:: - - sage: RealSet().is_finite() - True - sage: RealSet(0, 0).is_finite() - True - sage: RealSet(0, 1).is_finite() - False - """ - return self.is_empty() or all(i.is_point() for i in self._intervals) - def get_interval(self, i): """ Return the ``i``-th connected component. From 7d19916c0fbf2bf76054401df372fbedde7cdbb6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:35:40 -0700 Subject: [PATCH 547/706] Do not use QQ.completion(oo,oo) - not implemented --- src/sage/symbolic/ring.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index b5b788af6e9..e113bd44b78 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -410,7 +410,7 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.all import NN, ZZ, QQ, AA from sage.sets.real_set import RealSet oo = infinity - if (x.is_finite() or x in (NN, ZZ, QQ, AA, QQ.completion(oo,oo)) + if (x.is_finite() or x in (NN, ZZ, QQ, AA) or isinstance(x, RealSet)): exp = x else: From 9b4e2246708b683b3bbcee95eef1a96223a250c3 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 10 Nov 2017 07:39:52 +0100 Subject: [PATCH 548/706] 24176: check set argument of element_of() --- src/sage/functions/other.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 1749c4712d9..b83b95d4fad 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2317,6 +2317,22 @@ def __init__(self): """ BuiltinFunction.__init__(self, "element_of", nargs=2) + def _eval_(self, x, s): + """ + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: element_of(x, SR(RealSet(-oo, oo))) + element_of(x, (-oo, +oo)) + sage: element_of(x, 0) + Traceback (most recent call last): + ... + ValueError: not a set: 0 + """ + from sage.sets.set import is_Set + if not is_Set(s): + raise ValueError("not a set: {}".format(s)) + def _latex_(self): r""" EXAMPLES:: From 5310e04bae60c574eafe491ebcea366e57513822 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:55:07 -0700 Subject: [PATCH 549/706] Function_elementof: Remove use of is_Set --- src/sage/functions/other.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index b83b95d4fad..23f726b239c 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2329,8 +2329,8 @@ def _eval_(self, x, s): ... ValueError: not a set: 0 """ - from sage.sets.set import is_Set - if not is_Set(s): + from sage.sets.sets_cat import Sets + if not s in Sets(): raise ValueError("not a set: {}".format(s)) def _latex_(self): From 43cabe82529c55b1ab3add68304f3b2b73971cba Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 21:49:46 -0700 Subject: [PATCH 550/706] Do not use QQ.completion(oo,oo) - not implemented (fixup) --- src/sage/symbolic/ring.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index e113bd44b78..5836c26e173 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -262,8 +262,6 @@ cdef class SymbolicRing(CommutativeRing): Integer Ring sage: SR(Set([1/2, 2/3, 3/4])) {3/4, 2/3, 1/2} - sage: SR(QQ.completion(oo, oo)) - Real Field sage: SR(RealSet(0, 1)) (0, 1) @@ -409,7 +407,6 @@ cdef class SymbolicRing(CommutativeRing): elif x in Sets(): from sage.rings.all import NN, ZZ, QQ, AA from sage.sets.real_set import RealSet - oo = infinity if (x.is_finite() or x in (NN, ZZ, QQ, AA) or isinstance(x, RealSet)): exp = x From c793fcc4f9def7070c36785603958c48cf2710b0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 21:53:35 -0700 Subject: [PATCH 551/706] Function_elementof: Remove use of is_Set (fixup) --- src/sage/functions/other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 23f726b239c..57023dd0084 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2329,7 +2329,7 @@ def _eval_(self, x, s): ... ValueError: not a set: 0 """ - from sage.sets.sets_cat import Sets + from sage.categories.sets_cat import Sets if not s in Sets(): raise ValueError("not a set: {}".format(s)) From a8c94cab974c8983dad63c2c6e989936984de49a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 22:04:28 -0700 Subject: [PATCH 552/706] Function_elementof: Add conversion to sympy --- src/sage/functions/other.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 57023dd0084..709f2399676 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2315,7 +2315,8 @@ def __init__(self): sage: loads(dumps(element_of)) element_of """ - BuiltinFunction.__init__(self, "element_of", nargs=2) + BuiltinFunction.__init__(self, "element_of", nargs=2, + conversions=dict(sympy='Contains')) def _eval_(self, x, s): """ From d68a69f42ab90e51eb1b7ad1c938ddb05539114f Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 8 Nov 2017 08:55:47 +0100 Subject: [PATCH 553/706] RealSet.__bool__: New (split out from #24171) --- src/sage/sets/real_set.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..f36379548a4 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1017,6 +1017,20 @@ def get_interval(self, i): __getitem__ = get_interval + def __bool__(self): + """ + A set is considered True unless it is empty, in which case it is + considered to be False. + + EXAMPLES:: + + sage: bool(RealSet(0, 1)) + True + sage: bool(RealSet()) + False + """ + return not self.is_empty() + @staticmethod def normalize(intervals): """ From 9079a1030aabaa3042a968dab7cf12526597cb29 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 22:57:43 -0700 Subject: [PATCH 554/706] RealSet.is_subset: Rename from is_included_in --- src/sage/sets/real_set.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..e7c53f53320 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -93,6 +93,7 @@ class RealSet. from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity +from sage.misc.superseded import deprecated_function_alias @richcmp_method class InternalRealInterval(UniqueRepresentation, Parent): @@ -1599,13 +1600,13 @@ def contains(self, x): __contains__ = contains - def is_included_in(self, *other): + def is_subset(self, *other): r""" - Tests interval inclusion + Return whether ``self`` is a subset of ``other``. INPUT: - - ``*args`` -- a :class:`RealSet` or something that defines + - ``*other`` -- a :class:`RealSet` or something that defines one. OUTPUT: @@ -1617,13 +1618,15 @@ def is_included_in(self, *other): sage: I = RealSet((1,2)) sage: J = RealSet((1,3)) sage: K = RealSet((2,3)) - sage: I.is_included_in(J) + sage: I.is_subset(J) True - sage: J.is_included_in(K) + sage: J.is_subset(K) False """ return RealSet(*other).intersection(self) == self + is_included_in = deprecated_function_alias(31927, is_subset) + def an_element(self): """ Return a point of the set From 3d857863ae9de66e5ca94a95e0c5f67f87e33f79 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 9 Jun 2021 09:52:08 +0200 Subject: [PATCH 555/706] make modules fuzz ready --- src/sage/modules/fg_pid/fgp_module.py | 40 +++++++-- src/sage/modules/filtered_vector_space.py | 13 ++- src/sage/modules/free_module.py | 74 +++++++++++----- src/sage/modules/free_module_element.pyx | 77 ++++++++++------ src/sage/modules/free_module_integer.py | 87 ++++++------------- .../modules/multi_filtered_vector_space.py | 14 ++- src/sage/modules/vector_integer_dense.pyx | 7 +- src/sage/modules/vector_mod2_dense.pyx | 80 ++++++++--------- 8 files changed, 228 insertions(+), 164 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 38b95d40a39..9349f2fd42a 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1698,8 +1698,13 @@ def random_element(self, *args, **kwds): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W - sage: Q.random_element() - (1, 10) + sage: Q.random_element().parent() is Q + True + sage: Q.cardinality() + 48 + sage: S = set() + sage: while len(S) < 48: + ....: S.add(Q.random_element()) """ return self(self._V.random_element(*args, **kwds)) @@ -1921,7 +1926,23 @@ def random_fgp_module(n, R=ZZ, finite=False): sage: import sage.modules.fg_pid.fgp_module as fgp sage: fgp.random_fgp_module(4) - Finitely generated module V/W over Integer Ring with invariants (4) + Finitely generated module V/W over Integer Ring with invariants (...) + + In most cases the cardinality is small or infinite:: + + sage: for g in (1, 2, 3, +Infinity): + ....: while fgp.random_fgp_module(4).cardinality() != 1: + ....: pass + + One can force a finite module:: + + sage: fgp.random_fgp_module(4, finite=True).is_finite() + True + + Larger finite modules appear:: + + sage: while fgp.random_fgp_module(4, finite=True).cardinality() < 100: + ....: pass """ K = R.fraction_field() V = K**n @@ -1948,9 +1969,18 @@ def random_fgp_morphism_0(*args, **kwds): EXAMPLES:: sage: import sage.modules.fg_pid.fgp_module as fgp - sage: fgp.random_fgp_morphism_0(4) - Morphism from module over Integer Ring with invariants (4,) to module with invariants (4,) that sends the generators to [(0)] + sage: mor = fgp.random_fgp_morphism_0(4) + sage: mor.domain() == mor.codomain() + True + sage: fgp.is_FGP_Module(mor.domain()) + True + + Each generator is sent to a random multiple of itself:: + sage: gens = mor.domain().gens() + sage: im_gens = mor.im_gens() + sage: all(im_gens[i] == sum(im_gens[i])*gens[i] for i in range(len(gens))) + True """ A = random_fgp_module(*args, **kwds) return A.hom([ZZ.random_element() * g for g in A.smith_form_gens()]) diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py index fa67153e75b..4d0e451806e 100644 --- a/src/sage/modules/filtered_vector_space.py +++ b/src/sage/modules/filtered_vector_space.py @@ -1254,10 +1254,15 @@ def random_deformation(self, epsilon=None): [1 0 0] sage: G = F.random_deformation(1/50); G QQ^3 >= QQ^1 >= QQ^1 >= 0 - sage: G.get_degree(2) - Vector space of degree 3 and dimension 1 over Rational Field - Basis matrix: - [ 1 -15/304 0] + sage: D = G.get_degree(2) + sage: D.degree() + 3 + sage: v = D.basis_matrix()[0] + sage: v[0] + 1 + + sage: while F.random_deformation(1/50).get_degree(2).matrix() == matrix([1, 0, 0]): + ....: pass """ from sage.modules.free_module_element import random_vector R = self.base_ring() diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b4ce5196e48..c5099442012 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -2362,18 +2362,28 @@ def random_element(self, prob=1.0, *args, **kwds): EXAMPLES:: - sage: M = FreeModule(ZZ, 2).span([[1,1]]) - sage: M.random_element() - (-1, -1) - sage: M.random_element() - (2, 2) - sage: M.random_element() - (1, 1) + sage: M = FreeModule(ZZ, 2).span([[1, 1]]) + sage: v = M.random_element() + sage: v.parent() is M + True + sage: v in M + True + + Small entries are likely:: + + sage: for i in [-2, -1, 0, 1, 2]: + ....: while vector([i, i]) != M.random_element(): + ....: pass + + Large entries appear as well:: + + sage: while abs(M.random_element()[0]) < 100: + ....: pass Passes extra positional or keyword arguments through:: - sage: M.random_element(5,10) - (9, 9) + sage: all(i in range(5, 10) for i in M.random_element(1.0, 5, 10)) + True """ rand = current_randstate().python_random().random R = self.base_ring() @@ -5226,26 +5236,46 @@ def random_element(self, prob=1.0, *args, **kwds): EXAMPLES:: sage: M = FreeModule(ZZ, 3) - sage: M.random_element() - (-1, 2, 1) - sage: M.random_element() - (-95, -1, -2) - sage: M.random_element() - (-12, 0, 0) + sage: M.random_element().parent() is M + True Passes extra positional or keyword arguments through:: - sage: M.random_element(5,10) - (5, 5, 5) - + sage: all(i in range(5, 10) for i in M.random_element(1.0, 5, 10)) + True :: sage: M = FreeModule(ZZ, 16) - sage: M.random_element() - (-6, 5, 0, 0, -2, 0, 1, -4, -6, 1, -1, 1, 1, -1, 1, -1) - sage: M.random_element(prob=0.3) - (0, 0, 0, 0, -3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -3) + sage: M.random_element().parent() is M + True + + sage: def add_sample(**kwds): + ....: global total, zeros + ....: v = M.random_element(**kwds) + ....: total += M.rank() + ....: zeros += sum(i == 0 for i in v) + + sage: total = 0 + sage: zeros = 0 + sage: add_sample() + sage: expected = 1/5 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample() + + sage: total = 0 + sage: zeros = 0 + sage: add_sample(prob=0.3) + sage: expected = 1/5 * 3/10 + 7/10 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample(prob=0.3) + + sage: total = 0 + sage: zeros = 0 + sage: add_sample(prob=0.7) + sage: expected = 1/5 * 7/10 + 3/10 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample(prob=0.7) """ rand = current_randstate().python_random().random R = self.base_ring() diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 1bbe475695c..e6487e6f17a 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -776,58 +776,66 @@ def random_vector(ring, degree=None, *args, **kwds): :meth:`sage.rings.integer_ring.IntegerRing_class.random_element` for several other variants. :: - sage: random_vector(10) - (-8, 2, 0, 0, 1, -1, 2, 1, -95, -1) + sage: random_vector(10).parent() + Ambient free module of rank 10 over the principal ideal domain Integer Ring + sage: random_vector(20).parent() + Ambient free module of rank 20 over the principal ideal domain Integer Ring - sage: sorted(random_vector(20)) - [-12, -6, -4, -4, -2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 4, 5] - - sage: random_vector(ZZ, 20, x=4) - (2, 0, 3, 0, 1, 0, 2, 0, 2, 3, 0, 3, 1, 2, 2, 2, 1, 3, 2, 3) - - sage: random_vector(ZZ, 20, x=-20, y=100) - (43, 47, 89, 31, 56, -20, 23, 52, 13, 53, 49, -12, -2, 94, -1, 95, 60, 83, 28, 63) + sage: v = random_vector(ZZ, 20, x=4) + sage: all(i in range(4) for i in v) + True - sage: random_vector(ZZ, 20, distribution="1/n") - (0, -1, -2, 0, -1, -2, 0, 0, 27, -1, 1, 1, 0, 2, -1, 1, -1, -2, -1, 3) + sage: v = random_vector(ZZ, 20, x=-20, y=100) + sage: all(i in range(-20, 100) for i in v) + True If the ring is not specified, the default is the integers, and parameters for the random distribution may be passed without using keywords. This is a random vector with 20 entries uniformly distributed between -20 and 100. :: - sage: random_vector(20, -20, 100) - (70, 19, 98, 2, -18, 88, 36, 66, 76, 52, 82, 99, 55, -17, 82, -15, 36, 28, 79, 18) + sage: random_vector(20, -20, 100).parent() + Ambient free module of rank 20 over the principal ideal domain Integer Ring Now over the rationals. Note that bounds on the numerator and denominator may be specified. See :meth:`sage.rings.rational_field.RationalField.random_element` for documentation. :: - sage: random_vector(QQ, 10) - (0, -1, -4/3, 2, 0, -13, 2/3, 0, -4/5, -1) + sage: random_vector(QQ, 10).parent() + Vector space of dimension 10 over Rational Field - sage: random_vector(QQ, 10, num_bound = 15, den_bound = 5) - (-12/5, 9/4, -13/3, -1/3, 1, 5/4, 4, 1, -15, 10/3) + sage: v = random_vector(QQ, 10, num_bound=15, den_bound=5) + sage: v.parent() + Vector space of dimension 10 over Rational Field + sage: all(q.numerator() <= 15 and q.denominator() <= 5 for q in v) + True Inexact rings may be used as well. The reals have uniform distributions, with the range `(-1,1)` as the default. More at: :meth:`sage.rings.real_mpfr.RealField_class.random_element` :: - sage: random_vector(RR, 5) - (0.248997268533725, -0.112200126330480, 0.776829203293064, -0.899146461031406, 0.534465018743125) + sage: v = random_vector(RR, 5) + sage: v.parent() + Vector space of dimension 5 over Real Field with 53 bits of precision + sage: all(-1 <= r <= 1 for r in v) + True - sage: random_vector(RR, 5, min = 8, max = 14) - (8.43260944052606, 8.34129413391087, 8.92391495103829, 11.5784799413416, 11.0973561568002) + sage: v = random_vector(RR, 5, min=8, max=14) + sage: v.parent() + Vector space of dimension 5 over Real Field with 53 bits of precision + sage: all(8 <= r <= 14 for r in v) + True Any ring with a ``random_element()`` method may be used. :: sage: F = FiniteField(23) sage: hasattr(F, 'random_element') True - sage: random_vector(F, 10) - (21, 6, 5, 2, 6, 2, 18, 9, 9, 7) + sage: v = random_vector(F, 10) + sage: v.parent() + Vector space of dimension 10 over Finite Field of size 23 The default implementation is a dense representation, equivalent to setting ``sparse=False``. :: @@ -840,6 +848,25 @@ def random_vector(ring, degree=None, *args, **kwds): sage: w.is_sparse() True + The elements are chosen using the ring's ``random_element`` method:: + + sage: from sage.misc.randstate import current_randstate + sage: seed = current_randstate().seed() + sage: set_random_seed(seed) + sage: v1 = random_vector(ZZ, 20, distribution="1/n") + sage: v2 = random_vector(ZZ, 15, x=-1000, y=1000) + sage: v3 = random_vector(QQ, 10) + sage: v4 = random_vector(FiniteField(17), 10) + sage: v5 = random_vector(RR, 10) + sage: set_random_seed(seed) + sage: w1 = vector(ZZ.random_element(distribution="1/n") for _ in range(20)) + sage: w2 = vector(ZZ.random_element(x=-1000, y=1000) for _ in range(15)) + sage: w3 = vector(QQ.random_element() for _ in range(10)) + sage: w4 = vector(FiniteField(17).random_element() for _ in range(10)) + sage: w5 = vector(RR.random_element() for _ in range(10)) + sage: [v1, v2, v3, v4, v5] == [w1, w2, w3, w4, w5] + True + Inputs get checked before constructing the vector. :: sage: random_vector('junk') @@ -2570,7 +2597,7 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: u.dot_product(w) 0 - The cross product is defined for degree seven vectors as well: + The cross product is defined for degree seven vectors as well: see :wikipedia:`Cross_product`. The 3-D cross product is achieved using the quaternions, whereas the 7-D cross product is achieved using the octonions. :: diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 443083a3e11..3da0f6feacb 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -254,14 +254,10 @@ def __init__(self, ambient, basis, check=True, echelonize=False, [ 1 -2 0] [ 2 2 1] - sage: IntegerLattice(random_matrix(ZZ, 5, 5, x=-2^20, y=2^20)) - Free module of degree 5 and rank 5 over Integer Ring - User basis matrix: - [ -7945 -381123 85872 -225065 12924] - [-158254 120252 189195 -262144 -345323] - [ 232388 -49556 306585 -31340 401528] - [-353460 213748 310673 158140 172810] - [-287787 333937 -145713 -482137 186529] + sage: M = random_matrix(ZZ, 5, 5, x=-2^20, y=2^20) + sage: L = IntegerLattice(M) + sage: M.row_space() == L.matrix().row_space() + True sage: K. = NumberField(x^8+1) sage: O = K.ring_of_integers() @@ -307,32 +303,20 @@ def reduced_basis(self): EXAMPLES:: sage: from sage.modules.free_module_integer import IntegerLattice - sage: L = IntegerLattice(random_matrix(ZZ, 10, 10), lll_reduce=False) - sage: L.reduced_basis - [ -8 2 0 0 1 -1 2 1 -95 -1] - [ -2 -12 0 0 1 -1 1 -1 -2 -1] - [ 4 -4 -6 5 0 0 -2 0 1 -4] - [ -6 1 -1 1 1 -1 1 -1 -3 1] - [ 1 0 0 -3 2 -2 0 -2 1 0] - [ -1 1 0 0 1 -1 4 -1 1 -1] - [ 14 1 -5 4 -1 0 2 4 1 1] - [ -2 -1 0 4 -3 1 -5 0 -2 -1] - [ -9 -1 -1 3 2 1 -1 1 -2 1] - [ -1 2 -7 1 0 2 3 -1955 -22 -1] - - sage: _ = L.LLL() - sage: L.reduced_basis - [ 1 0 0 -3 2 -2 0 -2 1 0] - [ -1 1 0 0 1 -1 4 -1 1 -1] - [ -2 0 0 1 0 -2 -1 -3 0 -2] - [ -2 -2 0 -1 3 0 -2 0 2 0] - [ 1 1 1 2 3 -2 -2 0 3 1] - [ -4 1 -1 0 1 1 2 2 -3 3] - [ 1 -3 -7 2 3 -1 0 0 -1 -1] - [ 1 -9 1 3 1 -3 1 -1 -1 0] - [ 8 5 19 3 27 6 -3 8 -25 -22] - [ 172 -25 57 248 261 793 76 -839 -41 376] + sage: M = random_matrix(ZZ, 10, 10) + sage: while M.rank() < 10: + ....: M = random_matrix(ZZ, 10, 10) + sage: L = IntegerLattice(M, lll_reduce=False) + sage: L.reduced_basis == M + True + sage: LLL = L.LLL() + sage: LLL == L.reduced_basis + True + sage: bool(min(a.norm() for a in LLL) == LLL[0].norm()) + True + sage: bool(LLL[0].norm() <= M[0].norm()) + True """ return self._reduced_basis @@ -376,36 +360,19 @@ def LLL(self, *args, **kwds): sage: from sage.modules.free_module_integer import IntegerLattice sage: A = random_matrix(ZZ, 10, 10, x=-2000, y=2000) + sage: while A.rank() < 10: + ....: A = random_matrix(ZZ, 10, 10) sage: L = IntegerLattice(A, lll_reduce=False); L Free module of degree 10 and rank 10 over Integer Ring User basis matrix: - [ -645 -1037 -1775 -1619 1721 -1434 1766 1701 1669 1534] - [ 1303 960 1998 -1838 1683 -1332 149 327 -849 -1562] - [-1113 -1366 1379 669 54 1214 -1750 -605 -1566 1626] - [-1367 1651 926 1731 -913 627 669 -1437 -132 1712] - [ -549 1327 -1353 68 1479 -1803 -456 1090 -606 -317] - [ -221 -1920 -1361 1695 1139 111 -1792 1925 -656 1992] - [-1934 -29 88 890 1859 1820 -1912 -1614 -1724 1606] - [ -590 -1380 1768 774 656 760 -746 -849 1977 -1576] - [ 312 -242 -1732 1594 -439 -1069 458 -1195 1715 35] - [ 391 1229 -1815 607 -413 -860 1408 1656 1651 -628] - sage: min(v.norm().n() for v in L.reduced_basis) - 3346.57... - - sage: L.LLL() - [ -888 53 -274 243 -19 431 710 -83 928 347] - [ 448 -330 370 -511 242 -584 -8 1220 502 183] - [ -524 -460 402 1338 -247 -279 -1038 -28 -159 -794] - [ 166 -190 -162 1033 -340 -77 -1052 1134 -843 651] - [ -47 -1394 1076 -132 854 -151 297 -396 -580 -220] - [-1064 373 -706 601 -587 -1394 424 796 -22 -133] - [-1126 398 565 -1418 -446 -890 -237 -378 252 247] - [ -339 799 295 800 425 -605 -730 -1160 808 666] - [ 755 -1206 -918 -192 -1063 -37 -525 -75 338 400] - [ 382 -199 -1839 -482 984 -15 -695 136 682 563] - sage: L.reduced_basis[0].norm().n() - 1613.74... - + ... + sage: L.reduced_basis == A + True + sage: old_min = min(v.norm().n() for v in L.reduced_basis) + sage: _ = L.LLL() + sage: new_min = L.reduced_basis[0].norm().n() + sage: new_min <= old_min + True """ basis = self.reduced_basis basis = [v for v in basis.LLL(*args, **kwds) if v] diff --git a/src/sage/modules/multi_filtered_vector_space.py b/src/sage/modules/multi_filtered_vector_space.py index 05973ccf7ed..0d0e4b316e7 100644 --- a/src/sage/modules/multi_filtered_vector_space.py +++ b/src/sage/modules/multi_filtered_vector_space.py @@ -717,10 +717,16 @@ def random_deformation(self, epsilon=None): Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0] - sage: V.random_deformation(1/100).get_degree('b',1) - Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [ 1 8/1197] + sage: D = V.random_deformation(1/100).get_degree('b',1) + sage: D.degree() + 2 + sage: D.dimension() + 1 + sage: D.matrix()[0, 0] + 1 + + sage: while V.random_deformation(1/100).get_degree('b',1).matrix() == matrix([1, 0]): + ....: pass """ filtrations = {key: value.random_deformation(epsilon) for key, value in self._filt.items()} diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index 114c8868264..9b24aecd965 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -316,10 +316,9 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): sage: A = random_matrix(ZZ,1,3) sage: v = A.row(0) - sage: vs = singular(v); vs - -8, - 2, - 0 + sage: vs = singular(v) + sage: vs._repr_() == '{},\n{},\n{}'.format(*v) + True sage: vs.type() 'intvec' """ diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index d9bd624078a..3c08d14f2d7 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -14,12 +14,13 @@ AUTHOR: EXAMPLES:: sage: VS = GF(2)^3 - sage: e = VS.random_element(); e - (1, 0, 0) - sage: f = VS.random_element(); f - (0, 1, 1) - sage: e + f - (1, 1, 1) + sage: e = VS.random_element() + sage: e.parent() is VS + True + sage: S = set(vector(v, immutable=True) for v in VS) + sage: S1 = set() + sage: while S != S1: + ....: S1.add(vector(VS.random_element(), immutable=True)) TESTS:: @@ -94,10 +95,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): sage: w = copy(v) sage: w == v True - sage: v[:10] - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: w[:10] - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) + sage: v[:10] == w[:10] + True + sage: v[5] += 1 + sage: v == w + False """ cdef Vector_mod2_dense y = self._new_c() if self._degree: @@ -274,11 +276,12 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),4) - sage: v = VS.random_element(); v - (1, 0, 0, 0) - sage: v[0] = 0; v - (0, 0, 0, 0) - sage: v[1:3] = [1, 1]; v + sage: v = VS.random_element() + sage: v[0] = 0; v[0] + 0 + sage: v[1:3] = [1, 1]; v[1:3] + (1, 1) + sage: v[3] = 0; v (0, 1, 1, 0) sage: v[4] = 0 Traceback (most recent call last): @@ -398,12 +401,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: f = VS.random_element(); f - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e.pairwise_product(f) #indirect doctest - (1, 0, 0, 0, 1, 1, 0, 0, 0, 1) + sage: e = VS.random_element() + sage: f = VS.random_element() + sage: g = e.pairwise_product(f) #indirect doctest + sage: all(g[i] == e[i]*f[i] for i in range(10)) + True """ cdef Vector_mod2_dense z, r r = right @@ -418,26 +420,24 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) + sage: e = VS.random_element() sage: 0 * e (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - sage: 1 * e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: 2 * e #indirect doctest + sage: 1 * e == e + True + sage: 2 * e # indirect doctest (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) :: - sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e * 0 #indirect doctest - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - sage: e * 1 - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e * 2 - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + sage: VS = VectorSpace(GF(2), 100) + sage: e = VS.random_element() + sage: e * 0 == 0 # indirect doctest + True + sage: e * 1 == e + True + sage: e * 2 == 0 + True """ cdef IntegerMod_int a @@ -467,11 +467,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: - sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: e.list() - [1, 0, 0, 0, 1, 1, 1, 0, 0, 1] + sage: VS = VectorSpace(GF(2), 10) + sage: entries = [GF(2).random_element() for _ in range(10)] + sage: e = VS(entries) + sage: e.list() == entries + True """ cdef Py_ssize_t d = self._degree cdef Py_ssize_t i From d50e7e95475c7ac5f00da65816ddfedc4d7d5b5a Mon Sep 17 00:00:00 2001 From: David Loeffler Date: Wed, 9 Jun 2021 11:59:10 +0100 Subject: [PATCH 556/706] Fix local-component bug revealed by pari upgrade --- src/sage/modular/local_comp/local_comp.py | 49 +++++++++++++---------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index 9010baa6ba8..f13b1ab32c7 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -24,6 +24,7 @@ from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.misc.verbose import verbose +from sage.misc.flatten import flatten from sage.modular.modform.element import Newform from sage.structure.sequence import Sequence @@ -730,7 +731,9 @@ def characters(self): F = self.coefficient_field().extension(theta_poly, "d") G = G.base_extend(F) - gvals = [x[0] for x in theta_poly.roots(G.base_ring())] + # roots with repetitions allowed + gvals = flatten([[y[0]]*y[1] for y in theta_poly.roots(G.base_ring())]) + if len(gs) == 1: # This is always the case if p != 2 chi1, chi2 = [G.extend_character(n, self.central_character(), [x]) for x in gvals] @@ -741,18 +744,18 @@ def characters(self): g0 = gs[0] try: G._reduce_Qp(1, g0) - raise ZeroDivisionError + raise ArithmeticError("Bad generators returned") except ValueError: pass tr = (~T.rho(g0.matrix().list())).trace() X = polygen(G.base_ring()) - theta_poly = X**2 - (-1)**n*tr*X + self.central_character()(g0.norm()) + theta0_poly = X**2 - (-1)**n*tr*X + self.central_character()(g0.norm()) verbose("theta_poly for %s is %s" % (g0, theta_poly), level=1) - if theta_poly.is_irreducible(): - F = theta_poly.base_ring().extension(theta_poly, "e") + if theta0_poly.is_irreducible(): + F = theta0_poly.base_ring().extension(theta_poly, "e") G = G.base_extend(F) - g0vals = [y[0] for y in theta_poly.roots(G.base_ring())] + g0vals = flatten([[y[0]]*y[1] for y in theta0_poly.roots(G.base_ring())]) pairA = [ [g0vals[0], gvals[0]], [g0vals[1], gvals[1]] ] pairB = [ [g0vals[0], gvals[1]], [g0vals[1], gvals[0]] ] @@ -773,21 +776,26 @@ def characters(self): for x in G.ideal(n).invertible_residues(): try: # test if G mod p is in Fp - G._reduce_Qp(1, x) + flag = G._reduce_Qp(1, x) except ValueError: - verbose("testing x = %s" % x, level=1) - ti = (-1)**n * (~T.rho(x.matrix().list())).trace() - verbose(" trace of matrix is %s" % ti, level=1) - if ti != chisA[0](x) + chisA[1](x): - verbose(" chisA FAILED", level=1) - A_fail = 1 - break - if ti != chisB[0](x) + chisB[1](x): - verbose(" chisB FAILED", level=1) - B_fail = 1 - break - else: - verbose(" Trace identity check works for both", level=1) + flag = None + if flag is not None: + verbose("skipping x=%s as congruent to %s mod p" % (x, flag)) + continue + + verbose("testing x = %s" % x, level=1) + ti = (-1)**n * (~T.rho(x.matrix().list())).trace() + verbose(" trace of matrix is %s" % ti, level=1) + if ti != chisA[0](x) + chisA[1](x): + verbose(" chisA FAILED", level=1) + A_fail = 1 + break + if ti != chisB[0](x) + chisB[1](x): + verbose(" chisB FAILED", level=1) + B_fail = 1 + break + else: + verbose(" Trace identity check works for both", level=1) if B_fail and not A_fail: chi1, chi2 = chisA @@ -845,7 +853,6 @@ def characters(self): if theta_poly.is_irreducible(): F = self.coefficient_field().extension(theta_poly, "d") G = G.base_extend(F) - from sage.misc.flatten import flatten c1q, c2q = flatten([[x]*e for x,e in theta_poly.roots(G.base_ring())]) if len(qs) == 1: From bb0245abf74aba706583c94724d7a3e5b06e2b63 Mon Sep 17 00:00:00 2001 From: David Loeffler Date: Wed, 9 Jun 2021 12:06:22 +0100 Subject: [PATCH 557/706] fix unhandled case in local_comp --- src/sage/modular/local_comp/local_comp.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index f13b1ab32c7..eb82b18a941 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -771,6 +771,10 @@ def characters(self): except ValueError: B_fail = 1 + if chisA == chisB or chisA == reversed(chisB): + # repeated roots -- break symmetry arbitrarily + B_fail = 1 + # check the character relation from LW12 if (not A_fail and not B_fail): for x in G.ideal(n).invertible_residues(): From 4bc5a2c98192b207c8578928cefbc144a7e14a7e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 06:05:36 -0700 Subject: [PATCH 558/706] RealSet.is_disjoint: Rename from is_disjoint_from --- src/sage/sets/real_set.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 75ecf0d4018..db8f30be731 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1808,7 +1808,7 @@ def boundary(self): """ return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) - def is_disjoint_from(self, *other): + def is_disjoint(self, *other): """ Test whether the two sets are disjoint @@ -1826,14 +1826,16 @@ def is_disjoint_from(self, *other): (0, 1) ∪ (2, 3) sage: s2 = RealSet([1, 2]); s2 [1, 2] - sage: s1.is_disjoint_from(s2) + sage: s1.is_disjoint(s2) True - sage: s1.is_disjoint_from([1, 2]) + sage: s1.is_disjoint([1, 2]) True """ other = RealSet(*other) return self.intersection(other).is_empty() + is_disjoint_from = deprecated_function_alias(31927, is_disjoint) + @staticmethod def are_pairwise_disjoint(*real_set_collection): """ @@ -1865,7 +1867,7 @@ def are_pairwise_disjoint(*real_set_collection): for j in range(i): si = sets[i] sj = sets[j] - if not si.is_disjoint_from(sj): + if not si.is_disjoint(sj): return False return True From 3cac256c1af8a67ec21b90fbcb206a5001b635e8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 07:23:23 -0700 Subject: [PATCH 559/706] sage.interfaces.sympy_wrapper: Add doctests --- src/sage/interfaces/sympy_wrapper.py | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 19e9b7ce610..35d5fed5ba1 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -17,34 +17,123 @@ from sympy.core.sympify import sympify from sympy.sets.sets import Set + @sympify_method_args class SageSet(Set): + r""" + Wrapper for a Sage set providing the SymPy Set API. + + Parents in the category :class:`sage.categories.sets_cat.Sets`, unless + a more specific method is implemented, convert to SymPy by creating + an instance of this class. + + EXAMPLES:: + + sage: F = Family([2, 3, 5, 7]); F + Family (2, 3, 5, 7) + sage: sF = F._sympy_(); sF # indirect doctest + SageSet(Family (2, 3, 5, 7)) + sage: sF._sage_() is F + True + sage: bool(sF) + True + sage: len(sF) + 4 + sage: list(sF) + [2, 3, 5, 7] + sage: sF.is_finite_set + True + """ def __new__(cls, sage_set): return Basic.__new__(cls, sage_set) def _sage_(self): + r""" + Return the underlying Sage set of the wrapper ``self``. + + EXAMPLES:: + + sage: F = Set([1, 2]) + sage: F is Set([1, 2]) + False + sage: sF = F._sympy_(); sF + SageSet({1, 2}) + sage: sF._sage_() is F + True + """ return self._args[0] @property def is_empty(self): + r""" + EXAMPLES:: + + sage: Empty = Set([]) + sage: sEmpty = Empty._sympy_() + sage: sEmpty.is_empty + True + """ return self._sage_().is_empty() @property def is_finite_set(self): + r""" + EXAMPLES:: + + sage: W = WeylGroup(["A",1,1]) + sage: sW = W._sympy_(); sW + SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space)) + sage: sW.is_finite_set + False + """ return self._sage_().is_finite() @property def is_iterable(self): + r""" + EXAMPLES:: + + sage: W = WeylGroup(["A",1,1]) + sage: sW = W._sympy_(); sW + SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space)) + sage: sW.is_iterable + True + """ from sage.categories.enumerated_sets import EnumeratedSets return self._sage_() in EnumeratedSets() def __iter__(self): + r""" + EXAMPLES:: + + sage: sPrimes = Primes()._sympy_(); sPrimes + SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) + sage: iter_sPrimes = iter(sPrimes) + sage: next(iter_sPrimes), next(iter_sPrimes), next(iter_sPrimes) + (2, 3, 5) + """ for element in self._sage_(): yield sympify(element) def _contains(self, other): + """ + EXAMPLES:: + + sage: sPrimes = Primes()._sympy_(); sPrimes + SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) + sage: 91 in sPrimes + False + """ return other in self._sage_() def __len__(self): + """ + EXAMPLES:: + + sage: sB3 = WeylGroup(["B", 3])._sympy_(); sB3 + SageSet(Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space)) + sage: len(sB3) + 48 + """ return len(self._sage_()) From eef604e8e5e6441eed11fa4c86c6277fd8318277 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 08:34:06 -0700 Subject: [PATCH 560/706] SageSet: Finish docstrings; handle symbolic _contains --- src/sage/categories/sets_cat.py | 13 +++++++++++ src/sage/interfaces/sympy_wrapper.py | 32 ++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index c99f026dd0f..c1e6fb98e5a 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1690,12 +1690,17 @@ def _sympy_(self): """ Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + The default implementation creates an instance of + :class:`~sage.interfaces.sympy_wrapper`. + EXAMPLES:: sage: F = FiniteEnumeratedSets().example(); F An example of a finite enumerated set: {1,2,3} sage: sF = F._sympy_(); sF SageSet(An example of a finite enumerated set: {1,2,3}) + sage: sF.is_finite_set + True sage: bool(sF) True sage: len(sF) @@ -1708,6 +1713,14 @@ def _sympy_(self): sage: RR._sympy_().is_finite_set False + + sage: F = Set([1, 2]) + sage: F is Set([1, 2]) + False + sage: sF = F._sympy_(); sF + SageSet({1, 2}) + sage: sF._sage_() is F + True """ from sage.interfaces.sympy_wrapper import SageSet return SageSet(self) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 35d5fed5ba1..6c22b47f9c3 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -46,6 +46,9 @@ class SageSet(Set): """ def __new__(cls, sage_set): + r""" + Construct a wrapper for a Sage set. + """ return Basic.__new__(cls, sage_set) def _sage_(self): @@ -67,6 +70,8 @@ def _sage_(self): @property def is_empty(self): r""" + Return whether the set ``self`` is empty. + EXAMPLES:: sage: Empty = Set([]) @@ -79,6 +84,8 @@ def is_empty(self): @property def is_finite_set(self): r""" + Return whether the set ``self`` is finite. + EXAMPLES:: sage: W = WeylGroup(["A",1,1]) @@ -92,6 +99,8 @@ def is_finite_set(self): @property def is_iterable(self): r""" + Return whether the set ``self`` is iterable. + EXAMPLES:: sage: W = WeylGroup(["A",1,1]) @@ -105,6 +114,8 @@ def is_iterable(self): def __iter__(self): r""" + Iterator for the set ``self``. + EXAMPLES:: sage: sPrimes = Primes()._sympy_(); sPrimes @@ -116,19 +127,36 @@ def __iter__(self): for element in self._sage_(): yield sympify(element) - def _contains(self, other): + def _contains(self, element): """ + Return whether ``element`` is an element of the set ``self``. + EXAMPLES:: sage: sPrimes = Primes()._sympy_(); sPrimes SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) sage: 91 in sPrimes False + + sage: from sympy.abc import p + sage: sPrimes.contains(p) + Contains(p, SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)) + + sage: p in sPrimes + Traceback (most recent call last): + ... + TypeError: did not evaluate to a bool: None + """ - return other in self._sage_() + if element.is_symbol: + # keep symbolic + return None + return element in self._sage_() def __len__(self): """ + Return the cardinality of the finite set ``self``. + EXAMPLES:: sage: sB3 = WeylGroup(["B", 3])._sympy_(); sB3 From 6df1160bbb3cff3fe7e501a6d2b9a2221ea05624 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Sun, 6 Jun 2021 12:04:49 +0200 Subject: [PATCH 561/706] 30133: rename internal variables in Giac interface to avoid clashes with constants --- src/sage/calculus/calculus.py | 78 +++++++++++---------- src/sage/functions/exp_integral.py | 4 +- src/sage/functions/generalized.py | 4 +- src/sage/functions/other.py | 4 +- src/sage/interfaces/giac.py | 12 +++- src/sage/interfaces/interface.py | 2 +- src/sage/libs/giac/giac.pyx | 23 +++++- src/sage/matrix/matrix1.pyx | 4 +- src/sage/misc/parser.pyx | 24 +++++++ src/sage/symbolic/expression_conversions.py | 5 ++ src/sage/symbolic/integration/external.py | 11 ++- 11 files changed, 120 insertions(+), 51 deletions(-) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index b3214f9a558..6a853d7df24 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -2264,8 +2264,8 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): # use a global flag so all expressions obtained via # evaluation of maxima code are assumed pre-simplified is_simplified = True - parser_make_Mvar.set_names(var_syms) - parser_make_function.set_names(function_syms) + SRM_parser._variable_constructor().set_names(var_syms) + SRM_parser._callable_constructor().set_names(function_syms) return SRM_parser.parse_sequence(s) except SyntaxError: raise TypeError("unable to make sense of Maxima expression '%s' in Sage" % s) @@ -2326,7 +2326,7 @@ def maxima_options(**kwds): -def _find_var(name): +def _find_var(name, interface=None): """ Function to pass to Parser for constructing variables from strings. For internal use. @@ -2338,10 +2338,21 @@ def _find_var(name): y sage: sage.calculus.calculus._find_var('I') I + sage: sage.calculus.calculus._find_var(repr(maxima(y)), interface='maxima') + y + sage: sage.calculus.calculus._find_var(repr(giac(y)), interface='giac') + y """ - v = SR.symbols.get(name) - if v is not None: - return v + if interface == 'maxima': + if name.startswith("_SAGE_VAR_"): + return var(name[10:]) + elif interface == 'giac': + if name.startswith('sageVAR'): + return var(name[7:]) + else: + v = SR.symbols.get(name) + if v is not None: + return v # try to find the name in the global namespace # needed for identifiers like 'e', etc. @@ -2394,7 +2405,8 @@ def _find_func(name, create_when_missing=True): make_var = parser_make_var, make_function = parser_make_function) -def symbolic_expression_from_string(s, syms={}, accept_sequence=False): + +def symbolic_expression_from_string(s, syms=None, accept_sequence=False, *, parser=None): """ Given a string, (attempt to) parse it and return the corresponding Sage symbolic expression. Normally used @@ -2411,6 +2423,8 @@ def symbolic_expression_from_string(s, syms={}, accept_sequence=False): to allow a (possibly nested) set of lists and tuples as input + - ``parser`` -- (default: ``SR_parser``) parser for internal use + EXAMPLES:: sage: y = var('y') @@ -2425,42 +2439,34 @@ def symbolic_expression_from_string(s, syms={}, accept_sequence=False): 0.3333333333333333333333333333 sage: sage.calculus.calculus.symbolic_expression_from_string(str(RealField(100)(10^-500/3))) 3.333333333333333333333333333e-501 - """ - parse_func = SR_parser.parse_sequence if accept_sequence else SR_parser.parse_expression - parser_make_var.set_names({k: v for k, v in syms.items() - if not _is_function(v)}) - parser_make_function.set_names({k: v for k, v in syms.items() - if _is_function(v)}) - return parse_func(s) + The Giac interface uses a different parser (:trac:`30133`):: -def _find_Mvar(name): - """ - Function to pass to Parser for constructing - variables from strings. For internal use. - - EXAMPLES:: - - sage: y = var('y') - sage: sage.calculus.calculus._find_var('y') - y - sage: sage.calculus.calculus._find_var('I') - I + sage: from sage.calculus.calculus import SR_parser_giac + sage: sage.calculus.calculus.symbolic_expression_from_string(repr(giac(SR.var('e'))), parser=SR_parser_giac) + e """ - if name[:10] == "_SAGE_VAR_": - return var(name[10:]) + if syms is None: + syms = {} + if parser is None: + parser = SR_parser + parse_func = parser.parse_sequence if accept_sequence else parser.parse_expression + # this assumes that the parser has constructors of type `LookupNameMaker` + parser._variable_constructor().set_names({k: v for k, v in syms.items() + if not _is_function(v)}) + parser._callable_constructor().set_names({k: v for k, v in syms.items() + if _is_function(v)}) + return parse_func(s) - # try to find the name in the global namespace - # needed for identifiers like 'e', etc. - import sage.all - try: - return SR(sage.all.__dict__[name]) - except (KeyError, TypeError): - return var(name) -parser_make_Mvar = LookupNameMaker({}, fallback=_find_Mvar) +parser_make_Mvar = LookupNameMaker({}, fallback=lambda x: _find_var(x, interface='maxima')) SRM_parser = Parser(make_int = lambda x: SR(Integer(x)), make_float = lambda x: SR(RealDoubleElement(x)), make_var = parser_make_Mvar, make_function = parser_make_function) + +SR_parser_giac = Parser(make_int = lambda x: SR(Integer(x)), + make_float = lambda x: SR(create_RealNumber(x)), + make_var = LookupNameMaker({}, fallback=lambda x: _find_var(x, interface='giac')), + make_function = parser_make_function) diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py index ddc11735210..b20f7779f1a 100644 --- a/src/sage/functions/exp_integral.py +++ b/src/sage/functions/exp_integral.py @@ -810,7 +810,7 @@ def __init__(self): sage: sin_integral(x)._fricas_init_() 'Si(x)' sage: sin_integral(x)._giac_() - Si(x) + Si(sageVARx) """ BuiltinFunction.__init__(self, "sin_integral", nargs=1, latex_name=r'\operatorname{Si}', @@ -986,7 +986,7 @@ def __init__(self): sage: cos_integral(x)._fricas_init_() 'Ci(x)' sage: cos_integral(x)._giac_() - Ci(x) + Ci(sageVARx) """ BuiltinFunction.__init__(self, "cos_integral", nargs=1, latex_name=r'\operatorname{Ci}', diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py index 5839d82231f..6c38ef5621c 100644 --- a/src/sage/functions/generalized.py +++ b/src/sage/functions/generalized.py @@ -248,7 +248,7 @@ def __init__(self): sage: heaviside(x)._sympy_() Heaviside(x) sage: heaviside(x)._giac_() - Heaviside(x) + Heaviside(sageVARx) sage: h(x) = heaviside(x) sage: h(pi).numerical_approx() 1.00000000000000 @@ -405,7 +405,7 @@ class FunctionSignum(BuiltinFunction): sage: sgn(x)._fricas_init_() '(x+->abs(x)/x)(x)' sage: sgn(x)._giac_() - sign(x) + sign(sageVARx) Test for :trac:`31085`:: diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 506cf52c218..ddc82dde8f1 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1936,8 +1936,8 @@ def __init__(self): sage: isinstance(r.operator(), ....: sage.functions.other.Function_prod) # known bug True - sage: giac(sprod(m, m, 1, n)) - n! + sage: giac(sprod(m, m, 1, n)).sage() + factorial(n) """ BuiltinFunction.__init__(self, "product", nargs=4, conversions=dict(maxima='product', diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 9a68522ae95..87101e34811 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -1118,9 +1118,17 @@ def _sage_(self, locals={}): sage: giac('true')._sage_(), giac('false')._sage_() (True, False) + + Check that variables and constants are not mixed up (:trac:`30133`):: + + sage: ee, ii, pp = SR.var('e,i,pi') + sage: giac(ee * ii * pp).sage().variables() + (e, i, pi) + sage: giac(e * i * pi).sage().variables() + () """ from sage.libs.pynac.pynac import symbol_table - from sage.calculus.calculus import symbolic_expression_from_string + from sage.calculus.calculus import symbolic_expression_from_string, SR_parser_giac result = repr(self) # string representation @@ -1133,7 +1141,7 @@ def _sage_(self, locals={}): try: return symbolic_expression_from_string(result, lsymbols, - accept_sequence=True) + accept_sequence=True, parser=SR_parser_giac) except Exception: raise NotImplementedError("Unable to parse Giac output: %s" % result) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 66f57641c00..79bb07032b9 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -1169,7 +1169,7 @@ def _repr_(self): 2 sage: x = var('x') sage: giac(x) - x + sageVARx sage: giac(5) 5 sage: M = matrix(QQ,2,range(4)) diff --git a/src/sage/libs/giac/giac.pyx b/src/sage/libs/giac/giac.pyx index 9c47248e06a..51133055b55 100644 --- a/src/sage/libs/giac/giac.pyx +++ b/src/sage/libs/giac/giac.pyx @@ -163,7 +163,7 @@ from sage.plot.line import line from sage.plot.scatter_plot import scatter_plot from sage.libs.pynac.pynac import symbol_table -from sage.calculus.calculus import symbolic_expression_from_string +from sage.calculus.calculus import symbolic_expression_from_string, SR_parser_giac from sage.symbolic.ring import SR from sage.symbolic.expression import Expression from sage.symbolic.expression_conversions import InterfaceInit @@ -1573,7 +1573,7 @@ cdef class Pygen(GiacMethods_base): sage: f(4) 6 sage: f(x) - Gamma(x) + Gamma(sageVARx) sage: (f(x)).sage() gamma(x) @@ -1633,13 +1633,30 @@ cdef class Pygen(GiacMethods_base): sage: u,v=var('u,v');a=libgiac('cos(u+v)').texpand() sage: simplify(SR(a)+sin(u)*sin(v)) cos(u)*cos(v) + + TESTS: + + Check that variables and constants are not mixed up (:trac:`30133`):: + + sage: ee, ii, pp = SR.var('e,i,pi') + sage: libgiac(ee * ii * pp).sage().variables() + (e, i, pi) + sage: libgiac(e * i * pi).sage().variables() + () + sage: libgiac.integrate(ee^x, x).sage() + e^x/log(e) + sage: y = SR.var('π') + sage: libgiac.integrate(cos(y), y).sage() + sin(π) """ if isinstance(R,SR.__class__): # Try to convert some functions names to the symbolic ring lsymbols = symbol_table['giac'].copy() #lsymbols.update(locals) try: - result=symbolic_expression_from_string(self.__str__(),lsymbols,accept_sequence=True) + result = symbolic_expression_from_string(self.__str__(), lsymbols, + accept_sequence=True, + parser=SR_parser_giac) return result except Exception: diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index f3007973a9d..f5165c8b0de 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -226,8 +226,8 @@ cdef class Matrix(Matrix0): sage: y = var('y') sage: M = matrix(SR, 2, [y+sin(y), y - 4, 1/y, dilog(y)]) - sage: giac(M).det() - (y^2*dilog(y)+y*sin(y)*dilog(y)-y+4)/y + sage: giac(M).det().sage() + (y^2*dilog(y) + y*dilog(y)*sin(y) - y + 4)/y """ s = ','.join('[' + ','.join(cf._giac_init_() for cf in row) + ']' for row in self.rows()) diff --git a/src/sage/misc/parser.pyx b/src/sage/misc/parser.pyx index 366bdfa8bfe..b4b43c8a16f 100644 --- a/src/sage/misc/parser.pyx +++ b/src/sage/misc/parser.pyx @@ -508,6 +508,30 @@ cdef class Parser: self.callable_constructor = make_function self.implicit_multiplication = implicit_multiplication + def _variable_constructor(self): + """ + Return the variable constructor of this parser. + + EXAMPLES:: + + sage: from sage.calculus.calculus import SR_parser + sage: SR_parser._variable_constructor() + Date: Tue, 8 Jun 2021 21:20:56 +0200 Subject: [PATCH 562/706] 30133: fix conversion of polynomials to Giac --- src/sage/interfaces/giac.py | 4 ++-- src/sage/matrix/matrix1.pyx | 2 +- src/sage/modules/free_module_element.pyx | 2 +- .../rings/polynomial/multi_polynomial.pyx | 20 +++++++++++++++++++ .../rings/polynomial/polynomial_element.pyx | 20 +++++++++++++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index 87101e34811..e77baa0a106 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -301,8 +301,8 @@ class Giac(Expect): :: sage: R. = QQ[]; f = (2+a+b) - sage: p = giac.gcd(f^3+5*f^5,f^2+f^5); p; R(p) - a^2+2*a*b+4*a+b^2+4*b+4 + sage: p = giac.gcd(f^3+5*f^5,f^2+f^5); p; R(p.sage()) + sageVARa^2+2*sageVARa*sageVARb+4*sageVARa+sageVARb^2+4*sageVARb+4 a^2 + 2*a*b + b^2 + 4*a + 4*b + 4 Variable names in python and giac are independent:: diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index f5165c8b0de..99c0e11fa37 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -222,7 +222,7 @@ cdef class Matrix(Matrix0): sage: P. = ZZ[] sage: M = matrix(P, 2, [-9*x^2-2*x+2, x-1, x^2+8*x, -3*x^2+5]) sage: giac(M) - [[-9*x^2-2*x+2,x-1],[x^2+8*x,-3*x^2+5]] + [[-9*sageVARx^2-2*sageVARx+2,sageVARx-1],[sageVARx^2+8*sageVARx,-3*sageVARx^2+5]] sage: y = var('y') sage: M = matrix(SR, 2, [y+sin(y), y - 4, 1/y, dilog(y)]) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 1bbe475695c..a5f7602a19b 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -912,7 +912,7 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: P. = ZZ[] sage: v = vector(P, 3, [x^2 + 2, 2*x + 1, -2*x^2 + 4*x]) sage: giac(v) - [x^2+2,2*x+1,-2*x^2+4*x] + [sageVARx^2+2,2*sageVARx+1,-2*sageVARx^2+4*sageVARx] """ return self.list() diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 611a355ab11..e173901b0fd 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -1077,6 +1077,26 @@ cdef class MPolynomial(CommutativeRingElement): return '%s!(%s)'%(R.name(), s) + def _giac_init_(self): + r""" + Return a Giac string representation of this polynomial. + + TESTS:: + + sage: R. = GF(101)['e,i'][] + sage: f = R('e*i') * x + y^2 + sage: f._giac_init_() + '((1)*1)*sageVARy^2+((1)*sageVARe*sageVARi)*sageVARx' + sage: giac(f) + sageVARy^2+sageVARe*sageVARi*sageVARx + sage: giac(R.zero()) + 0 + """ + g = ['sageVAR' + x for x in self.parent().variable_names()] + s = '+'.join('(%s)*%s' % (c._giac_init_(), + m._repr_with_changed_varnames(g)) + for c, m in self) + return s if s else '0' def gradient(self): r""" diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 570c148e72a..55bf33a3094 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -6352,6 +6352,26 @@ cdef class Polynomial(CommutativeAlgebraElement): from sage.libs.gap.libgap import libgap return self._gap_(libgap) + def _giac_init_(self): + r""" + Return a Giac string representation of this polynomial. + + TESTS:: + + sage: R. = GF(101)['e,i'][] + sage: f = R('e*i') * x + x^2 + sage: f._giac_init_() + '((1)*1)*sageVARx^2+((1)*sageVARe*sageVARi)*sageVARx' + sage: giac(f) + sageVARx^2+sageVARe*sageVARi*sageVARx + sage: giac(R.zero()) + 0 + """ + g = 'sageVAR' + self.variable_name() + s = '+'.join('(%s)*%s' % (self.monomial_coefficient(m)._giac_init_(), + m._repr(g)) for m in self.monomials()) + return s if s else '0' + ###################################################################### @coerce_binop From c78b1470fccd915e2fa93f95f2fefba6220fb1f7 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 9 Jun 2021 19:44:37 +0100 Subject: [PATCH 563/706] fix pari 2.13-related issue of different Smith form --- src/sage/modular/local_comp/local_comp.py | 16 ++++++---------- .../rings/number_field/number_field_ideal.py | 10 +++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/sage/modular/local_comp/local_comp.py b/src/sage/modular/local_comp/local_comp.py index eb82b18a941..a00268ee911 100644 --- a/src/sage/modular/local_comp/local_comp.py +++ b/src/sage/modular/local_comp/local_comp.py @@ -667,11 +667,9 @@ def characters(self): sage: f = Newform('81a', names='j'); f q + j0*q^2 + q^4 - j0*q^5 + O(q^6) - sage: LocalComponent(f, 3).characters() # long time (12s on sage.math, 2012) - [ - Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d + j0, 4 |--> 1, 3*s + 1 |--> j0*d + 1, 3 |--> 1, - Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d - j0, 4 |--> 1, 3*s + 1 |--> -j0*d - 2, 3 |--> 1 - ] + sage: set(LocalComponent(f, 3).characters()) # long time (12s on sage.math, 2012) + {Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> -2*d + j0, 4 |--> 1, 3*s + 1 |--> -j0*d + 1, 3 |--> 1, + Character of unramified extension Q_3(s)* (s^2 + 2*s + 2 = 0), of level 2, mapping -2*s |--> 2*d - j0, 4 |--> 1, 3*s + 1 |--> j0*d - 2, 3 |--> 1} Some ramified examples:: @@ -701,11 +699,9 @@ def characters(self): Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> 1, -1 |--> 1, 2 |--> 1, Character of unramified extension Q_2(s)* (s^2 + s + 1 = 0), of level 3, mapping s |--> 1, 2*s + 1 |--> 1/2*a0, 4*s + 1 |--> -1, -1 |--> 1, 2 |--> 1 ] - sage: Newform('243a',names='a').local_component(3).characters() # long time - [ - Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -s - 1 |--> 1, 4 |--> 1, 3*s + 1 |--> d, s |--> 1, - Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -s - 1 |--> 1, 4 |--> 1, 3*s + 1 |--> -d - 1, s |--> 1 - ] + sage: set(Newform('243a',names='a').local_component(3).characters()) # long time + {Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> -d - 1, 4 |--> 1, 3*s + 1 |--> -d - 1, s |--> 1, + Character of ramified extension Q_3(s)* (s^2 - 6 = 0), of level 4, mapping -2*s - 1 |--> d, 4 |--> 1, 3*s + 1 |--> d, s |--> 1} """ T = self.type_space() p = self.prime() diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index dec4d237c8e..e75e61d1616 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -2191,6 +2191,14 @@ def invertible_residues(self, reduce=True): sage: len(list(K.primes_above(3)[0].invertible_residues())) 80 + TESTS: + + Check that the integrality is not lost, cf. :trac:`30801`:: + + sage: K. = NumberField(x^2 + x + 1) + sage: list(K.ideal(8).invertible_residues())[:5] + [1, a - 1, -3*a, -2*a + 3, -a - 1] + AUTHOR: John Cremona """ return self.invertible_residues_mod(subgp_gens=None, reduce=reduce) @@ -2277,7 +2285,7 @@ def invertible_residues_mod(self, subgp_gens=[], reduce=True): A, U, V = M.smith_form() V = V.inverse() - new_basis = [prod([g[j]**V[i, j] for j in range(n)]) for i in range(n)] + new_basis = [prod([g[j]**(V[i, j] % invs[j]) for j in range(n)]) for i in range(n)] if reduce: combo = lambda c: self.small_residue(prod(new_basis[i] ** c[i] From 2debc4957d4ef15c635abeaa217736aa80152a2b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:37:03 -0700 Subject: [PATCH 564/706] Matrix._sympy_: New --- src/sage/interfaces/sympy.py | 16 ++++++ src/sage/matrix/matrix1.pyx | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index b7419ab9f47..bb2e90fcd70 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -748,6 +748,17 @@ def _sympysage_crootof(self): from sage.symbolic.ring import SR return complex_root_of(self.args[0]._sage_(), SR(self.args[1])) +def _sympysage_matrix(self): + try: + return self._sage_object + except AttributeError: + from sympy.matrices import SparseMatrix + from sage.matrix.constructor import matrix + rows, cols = self.shape() + return matrix(rows, cols, self.todok(), + sparse=isinstance(self, SparseMatrix), + immutable=True) + def _sympysage_relational(self): """ EXAMPLES:: @@ -846,6 +857,7 @@ def sympy_init(): from sympy.integrals.integrals import Integral from sympy.polys.rootoftools import CRootOf from sympy.series.order import Order + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix, Matrix, SparseMatrix Float._sage_ = _sympysage_float Integer._sage_ = _sympysage_integer @@ -854,6 +866,10 @@ def sympy_init(): NegativeInfinity._sage_ = _sympysage_ninfty ComplexInfinity._sage_ = _sympysage_uinfty sympy_nan._sage_ = _sympysage_nan + ImmutableMatrix._sage_ = _sympysage_matrix + ImmutableSparseMatrix._sage_ = _sympysage_matrix + Matrix._sage_ = _sympysage_matrix + SparseMatrix._sage_ = _sympysage_matrix Relational._sage_ = _sympysage_relational Exp1._sage_ = _sympysage_e Pi._sage_ = _sympysage_pi diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index f3007973a9d..8d522f9d239 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -524,6 +524,103 @@ cdef class Matrix(Matrix0): """ return scilab(self._scilab_init_()) + def _sympy_(self): + r""" + Return a SymPy matrix corresponding to ``self``. + + OUTPUT: + + - An instance of either an ``ImmutableMatrix`` or ``ImmutableSparseMatrix``, + regardless of whether ``self`` is mutable or not. + + EXAMPLES:: + + sage: A = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); A + [1 2 3] + [4 5 6] + [7 8 9] + sage: sA = A._sympy_(); sA + Matrix([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + sage: type(sA) + + + sage: I = MatrixSpace(QQ, 5, 5, sparse=True).identity_matrix() + sage: sI = I._sympy_(); sI + Matrix([ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1]]) + sage: type(sI) + + + If ``self`` was immutable, then converting the result to Sage gives + back ``self``:: + + sage: immA = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], immutable=True) + sage: immA._sympy_()._sage_() is immA + True + + If ``self`` is mutable, then converting back to Sage creates a new matrix:: + + sage: sA._sage_() is A + Traceback (most recent call last): + ... + TypeError: 'tuple' object is not callable + True + sage: sA._sage_() == A + Traceback (most recent call last): + ... + TypeError: 'tuple' object is not callable + True + + Symbolic matrices are supported:: + + sage: M = matrix([[sin(x), cos(x)], [-cos(x), sin(x)]]); M + [ sin(x) cos(x)] + [-cos(x) sin(x)] + sage: sM = M._sympy_(); sM + Matrix([ + [ sin(x), cos(x)], + [-cos(x), sin(x)]]) + sage: sM.subs(x, pi/4) + Matrix([ + [ sqrt(2)/2, sqrt(2)/2], + [-sqrt(2)/2, sqrt(2)/2]]) + + TESTS: + + Dense 0-column/0-row matrices:: + + sage: ZeroCol = matrix(QQ, 3, 0, sparse=False); ZeroCol + [] + sage: sZeroCol = ZeroCol._sympy_(); sZeroCol + Matrix(3, 0, []) + + sage: ZeroRow = matrix(QQ, 0, 2, sparse=False); ZeroRow + [] + sage: sZeroRow = ZeroRow._sympy_(); sZeroRow + Matrix(0, 2, []) + + """ + from sage.interfaces.sympy import sympy_init + sympy_init() + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix + if self.is_sparse(): + matrix = ImmutableSparseMatrix(self.nrows(), self.ncols(), + self.dict(copy=False)) + else: + if not self.nrows() or not self.ncols(): + matrix = ImmutableMatrix(self.nrows(), self.ncols(), ()) + else: + matrix = ImmutableMatrix(self.rows()) + if self.is_immutable(): + matrix._sage_object = self + return matrix def _sage_input_(self, sib, coerce): r""" From 2baae58a51cd4f136cf0f89e3e6484804983908b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:48:08 -0700 Subject: [PATCH 565/706] Sets.ParentMethods._sympy_: Call sympy_init --- src/sage/categories/sets_cat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index c1e6fb98e5a..288a5204793 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1723,6 +1723,8 @@ def _sympy_(self): True """ from sage.interfaces.sympy_wrapper import SageSet + from sage.interfaces.sympy import sympy_init + sympy_init() return SageSet(self) class ElementMethods: From 93fbb2be5b07dfa18b4b1aec6d30c3c2895c54ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:52:18 -0700 Subject: [PATCH 566/706] Call sympy_init in all added _sympy_ methods --- src/sage/categories/sets_cat.py | 2 ++ src/sage/modules/free_module.py | 2 ++ src/sage/rings/integer_ring.pyx | 2 ++ src/sage/rings/rational_field.py | 2 ++ src/sage/sets/non_negative_integers.py | 2 ++ src/sage/sets/positive_integers.py | 2 ++ src/sage/sets/real_set.py | 4 ++++ 7 files changed, 16 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 6b2f57e18cf..4b17b8f23d2 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2451,6 +2451,8 @@ def _sympy_(self): True """ from sympy import ProductSet + from sage.interfaces.sympy import sympy_init + sympy_init() return ProductSet(*self.cartesian_factors()) class ElementMethods: diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 36f668d99fe..c8617d1885b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -5318,6 +5318,8 @@ def _sympy_(self): True """ from sympy import ProductSet + from sage.interfaces.sympy import sympy_init + sympy_init() return ProductSet(*([self.coordinate_ring()] * self.rank())) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 737dda9ecb3..2d4aa4eb8af 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1477,6 +1477,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): Integers """ from sympy import Integers + from sage.interfaces.sympy import sympy_init + sympy_init() return Integers def _sage_input_(self, sib, coerced): diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 2a4237f0dfc..c79d2655535 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1586,6 +1586,8 @@ def _sympy_(self): Rationals """ from sympy import Rationals + from sage.interfaces.sympy import sympy_init + sympy_init() return Rationals def _sage_input_(self, sib, coerced): diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index adabc89a987..9b01ad6f3d4 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -233,4 +233,6 @@ def _sympy_(self): Naturals0 """ from sympy import Naturals0 + from sage.interfaces.sympy import sympy_init + sympy_init() return Naturals0 diff --git a/src/sage/sets/positive_integers.py b/src/sage/sets/positive_integers.py index 693ca04706c..7ed7d9657e9 100644 --- a/src/sage/sets/positive_integers.py +++ b/src/sage/sets/positive_integers.py @@ -86,4 +86,6 @@ def _sympy_(self): Naturals """ from sympy import Naturals + from sage.interfaces.sympy import sympy_init + sympy_init() return Naturals diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index f2eb7d97202..1f594c59752 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -433,6 +433,8 @@ def _sympy_(self): Interval.open(0, oo) """ from sympy import Interval + from sage.interfaces.sympy import sympy_init + sympy_init() return Interval(self.lower(), self.upper(), left_open=not self._lower_closed, right_open=not self._upper_closed) @@ -1869,6 +1871,8 @@ def _sympy_(self): False """ from sympy import Reals, Union + from sage.interfaces.sympy import sympy_init + sympy_init() if self.is_universe(): return Reals else: From bd4b6699689e5fc1162d8c6d3f6bd887cdc91929 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:06:22 -0700 Subject: [PATCH 567/706] sage.interfaces.sympy._sympysage_matrix: Complete implementation --- src/sage/interfaces/sympy.py | 44 ++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index bb2e90fcd70..4c271e20b06 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -749,13 +749,53 @@ def _sympysage_crootof(self): return complex_root_of(self.args[0]._sage_(), SR(self.args[1])) def _sympysage_matrix(self): + """ + Convert SymPy matrix ``self`` to Sage. + + EXAMPLES:: + + sage: from sympy.matrices import Matrix, SparseMatrix + sage: from sage.interfaces.sympy import sympy_init + sage: from sympy.abc import x + sage: sympy_init() + sage: sM = Matrix([[1, x + 1], [x - 1, 1]]); sM + Matrix([ + [ 1, x + 1], + [x - 1, 1]]) + sage: M = sM._sage_(); M + [ 1 x + 1] + [x - 1 1] + sage: M.parent() + Full MatrixSpace of 2 by 2 dense matrices over Symbolic Ring + + sage: sN = SparseMatrix.eye(3); sN + Matrix([ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) + sage: N = sN._sage_(); N + [1 0 0] + [0 1 0] + [0 0 1] + sage: N.parent() + Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + """ try: return self._sage_object except AttributeError: from sympy.matrices import SparseMatrix from sage.matrix.constructor import matrix - rows, cols = self.shape() - return matrix(rows, cols, self.todok(), + from sage.structure.element import get_coercion_model + from sage.symbolic.ring import SR + + rows, cols = self.shape + d = {row_col: value._sage_() + for row_col, value in self.todok().items()} + coercion_model = get_coercion_model() + base_ring = coercion_model.common_parent(*d.values()) + if base_ring is None: + base_ring = SR + return matrix(base_ring, rows, cols, d, sparse=isinstance(self, SparseMatrix), immutable=True) From 26ce752f37ef75ace9432260ac55240fcc884289 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:09:34 -0700 Subject: [PATCH 568/706] FreeModuleElement._sympy_: New --- src/sage/matrix/matrix1.pyx | 2 +- src/sage/modules/free_module_element.pyx | 66 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 8d522f9d239..eff0a61037f 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -565,7 +565,7 @@ cdef class Matrix(Matrix0): sage: immA._sympy_()._sage_() is immA True - If ``self`` is mutable, then converting back to Sage creates a new matrix:: + If ``self`` was mutable, then converting back to Sage creates a new matrix:: sage: sA._sage_() is A Traceback (most recent call last): diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 1bbe475695c..e17ec2a6241 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -3543,6 +3543,72 @@ cdef class FreeModuleElement(Vector): # abstract base class """ return '{' + ', '.join(x._mathematica_init_() for x in self.list()) + '}' + def _sympy_(self): + """ + Return a SymPy column vector (matrix) corresponding to ``self``. + + OUTPUT: + + - An instance of either an ``ImmutableMatrix`` or ``ImmutableSparseMatrix``, + regardless of whether ``self`` is mutable or not. + + EXAMPLES:: + + sage: v = vector([1, 2, 3]); v + (1, 2, 3) + sage: sv = v._sympy_(); sv + Matrix([ + [1], + [2], + [3]]) + sage: type(sv) + + + sage: w = vector({1: 1, 5: -1}, sparse=True) + sage: sw = w._sympy_(); sw + Matrix([ + [ 0], + [ 1], + [ 0], + [ 0], + [ 0], + [-1]]) + sage: type(sw) + + + If ``self`` was immutable, then converting the result to Sage gives + back ``self``:: + + sage: immv = vector([1, 2, 3], immutable=True) + sage: immv._sympy_()._sage_() is immv + True + + If ``self`` was mutable, then converting back to Sage creates a new + matrix (column vector):: + + sage: sv._sage_() + [1] + [2] + [3] + sage: sv._sage_() is v + False + sage: sv._sage_() == v + False + """ + from sage.interfaces.sympy import sympy_init + sympy_init() + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix + if self.is_sparse(): + matrix = ImmutableSparseMatrix(self._degree, 1, + {(i, 0): v + for i, v in self.dict(copy=False).items()}) + else: + matrix = ImmutableMatrix(self._degree, 1, + self.list(copy=False)) + if self.is_immutable(): + matrix._sage_object = self + return matrix + def nonzero_positions(self): """ Return the sorted list of integers ``i`` such that ``self[i] != 0``. From 9e0f73634dfff54ee42b2c30670cfa62d12de98f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:10:55 -0700 Subject: [PATCH 569/706] Matrix._sympy_: Fix up doctest --- src/sage/matrix/matrix1.pyx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index eff0a61037f..e56b1e8518b 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -568,14 +568,8 @@ cdef class Matrix(Matrix0): If ``self`` was mutable, then converting back to Sage creates a new matrix:: sage: sA._sage_() is A - Traceback (most recent call last): - ... - TypeError: 'tuple' object is not callable - True + False sage: sA._sage_() == A - Traceback (most recent call last): - ... - TypeError: 'tuple' object is not callable True Symbolic matrices are supported:: From c06c965d81713d698d6e6a73c69f3473aa26e1b2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:16:06 -0700 Subject: [PATCH 570/706] sage.interfaces.sympy_wrapper.SageSet: Add another doctest --- src/sage/interfaces/sympy_wrapper.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 6c22b47f9c3..00f7b65dd27 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -48,6 +48,14 @@ class SageSet(Set): def __new__(cls, sage_set): r""" Construct a wrapper for a Sage set. + + TESTS:: + + sage: from sage.interfaces.sympy_wrapper import SageSet + sage: F = Set([1, 2]); F + {1, 2} + sage: sF = SageSet(F); sF + SageSet({1, 2}) """ return Basic.__new__(cls, sage_set) From 8abdc8b16f9762041c1c60bcb4672fefea12f4b4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:22:24 -0700 Subject: [PATCH 571/706] src/sage/functions/piecewise.py: Add coding header --- src/sage/functions/piecewise.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index a0276824a3a..4428543bf6b 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Piecewise-defined Functions From 9d41f0e3e56ba43797296074ffb9ac4fc36ce8c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:24:57 -0700 Subject: [PATCH 572/706] src/sage/sets/real_set.py: Remove unnecessary import --- src/sage/sets/real_set.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 433cd77cfb3..de987727717 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -85,8 +85,6 @@ class RealSet. # http://www.gnu.org/licenses/ #***************************************************************************** -import itertools - from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation From 7af909561cb10213c8af97e77d05f3694060cf25 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:43:17 -0700 Subject: [PATCH 573/706] sage.interfaces.sympy._sympysage_matrix: Handle TypeError from common_parent --- src/sage/interfaces/sympy.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 4c271e20b06..dc568808e63 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -779,22 +779,39 @@ def _sympysage_matrix(self): [0 0 1] sage: N.parent() Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + + sage: sO = SparseMatrix.zeros(3); sO + Matrix([ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]) + sage: O = sO._sage_(); O + [0 0 0] + [0 0 0] + [0 0 0] + sage: O.parent() + Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring """ try: return self._sage_object except AttributeError: from sympy.matrices import SparseMatrix from sage.matrix.constructor import matrix - from sage.structure.element import get_coercion_model - from sage.symbolic.ring import SR rows, cols = self.shape d = {row_col: value._sage_() for row_col, value in self.todok().items()} - coercion_model = get_coercion_model() - base_ring = coercion_model.common_parent(*d.values()) - if base_ring is None: - base_ring = SR + if not d: + from sage.rings.integer_ring import ZZ + base_ring = ZZ + else: + from sage.structure.element import get_coercion_model + from sage.symbolic.ring import SR + coercion_model = get_coercion_model() + try: + base_ring = coercion_model.common_parent(*d.values()) + except TypeError: # no common canonical parent + base_ring = SR return matrix(base_ring, rows, cols, d, sparse=isinstance(self, SparseMatrix), immutable=True) From c365f1877c0369000930d4bd6f750db7a3c09e3d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:51:05 -0700 Subject: [PATCH 574/706] sage.interfaces.sympy._sympysage_matrix: Cache the result if self is immutable --- src/sage/interfaces/sympy.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index dc568808e63..a64a58c8fa2 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -754,7 +754,7 @@ def _sympysage_matrix(self): EXAMPLES:: - sage: from sympy.matrices import Matrix, SparseMatrix + sage: from sympy.matrices import Matrix, SparseMatrix, ImmutableMatrix sage: from sage.interfaces.sympy import sympy_init sage: from sympy.abc import x sage: sympy_init() @@ -791,11 +791,33 @@ def _sympysage_matrix(self): [0 0 0] sage: O.parent() Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + + If ``self`` is immutable, the result is cached:: + + sage: sImmM = ImmutableMatrix([[1, x + 1], [x - 1, 1]]); sImmM + Matrix([ + [ 1, x + 1], + [x - 1, 1]]) + sage: ImmM = sImmM._sage_(); ImmM + [ 1 x + 1] + [x - 1 1] + sage: ImmM is sImmM._sage_() + True + + If ``self`` is mutable, the conversion is redone every time:: + + sage: sM[0, 0] = 1000 + sage: MutatedM = sM._sage_(); MutatedM + [ 1000 x + 1] + [x - 1 1] + sage: M == MutatedM + False + """ try: return self._sage_object except AttributeError: - from sympy.matrices import SparseMatrix + from sympy.matrices import SparseMatrix, ImmutableMatrix from sage.matrix.constructor import matrix rows, cols = self.shape @@ -812,9 +834,12 @@ def _sympysage_matrix(self): base_ring = coercion_model.common_parent(*d.values()) except TypeError: # no common canonical parent base_ring = SR - return matrix(base_ring, rows, cols, d, - sparse=isinstance(self, SparseMatrix), - immutable=True) + result = matrix(base_ring, rows, cols, d, + sparse=isinstance(self, SparseMatrix), + immutable=True) + if isinstance(self, ImmutableMatrix): + self._sage_object = result + return result def _sympysage_relational(self): """ From 50c1ded26ccc4a7eb76f007dd28c4693620c41b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Jun 2021 13:00:02 +0200 Subject: [PATCH 575/706] =?UTF-8?q?Poincar=C3=A9=20polynomial=20of=20modul?= =?UTF-8?q?i=20space=20of=20semi-stable=20quiver=20representations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../combinat/cluster_algebra_quiver/quiver.py | 111 +++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 850f5531c66..04615355c10 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Quiver @@ -41,6 +42,13 @@ from sage.rings.all import ZZ, CC, infinity from sage.graphs.all import Graph, DiGraph from sage.graphs.views import EdgesView +from sage.arith.misc import gcd +from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix +from sage.categories.cartesian_product import cartesian_product +from sage.misc.misc_c import prod +from sage.rings.rational_field import QQ +from sage.rings.polynomial.polynomial_ring import polygen from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter, _digraph_to_dig6, _dig6_to_matrix from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite @@ -1919,7 +1927,7 @@ def is_mutation_finite( self, nr_of_checks=None, return_path=False ): # and getting the corresponding matrix M = _dig6_to_matrix(dig6) - is_finite, path = is_mutation_finite(M,nr_of_checks=nr_of_checks) + is_finite, path = is_mutation_finite(M, nr_of_checks=nr_of_checks) if return_path: return is_finite, path else: @@ -1992,7 +2000,8 @@ def relabel(self, relabelling, inplace=True): # If the key is in the old vertices, use that mapping digraph_labels[key] = val # And place it in the right order for our dictionary - loc = [i for i,x in enumerate(old_vertices) if x == key][0] + loc = [i for i, x in enumerate(old_vertices) + if x == key][0] dict_labels[loc] = val elif isinstance(key, int) and len(old_vertices) > key: # If the key is an integer, grab that particular vertex @@ -2004,6 +2013,104 @@ def relabel(self, relabelling, inplace=True): quiver._vertex_dictionary = dict_labels return quiver + def poincare_semistable(self, theta, d): + r""" + Return the Poincaré polynomial of the moduli space of semi-stable + representations of dimension vector `d`. + + INPUT: + + - ``theta`` -- stability weight, as list or vector of rationals + - ``d`` -- dimension vector, as list or vector of coprime integers + + The semi-stability is taken with respect to the slope function + + .. MATH:: + + \mu(d) = \theta(d) / \operatorname{dim}(d) + + where `d` is a dimension vector. + + This uses the matrix-inversion algorithm from [Rei2002]_. + + EXAMPLES:: + + sage: Q = ClusterQuiver(['A',2]) + sage: Q.poincare_semistable([1,0],[1,0]) + 1 + sage: Q.poincare_semistable([1,0],[1,1]) + 1 + + sage: K2 = ClusterQuiver(matrix([[0,2],[-2,0]])) + sage: theta = (1, 0) + sage: K2.poincare_semistable(theta, [1,0]) + 1 + sage: K2.poincare_semistable(theta, [1,1]) + v^2 + 1 + sage: K2.poincare_semistable(theta, [1,2]) + 1 + sage: K2.poincare_semistable(theta, [1,3]) + 0 + + sage: K3 = ClusterQuiver(matrix([[0,3],[-3,0]])) + sage: theta = (1, 0) + sage: K3.poincare_semistable(theta, (2,3)) + v^12 + v^10 + 3*v^8 + 3*v^6 + 3*v^4 + v^2 + 1 + sage: K3.poincare_semistable(theta, (3,4))(1) + 68 + + TESTS:: + + sage: Q = ClusterQuiver(['A',2]) + sage: Q.poincare_semistable([1,0],[2,2]) + Traceback (most recent call last): + ... + ValueError: dimension vector d is not coprime + + REFERENCES: + + .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum + groups and cohomology of quiver moduli*, :arxiv:`math/0204059` + """ + if gcd([x for x in d if x]) != 1: + raise ValueError("dimension vector d is not coprime") + d = vector(ZZ, d) + theta = vector(theta) + + n = self.n() + b_mat = self.b_matrix() + Eu = matrix(ZZ, n, n, + lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0) + Eu = 1 + Eu + + mu_d = theta.dot_product(d) / sum(d) + + Li = [0 * d] + it = (vector(e) for e in cartesian_product([range(d_i + 1) + for d_i in d])) + Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] + Li.append(d) + + v = polygen(QQ, 'v') + q = v**2 + + def cardinal_RG(d): + cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i)) + cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j]) + for i, j in self.digraph().edges(labels=False)) + return cardinal_R / cardinal_G + + Reineke_mat = matrix(v.parent().fraction_field(), len(Li), len(Li), 1) + + for i, e in enumerate(Li): + for j, f in enumerate(Li): + f_e = f - e + if all(x >= 0 for x in f_e): + power = (-f_e) * Eu * e + Reineke_mat[i, j] = q**power * cardinal_RG(f_e) + + return ((1 - v**2) * Reineke_mat.inverse()[0, -1]).numerator() + def d_vector_fan(self): r""" Return the d-vector fan associated with the quiver. From 73e39ce9c9121c34da6d3ced36b36262ae4e9026 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:43:40 -0700 Subject: [PATCH 576/706] ConvexRationalPolyhedralCone.is_compact: Define --- src/sage/geometry/cone.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index aa1acf765ac..8e42648aef5 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3311,6 +3311,8 @@ def is_trivial(self): """ return self.nrays() == 0 + is_compact = is_trivial + def is_strictly_convex(self): r""" Check if ``self`` is strictly convex. From 92f0610e33b15cba97247b787bbcc0a9593d9d34 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:45:44 -0700 Subject: [PATCH 577/706] ConvexSet_open: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 5a531dd9c44..f77498b4fb1 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -196,9 +196,8 @@ def is_compact(self): class ConvexSet_relatively_open(ConvexSet_base): - r""" - Abstract base class for relatively open sets. + Abstract base class for relatively open convex sets. """ def is_relatively_open(self): @@ -222,3 +221,31 @@ def is_open(self): """ return self.is_full_dimensional() + + +class ConvexSet_open(ConvexSet_relatively_open): + r""" + Abstract base class for open convex sets. + """ + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() From 03a31efc560cffebe2cf270ae7d460302307ac95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:48:50 -0700 Subject: [PATCH 578/706] Polyhedron_base.is_full_dimensional: Merge into ConvexSet_base.is_full_dimensional --- src/sage/geometry/convex_set.py | 11 +++++++++++ src/sage/geometry/polyhedron/base.py | 18 ------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f77498b4fb1..cd8dd1d9911 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,17 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.is_full_dimensional() + False + + sage: polytopes.hypercube(3).is_full_dimensional() + True + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() + False """ return self.dim() == self.ambient_dim() diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 03ee54f8077..8e242132313 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -9814,24 +9814,6 @@ def edge_label(i, j, c_ij): else: return MatrixGroup(matrices) - def is_full_dimensional(self): - """ - Return whether the polyhedron is full dimensional. - - OUTPUT: - - Boolean. Whether the polyhedron is not contained in any strict - affine subspace. - - EXAMPLES:: - - sage: polytopes.hypercube(3).is_full_dimensional() - True - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() - False - """ - return self.dim() == self.ambient_dim() - def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): r""" Return whether the polyhedron is combinatorially isomorphic to another polyhedron. From a71507e11c33cbc2c61600b76098a814e6132f1d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:49:54 -0700 Subject: [PATCH 579/706] ConvexSet_base: Add some doctests --- src/sage/geometry/convex_set.py | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index cd8dd1d9911..53caea0b12b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -16,7 +16,6 @@ from sage.misc.abstract_method import abstract_method class ConvexSet_base(SageObject): - """ Abstract base class for convex sets. """ @@ -28,6 +27,14 @@ def is_empty(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p + -1-d lattice polytope in 3-d lattice M + sage: p.is_empty() + True + """ return self.dim() < 0 @@ -90,12 +97,24 @@ def is_open(self): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. + + The default implementation of this method only knows that open + sets are also relatively open. OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_open(self): + ....: return True + sage: ExampleSet().is_relatively_open() + True + """ if self.is_open(): return True @@ -123,13 +142,20 @@ def is_compact(self): """ if not self.is_closed(): return False - if self.dimension() < 1: + if self.dim() < 1: return True raise NotImplementedError def closure(self): r""" Return the topological closure of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: C = ConvexSet_closed() + sage: C.closure() is C + True """ if self.is_closed(): return self @@ -138,6 +164,13 @@ def closure(self): def interior(self): r""" Return the topological interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: C = ConvexSet_open() + sage: C.interior() is C + True """ if self.is_open(): return self @@ -151,7 +184,6 @@ def affine_hull(self): class ConvexSet_closed(ConvexSet_base): - r""" Abstract base class for closed convex sets. """ @@ -179,7 +211,6 @@ def is_open(self): class ConvexSet_compact(ConvexSet_closed): - r""" Abstract base class for compact convex sets. """ From 3a831826846c776c95a51133e1b4e216d248acc3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:23 -0700 Subject: [PATCH 580/706] ConvexSet_base.relative_interior: New --- src/sage/geometry/convex_set.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 53caea0b12b..114018dc629 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -176,6 +176,21 @@ def interior(self): return self raise NotImplementedError + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_relatively_open + sage: C = ConvexSet_relatively_open() + sage: C.relative_interior() is C + True + """ + if self.is_relatively_open(): + return self + raise NotImplementedError + @abstract_method(optional=True) def affine_hull(self): r""" From 6a0baac5f679d90a1acd3696f3613350af5af9f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:51 -0700 Subject: [PATCH 581/706] ConvexSet_base._test_convex_set: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 114018dc629..8c5a0019c16 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -197,6 +197,37 @@ def affine_hull(self): Return the affine hull of ``self``. """ + def _test_convex_set(self, tester=None, **options): + """ + Run some tests on the methods of :class:`ConvexSet_base`. + """ + if tester is None: + tester = self._tester(**options) + dim = self.dim() + tester.assertTrue(dim <= self.ambient_dim()) + if self.is_empty(): + tester.assertTrue(dim == -1) + if self.is_universe(): + tester.assertTrue(self.is_full_dimensional()) + cl_self = self.closure() + try: + int_self = self.interior() + except NotImplementedError: + int_self = None + try: + relint_self = self.relative_interior() + except NotImplementedError: + relint_self = None + if self.is_full_dimensional(): + tester.assertTrue(int_self == relint_self) + if self.is_relatively_open(): + tester.assertTrue(self == relint_self) + if self.is_open(): + tester.assertTrue(self == int_self) + if self.is_closed(): + tester.assertTrue(self == cl_self) + if self.is_compact(): + tester.assertTrue(self.is_closed()) class ConvexSet_closed(ConvexSet_base): r""" From 0495bb05c37f55257b892c6790234d9fb5dd77f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 16:34:03 -0700 Subject: [PATCH 582/706] RelativeInterior: Fix up doctest --- src/sage/geometry/relative_interior.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index e0dfc9ce6b9..795ef408692 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -47,7 +47,7 @@ def __init__(self, polyhedron): TESTS:: - sage: P = Polyhedron() + sage: P = Polyhedron([[1, 2], [3, 4]]) sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() """ From 9a7ce3a53d63ace6ed56f4addc92f61b85549b32 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 17:22:27 -0700 Subject: [PATCH 583/706] src/sage/geometry/convex_set.py: More examples and tests --- src/sage/geometry/convex_set.py | 141 ++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8c5a0019c16..9ea892c99b9 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -22,7 +22,7 @@ class ConvexSet_base(SageObject): def is_empty(self): r""" - Test whether ``self`` is the empty set + Test whether ``self`` is the empty set. OUTPUT: @@ -34,13 +34,12 @@ def is_empty(self): -1-d lattice polytope in 3-d lattice M sage: p.is_empty() True - """ return self.dim() < 0 def is_universe(self): r""" - Test whether ``self`` is the whole ambient space + Test whether ``self`` is the whole ambient space. OUTPUT: @@ -84,23 +83,39 @@ def is_full_dimensional(self): """ return self.dim() == self.ambient_dim() - @abstract_method def is_open(self): r""" Return whether ``self`` is open. + The default implementation of this method only knows that the + empty set and the ambient space are open. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_empty(self): + ....: return False + ....: def is_universe(self): + ....: return True + sage: ExampleSet().is_open() + True """ + if self.is_empty() or self.is_universe(): + return True + raise NotImplementedError def is_relatively_open(self): r""" Return whether ``self`` is relatively open. The default implementation of this method only knows that open - sets are also relatively open. + sets are also relatively open, and in addition singletons are + relatively open. OUTPUT: @@ -114,31 +129,55 @@ def is_relatively_open(self): ....: return True sage: ExampleSet().is_relatively_open() True - """ if self.is_open(): return True + if self.dim() == 0: + return True raise NotImplementedError - @abstract_method def is_closed(self): r""" Return whether ``self`` is closed. + The default implementation of this method only knows that the + empty set, a singleton set, and the ambient space are closed. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_closed() + True """ + if self.is_empty() or self.dim() == 0 or self.is_universe(): + return True + raise NotImplementedError def is_compact(self): r""" Return whether ``self`` is compact. + The default implementation of this method only knows that a + non-closed set cannot be compact, and that the empty set and + a singleton set are compact. + OUTPUT: Boolean. + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_compact() + True """ if not self.is_closed(): return False @@ -200,6 +239,32 @@ def affine_hull(self): def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class FaultyConvexSet(ConvexSet_open): + ....: def is_universe(self): + ....: return True + ....: def dim(self): + ....: return 42 + ....: def ambient_dim(self): + ....: return 91 + sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + + sage: class BiggerOnTheInside(ConvexSet_open): + ....: def dim(self): + ....: return 100000 + ....: def ambient_dim(self): + ....: return 3 + sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + """ if tester is None: tester = self._tester(**options) @@ -241,6 +306,12 @@ def is_closed(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_closed() + True """ return True @@ -252,6 +323,15 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_open() + False + + sage: zerocube = polytopes.hypercube(0) + sage: zerocube.is_open() + True """ return self.is_empty() or self.is_universe() @@ -268,6 +348,16 @@ def is_universe(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_universe() + False + sage: point0 = LatticePolytope([[]]); point0 + 0-d reflexive polytope in 0-d lattice M + sage: point0.is_universe() + True """ return self.ambient_dim() == 0 and not self.is_empty() @@ -279,9 +369,16 @@ def is_compact(self): Boolean. + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_compact() + True """ return True + is_relatively_open = ConvexSet_closed.is_open + class ConvexSet_relatively_open(ConvexSet_base): r""" @@ -296,6 +393,12 @@ def is_relatively_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_relatively_open() + True """ return True @@ -307,8 +410,14 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_open() + False """ - return self.is_full_dimensional() + return self.is_empty() or self.is_full_dimensional() class ConvexSet_open(ConvexSet_relatively_open): @@ -324,6 +433,12 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: b = ConvexSet_open() + sage: b.is_open() + True """ return True @@ -335,5 +450,15 @@ def is_closed(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class OpenBall(ConvexSet_open): + ....: def dim(self): + ....: return 3 + ....: def is_universe(self): + ....: return False + sage: OpenBall().is_closed() + False """ return self.is_empty() or self.is_universe() From e2b0ef7390426c7d8f6f7f0a1382b326d5c9bc6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 18:39:17 -0700 Subject: [PATCH 584/706] ConvexSet_base._test_convex_set: Fix doctest output --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 9ea892c99b9..4372d90a1cd 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -251,7 +251,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 91 sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -261,7 +261,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set From 45c840a98ea222b30847b6ae8411d52f7cd778ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:41:58 -0700 Subject: [PATCH 585/706] ConvexSet_base.codim, codimension: New --- src/sage/geometry/cone.py | 3 +++ src/sage/geometry/convex_set.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8e42648aef5..eab23498e85 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1225,8 +1225,11 @@ def codim(self): sage: K.codim() == K.dual().lineality() True """ + # same as ConvexSet_base.codim; the main point is the much more detailed + # docstring. return (self.lattice_dim() - self.dim()) + codimension = codim def span(self, base_ring=None): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 4372d90a1cd..dab6e61b25e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -61,6 +61,21 @@ def ambient_dim(self): Return the dimension of the ambient space. """ + def codimension(self): + r""" + Return the codimension of ``self``. + + An alias is :meth:`codim`. + + EXAMPLES:: + + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + 2 + """ + return self.ambient_dim() - self.dim() + + codim = codimension + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. @@ -269,7 +284,10 @@ def _test_convex_set(self, tester=None, **options): if tester is None: tester = self._tester(**options) dim = self.dim() + codim = self.codim() tester.assertTrue(dim <= self.ambient_dim()) + if dim >= 0: + tester.assertTrue(dim + codim == self.ambient_dim()) if self.is_empty(): tester.assertTrue(dim == -1) if self.is_universe(): From 17467c498978ca9f165b0619b30d5ab2fc84272b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:43:20 -0700 Subject: [PATCH 586/706] ConvexSet_base: Make dimension, ambient_dimension aliases for dim, ambient_dim --- src/sage/geometry/convex_set.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index dab6e61b25e..d2ae244dd3c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -55,12 +55,46 @@ def dim(self): Return the dimension of ``self``. """ + def dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 42 + sage: ExampleSet().dimension() + 42 + """ + return self.dim() + @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient space. """ + def ambient_dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`ambient_dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_dim(self): + ....: return 91 + sage: ExampleSet().ambient_dimension() + 91 + """ + return self.ambient_dim() + def codimension(self): r""" Return the codimension of ``self``. From fa5dc6eb2c696c0094261c76b72a16cb0bc92846 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:47:03 -0700 Subject: [PATCH 587/706] ConvexSet_base.cartesian_product: New --- src/sage/geometry/convex_set.py | 29 ++++++++++++++++++++++------ src/sage/geometry/polyhedron/base.py | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d2ae244dd3c..465e975c15c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -279,12 +279,6 @@ def relative_interior(self): return self raise NotImplementedError - @abstract_method(optional=True) - def affine_hull(self): - r""" - Return the affine hull of ``self``. - """ - def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. @@ -346,6 +340,29 @@ def _test_convex_set(self, tester=None, **options): if self.is_compact(): tester.assertTrue(self.is_closed()) + # Optional methods + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + @abstract_method(optional=True) + def cartesian_product(self, other): + """ + Return the Cartesian product. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The Cartesian product of ``self`` and ``other``. + """ + + class ConvexSet_closed(ConvexSet_base): r""" Abstract base class for closed convex sets. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e242132313..1d17d27df09 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4817,6 +4817,8 @@ def product(self, other): _mul_ = product + cartesian_product = product + def _test_product(self, tester=None, **options): """ Run tests on the method :meth:`.product`. From f4bdffda473c608289d6b8c90a3687484d24f3be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:58:07 -0700 Subject: [PATCH 588/706] ConvexSet_base.contains, intersection: New --- src/sage/geometry/convex_set.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 465e975c15c..6827b0a10d8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -362,6 +362,30 @@ def cartesian_product(self, other): The Cartesian product of ``self`` and ``other``. """ + @abstract_method(optional=True) + def contains(self, point): + """ + Test whether ``self`` contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + """ + + @abstract_method(optional=True) + def intersection(self, other): + r""" + Return the intersection of ``self`` and ``other``. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The intersection. + """ + class ConvexSet_closed(ConvexSet_base): r""" From 2f0f4c05692a26463c41c9032728e04e6d3aa158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Jun 2021 08:12:41 +0200 Subject: [PATCH 589/706] =?UTF-8?q?some=20details=20in=20Poincar=C3=A9=5Fs?= =?UTF-8?q?emistable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../combinat/cluster_algebra_quiver/quiver.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 04615355c10..1a6c2aedef6 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -2067,6 +2067,10 @@ def poincare_semistable(self, theta, d): ... ValueError: dimension vector d is not coprime + sage: Q = ClusterQuiver(['A',3]) + sage: Q.poincare_semistable([1,1,0],[2,3,4]) + 0 + REFERENCES: .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum @@ -2082,6 +2086,7 @@ def poincare_semistable(self, theta, d): Eu = matrix(ZZ, n, n, lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0) Eu = 1 + Eu + edges = list(self.digraph().edges(labels=False)) mu_d = theta.dot_product(d) / sum(d) @@ -2091,16 +2096,15 @@ def poincare_semistable(self, theta, d): Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] Li.append(d) - v = polygen(QQ, 'v') - q = v**2 + q = polygen(QQ, 'v') # q stands for v**2 until the last line def cardinal_RG(d): cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i)) cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j]) - for i, j in self.digraph().edges(labels=False)) + for i, j in edges) return cardinal_R / cardinal_G - Reineke_mat = matrix(v.parent().fraction_field(), len(Li), len(Li), 1) + Reineke_mat = matrix(q.parent().fraction_field(), len(Li), len(Li), 1) for i, e in enumerate(Li): for j, f in enumerate(Li): @@ -2109,7 +2113,8 @@ def cardinal_RG(d): power = (-f_e) * Eu * e Reineke_mat[i, j] = q**power * cardinal_RG(f_e) - return ((1 - v**2) * Reineke_mat.inverse()[0, -1]).numerator() + poly = ((1 - q) * Reineke_mat.inverse()[0, -1]).numerator() + return poly(q**2) # replacing q by v**2 def d_vector_fan(self): r""" From 20e4de05a463ebdf843b2dffe22b1e2f19654341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Jun 2021 08:58:18 +0200 Subject: [PATCH 590/706] =?UTF-8?q?using=20a=20det=20for=20the=20Poincar?= =?UTF-8?q?=C3=A9=20poly=20of=20moduli=20space?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/combinat/cluster_algebra_quiver/quiver.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 1a6c2aedef6..dedef28db11 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -2095,6 +2095,7 @@ def poincare_semistable(self, theta, d): for d_i in d])) Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] Li.append(d) + N = len(Li) - 1 q = polygen(QQ, 'v') # q stands for v**2 until the last line @@ -2104,16 +2105,19 @@ def cardinal_RG(d): for i, j in edges) return cardinal_R / cardinal_G - Reineke_mat = matrix(q.parent().fraction_field(), len(Li), len(Li), 1) + Reineke_submat = matrix(q.parent().fraction_field(), N, N) - for i, e in enumerate(Li): - for j, f in enumerate(Li): + for i, e in enumerate(Li[:-1]): + for j, f in enumerate(Li[1:]): + if e == f: + Reineke_submat[i, j] = 1 + continue f_e = f - e if all(x >= 0 for x in f_e): power = (-f_e) * Eu * e - Reineke_mat[i, j] = q**power * cardinal_RG(f_e) + Reineke_submat[i, j] = q**power * cardinal_RG(f_e) - poly = ((1 - q) * Reineke_mat.inverse()[0, -1]).numerator() + poly = (-1)**N * ((1 - q) * Reineke_submat.det()).numerator() return poly(q**2) # replacing q by v**2 def d_vector_fan(self): From c2c9b3c735c9b651045c471360f3a1314e883a8b Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 11 Jun 2021 09:35:33 -0700 Subject: [PATCH 591/706] trac 31948: very rough draft --- build/make/Makefile.in | 22 +++++++++++++++++++++- src/sage_docbuild/__init__.py | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index fb3a6ed5bcf..c199de440df 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -335,7 +335,27 @@ DOC_DEPENDENCIES = sagelib sage_docbuild $(inst_sphinx) \ $(inst_ipykernel) $(inst_jupyter_client) $(inst_conway_polynomials) \ $(inst_tachyon) $(inst_jmol) $(inst_thebe) $(inst_ipywidgets) -doc: doc-html +doc: doc-references doc-other + +doc-references: $(DOC_DEPENDENCIES) + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval biblio = $(firstword $(DOCS))) + $(eval other_docs = $(wordlist 2, 100, $(DOCS))) + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(biblio) inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + for doc in $(other_docs); do \ + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + done + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links reference inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(biblio) html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + for doc in $(other_docs); do \ + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links reference html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ + done + +doc-other: $(DOC_DEPENDENCIES) doc-references + for doc in $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)); do \ + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc html $(SAGE_DOCBUILD_OPTS)" logs/docs-other-html.log; \ + done doc-html: $(DOC_DEPENDENCIES) $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links all html $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index 79005b903ab..a3caa8a92d4 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -116,7 +116,7 @@ def f(self, *args, **kwds): # WEBSITESPHINXOPTS is either empty or " -A hide_pdf_links=1 " options += WEBSITESPHINXOPTS - if kwds.get('use_multidoc_inventory', True): + if kwds.get('use_multidoc_inventory', True) and type != 'inventory': options += ' -D multidoc_first_pass=0' else: options += ' -D multidoc_first_pass=1' From aaf26b907ca7f148f862e1b52d434e9e7b573430 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Fri, 11 Jun 2021 19:23:56 +0200 Subject: [PATCH 592/706] 30133: fix pyflakes warning --- src/sage/symbolic/expression_conversions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 52e140f5ea0..1c57da162ca 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -18,7 +18,6 @@ import operator as _operator from sage.rings.rational_field import QQ from sage.symbolic.ring import SR -from sage.symbolic.constants import I from sage.functions.all import exp from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg from sage.rings.number_field.number_field import GaussianField From 393ab650dd0bd38599e4716092b5217b8ecae9ed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 10:43:43 -0700 Subject: [PATCH 593/706] build/make/Makefile.in (doc-reference): Invoke make recursively to build documents in parallel --- build/make/Makefile.in | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index c199de440df..339497fd175 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -337,20 +337,24 @@ DOC_DEPENDENCIES = sagelib sage_docbuild $(inst_sphinx) \ doc: doc-references doc-other +# Matches doc-inventory-reference-manifolds etc. +doc-inventory-%: + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory-,,$@)) inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log + +# Matches doc-html-developer, doc-html-reference-manifolds etc. +doc-html-%: + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html-,,$@)) html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log + doc-references: $(DOC_DEPENDENCIES) $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) $(eval biblio = $(firstword $(DOCS))) $(eval other_docs = $(wordlist 2, 100, $(DOCS))) - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(biblio) inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - for doc in $(other_docs); do \ - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - done - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links reference inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(biblio) html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - for doc in $(other_docs); do \ - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links reference html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log; \ - done + +$(MAKE_REC) doc-inventory-$(subst /,-,$(biblio)) + +$(MAKE_REC) $(foreach doc, $(other_docs), doc-inventory-$(subst /,-,$(doc))) + +$(MAKE_REC) doc-inventory-reference + +$(MAKE_REC) doc-html-$(subst /,-,$(biblio)) + +$(MAKE_REC) $(foreach doc, $(other_docs), doc-html-$(subst /,-,$(doc))) + +$(MAKE_REC) doc-html-reference doc-other: $(DOC_DEPENDENCIES) doc-references for doc in $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)); do \ From 8b6441ea78e86d30c15660f192d9adaef8bb2a2f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 11:50:27 -0700 Subject: [PATCH 594/706] installed_packages: Remove problematic test for sage_conf's version --- src/sage/misc/package.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 82106a1af6d..1afd054a50f 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -356,8 +356,6 @@ def installed_packages(exclude_pip=True): [...'alabaster', ...'sage_conf', ...] sage: installed_packages()['alabaster'] # optional - build, random '0.7.12' - sage: installed_packages()['sage_conf'] == sage.env.SAGE_VERSION # optional - build - True .. SEEALSO:: From 7fbdf5d2d9b177bfa812427dcc83931e83c62be4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 11 Jun 2021 21:13:58 +0200 Subject: [PATCH 595/706] replace unstable doctest by doctest according to code and documentation --- src/sage/modules/free_module_integer.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 3da0f6feacb..a3b201657a6 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -311,11 +311,7 @@ def reduced_basis(self): True sage: LLL = L.LLL() - sage: LLL == L.reduced_basis - True - sage: bool(min(a.norm() for a in LLL) == LLL[0].norm()) - True - sage: bool(LLL[0].norm() <= M[0].norm()) + sage: LLL == L.reduced_basis or bool(LLL[0].norm() >= M[0].norm()) True """ return self._reduced_basis From ee00642b382baf7b5ee7ed3b42e5bf6dcaa9fb64 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:19 -0700 Subject: [PATCH 596/706] ConvexSet_base.ambient: New; clarify ambient_dim, ambient_dimension, codimension --- src/sage/geometry/convex_set.py | 16 +++++++++++++--- src/sage/geometry/polyhedron/base.py | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..f34bcb78493 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,15 +72,21 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient(self): + r""" + Return the ambient convex set or space. + """ + @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. @@ -97,7 +103,7 @@ def ambient_dimension(self): def codimension(self): r""" - Return the codimension of ``self``. + Return the codimension of ``self`` in `self.ambient()``. An alias is :meth:`codim`. @@ -287,6 +293,8 @@ def _test_convex_set(self, tester=None, **options): sage: from sage.geometry.convex_set import ConvexSet_open sage: class FaultyConvexSet(ConvexSet_open): + ....: def ambient(self): + ....: return QQ^55 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -301,6 +309,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient(self): + ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..0f9d65c4594 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2478,7 +2478,7 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient_space = Vrepresentation_space + ambient = ambient_space = Vrepresentation_space def Hrepresentation_space(self): r""" From 2c756d43f92db60d12be732c515fee2a0928ea99 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:41 -0700 Subject: [PATCH 597/706] PolyhedronFace: Make it a subclass of ConvexSet_closed --- src/sage/geometry/polyhedron/face.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 7a00f46ab19..12baa5425b8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -78,12 +78,12 @@ from sage.misc.all import cached_method from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix - +from sage.geometry.convex_set import ConvexSet_closed ######################################################################### @richcmp_method -class PolyhedronFace(SageObject): +class PolyhedronFace(ConvexSet_closed): r""" A face of a polyhedron. @@ -121,6 +121,11 @@ class PolyhedronFace(SageObject): (An inequality (1, 1, 1) x + 1 >= 0,) sage: face.ambient_Vrepresentation() (A vertex at (-1, 0, 0), A vertex at (0, -1, 0), A vertex at (0, 0, -1)) + + TESTS:: + + sage: TestSuite(face).run() + """ def __init__(self, polyhedron, V_indices, H_indices): @@ -147,6 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 + sage: TestSuite(_).run() """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -649,6 +655,8 @@ def polyhedron(self): """ return self._polyhedron + ambient = polyhedron + @cached_method def as_polyhedron(self): """ From 8d77b3e67c27e24ccae8e529eddb4beece0e8a30 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:47:17 -0700 Subject: [PATCH 598/706] PolyhedronFace.is_relatively_open, is_compact: New, add doctests --- src/sage/geometry/polyhedron/face.py | 61 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 12baa5425b8..31f0de091e1 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -124,7 +124,7 @@ class PolyhedronFace(ConvexSet_closed): TESTS:: - sage: TestSuite(face).run() + sage: TestSuite(face).run(skip='_test_pickling') """ @@ -152,7 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 - sage: TestSuite(_).run() + sage: TestSuite(_).run(skip='_test_pickling') """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -186,6 +186,10 @@ def vertex_generator(self): A vertex at (1, 1) sage: type(face.vertex_generator()) <... 'generator'> + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_vertex(): @@ -206,6 +210,10 @@ def vertices(self): sage: face = triangle.faces(1)[2] sage: face.vertices() (A vertex at (0, 1), A vertex at (1, 0)) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return tuple(self.vertex_generator()) @@ -224,6 +232,10 @@ def n_vertices(self): sage: face = Q.faces(2)[0] sage: face.n_vertices() 3 + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return len(self.vertices()) @@ -237,6 +249,10 @@ def ray_generator(self): sage: face = pi.faces(1)[1] sage: next(face.ray_generator()) A ray in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_ray(): @@ -288,6 +304,10 @@ def line_generator(self): sage: face = pr.faces(1)[0] sage: next(face.line_generator()) A line in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_line(): @@ -657,6 +677,43 @@ def polyhedron(self): ambient = polyhedron + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_relatively_open() + True + """ + return self.as_polyhedron().is_relatively_open() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_compact() + False + """ + return not any(V.is_ray() or V.is_line() + for V in self.ambient_Vrepresentation()) + @cached_method def as_polyhedron(self): """ From 6ba5d9c2bd5dca867d632b9f569407ac5f758e6f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:23:51 -0700 Subject: [PATCH 599/706] ConvexSet_base.ambient_vector_space: New --- src/sage/geometry/cone.py | 20 ++++++++++++++++ src/sage/geometry/convex_set.py | 10 ++++++++ src/sage/geometry/lattice_polytope.py | 20 ++++++++++++++++ src/sage/geometry/polyhedron/base.py | 34 +++++++++++++++++++++++++-- src/sage/geometry/polyhedron/face.py | 23 ++++++++++++++++++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index eab23498e85..fd2074676c7 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -981,6 +981,26 @@ def lattice(self): """ return self._lattice + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: c.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + @cached_method def dual_lattice(self): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f34bcb78493..fbb297d4ae7 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,6 +72,12 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + """ + @abstract_method def ambient(self): r""" @@ -295,6 +301,8 @@ def _test_convex_set(self, tester=None, **options): sage: class FaultyConvexSet(ConvexSet_open): ....: def ambient(self): ....: return QQ^55 + ....: def ambient_vector_space(self, base_field=None): + ....: return QQ^16 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -309,6 +317,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient_vector_space(self): + ....: return QQ^3 ....: def ambient(self): ....: return QQ^3 ....: def ambient_dim(self): diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index a063657e30a..bde2f0a6be7 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2617,6 +2617,26 @@ def lattice_dim(self): ambient_dim = lattice_dim + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: p = LatticePolytope([(1,0)]) + sage: p.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: p.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0f9d65c4594..67208a3b5d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2462,7 +2462,7 @@ def bounded_edges(self): def Vrepresentation_space(self): r""" - Return the ambient vector space. + Return the ambient free module. OUTPUT: @@ -2478,7 +2478,37 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient = ambient_space = Vrepresentation_space + ambient_space = Vrepresentation_space + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module (:meth:`Vrepresentation_space`) tensored + with a field. + + INPUT:: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.ambient_vector_space() + Vector space of dimension 4 over Rational Field + sage: poly_test.ambient_vector_space() is poly_test.ambient() + True + + sage: poly_test.ambient_vector_space(AA) + Vector space of dimension 4 over Algebraic Real Field + sage: poly_test.ambient_vector_space(RR) + Vector space of dimension 4 over Real Field with 53 bits of precision + sage: poly_test.ambient_vector_space(SR) + Vector space of dimension 4 over Symbolic Ring + """ + return self.Vrepresentation_space().vector_space(base_field=base_field) + + ambient = ambient_vector_space def Hrepresentation_space(self): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 31f0de091e1..af317977e5a 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -677,6 +677,29 @@ def polyhedron(self): ambient = polyhedron + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module of the containing polyhedron tensored + with a field. + + INPUT:: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: line.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.polyhedron().ambient_vector_space(base_field=base_field) + def is_relatively_open(self): r""" Return whether ``self`` is relatively open. From e66448ea0b3de1286595ef4af19fe95c5d9ef18e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:24:30 -0700 Subject: [PATCH 600/706] PolyhedronFace.contains, ConvexSet_base._test_contains: New --- src/sage/geometry/convex_set.py | 16 +++++++++-- src/sage/geometry/polyhedron/face.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index fbb297d4ae7..518ed2bc51f 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -309,7 +309,7 @@ def _test_convex_set(self, tester=None, **options): ....: return 42 ....: def ambient_dim(self): ....: return 91 - sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + sage: TestSuite(FaultyConvexSet()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -323,7 +323,7 @@ def _test_convex_set(self, tester=None, **options): ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 - sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + sage: TestSuite(BiggerOnTheInside()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -392,6 +392,18 @@ def contains(self, point): - ``point`` -- a point or its coordinates """ + def _test_contains(self, tester=None, **options): + """ + Test the ``contains`` method. + """ + if tester is None: + tester = self._tester(**options) + space = self.ambient_vector_space() + point = space.an_element() + coords = space.coordinates(point) + if self.contains != NotImplemented: + tester.assertEqual(self.contains(point), self.contains(coords)) + @abstract_method(optional=True) def intersection(self, other): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index af317977e5a..56305d035e8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -764,6 +764,49 @@ def as_polyhedron(self): Vrep = (self.vertices(), self.rays(), self.lines()) return P.__class__(parent, Vrep, None) + def contains(self, point): + """ + Test whether the polyhedron contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.contains([0, 1]) + True + + As a shorthand, one may use the usual ``in`` operator:: + + sage: [5, 7] in line + False + """ + # preprocess in the same way as Polyhedron_base.contains + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + if len(point) > 0: + return False + else: + p = vector(self.polyhedron().base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + if not self.polyhedron().contains(p): + return False + + for H in self.ambient_Hrepresentation(): + if H.eval(p) != 0: + return False + return True + + __contains__ = contains + @cached_method def normal_cone(self, direction='outer'): """ From d08633f65901a3accd709e496527fa8ac5bc4f2e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 19:02:27 -0700 Subject: [PATCH 601/706] build/make/Makefile.in, src/sage_docbuild/__init__.py: New option --no-prune-empty-dirs, use it to remove races --- build/make/Makefile.in | 47 ++++++++++++++++------------------- src/sage_docbuild/__init__.py | 20 +++++++++------ 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 339497fd175..93a88156de2 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -335,44 +335,41 @@ DOC_DEPENDENCIES = sagelib sage_docbuild $(inst_sphinx) \ $(inst_ipykernel) $(inst_jupyter_client) $(inst_conway_polynomials) \ $(inst_tachyon) $(inst_jmol) $(inst_thebe) $(inst_ipywidgets) -doc: doc-references doc-other +doc: doc-html -# Matches doc-inventory-reference-manifolds etc. -doc-inventory-%: - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory-,,$@)) inventory $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log +# Matches doc-inventory--reference-manifolds etc. +doc-inventory--%: + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory--,,$@)) inventory $(SAGE_DOCBUILD_OPTS)" logs/dochtml.log -# Matches doc-html-developer, doc-html-reference-manifolds etc. -doc-html-%: - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html-,,$@)) html $(SAGE_DOCBUILD_OPTS)" logs/docs-reference-html.log +# Matches doc-html--developer, doc-html--reference-manifolds etc. +doc-html--%: + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS)" logs/dochtml.log -doc-references: $(DOC_DEPENDENCIES) +doc-html-references: $(DOC_DEPENDENCIES) $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) $(eval biblio = $(firstword $(DOCS))) $(eval other_docs = $(wordlist 2, 100, $(DOCS))) - +$(MAKE_REC) doc-inventory-$(subst /,-,$(biblio)) - +$(MAKE_REC) $(foreach doc, $(other_docs), doc-inventory-$(subst /,-,$(doc))) - +$(MAKE_REC) doc-inventory-reference - +$(MAKE_REC) doc-html-$(subst /,-,$(biblio)) - +$(MAKE_REC) $(foreach doc, $(other_docs), doc-html-$(subst /,-,$(doc))) - +$(MAKE_REC) doc-html-reference - -doc-other: $(DOC_DEPENDENCIES) doc-references - for doc in $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)); do \ - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $$doc html $(SAGE_DOCBUILD_OPTS)" logs/docs-other-html.log; \ - done + +$(MAKE_REC) doc-inventory--$(subst /,-,$(biblio)) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-inventory--$(subst /,-,$(doc))) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--$(subst /,-,$(biblio)) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-html--$(subst /,-,$(doc))) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference + +doc-html-other: $(DOC_DEPENDENCIES) doc-html-references + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-html--$(subst /,-,$(doc))) -doc-html: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links all html $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log +doc-html: doc-html-references doc-html-other # 'doc-html-no-plot': build docs without building the graphics coming # from the '.. plot' directive, in case you want to save a few # megabytes of disk space. 'doc-clean' is a prerequisite because the # presence of graphics is cached in src/doc/output. -doc-html-no-plot: doc-clean $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links --no-plot all html $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log +doc-html-no-plot: doc-clean + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-plot" doc-html -doc-html-mathjax: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild --no-pdf-links all html -j $(SAGE_DOCBUILD_OPTS)' logs/dochtml.log +doc-html-mathjax: + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) -j" doc-html # Keep target 'doc-html-jsmath' for backwards compatibility. doc-html-jsmath: doc-html-mathjax diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index a3caa8a92d4..9479f67cc35 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -1571,6 +1571,9 @@ def setup_parser(): standard.add_option("--check-nested", dest="check_nested", action="store_true", help="check picklability of nested classes in DOCUMENT 'reference'") + standard.add_option("--no-prune-empty-dirs", dest="no_prune_empty_dirs", + action="store_true", + help="do not prune empty directories in the documentation sources") standard.add_option("-N", "--no-colors", dest="color", default=True, action="store_false", help="do not color output; does not affect children") @@ -1747,13 +1750,16 @@ def excepthook(*exc_info): ABORT_ON_ERROR = not options.keep_going - # Delete empty directories. This is needed in particular for empty - # directories due to "git checkout" which never deletes empty - # directories it leaves behind. See Trac #20010. - for dirpath, dirnames, filenames in os.walk(SAGE_DOC_SRC, topdown=False): - if not dirnames + filenames: - logger.warning('Deleting empty directory {0}'.format(dirpath)) - os.rmdir(dirpath) + if not options.no_prune_empty_dirs: + # Delete empty directories. This is needed in particular for empty + # directories due to "git checkout" which never deletes empty + # directories it leaves behind. See Trac #20010. + # Trac #31948: This is not parallelization-safe; use the option + # --no-prune-empty-dirs to turn it off + for dirpath, dirnames, filenames in os.walk(SAGE_DOC_SRC, topdown=False): + if not dirnames + filenames: + logger.warning('Deleting empty directory {0}'.format(dirpath)) + os.rmdir(dirpath) # Set up Intersphinx cache C = IntersphinxCache() From 240095296e65214ba8ee7dcefc77c8cba0ad0cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Jun 2021 09:02:56 +0200 Subject: [PATCH 602/706] finer category for affine linear groups --- src/sage/groups/affine_gps/affine_group.py | 76 +++++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index 516cdb9cd13..6c1629f6dc8 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -18,6 +18,9 @@ from sage.groups.group import Group +from sage.categories.groups import Groups +from sage.groups.matrix_gps.linear import GL +from sage.categories.rings import Rings from sage.matrix.all import MatrixSpace from sage.modules.all import FreeModule from sage.structure.unique_representation import UniqueRepresentation @@ -202,9 +205,21 @@ def __init__(self, degree, ring): sage: G = AffineGroup(2, GF(5)); G Affine Group of degree 2 over Finite Field of size 5 sage: TestSuite(G).run() + sage: G.category() + Category of finite groups + + sage: Aff6 = AffineGroup(6, QQ) + sage: Aff6.category() + Category of infinite groups """ self._degree = degree - Group.__init__(self, base=ring) + cat = Groups() + if degree == 0 or ring in Rings().Finite(): + cat = cat.Finite() + elif ring in Rings().Infinite(): + cat = cat.Infinite() + self._GL = GL(degree, ring) + Group.__init__(self, base=ring, category=cat) Element = AffineGroupElement @@ -253,7 +268,8 @@ def _latex_(self): sage: latex(G) \mathrm{Aff}_{6}(\Bold{F}_{5}) """ - return "\\mathrm{Aff}_{%s}(%s)"%(self.degree(), self.base_ring()._latex_()) + return "\\mathrm{Aff}_{%s}(%s)" % (self.degree(), + self.base_ring()._latex_()) def _repr_(self): """ @@ -264,7 +280,22 @@ def _repr_(self): sage: AffineGroup(6, GF(5)) Affine Group of degree 6 over Finite Field of size 5 """ - return "Affine Group of degree %s over %s"%(self.degree(), self.base_ring()) + return "Affine Group of degree %s over %s" % (self.degree(), + self.base_ring()) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: AffineGroup(6, GF(5)).cardinality() + 172882428468750000000000000000 + sage: AffineGroup(6, ZZ).cardinality() + +Infinity + """ + card_GL = self._GL.cardinality() + return card_GL * self.base_ring().cardinality()**self.degree() def degree(self): """ @@ -448,9 +479,7 @@ def random_element(self): sage: G.random_element() in G True """ - A = self.matrix_space().random_element() - while not A.is_invertible(): # a generic matrix is invertible - A.randomize() + A = self._GL.random_element() b = self.vector_space().random_element() return self.element_class(self, A, b, check=False, convert=False) @@ -465,9 +494,38 @@ def _an_element_(self): sage: G.an_element() in G True """ - A = self.matrix_space().an_element() - while not A.is_invertible(): # a generic matrix is not always invertible - A.randomize() + A = self._GL.an_element() b = self.vector_space().an_element() return self.element_class(self, A, b, check=False, convert=False) + def some_elements(self): + """ + Return some elements. + + EXAMPLES:: + + sage: G = AffineGroup(4,5) + sage: G.some_elements() + [ [2 0 0 0] [1] + [0 1 0 0] [0] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [0], + [2 0 0 0] [0] + [0 1 0 0] [0] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [0], + [2 0 0 0] [0] + [0 1 0 0] [2] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [1]] + + sage: G = AffineGroup(2,QQ) + sage: G.some_elements() + [ [1 0] [1] + x |-> [0 1] x + [0], + ...] + """ + mats = self._GL.some_elements() + vecs = self.vector_space().some_elements() + return [self.element_class(self, A, b, check=False, convert=False) + for A in mats for b in vecs] From 52725203ad5af01d8b503c99dea1ca6f11ade854 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 12 Jun 2021 14:23:54 +0200 Subject: [PATCH 603/706] Ignore warnings from docutils about missing rST localisation --- src/sage_docbuild/sphinxbuild.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index f58f6c61d76..d917c3e9d41 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -110,6 +110,7 @@ def _init_chatter(self): re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), + re.compile('WARNING: rST localisation for language .* not found') ) # The warning "unknown config value 'multidoc_first_pass'..." # should only appear when building the documentation for a From 1f7826d757f80ba7bae0aa64fb34c57206b2ad8f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:42:08 -0700 Subject: [PATCH 604/706] ConvexSet_base._test_contains: Expand and add doctests --- src/sage/geometry/convex_set.py | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 518ed2bc51f..56ff0d75bc4 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -395,14 +395,61 @@ def contains(self, point): def _test_contains(self, tester=None, **options): """ Test the ``contains`` method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: class FaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: ambient = ambient_vector_space + ....: def contains(self, point): + ....: if isinstance(point, (tuple, list)): + ....: return all(x in ZZ for x in point) + ....: return point.parent() == ZZ^2 + sage: FaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: False != True + + sage: class AlsoFaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: def ambient(self): + ....: return ZZ^2 + ....: def contains(self, point): + ....: return point in ZZ^2 + sage: AlsoFaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: True != False """ if tester is None: tester = self._tester(**options) + ambient = self.ambient() space = self.ambient_vector_space() - point = space.an_element() - coords = space.coordinates(point) + try: + ambient_point = ambient.an_element() + except (AttributeError, NotImplementedError): + ambient_point = None + space_point = space.an_element() + else: + space_point = space(ambient_point) + space_coords = space.coordinates(space_point) if self.contains != NotImplemented: - tester.assertEqual(self.contains(point), self.contains(coords)) + contains_space_point = self.contains(space_point) + if ambient_point is not None: + tester.assertEqual(contains_space_point, self.contains(ambient_point)) + tester.assertEqual(contains_space_point, self.contains(space_coords)) + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + from sage.symbolic.ring import SR + symbolic_space = self.ambient_vector_space(SR) + symbolic_space_point = symbolic_space(space_point) + # Only test that it can accept SR vectors without error. + self.contains(symbolic_space_point) @abstract_method(optional=True) def intersection(self, other): From 142fb462e22a0598eb56c5b61f6b7b602fcbbaac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:45:28 -0700 Subject: [PATCH 605/706] ConvexSet_base.codimension: Add doctest for alias 'codim' --- src/sage/geometry/convex_set.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..7945296471d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -99,11 +99,15 @@ def codimension(self): r""" Return the codimension of ``self``. - An alias is :meth:`codim`. - EXAMPLES:: - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + sage: P = Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]) + sage: P.codimension() + 2 + + An alias is :meth:`codim`:: + + sage: P.codim() 2 """ return self.ambient_dim() - self.dim() From 30ebaf5a657040cb787bea0f5c2540ca08115284 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:49:31 -0700 Subject: [PATCH 606/706] ConvexSet_base.ambient_dim, ambient_dimension: Fix doc --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 7945296471d..b7793476cf8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -75,12 +75,12 @@ def dimension(self): @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. From fcf2a32a768097f9248cda456624784cc9f199c6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 14:30:04 +0200 Subject: [PATCH 607/706] fix coverage --- src/sage/geometry/convex_set.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index b7793476cf8..8ef66c68285 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -44,6 +44,15 @@ def is_universe(self): OUTPUT: Boolean. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.is_universe() + Traceback (most recent call last): + ... + NotImplementedError: """ if not self.is_full_dimensional(): return False @@ -53,6 +62,15 @@ def is_universe(self): def dim(self): r""" Return the dimension of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def dimension(self): @@ -76,6 +94,15 @@ def dimension(self): def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def ambient_dimension(self): @@ -350,6 +377,15 @@ def _test_convex_set(self, tester=None, **options): def affine_hull(self): r""" Return the affine hull of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.affine_hull() + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -364,6 +400,15 @@ def cartesian_product(self, other): OUTPUT: The Cartesian product of ``self`` and ``other``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.cartesian_product(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -374,6 +419,15 @@ def contains(self, point): INPUT: - ``point`` -- a point or its coordinates + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.contains(vector([0, 0])) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -388,6 +442,15 @@ def intersection(self, other): OUTPUT: The intersection. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.intersection(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ From 6bef52ba121da086a07e2fe2b6e5f9b7ff3cbb51 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:20:46 -0700 Subject: [PATCH 608/706] ConvexSet_base._test_convex_set: Run the testsuite of relint --- src/sage/geometry/convex_set.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8ef66c68285..f55a4cd17b5 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -370,6 +370,12 @@ def _test_convex_set(self, tester=None, **options): tester.assertTrue(self == cl_self) if self.is_compact(): tester.assertTrue(self.is_closed()) + from sage.misc.sage_unittest import TestSuite + if relint_self is not None and relint_self is not self: + tester.info("\n Running the test suite of self.relative_interior()") + TestSuite(relint_self).run(verbose=tester._verbose, + prefix=tester._prefix + " ") + tester.info(tester._prefix + " ", newline=False) # Optional methods From 06c6365c0dba9b4ce85feac19fb1f97cf7bea3e3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 10:51:37 -0700 Subject: [PATCH 609/706] build/make/Makefile.in: Build doc-pdf in parallel using make --- build/make/Makefile.in | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 93a88156de2..4f235654375 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -345,13 +345,18 @@ doc-inventory--%: doc-html--%: $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS)" logs/dochtml.log -doc-html-references: $(DOC_DEPENDENCIES) +doc-inventory-references: $(DOC_DEPENDENCIES) $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) $(eval biblio = $(firstword $(DOCS))) $(eval other_docs = $(wordlist 2, 100, $(DOCS))) +$(MAKE_REC) doc-inventory--$(subst /,-,$(biblio)) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-inventory--$(subst /,-,$(doc))) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference + +doc-html-references: doc-inventory-references + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval biblio = $(firstword $(DOCS))) + $(eval other_docs = $(wordlist 2, 100, $(DOCS))) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--$(subst /,-,$(biblio)) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-html--$(subst /,-,$(doc))) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference @@ -374,8 +379,18 @@ doc-html-mathjax: # Keep target 'doc-html-jsmath' for backwards compatibility. doc-html-jsmath: doc-html-mathjax -doc-pdf: $(DOC_DEPENDENCIES) - $(AM_V_at)cd ../.. && sage-logger -p './sage --docbuild all pdf $(SAGE_DOCBUILD_OPTS)' logs/docpdf.log +# Matches doc-pdf--developer, doc-pdf--reference-manifolds etc. +doc-pdf--%: + $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-pdf--,,$@)) pdf $(SAGE_DOCBUILD_OPTS)" logs/docpdf.log + +doc-pdf: doc-inventory-references + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval biblio = $(firstword $(DOCS))) + $(eval other_docs = $(wordlist 2, 100, $(DOCS))) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--$(subst /,-,$(biblio)) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-pdf--$(subst /,-,$(doc))) + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--reference + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-pdf--$(subst /,-,$(doc))) doc-clean: doc-src-clean doc-output-clean From 6ab5677085b94f2d7453a4b619e16806cff3b233 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 12:44:58 -0700 Subject: [PATCH 610/706] RelativeInterior.is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 795ef408692..3fd2f6c6b54 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -79,7 +79,8 @@ def ambient_dim(self): sage: segment.ambient_dim() 2 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient_dim() 2 """ @@ -95,7 +96,8 @@ def dim(self): sage: segment.dim() 1 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.dim() 1 """ @@ -154,6 +156,28 @@ def closure(self): """ return self._polyhedron + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_universe() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_universe() + return False + def is_closed(self): r""" Return whether ``self`` is closed. From c085d30d1c10ceacd9980520d0f3c7e2a78d531c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:33:31 -0700 Subject: [PATCH 611/706] Polyhedron_base.interior: Handle the empty polyhedron correctly --- src/sage/geometry/polyhedron/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..50274eced76 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8425,7 +8425,15 @@ def interior(self): sage: P_lower.interior() The empty polyhedron in ZZ^2 + TESTS:: + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.interior() is Empty + True """ + if self.is_open(): + return self if not self.is_full_dimensional(): return self.parent().element_class(self.parent(), None, None) return self.relative_interior() From 686d0afbeba9f5d33131ecbe20a907c20635faa5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:39:53 -0700 Subject: [PATCH 612/706] Polyhedron_base.product: Add doctest for alias 'cartesian_product' --- src/sage/geometry/polyhedron/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 50274eced76..fe4253fe3bc 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4767,6 +4767,11 @@ def product(self, other): sage: P1 * 2.0 A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices + An alias is :meth:`cartesian_product`:: + + sage: P1.cartesian_product(P2) == P1.product(P2) + True + TESTS: Check that :trac:`15253` is fixed:: From fdcef38fda74712c715c515fb7bf715f9223e89d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 22:35:04 +0200 Subject: [PATCH 613/706] some E701, E702 --- src/sage/interacts/library.py | 48 ++++++++++++++++---------- src/sage/knots/link.py | 3 +- src/sage/rings/padics/local_generic.py | 48 +++++++++++++++++--------- src/sage/rings/padics/padic_generic.py | 44 +++++++++++++---------- 4 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 3ce3bb6b152..dd46fda92e5 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -810,7 +810,9 @@ def _bisection_method(f, a, b, maxn, eps): c = (b+a)/two if abs(f(c)) < h or round >= maxn: break - fa = f(a); fb = f(b); fc = f(c) + fa = f(a) + fb = f(b) + fc = f(c) if abs(fc) < eps: return c, intervals if fa*fc < 0: @@ -1150,7 +1152,8 @@ def parabola(a, b, c): K = solve([A*a[0]**2+B*a[0]+C==a[1], A*b[0]**2+B*b[0]+C==b[1], A*c[0]**2+B*c[0]+C==c[1]], [A, B, C], solution_dict=True)[0] f = K[A]*x**2+K[B]*x+K[C] return f - xs = []; ys = [] + xs = [] + ys = [] dx = float(interval[1]-interval[0])/n for i in range(n+1): @@ -1357,14 +1360,17 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab """ x = SR.var('x') try: - f = SR(f); g = SR(g); a = SR(a) + f = SR(f) + g = SR(g) + a = SR(a) except TypeError as msg: print(msg[-200:]) print("Unable to make sense of f,g, or a as symbolic expressions in single variable x.") return if not (isinstance(xrange, tuple) and len(xrange) == 2): xrange = (0,1) - h = 0; lbl = '' + h = 0 + lbl = '' if action == 'f': h = f lbl = 'f' @@ -1659,33 +1665,37 @@ def polar_prime_spiral( list2 = [] if not show_factors: for i in srange(start, end, include_endpoint = True): - if Integer(i).is_pseudoprime(): list.append(f(i-start+1)) #Primes list - else: list2.append(f(i-start+1)) #Composites list + if Integer(i).is_pseudoprime(): + list.append(f(i-start+1)) # Primes list + else: + list2.append(f(i-start+1)) # Composites list P = points(list) - R = points(list2, alpha = .1) #Faded Composites + R = points(list2, alpha = .1) # Faded Composites else: for i in srange(start, end, include_endpoint = True): - list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) #resizes each of the dots depending of the number of factors of each number - if Integer(i).is_pseudoprime() and highlight_primes: list2.append(f(i-start+1)) + list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number + if Integer(i).is_pseudoprime() and highlight_primes: + list2.append(f(i-start+1)) P = Graphics() for g in list: P += g - p_size = 5 #the orange dot size of the prime markers - if not highlight_primes: list2 = [(f(n-start+1))] + p_size = 5 # the orange dot size of the prime markers + if not highlight_primes: + list2 = [(f(n-start+1))] R = points(list2, hue = .1, pointsize = p_size) if n > 0: html('$n = %s$' % factor(n)) p = 1 - #The X which marks the given n + # The X which marks the given n W1 = disk((f(n-start+1)), p, (pi/6, 2*pi/6), alpha=.1) W2 = disk((f(n-start+1)), p, (4*pi/6, 5*pi/6), alpha=.1) W3 = disk((f(n-start+1)), p, (7*pi/6, 8*pi/6), alpha=.1) W4 = disk((f(n-start+1)), p, (10*pi/6, 11*pi/6), alpha=.1) Q = W1 + W2 + W3 + W4 - n = n - start +1 #offsets the n for different start values to ensure accurate plotting + n = n - start +1 # offsets the n for different start values to ensure accurate plotting if show_curves: begin_curve = 0 t = SR.var('t') @@ -1704,7 +1714,7 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness = .3) #Pink Line + (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness=.3) #Pink Line b = 1 c = c2; @@ -1712,8 +1722,10 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness = .3) #Green Line + (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness=.3) #Green Line - show(R+P+S1+S2+Q, aspect_ratio = 1, axes = False, dpi = dpi) - else: show(R+P+Q, aspect_ratio = 1, axes = False, dpi = dpi) - else: show(R+P, aspect_ratio = 1, axes = False, dpi = dpi) + show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) + else: + show(R+P+Q, aspect_ratio=1, axes=False, dpi=dpi) + else: + show(R+P, aspect_ratio=1, axes=False, dpi=dpi) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 3792ec5556a..6b5a864b0ff 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -3785,7 +3785,8 @@ def answer(L): return L chiral = True - ach = L.is_amphicheiral(); achp = L.is_amphicheiral(positive=True) + ach = L.is_amphicheiral() + achp = L.is_amphicheiral(positive=True) if ach is None and achp is None: if unique: raise NotImplementedError('this link cannot be uniquely determined (unknown chirality)%s' %non_unique_hint) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 63960d81fd6..9bbc314b3b0 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -1127,7 +1127,7 @@ def _matrix_flatten_precision(self, M): This method is useful for increasing the numerical stability. It is called by :meth:`_matrix_smith_form` - and :meth:`_matrix_determinant` + and :meth:`_matrix_determinant` Only for internal use. @@ -1151,18 +1151,21 @@ def _matrix_flatten_precision(self, M): """ parent = M.base_ring() cap = parent.precision_cap() - n = M.nrows(); m = M.ncols() + n = M.nrows() + m = M.ncols() shift_rows = n * [ ZZ(0) ] shift_cols = m * [ ZZ(0) ] for i in range(n): prec = min(M[i,j].precision_absolute() for j in range(m)) - if prec is Infinity or prec == cap: continue + if prec is Infinity or prec == cap: + continue shift_rows[i] = s = cap - prec for j in range(m): M[i,j] <<= s for j in range(m): prec = min(M[i,j].precision_absolute() for i in range(n)) - if prec is Infinity or prec == cap: continue + if prec is Infinity or prec == cap: + continue shift_cols[j] = s = cap - prec for i in range(n): M[i,j] <<= s @@ -1341,10 +1344,13 @@ def _matrix_smith_form(self, M, transformation, integral, exact): if exact: # we only care in this case allexact = allexact and Sij.precision_absolute() is infinity if v < curval: - pivi = i; pivj = j + pivi = i + pivj = j curval = v - if v == val: break - else: continue + if v == val: + break + else: + continue break val = curval @@ -1362,8 +1368,10 @@ def _matrix_smith_form(self, M, transformation, integral, exact): for i in range(i,n): for j in range(piv,m): allexact = allexact and S[i,j].precision_absolute() is infinity - if not allexact: break - else: continue + if not allexact: + break + else: + continue break if not allexact: raise PrecisionError("some elementary divisors indistinguishable from zero (try exact=False)") @@ -1549,11 +1557,11 @@ def _matrix_determinant(self, M): O(37) """ n = M.nrows() - + # For 2x2 matrices, we use the formula if n == 2: return M[0,0]*M[1,1] - M[0,1]*M[1,0] - + R = M.base_ring() track_precision = R._prec_type() in ['capped-rel','capped-abs'] @@ -1572,7 +1580,8 @@ def _matrix_determinant(self, M): for j in range(piv,n): v = S[i,j].valuation() if v < curval: - pivi = i; pivj = j + pivi = i + pivj = j curval = v if v == val: break @@ -1588,9 +1597,11 @@ def _matrix_determinant(self, M): valdet += val S.swap_rows(pivi,piv) - if pivi > piv: sign = -sign + if pivi > piv: + sign = -sign S.swap_columns(pivj,piv) - if pivj > piv: sign = -sign + if pivj > piv: + sign = -sign det *= S[piv,piv] inv = ~(S[piv,piv] >> val) @@ -1608,9 +1619,12 @@ def _matrix_determinant(self, M): for j in range(n): prec = min(prec, S[i,j].precision_absolute()) prec -= S[i,i].valuation() - if prec < relprec: relprec = prec - if prec < 0: relprec_neg += prec - if relprec_neg < 0: relprec = relprec_neg + if prec < relprec: + relprec = prec + if prec < 0: + relprec_neg += prec + if relprec_neg < 0: + relprec = relprec_neg det = (sign*det).add_bigoh(valdet+relprec) else: det = sign*det diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index ae15159be84..5c81ce18e77 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -837,7 +837,8 @@ def _test_invert(self, **options): y = ~x except (ZeroDivisionError, PrecisionError, ValueError): tester.assertFalse(x.is_unit()) - if not self.is_fixed_mod(): tester.assertTrue(x.is_zero()) + if not self.is_fixed_mod(): + tester.assertTrue(x.is_zero()) else: try: e = y * x @@ -904,8 +905,10 @@ def _test_div(self, **options): try: z = x / y except (ZeroDivisionError, PrecisionError, ValueError): - if self.is_fixed_mod(): tester.assertFalse(y.is_unit()) - else: tester.assertTrue(y.is_zero()) + if self.is_fixed_mod(): + tester.assertFalse(y.is_unit()) + else: + tester.assertTrue(y.is_zero()) else: try: xx = z*y @@ -1011,7 +1014,8 @@ def _test_log(self, **options): """ tester = self._tester(**options) for x in tester.some_elements(): - if x.is_zero(): continue + if x.is_zero(): + continue try: l = x.log(p_branch=0) tester.assertIs(l.parent(), self) @@ -1026,14 +1030,16 @@ def _test_log(self, **options): if self.is_capped_absolute() or self.is_capped_relative(): # In the fixed modulus setting, rounding errors may occur for x, y, b in tester.some_elements(repeat=3): - if (x*y).is_zero(): continue + if (x*y).is_zero(): + continue r1 = x.log(pi_branch=b) + y.log(pi_branch=b) r2 = (x*y).log(pi_branch=b) tester.assertEqual(r1, r2) p = self.prime() for x in tester.some_elements(): - if x.is_zero(): continue + if x.is_zero(): + continue if p == 2: a = 4 * x.unit_part() else: @@ -1416,7 +1422,7 @@ def roots_of_unity(self, n=None): 3 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + 2*5^6 + 5^7 + 4*5^8 + 5^9 + O(5^10), 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + O(5^10)] - In general, there might be more roots of unity (it happens when the ring has non + In general, there might be more roots of unity (it happens when the ring has non trivial ``p``-th roots of unity):: sage: W. = Zq(3^2, 2) @@ -1426,7 +1432,7 @@ def roots_of_unity(self, n=None): sage: roots = R.roots_of_unity(); roots [1 + O(pi^4), a + 2*a*pi + 2*a*pi^2 + a*pi^3 + O(pi^4), - ... + ... 1 + pi + O(pi^4), a + a*pi^2 + 2*a*pi^3 + O(pi^4), ... @@ -1455,23 +1461,23 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur - ``ring`` -- a ring into which this ring coerces - - ``multiplicities`` -- a boolean (default: ``True``); + - ``multiplicities`` -- a boolean (default: ``True``); whether we have to return the multiplicities of each root or not - - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: - ``None``); Sage provides an implementation for any extension of - `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented - in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, + - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: + ``None``); Sage provides an implementation for any extension of + `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented + in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, ``"sage"`` otherwise. - ``secure`` -- a boolean (default: ``False``) NOTE: - When ``secure`` is ``True``, this method raises an error when - the precision on the input polynomial is not enough to determine - the number of roots in the ground field. This happens when two + When ``secure`` is ``True``, this method raises an error when + the precision on the input polynomial is not enough to determine + the number of roots in the ground field. This happens when two roots cannot be separated. A typical example is the polynomial @@ -1479,14 +1485,14 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur (1 + O(p^10))*X^2 + O(p^10)*X + O(p^10) - Indeed its discriminant might be any `p`-adic integer divisible - by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well + Indeed its discriminant might be any `p`-adic integer divisible + by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well zero, a square and a non-square. In the first case, the polynomial has one double root; in the second case, it has two roots; in the third case, it has no root in `\QQ_p`. - When ``secure`` is ``False``, this method assumes that two + When ``secure`` is ``False``, this method assumes that two inseparable roots actually collapse. In the above example, it then answers that the given polynomial has a double root `O(p^5)`. From 8cf18ccb90bdf9c90ef4fc4a7eeccb4abfd8702f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 22:43:36 +0200 Subject: [PATCH 614/706] some E701, E702 --- src/sage/rings/padics/factory.py | 6 +- src/sage/rings/padics/generic_nodes.py | 3 +- src/sage/rings/padics/lattice_precision.py | 137 ++++++++++-------- .../polynomial_padic_capped_relative_dense.py | 9 +- .../polynomial/polynomial_element_generic.py | 9 +- .../rings/polynomial/polynomial_fateman.py | 3 +- 6 files changed, 97 insertions(+), 70 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 38546c6d262..90c54a2524a 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1320,8 +1320,10 @@ def Qq(q, prec = None, type = 'capped-rel', modulus = None, names=None, q = tuple(q) p,k = q - if not isinstance(p, Integer): p = Integer(p) - if not isinstance(k, Integer): k = Integer(k) + if not isinstance(p, Integer): + p = Integer(p) + if not isinstance(k, Integer): + k = Integer(k) if check: if not p.is_prime() or k <=0: diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 6a1da3b4747..640eef1ca04 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -682,7 +682,8 @@ def convert_multiple(self, *elts): except PrecisionError: raise NotImplementedError("multiple conversion of a set of variables for which the module precision is not a lattice is not implemented yet") for j in range(len(L)): - x = L[j]; dx = [ ] + x = L[j] + dx = [ ] for i in range(j): dx.append([L[i], lattice[i,j]]) prec = lattice[j,j].valuation(p) diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index b2901ea7008..ade4a6950d0 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -179,7 +179,7 @@ def reduce(self, prec): exp -= valdenom if prec > exp: modulo = self.p ** (prec - exp) - # probably we should use Newton iteration instead + # probably we should use Newton iteration instead # (but it is actually slower for now - Python implementation) _, inv, _ = denom.xgcd(modulo) x = (num*inv) % modulo @@ -219,7 +219,7 @@ def reduce_relative(self, prec): def normalize(self): r""" - Normalize this element, i.e. write it as ``p^v * u`` where + Normalize this element, i.e. write it as ``p^v * u`` where ``u`` is coprime to `p`. TESTS:: @@ -311,8 +311,10 @@ def __add__(self, other): p = self.p sexp = self.exponent oexp = other.exponent - if sexp is Infinity: return other - if oexp is Infinity: return self + if sexp is Infinity: + return other + if oexp is Infinity: + return self if self._valuation is None or other._valuation is None: val = None elif self._valuation < other._valuation: @@ -560,7 +562,7 @@ def value(self): def list(self, prec): r""" - Return the list of the digits of this element (written in radix + Return the list of the digits of this element (written in radix `p`) up to position ``prec``. The first zeros are omitted. @@ -747,7 +749,7 @@ def prime(self): def _index(self, ref): r""" - Return the index of the column in the precision matrix that + Return the index of the column in the precision matrix that corresponds to ``ref``. Only for internal use. @@ -877,15 +879,15 @@ def _record_collected_element(self, ref): @abstract_method def del_elements(self, threshold=None): r""" - Delete (or mark for future deletion) the columns of precision - matrix corresponding to elements that were collected by the + Delete (or mark for future deletion) the columns of precision + matrix corresponding to elements that were collected by the garbage collector. INPUT: - ``threshold`` -- an integer or ``None`` (default: ``None``): a column whose distance to the right is greater than the - threshold is not erased but marked for deletion; + threshold is not erased but marked for deletion; if ``None``, always erase (never mark for deletion). EXAMPLES:: @@ -988,7 +990,7 @@ def precision_lattice(self, elements=None): def diffused_digits(self, elements=None): r""" - Return the number of diffused digits of precision within a + Return the number of diffused digits of precision within a subset of elements. A diffused digit of precision is a known digit which is not @@ -997,7 +999,7 @@ def diffused_digits(self, elements=None): The number of diffused digits of precision quantifies the quality of the approximation of the lattice precision by a - jagged precision (that is a precision which is split over + jagged precision (that is a precision which is split over all variables). We refer to [CRV2018]_ for a detail exposition of the notion of @@ -1110,7 +1112,7 @@ def history_enable(self): r""" Enable history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1126,7 +1128,7 @@ def history_enable(self): sage: prec.history_enable() sage: print(prec.history()) Timings - --- + --- .. SEEALSO:: @@ -1140,7 +1142,7 @@ def history_disable(self): r""" Disable history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1156,7 +1158,7 @@ def history_disable(self): sage: prec.history_enable() sage: print(prec.history()) Timings - --- + --- sage: prec.history_disable() sage: print(prec.history()) @@ -1174,7 +1176,7 @@ def history_clear(self): r""" Clear history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1215,18 +1217,18 @@ def history_clear(self): def _format_history(self, time, status, timings): r""" Return a formatted output for the history. - + This is a helper function for the method :meth:`history`. - + TESTS:: - + sage: R = ZpLC(2, label='history_en') sage: prec = R.precision() sage: prec._format_history(1.23456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) '1.234568s oooooo~oo' sage: prec._format_history(1.23456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], false) 'oooooo~oo' - + sage: prec._format_history(12.3456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) ' >= 10s oooooo~oo' sage: prec._format_history(10^(-10), ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) @@ -1253,12 +1255,12 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type r""" Show history. - The history records creations and deletions of elements attached + The history records creations and deletions of elements attached to this precision lattice, together with many timings. INPUT: - - ``compact`` -- a boolean (default: ``True``); if true, all + - ``compact`` -- a boolean (default: ``True``); if true, all consecutive operations of the same type appear on a single row - ``separate_reduce`` -- a boolean (default: ``False``); specify @@ -1288,7 +1290,7 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type At the beginning, the history is of course empty:: sage: print(prec.history()) - Timings + Timings --- Now we start creating and deleting elements:: @@ -1347,8 +1349,8 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type Timings for automatic reduction do not appear because they are included in the timings for deletion. - The symbol ``R`` is used to symbolize a column which is under full - Hermite reduction. Note that full Hermite reduction are never performed + The symbol ``R`` is used to symbolize a column which is under full + Hermite reduction. Note that full Hermite reduction are never performed automatically but needs to be called by hand:: sage: prec.reduce() @@ -1442,7 +1444,8 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type if separate_reduce: if status: hist.append(self._format_history(total_time, status, timings)) - if event == 'partial reduce': code = 'r' + if event == 'partial reduce': + code = 'r' else: code = 'R' status_red = status[:index] + (len(status) - index) * [code] hist.append(self._format_history(tme, status_red, timings)) @@ -1474,13 +1477,13 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type def timings(self, action=None): r""" - Return cumulated timings (grouped by actions) since the last + Return cumulated timings (grouped by actions) since the last time history has been cleared. INPUT: - ``action`` -- ``None`` (the default), ``add``, ``mark``, ``del``, - ``partial reduce`` or ``full reduce``; if not None, return the + ``partial reduce`` or ``full reduce``; if not None, return the cumulated timing corresponding to this action; otherwise, return a dictionary @@ -1584,20 +1587,20 @@ def __reduce__(self): def _index(self, ref): r""" Return the index of the element whose reference is ``ref``. - + TESTS:: - + sage: from sage.rings.padics.lattice_precision import pAdicLatticeElementWeakProxy sage: R = ZpLC(2, label="index") sage: prec = R.precision() sage: x = R(1, 10) sage: y = R(1, 5) - + sage: prec._index(pAdicLatticeElementWeakProxy(x)) 0 sage: prec._index(pAdicLatticeElementWeakProxy(y)) 1 - + sage: del x sage: prec.del_elements() sage: prec._index(pAdicLatticeElementWeakProxy(y)) @@ -1636,8 +1639,8 @@ def reduce(self, index=0, partial=False): NOTE: - The partial reduction has cost `O(m^2)` where `m` is the number of - rows that need to be reduced (that is the difference between the + The partial reduction has cost `O(m^2)` where `m` is the number of + rows that need to be reduced (that is the difference between the total number of rows and ``index``). The full Hermite reduction has cost `O(m^3)`. @@ -1686,7 +1689,8 @@ def reduce(self, index=0, partial=False): for i in range(index, j): reduced = col[i].reduce(valpivot) scalar = (col[i] - reduced) >> valpivot - if scalar.is_zero(): continue + if scalar.is_zero(): + continue col[i] = reduced col[i].normalize() for j2 in range(j+1, n): @@ -1711,18 +1715,18 @@ def _new_element(self, x, dx, bigoh, dx_mode='linear_combination', capped=False) - ``dx`` -- a dictionary representing the differential of ``x`` - - ``bigoh`` -- an integer or ``None`` (default: ``None``): the + - ``bigoh`` -- an integer or ``None`` (default: ``None``): the bigoh to be added to the precision of ``x``; if ``None``, the default cap is used. - ``dx_mode`` -- a string, either ``linear_combination`` (the default) or ``values`` - - ``capped`` -- a boolean, whether this element has been capped + - ``capped`` -- a boolean, whether this element has been capped according to the parent's cap - If ``dx_mode`` is ``linear_combination``, the dictionary ``dx`` - encodes the expression of the differential of ``x``. + If ``dx_mode`` is ``linear_combination``, the dictionary ``dx`` + encodes the expression of the differential of ``x``. For example, if ``x`` was defined as ``x = y*z`` then: .. MATH:: @@ -1838,8 +1842,10 @@ def del_elements(self, threshold=None): self._marked_for_deletion.sort(reverse=True) count = 0 for index in self._marked_for_deletion: - if threshold is not None and index < n - threshold: break - n -= 1; count += 1 + if threshold is not None and index < n - threshold: + break + n -= 1 + count += 1 tme = walltime() ref = self._elements[index] @@ -1856,7 +1862,7 @@ def del_elements(self, threshold=None): self._capped[ref], capped = capped, capped or self._capped[ref] else: capped = capped or self._capped[ref] - + d, u, v = col[i].xgcd(col[i+1]) up, vp = col[i+1]/d, col[i]/d col[i] = d @@ -1894,7 +1900,7 @@ def _lift_to_precision(self, x, prec): p^{prec} \Z_p dx \oplus \bigoplus_{y \neq x} \Q_p dy - This function may change at the same time the precision of + This function may change at the same time the precision of other elements having the same parent. .. NOTE:: @@ -1937,7 +1943,8 @@ def _lift_to_precision(self, x, prec): rows = rows_by_val[v] piv = max(rows) for i in rows: - if i == piv: continue + if i == piv: + continue # We clear the entry on the i-th row scalar = (col[i]/col[piv]).reduce(prec-v) for j in range(piv,n): @@ -2116,7 +2123,8 @@ def precision_lattice(self, elements=None): col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: val = valcol + if valcol < val: + val = valcol row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix @@ -2153,7 +2161,7 @@ def __init__(self, p, label, prec): NOTE: - The precision module is automatically initialized at the + The precision module is automatically initialized at the creation of the parent. TESTS:: @@ -2191,7 +2199,7 @@ def internal_prec(self): internally. It is slightly greater than the actual precision and increases - a bit (at a logarithmic rate) when new elements are created + a bit (at a logarithmic rate) when new elements are created and/or computed. EXAMPLES:: @@ -2237,7 +2245,7 @@ def dimension(self): sage: prec.dimension() 2 - Of course, it may also decrease when a sufficient + Of course, it may also decrease when a sufficient number of variables are collected:: sage: del x, y, u @@ -2293,7 +2301,7 @@ def _new_element(self, x, dx, bigoh, dx_mode='linear_combination'): - ``dx`` -- a dictionary representing the differential of ``x`` - - ``bigoh`` -- an integer or ``None`` (default: ``None``): the + - ``bigoh`` -- an integer or ``None`` (default: ``None``): the bigoh to be added to the precision of ``x``; if ``None``, the default cap is used. @@ -2387,7 +2395,7 @@ def del_elements(self, threshold=None): INPUT: - ``threshold`` -- an integer or ``None`` (default: ``None``): - a non-pivot column whose distance to the right is greater than + a non-pivot column whose distance to the right is greater than the threshold is not erased but only marked for future deletion EXAMPLES:: @@ -2447,8 +2455,10 @@ def del_elements(self, threshold=None): self._marked_for_deletion.sort(reverse=True) count = 0 for index in self._marked_for_deletion: - if threshold is not None and index < n - threshold: break - n -= 1; count += 1 + if threshold is not None and index < n - threshold: + break + n -= 1 + count += 1 tme = walltime() @@ -2462,7 +2472,7 @@ def del_elements(self, threshold=None): end = n while i < n: col = self._matrix[self._elements[i]] - if len(col) > length: + if len(col) > length: end = i break v = col[-1].valuation() @@ -2478,7 +2488,8 @@ def del_elements(self, threshold=None): # No pivot was found. We re-echelonize for i in range(start, end): del self._matrix[self._elements[i]][-1] - if end == n: break + if end == n: + break # col is the column of index "end" # its size is (length + 1) d, u, v = col[length-1].xgcd(col[length]) @@ -2488,8 +2499,11 @@ def del_elements(self, threshold=None): start = end + 1 for j in range(start, n): col = self._matrix[self._elements[j]] - a1 = u*col[length-1]; a2 = v*col[length]; a = a1 + a2 - b1 = up*col[length-1]; b2 = vp*col[length]; b = b1 + b2 + a1 = u*col[length-1] + a2 = v*col[length] + a = a1 + a2 + b1 = up*col[length-1] + b2 = vp*col[length]; b = b1 + b2 if a.valuation() > min(a1.valuation(), a2.valuation()) + self._zero_cap: col[length-1] = self._approx_zero else: @@ -2525,7 +2539,7 @@ def _lift_to_precision(self, x, prec): p^{prec} \Z_p dx \oplus \bigoplus_{y \neq x} \Q_p dy - This function may change at the same time the precision of + This function may change at the same time the precision of other elements having the same parent. .. NOTE:: @@ -2569,7 +2583,8 @@ def _lift_to_precision(self, x, prec): rows = rows_by_val[v] piv = max(rows) for i in rows: - if i == piv: continue + if i == piv: + continue # We clear the entry on the i-th row scalar = (col[i]/col[piv]).reduce(prec-v) for j in range(n): @@ -2696,12 +2711,14 @@ def precision_lattice(self, elements=None): else: elements = list_of_padics(elements) n = len(self._elements) - rows = [ ]; val = 0 + rows = [ ] + val = 0 for ref in elements: col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: val = valcol + if valcol < val: + val = valcol row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 760fde7c563..13d09467427 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -1245,13 +1245,16 @@ def is_eisenstein(self, secure=False): valaddeds = self._valaddeds relprecs = self._relprecs if relprecs[0] <= compval: # not enough precision - if valaddeds[0] < relprecs[0]: return False + if valaddeds[0] < relprecs[0]: + return False raise PrecisionError("Not enough precision on the constant coefficient") else: - if valaddeds[0] != compval: return False + if valaddeds[0] != compval: + return False for i in range(1, deg): if relprecs[i] < compval: # not enough precision - if valaddeds[i] < relprecs[i]: return False + if valaddeds[i] < relprecs[i]: + return False if secure: if i == 1: raise PrecisionError("Not enough precision on the coefficient of %s" % self.variable_name()) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 67de318829e..d9578903c64 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -1199,7 +1199,8 @@ def hensel_lift(self, a): b = ~dera while(True): na = a - selfa * b - if na == a: return a + if na == a: + return a a = na selfa = self(a) dera = der(a) @@ -1477,7 +1478,8 @@ def _roots(self, secure, minval, hint): continue if hint is not None and slope == minval: rootsbar = hint - if not rootsbar: continue + if not rootsbar: + continue if i < len(vertices) - 1: F = P._factor_of_degree(deg_right - deg) P = P // F @@ -1491,7 +1493,8 @@ def _roots(self, secure, minval, hint): if hint is None or slope != minval: Fbar = Pk([ F[j] >> (val - j*slope) for j in range(F.degree()+1) ]) rootsbar = [ r for (r, _) in Fbar.roots() ] - if not rootsbar: continue + if not rootsbar: + continue rbar = rootsbar.pop() shift = K(rbar).lift_to_precision() << slope # probably we should choose a better lift roots += [(r+shift, m) for (r, m) in F(x+shift)._roots(secure, slope, [r-rbar for r in rootsbar])] # recursive call diff --git a/src/sage/rings/polynomial/polynomial_fateman.py b/src/sage/rings/polynomial/polynomial_fateman.py index 557417d4470..580cee21d63 100644 --- a/src/sage/rings/polynomial/polynomial_fateman.py +++ b/src/sage/rings/polynomial/polynomial_fateman.py @@ -88,7 +88,8 @@ def _mul_fateman_mul(f,g): n_f = z_poly_f(1< Date: Sat, 12 Jun 2021 22:44:56 +0200 Subject: [PATCH 615/706] W605 --- src/sage/rings/padics/padic_base_leaves.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index cbf71588bfd..4d4f44735d3 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1101,7 +1101,7 @@ def random_element(self, prec=None, integral=False): ######### class pAdicRingRelaxed(pAdicRelaxedGeneric, pAdicRingBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\ZZ_p`. INPUT: @@ -1136,7 +1136,7 @@ def __init__(self, p, prec, print_mode, names): self._element_class_prefix = "pAdicRelaxedElement_" class pAdicFieldRelaxed(pAdicRelaxedGeneric, pAdicFieldBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\QQ_p`. INPUT: From 94a60f9ee19e53e3cdadd5c46b325b4dc390a039 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:05:55 +0200 Subject: [PATCH 616/706] E701, E702 for algebras, crypto, quadratic form, structure --- src/sage/algebras/free_algebra_quotient_element.py | 6 ++++-- .../algebras/lie_algebras/nilpotent_lie_algebra.py | 9 ++++++--- src/sage/crypto/lattice.py | 9 ++++++--- src/sage/crypto/mq/sr.py | 3 ++- src/sage/quadratic_forms/binary_qf.py | 3 ++- src/sage/structure/list_clone_timings.py | 12 ++++++++---- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sage/algebras/free_algebra_quotient_element.py b/src/sage/algebras/free_algebra_quotient_element.py index 9e291010fa2..0b364492741 100644 --- a/src/sage/algebras/free_algebra_quotient_element.py +++ b/src/sage/algebras/free_algebra_quotient_element.py @@ -244,7 +244,8 @@ def monomial_product(X,w,m): mats = X._FreeAlgebraQuotient__matrix_action for (j,k) in m._element_list: M = mats[int(j)] - for l in range(k): w *= M + for l in range(k): + w *= M return w u = self.__vector.__copy__() v = y.__vector @@ -252,7 +253,8 @@ def monomial_product(X,w,m): B = A.monomial_basis() for i in range(A.dimension()): c = v[i] - if c != 0: z.__vector += monomial_product(A,c*u,B[i]) + if c != 0: + z.__vector += monomial_product(A,c*u,B[i]) return z def _rmul_(self, c): diff --git a/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py b/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py index d2919917d33..54a0fb97d2a 100644 --- a/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py @@ -113,10 +113,13 @@ def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, # extract names from structural coefficients names = [] for (X, Y), d in s_coeff.items(): - if X not in names: names.append(X) - if Y not in names: names.append(Y) + if X not in names: + names.append(X) + if Y not in names: + names.append(Y) for k in d: - if k not in names: names.append(k) + if k not in names: + names.append(k) from sage.structure.indexed_generators import standardize_names_index_set names, index_set = standardize_names_index_set(names, index_set) diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index 84da16e3401..b593bac9b03 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -222,7 +222,8 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, set_random_seed(seed) if type == 'random': - if n != 1: raise ValueError('random bases require n = 1') + if n != 1: + raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) @@ -272,8 +273,10 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): - if abs(a-q) < abs(a): return a-q - else: return a + if abs(a-q) < abs(a): + return a-q + else: + return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index ab047b5f6cb..448583ea3a2 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -2650,7 +2650,8 @@ def phi(self, l, diffusion_matrix=False): return tuple(ret) elif is_Matrix(l): return Matrix(GF(2), l.ncols(), l.nrows()*self.e, ret).transpose() - else: raise TypeError + else: + raise TypeError def antiphi(self, l): r""" diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index a853f46651d..4a143e41487 100755 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -1664,7 +1664,8 @@ def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True): a4 = 4*a s = D + a*a4 w = 1+(s-1).isqrt() if s > 0 else 0 - if w%2 != D%2: w += 1 + if w%2 != D%2: + w += 1 for b in xsrange(w, a+1, 2): t = b*b-D if t % a4 == 0: diff --git a/src/sage/structure/list_clone_timings.py b/src/sage/structure/list_clone_timings.py index 73f5da69f24..efe5d20efc7 100644 --- a/src/sage/structure/list_clone_timings.py +++ b/src/sage/structure/list_clone_timings.py @@ -128,7 +128,8 @@ def add1_internal(bla): """ blo = bla.__copy__() lst = blo._get_list() - for i in range(len(blo)): lst[i] += 1 + for i in range(len(blo)): + lst[i] += 1 blo.set_immutable() blo.check() return blo @@ -142,7 +143,8 @@ def add1_immutable(bla): [2, 5, 6] """ lbla = bla[:] - for i in range(len(lbla)): lbla[i] += 1 + for i in range(len(lbla)): + lbla[i] += 1 return bla.__class__(bla.parent(), lbla) def add1_mutable(bla): @@ -154,7 +156,8 @@ def add1_mutable(bla): [2, 5, 6] """ blo = bla.__copy__() - for i in range(len(blo)): blo[i] += 1 + for i in range(len(blo)): + blo[i] += 1 blo.set_immutable() blo.check() return blo @@ -168,5 +171,6 @@ def add1_with(bla): [2, 5, 6] """ with bla.clone() as blo: - for i in range(len(blo)): blo[i] += 1 + for i in range(len(blo)): + blo[i] += 1 return blo From 1d8a6d68eaef586246625afc9512f483c03d7b60 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:19:56 +0200 Subject: [PATCH 617/706] E701, E702 for interfaces --- src/sage/interfaces/expect.py | 3 ++- src/sage/interfaces/maxima.py | 3 ++- src/sage/interfaces/qepcad.py | 35 ++++++++++++++++++++++----------- src/sage/interfaces/singular.py | 3 ++- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index bfd2817cc53..055b7c4b79d 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -1453,7 +1453,8 @@ class ExpectElement(InterfaceElement): def __init__(self, parent, value, is_name=False, name=None): RingElement.__init__(self, parent) self._create = value - if parent is None: return # means "invalid element" + if parent is None: + return # means "invalid element" # idea: Joe Wetherell -- try to find out if the output # is too long and if so get it using file, otherwise # don't. diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index a3192fffa3f..db362ae0997 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -839,7 +839,8 @@ def _synchronize(self): 4 """ marker = '__SAGE_SYNCHRO_MARKER_' - if self._expect is None: return + if self._expect is None: + return r = randrange(2147483647) s = marker + str(r+1) diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 4d61ea1306e..222be41d285 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -1214,7 +1214,8 @@ def make_cells(self, text): for line in lines: if 'Information about the cell' in line: in_cell = True - if in_cell: cell_lines.append(line) + if in_cell: + cell_lines.append(line) if line == '----------------------------------------------------': cells.append(QepcadCell(self, cell_lines)) cell_lines = [] @@ -1285,10 +1286,12 @@ def _eval_line(self, cmd, restart_if_needed=False): result = self._qex._eval_line(cmd + ' &') nl = result.find('\n') - if nl < 0: nl = len(result) + if nl < 0: + nl = len(result) amp = result.find('&', 0, nl) - if amp > 0: result = result[amp+1:] + if amp > 0: + result = result[amp+1:] result = result.strip() @@ -1802,15 +1805,23 @@ def _normalize_op(self, op): '=' """ import operator - if op == operator.eq: return '=' - if op == operator.ne: return '/=' - if op == operator.lt: return '<' - if op == operator.gt: return '>' - if op == operator.le: return '<=' - if op == operator.ge: return '>=' - - if op == '==': return '=' - if op == '!=': return '/=' + if op == operator.eq: + return '=' + if op == operator.ne: + return '/=' + if op == operator.lt: + return '<' + if op == operator.gt: + return '>' + if op == operator.le: + return '<=' + if op == operator.ge: + return '>=' + + if op == '==': + return '=' + if op == '!=': + return '/=' return op diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index bc29515ccba..89b687d3182 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1365,7 +1365,8 @@ def __init__(self, parent, type, value, is_name=False): 2 """ RingElement.__init__(self, parent) - if parent is None: return + if parent is None: + return if not is_name: try: self._name = parent._create(value, type) From a93d8f414ba2924a2ba98c3be676fad7dcd77699 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:22:08 +0200 Subject: [PATCH 618/706] E701, E702 for groups --- src/sage/groups/perm_gps/cubegroup.py | 72 ++++++++++++++++++--------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/src/sage/groups/perm_gps/cubegroup.py b/src/sage/groups/perm_gps/cubegroup.py index 8be5b5611b3..4ace0ce13e3 100644 --- a/src/sage/groups/perm_gps/cubegroup.py +++ b/src/sage/groups/perm_gps/cubegroup.py @@ -429,30 +429,54 @@ def cubie_colors(label, state0): # colors of the cubies on the F,U, R faces clr_any = named_colors['white'] state = inv_list(state0) - if label == 1: return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb, - if label == 2: return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub, - if label == 3: return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr, - if label == 4: return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul, - if label == 5: return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur, - if label == 6: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl, - if label == 7: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf, - if label == 8: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # urf, - if label == 17: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu - if label == 18: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu - if label == 19: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # fur - if label == 20: return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl - if label == 21: return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr - if label == 22: return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl - if label == 23: return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd - if label == 24: return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd - if label == 25: return [named_colors[color_of_square(state[19-1])],named_colors[color_of_square(state[8-1])],named_colors[color_of_square(state[25-1])]] #rfu, - if label == 26: return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru, - if label == 27: return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub, - if label == 28: return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf, - if label == 29: return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb, - if label == 30: return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf, - if label == 31: return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd, - if label == 32: return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd, + if label == 1: + return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb, + if label == 2: + return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub, + if label == 3: + return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr, + if label == 4: + return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul, + if label == 5: + return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur, + if label == 6: + return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl, + if label == 7: + return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf, + if label == 8: + return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # urf, + if label == 17: + return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu + if label == 18: + return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu + if label == 19: + return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # fur + if label == 20: + return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl + if label == 21: + return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr + if label == 22: + return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl + if label == 23: + return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd + if label == 24: + return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd + if label == 25: + return [named_colors[color_of_square(state[19-1])],named_colors[color_of_square(state[8-1])],named_colors[color_of_square(state[25-1])]] #rfu, + if label == 26: + return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru, + if label == 27: + return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub, + if label == 28: + return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf, + if label == 29: + return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb, + if label == 30: + return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf, + if label == 31: + return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd, + if label == 32: + return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd, def plot3d_cubie(cnt, clrs): From 3d0726c72e69935a3312c94631c818111977f547 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:26:09 +0200 Subject: [PATCH 619/706] E701, E702 for databases --- src/sage/databases/cremona.py | 24 ++++++++++++++++-------- src/sage/databases/sql_db.py | 21 ++++++++++++++------- src/sage/databases/symbolic_data.py | 3 ++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 680543209de..e388d0558b0 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -1437,7 +1437,8 @@ def _init_allcurves(self, ftpdata, largest_conductor=0): curve_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, ainvs, r, tor = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break cls = N+iso cur = cls+num if num == "1": @@ -1452,7 +1453,8 @@ def _init_allcurves(self, ftpdata, largest_conductor=0): print("Committing...") print("num_iso_classes =", num_iso_classes) self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break return num_curves, num_iso_classes @@ -1586,13 +1588,15 @@ def _init_degphi(self, ftpdata, largest_conductor=0): class_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, degree, primes, curve = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break class_data.append((degree,N+iso)) con.executemany('UPDATE t_class SET deg=? WHERE class=?', class_data) print("Committing...") self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break def _init_allbsd(self, ftpdata, largest_conductor=0): """ @@ -1620,7 +1624,8 @@ def _init_allbsd(self, ftpdata, largest_conductor=0): class_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, eqn, rank, tor, cp, om, L, reg, sha = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break cls = N+iso if num == "1": class_data.append((L,cls)) @@ -1630,7 +1635,8 @@ def _init_allbsd(self, ftpdata, largest_conductor=0): + "curve=?", curve_data) print("Committing...") self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break def _init_allgens(self, ftpdata, largest_conductor=0): """ @@ -1657,13 +1663,15 @@ def _init_allgens(self, ftpdata, largest_conductor=0): curve_data = [] for L in open(ftpdata + "/" + F).readlines(): v = L.split() - if largest_conductor and int(v[0]) > largest_conductor: break + if largest_conductor and int(v[0]) > largest_conductor: + break gens = '['+','.join(v[6:6+int(v[4])]).replace(':',',')+']' curve_data.append((gens,''.join(v[:3]))) con.executemany("UPDATE t_curve SET gens=? WHERE curve=?", curve_data) print("Committing...") - if largest_conductor and int(v[0]) > largest_conductor: break + if largest_conductor and int(v[0]) > largest_conductor: + break _db = None diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 7567ada4c2c..6189e4d3d91 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -672,7 +672,8 @@ def show(self, **kwds): C^ [2, 2, 3, 3] C~ [3, 3, 3, 3] """ - if not self.__query_string__: return self.__database__.show() + if not self.__query_string__: + return self.__database__.show() try: cur = self.__database__.__connection__.cursor() @@ -761,13 +762,16 @@ def intersect(self, other, join_table=None, join_dict=None, \ if not self.__query_string__: self.__query_string__ = other.__query_string__ self.__param_tuple__ = other.__param_tuple__ - elif not other.__query_string__: return + elif not other.__query_string__: + return else: self._merge_queries(other, self, join_table, join_dict, 'AND') else: from copy import copy - if not self.__query_string__: return copy(other) - if not other.__query_string__: return copy(self) + if not self.__query_string__: + return copy(other) + if not other.__query_string__: + return copy(self) return self._merge_queries(other, copy(self), join_table, \ join_dict, 'AND') @@ -882,8 +886,10 @@ def union(self, other, join_table=None, join_dict=None, in_place=False): self._merge_queries(other, self, join_table, join_dict, 'OR') else: from copy import copy - if not self.__query_string__: return copy(self) - if not other.__query_string__: return copy(other) + if not self.__query_string__: + return copy(self) + if not other.__query_string__: + return copy(other) return self._merge_queries(other, copy(self), join_table, \ join_dict, 'OR') @@ -1637,7 +1643,8 @@ def _rebuild_table(self, table_name, col_name=None, default=''): self.__skeleton__[table_name] if \ self.__skeleton__[table_name][col]['index'] and not \ self.__skeleton__[table_name][col]['primary_key']]) - if index_statement: self.__connection__.executescript(index_statement) + if index_statement: + self.__connection__.executescript(index_statement) # Now we can plop our data into the *new* table: self.__connection__.executescript(""" diff --git a/src/sage/databases/symbolic_data.py b/src/sage/databases/symbolic_data.py index 7f636c498c6..ccd7fb3eadc 100644 --- a/src/sage/databases/symbolic_data.py +++ b/src/sage/databases/symbolic_data.py @@ -201,7 +201,8 @@ def trait_names(self): 'Curves__curve10_20', 'Curves__curve10_30'] """ - if hasattr(self,"__ideals"): return self.__ideals + if hasattr(self,"__ideals"): + return self.__ideals try: __ideals = [s.replace('.xml','') for s in os.listdir(self.__intpath)] __ideals += [s.replace('.xml','') for s in os.listdir(self.__genpath)] From 501d3fa85311bdaf07f5f77a776d6869ce789d98 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sat, 12 Jun 2021 15:56:02 -0700 Subject: [PATCH 620/706] trac 31948: no longer use sphinx.ext.graphviz or sphinx.ext.inheritance_diagram --- src/sage/docs/conf.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index d249f2cdafe..9280ad63cb0 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -20,8 +20,6 @@ extensions = ['sage_docbuild.ext.inventory_builder', 'sage_docbuild.ext.multidocs', 'sage_docbuild.ext.sage_autodoc', - 'sphinx.ext.graphviz', - 'sphinx.ext.inheritance_diagram', 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'IPython.sphinxext.ipython_directive', @@ -157,12 +155,6 @@ def sphinx_plot(graphics, **kwds): highlighting.lexers['ipython'] = IPyLexer() highlight_language = 'ipycon' -# GraphViz includes dot, neato, twopi, circo, fdp. -graphviz_dot = 'dot' -inheritance_graph_attrs = { 'rankdir' : 'BT' } -inheritance_node_attrs = { 'height' : 0.5, 'fontsize' : 12, 'shape' : 'oval' } -inheritance_edge_attrs = {} - # Extension configuration # ----------------------- From 1af0980d128b6685f6e6c0447b62b799842cb51c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 13 Jun 2021 08:07:27 +0200 Subject: [PATCH 621/706] some fixes in the lines touched anyway --- src/sage/interacts/library.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index dd46fda92e5..13c9e6d73ff 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -1670,7 +1670,7 @@ def polar_prime_spiral( else: list2.append(f(i-start+1)) # Composites list P = points(list) - R = points(list2, alpha = .1) # Faded Composites + R = points(list2, alpha=.1) # Faded Composites else: for i in srange(start, end, include_endpoint = True): list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number @@ -1714,7 +1714,8 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness=.3) #Pink Line + (begin_curve, ceil(sqrt(end-start))), + color=hue(0.8), thickness=.3) # Pink Line b = 1 c = c2; @@ -1722,7 +1723,8 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness=.3) #Green Line + (begin_curve, ceil(sqrt(end-start))), + color=hue(0.6), thickness=.3) # Green Line show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) else: From 6f60fd9f66622f2d6878de9ccad4752e107fe178 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 13 Jun 2021 14:01:35 -0700 Subject: [PATCH 622/706] trac 31948: deprecate "sage --docbuild all ..." --- src/sage_docbuild/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index 9479f67cc35..2e31a1f0a11 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -1274,6 +1274,9 @@ def get_builder(name): documentation. """ if name == 'all': + from sage.misc.superseded import deprecation + deprecation(31948, 'avoid using "sage --docbuild all html" and "sage --docbuild all pdf"; ' + 'use "make doc" and "make doc-pdf" instead, if available.') return AllBuilder() elif name.endswith('reference'): return ReferenceBuilder(name) @@ -1376,9 +1379,7 @@ def help_documents(s=""): documentation builder. """ docs = get_documents() - s += "DOCUMENTs:\n" - s += format_columns(docs + ['all (!)']) - s += "(!) Builds everything.\n\n" + s += "DOCUMENTs:\n\n" if 'reference' in docs: s+= "Other valid document names take the form 'reference/DIR', where\n" s+= "DIR is a subdirectory of SAGE_DOC_SRC/en/reference/.\n" From d61b13a7a911c8643b04049d6e008f97e4b46597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sun, 13 Jun 2021 23:31:27 +0200 Subject: [PATCH 623/706] 31968: Improve imports and docstrings - Improve imports in `sage/interacts/library.py` - Make docstrings raw - Fix input blocks in docstrings - Fix a few typos such as swith -> switch --- src/sage/interacts/library.py | 309 ++++++++++++++----------- src/sage/knots/link.py | 50 ++-- src/sage/rings/padics/local_generic.py | 92 ++++---- src/sage/rings/padics/padic_generic.py | 258 +++++++++++---------- 4 files changed, 376 insertions(+), 333 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 13c9e6d73ff..f4abde285a4 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -1,4 +1,4 @@ -""" +r""" Sage Interacts Sage interacts are applications of the `@interact decorator <../../sagenb/notebook/interact.html>`_. @@ -20,23 +20,53 @@ AUTHORS: - William Stein - -- Harald Schilly, Robert Marik (2011-01-16): added many examples (#9623) partially based on work by Lauri Ruotsalainen - +- Harald Schilly, Robert Marik (2011-01-16): added many examples (#9623) + partially based on work by Lauri Ruotsalainen """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2009 William Stein # Copyright (C) 2011 Harald Schilly # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.arith.misc import factor +from sage.arith.srange import srange +from sage.calculus.all import symbolic_expression +from sage.calculus.functional import derivative +from sage.calculus.integration import numerical_integral as integral_numerical +from sage.ext.fast_callable import fast_callable +from sage.functions.log import exp +from sage.functions.other import sqrt +from sage.functions.trig import (acos, cos, sin, tan) +from sage.misc.decorators import sage_wraps +from sage.misc.functional import N +from sage.misc.latex import latex +from sage.misc.sage_eval import sage_eval +from sage.misc.table import table +from sage.plot.circle import circle +from sage.plot.complex_plot import complex_plot +from sage.plot.disk import disk +from sage.plot.graphics import Graphics +from sage.plot.line import (line, line2d) +from sage.plot.matrix_plot import matrix_plot +from sage.plot.plot import (graphics_array, parametric_plot, plot) +from sage.plot.point import (point, points) +from sage.plot.polygon import polygon2d +from sage.plot.text import text +from sage.repl.rich_output.pretty_print import (pretty_print, show) +from sage.rings.complex_double import CDF +from sage.rings.integer import Integer +from sage.symbolic.constants import pi +from sage.symbolic.relation import solve +from sage.symbolic.ring import SR +import math -from sage.all import * x = SR.var('x') # It is important that this file is lazily imported for this to work @@ -46,7 +76,7 @@ input_box, input_grid, range_slider, selector, slider, text_control) def library_interact(f): - """ + r""" This is a decorator for using interacts in the Sage library. This is just the ``interact`` function wrapped in an additional @@ -71,7 +101,7 @@ def library_wrapper(): def html(obj): - """ + r""" Shorthand to pretty print HTML EXAMPLES:: @@ -86,13 +116,13 @@ def html(obj): @library_interact def demo(n=slider(range(10)), m=slider(range(10))): - """ + r""" This is a demo interact that sums two numbers. INPUT: - - ``n`` -- integer slider - - ``m`` -- integer slider + - ``n`` -- integer slider + - ``m`` -- integer slider EXAMPLES: @@ -112,12 +142,14 @@ def demo(n=slider(range(10)), m=slider(range(10))): def taylor_polynomial( title = text_control('

Taylor polynomial

'), f=input_box(sin(x)*exp(-x),label="$f(x)=$"), order=slider(range(1,13))): - """ - An interact which illustrates the Taylor polynomial approximation + r""" + Illustrate the Taylor polynomial approximation of various orders around `x=0`. - - ``f`` -- function expression - - ```order``` -- integer slider + INPUT: + + - ``f`` -- function expression + - ``order`` -- integer slider EXAMPLES: @@ -151,16 +183,16 @@ def definite_integral( interval = range_slider(-10,10,default=(0,3), label="Interval"), x_range = range_slider(-10,10,default=(0,3), label = "plot range (x)"), selection = selector(["f", "g", "f and g", "f - g"], default="f and g", label="Select")): - """ + r""" This is a demo interact for plotting the definite integral of a function based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``function`` -- input box, function in x - - ``interval`` -- interval for the definite integral - - ``x_range`` -- range slider for plotting range - - ``selection`` -- selector on how to visualize the integrals + - ``function`` -- input box, function in x + - ``interval`` -- interval for the definite integral + - ``x_range`` -- range slider for plotting range + - ``selection`` -- selector on how to visualize the integrals EXAMPLES: @@ -176,7 +208,8 @@ def definite_integral( g: EvalText(value=u'x^2', description=u'$g(x)=$', layout=Layout(max_width=u'81em')) interval: IntRangeSlider(value=(0, 3), description=u'Interval', max=10, min=-10) x_range: IntRangeSlider(value=(0, 3), description=u'plot range (x)', max=10, min=-10) - selection: Dropdown(description=u'Select', index=2, options=('f', 'g', 'f and g', 'f - g'), value='f and g') + selection: Dropdown(description=u'Select', index=2, + options=('f', 'g', 'f and g', 'f - g'), value='f and g') """ x = SR.var('x') f = symbolic_expression(f).function(x) @@ -233,7 +266,7 @@ def function_derivative( function = input_box(default="x^5-3*x^3+1", label="Function:"), x_range = range_slider(-15,15,0.1, default=(-2,2), label="Range (x)"), y_range = range_slider(-15,15,0.1, default=(-8,6), label="Range (y)")): - """ + r""" This is a demo interact for plotting derivatives of a function based on work by Lauri Ruotsalainen, 2010. @@ -279,16 +312,16 @@ def difference_quotient( interval= range_slider(0, 10, 0.1, default=(0.0,10.0), label="Range"), a = slider(0, 10, None, 5.5, label = '$a$'), x0 = slider(0, 10, None, 2.5, label = '$x_0$ (start point)')): - """ + r""" This is a demo interact for difference quotient based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``f`` -- input box, function in `x` - - ``interval`` -- range slider for plotting - - ``a`` -- slider for `a` - - ``x0`` -- slider for starting point `x_0` + - ``f`` -- input box, function in `x` + - ``interval`` -- range slider for plotting + - ``a`` -- slider for `a` + - ``x0`` -- slider for starting point `x_0` EXAMPLES: @@ -340,15 +373,15 @@ def difference_quotient( @library_interact def quadratic_equation(A = slider(-7, 7, 1, 1), B = slider(-7, 7, 1, 1), C = slider(-7, 7, 1, -2)): - """ + r""" This is a demo interact for solving quadratic equations based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``A`` -- integer slider - - ``B`` -- integer slider - - ``C`` -- integer slider + - ``A`` -- integer slider + - ``B`` -- integer slider + - ``C`` -- integer slider EXAMPLES: @@ -401,15 +434,15 @@ def trigonometric_properties_triangle( a0 = slider(0, 360, 1, 30, label="A"), a1 = slider(0, 360, 1, 180, label="B"), a2 = slider(0, 360, 1, 300, label="C")): - """ + r""" This is an interact for demonstrating trigonometric properties in a triangle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``a0`` -- angle - - ``a1`` -- angle - - ``a2`` -- angle + - ``a0`` -- angle + - ``a1`` -- angle + - ``a2`` -- angle EXAMPLES: @@ -481,14 +514,14 @@ def area(alpha, a, b): def unit_circle( function = selector([(0, sin(x)), (1, cos(x)), (2, tan(x))]), x = slider(0,2*pi, 0.005*pi, 0)): - """ + r""" This is an interact for Sin, Cos and Tan in the Unit Circle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``function`` -- select Sin, Cos or Tan - - ``x`` -- slider to select angle in unit circle + - ``function`` -- select Sin, Cos or Tan + - ``x`` -- slider to select angle in unit circle EXAMPLES: @@ -563,21 +596,21 @@ def special_points( show_ab = checkbox(False, label="Angle Bisectors"), show_incircle = checkbox(False, label="Incircle"), show_euler = checkbox(False, label="Euler's Line")): - """ + r""" This interact demo shows special points in a triangle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``a0`` -- angle - - ``a1`` -- angle - - ``a2`` -- angle - - ``show_median`` -- checkbox - - ``show_pb`` -- checkbox to show perpendicular bisectors - - ``show_alt`` -- checkbox to show altitudes - - ``show_ab`` -- checkbox to show angle bisectors - - ``show_incircle`` -- checkbox to show incircle - - ``show_euler`` -- checkbox to show euler's line + - ``a0`` -- angle + - ``a1`` -- angle + - ``a2`` -- angle + - ``show_median`` -- checkbox + - ``show_pb`` -- checkbox to show perpendicular bisectors + - ``show_alt`` -- checkbox to show altitudes + - ``show_ab`` -- checkbox to show angle bisectors + - ``show_incircle`` -- checkbox to show incircle + - ``show_euler`` -- checkbox to show euler's line EXAMPLES: @@ -732,7 +765,7 @@ def line_to_points(x1_y1, x2_y2, **plot_kwargs): @library_interact def coin(n = slider(2,10000, 100, default=1000, label="Number of Tosses"), interval = range_slider(0, 1, default=(0.45, 0.55), label="Plotting range (y)")): - """ + r""" This interact demo simulates repeated tosses of a coin, based on work by Lauri Ruotsalainen, 2010. @@ -744,9 +777,8 @@ def coin(n = slider(2,10000, 100, default=1000, label="Number of Tosses"), inter INPUT: - - ``n`` -- number of tosses - - ``interval`` -- plot range along - vertical axis + - ``n`` -- number of tosses + - ``interval`` -- plot range along vertical axis EXAMPLES: @@ -776,16 +808,16 @@ def bisection_method( interval = range_slider(-5,5,default=(0, 4), label="range"), d = slider(1, 8, 1, 3, label="$10^{-d}$ precision"), maxn = slider(0,50,1,10, label="max iterations")): - """ + r""" Interact explaining the bisection method, based on similar interact explaining secant method and Wiliam Stein's example from wiki. INPUT: - - ``f`` -- function - - ``interval`` -- range slider for the search interval - - ``d`` -- slider for the precision (`10^{-d}`) - - ``maxn`` -- max number of iterations + - ``f`` -- function + - ``interval`` -- range slider for the search interval + - ``d`` -- slider for the precision (`10^{-d}`) + - ``maxn`` -- max number of iterations EXAMPLES: @@ -853,17 +885,17 @@ def secant_method( interval = range_slider(-5,5,default=(0, 4), label="range"), d = slider(1, 16, 1, 3, label="10^-d precision"), maxn = slider(0,15,1,10, label="max iterations")): - """ + r""" Interact explaining the secant method, based on work by Lauri Ruotsalainen, 2010. Originally this is based on work by William Stein. INPUT: - - ``f`` -- function - - ``interval`` -- range slider for the search interval - - ``d`` -- slider for the precision (10^-d) - - ``maxn`` -- max number of iterations + - ``f`` -- function + - ``interval`` -- range slider for the search interval + - ``d`` -- slider for the precision (10^-d) + - ``maxn`` -- max number of iterations EXAMPLES: @@ -922,19 +954,19 @@ def newton_method( maxn = slider(0, 15, 1, 10, label="max iterations"), interval = range_slider(-10,10, default = (0,6), label="Interval"), list_steps = checkbox(default=False, label="List steps")): - """ + r""" Interact explaining the Newton method, based on work by Lauri Ruotsalainen, 2010. Originally this is based on work by William Stein. INPUT: - - ``f`` -- function - - ``c`` -- starting position (`x`) - - ``d`` -- slider for the precision (`10^{-d}`) - - ``maxn`` -- max number of iterations - - ``interval`` -- range slider for the search interval - - ``list_steps`` -- checkbox, if true shows the steps numerically + - ``f`` -- function + - ``c`` -- starting position (`x`) + - ``d`` -- slider for the precision (`10^{-d}`) + - ``maxn`` -- max number of iterations + - ``interval`` -- range slider for the search interval + - ``list_steps`` -- checkbox, if true shows the steps numerically EXAMPLES: @@ -995,19 +1027,21 @@ def trapezoid_integration( interval_g = input_grid(1,2,default=[[0,8]], label="keyboard: "), output_form = selector(['traditional','table','none'], label='Computations form', buttons=True) ): - """ - Interact explaining the trapezoid method for definite integrals, based on work by + r""" + Interact explaining the trapezoid method for definite integrals. + + Based on work by Lauri Ruotsalainen, 2010 (based on the application "Numerical integrals with various rules" by Marshall Hampton and Nick Alexander) INPUT: - - ``f`` -- function of variable x to integrate - - ``n`` -- number of divisions - - ``interval_input`` -- swithes the input for interval between slider and keyboard - - ``interval_s`` -- slider for interval to integrate - - ``interval_g`` -- input grid for interval to integrate - - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing + - ``f`` -- function of variable x to integrate + - ``n`` -- number of divisions + - ``interval_input`` -- switches the input for interval between slider and keyboard + - ``interval_s`` -- slider for interval to integrate + - ``interval_g`` -- input grid for interval to integrate + - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing EXAMPLES: @@ -1109,19 +1143,21 @@ def simpson_integration( interval_s = range_slider(-10,10,default=(0,10), label="slider: "), interval_g = input_grid(1,2,default=[[0,10]], label="keyboard: "), output_form = selector(['traditional','table','none'], label='Computations form', buttons=True)): - """ - Interact explaining the simpson method for definite integrals, based on work by + r""" + Interact explaining the simpson method for definite integrals. + + Based on work by Lauri Ruotsalainen, 2010 (based on the application "Numerical integrals with various rules" by Marshall Hampton and Nick Alexander) INPUT: - - ``f`` -- function of variable x to integrate - - ``n`` -- number of divisions (mult. of 2) - - ``interval_input`` -- swithes the input for interval between slider and keyboard - - ``interval_s`` -- slider for interval to integrate - - ``interval_g`` -- input grid for interval to integrate - - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing + - ``f`` -- function of variable x to integrate + - ``n`` -- number of divisions (mult. of 2) + - ``interval_input`` -- switches the input for interval between slider and keyboard + - ``interval_s`` -- slider for interval to integrate + - ``interval_g`` -- input grid for interval to integrate + - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing EXAMPLES: @@ -1242,14 +1278,14 @@ def riemann_sum( hr2 = text_control('
'), list_table = checkbox(default=False, label="List table"), auto_update = False): - """ + r""" Interact explaining the definition of Riemann integral INPUT: - ``f`` -- function of variable x to integrate - ``n`` -- number of divisions - - ``interval_input`` -- swithes the input for interval between slider and keyboard + - ``interval_input`` -- switches the input for interval between slider and keyboard - ``interval_s`` -- slider for interval to integrate - ``interval_g`` -- input grid for interval to integrate - ``list_table`` -- print table with values of the function @@ -1275,7 +1311,7 @@ def riemann_sum( AUTHORS: - - Robert Marik (08-2010) + - Robert Marik (2010-08) """ x = SR.var('x') from random import random @@ -1327,19 +1363,19 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'], width=15, nrows=5, label="h = "), do_plot = ("Draw Plots", True)): - """ + r""" `Function Plotting Tool `_ (by William Stein (?)) INPUT: - - ``f`` -- function f(x) - - ``g`` -- function g(x) - - ``xrange`` -- range for plotting (x) - - ``yrange`` -- range for plotting ('auto' is default, otherwise a tuple) - - ``a`` -- factor ``a`` - - ``action`` -- select given operation on or combination of functions - - ``do_plot`` -- if true, a plot is drawn + - ``f`` -- function f(x) + - ``g`` -- function g(x) + - ``xrange`` -- range for plotting (x) + - ``yrange`` -- range for plotting ('auto' is default, otherwise a tuple) + - ``a`` -- factor ``a`` + - ``action`` -- select given operation on or combination of functions + - ``do_plot`` -- if true, a plot is drawn EXAMPLES: @@ -1390,7 +1426,7 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab h = 1/f lbl = r'\frac{1}{f}' elif action == 'finv': - h = solve(f == var('y'), x)[0].rhs() + h = solve(f == SR.var('y'), x)[0].rhs() lbl = 'f^{-1}(y)' elif action == 'f+a': h = f+a @@ -1453,20 +1489,20 @@ def julia(expo = slider(-10,10,0.1,2), zoom_y = range_slider(-2,2,0.01,(-1.5,1.5), label='Zoom Y'), plot_points = slider(20,400,20, default=150, label='plot points'), dpi = slider(20, 200, 10, default=80, label='dpi')): - """ + r""" Julia Fractal, based on `Julia by Harald Schilly `_. INPUT: - - ``exponent`` -- exponent ``e`` in `z^e+c` - - ``c_real`` -- real part of the constant ``c`` - - ``c_imag`` -- imaginary part of the constant ``c`` - - ``iterations`` -- number of iterations - - ``zoom_x`` -- range slider for zoom in x direction - - ``zoom_y`` -- range slider for zoom in y direction - - ``plot_points`` -- number of points to plot - - ``dpi`` -- dots-per-inch parameter for the plot + - ``exponent`` -- exponent ``e`` in `z^e+c` + - ``c_real`` -- real part of the constant ``c`` + - ``c_imag`` -- imaginary part of the constant ``c`` + - ``iterations`` -- number of iterations + - ``zoom_x`` -- range slider for zoom in x direction + - ``zoom_y`` -- range slider for zoom in y direction + - ``plot_points`` -- number of points to plot + - ``dpi`` -- dots-per-inch parameter for the plot EXAMPLES: @@ -1504,18 +1540,18 @@ def mandelbrot(expo = slider(-10,10,0.1,2), zoom_y = range_slider(-2,2,0.01,(-1.5,1.5), label='Zoom Y'), plot_points = slider(20,400,20, default=150, label='plot points'), dpi = slider(20, 200, 10, default=80, label='dpi')): - """ + r""" Mandelbrot Fractal, based on `Mandelbrot by Harald Schilly `_. INPUT: - - ``exponent`` -- exponent ``e`` in `z^e+c` - - ``iterations`` -- number of iterations - - ``zoom_x`` -- range slider for zoom in x direction - - ``zoom_y`` -- range slider for zoom in y direction - - ``plot_points`` -- number of points to plot - - ``dpi`` -- dots-per-inch parameter for the plot + - ``exponent`` -- exponent ``e`` in `z^e+c` + - ``iterations`` -- number of iterations + - ``zoom_x`` -- range slider for zoom in x direction + - ``zoom_y`` -- range slider for zoom in y direction + - ``plot_points`` -- number of points to plot + - ``dpi`` -- dots-per-inch parameter for the plot EXAMPLES: @@ -1549,7 +1585,7 @@ def cellular_automaton( N=slider(1,500,1,label='Number of iterations',default=100), rule_number=slider(0, 255, 1, default=110, label='Rule number'), size = slider(1, 11, step_size=1, default=6, label='size of graphic')): - """ + r""" Yields a matrix showing the evolution of a `Wolfram's cellular automaton `_. @@ -1557,9 +1593,9 @@ def cellular_automaton( INPUT: - - ``N`` -- iterations - - ``rule_number`` -- rule number (0 to 255) - - ``size`` -- size of the shown picture + - ``N`` -- iterations + - ``rule_number`` -- rule number (0 to 255) + - ``size`` -- size of the shown picture EXAMPLES: @@ -1604,7 +1640,7 @@ def polar_prime_spiral( show_curves = True, n = slider(1,200, 1, default=89, label="number $n$"), dpi = slider(10,300, 10, default=100, label="dpi")): - """ + r""" Polar Prime Spiral interact, based on work by David Runde. For more information about the factors in the spiral, @@ -1612,12 +1648,12 @@ def polar_prime_spiral( INPUT: - - ``interval`` -- range slider to specify start and end - - ``show_factors`` -- if true, show factors - - ``highlight_primes`` -- if true, prime numbers are highlighted - - ``show_curves`` -- if true, curves are plotted - - ``n`` -- number `n` - - ``dpi`` -- dots per inch resolution for plotting + - ``interval`` -- range slider to specify start and end + - ``show_factors`` -- if true, show factors + - ``highlight_primes`` -- if true, prime numbers are highlighted + - ``show_curves`` -- if true, curves are plotted + - ``n`` -- number `n` + - ``dpi`` -- dots per inch resolution for plotting EXAMPLES: @@ -1666,14 +1702,15 @@ def polar_prime_spiral( if not show_factors: for i in srange(start, end, include_endpoint = True): if Integer(i).is_pseudoprime(): - list.append(f(i-start+1)) # Primes list + list.append(f(i-start+1)) # primes list else: - list2.append(f(i-start+1)) # Composites list + list2.append(f(i-start+1)) # composites list P = points(list) - R = points(list2, alpha=.1) # Faded Composites + R = points(list2, alpha=.1) # faded composites else: for i in srange(start, end, include_endpoint = True): - list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number + # Resize each of the dots depending of the number of factors of each number + list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) if Integer(i).is_pseudoprime() and highlight_primes: list2.append(f(i-start+1)) P = Graphics() @@ -1695,12 +1732,12 @@ def polar_prime_spiral( W4 = disk((f(n-start+1)), p, (10*pi/6, 11*pi/6), alpha=.1) Q = W1 + W2 + W3 + W4 - n = n - start +1 # offsets the n for different start values to ensure accurate plotting + n -= start - 1 # offset n for different start values to ensure accurate plotting if show_curves: begin_curve = 0 t = SR.var('t') - a=1.0 - b=0.0 + a = 1.0 + b = 0.0 S = int(sqrt(n)) if n <= S * (S + 1): c = n - S**2 @@ -1715,7 +1752,7 @@ def polar_prime_spiral( theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), (begin_curve, ceil(sqrt(end-start))), - color=hue(0.8), thickness=.3) # Pink Line + color=hue(0.8), thickness=.3) # pink line b = 1 c = c2; @@ -1724,7 +1761,7 @@ def polar_prime_spiral( theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), (begin_curve, ceil(sqrt(end-start))), - color=hue(0.6), thickness=.3) # Green Line + color=hue(0.6), thickness=.3) # green line show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) else: diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 6b5a864b0ff..38ba588d882 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -48,6 +48,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. +# # https://www.gnu.org/licenses/ # **************************************************************************** @@ -277,7 +278,7 @@ class Link(SageObject): """ def __init__(self, data): - """ + r""" Initialize ``self``. TESTS:: @@ -509,7 +510,7 @@ def fundamental_group(self, presentation='wirtinger'): return F.quotient(rels) def _repr_(self): - """ + r""" Return a string representation. OUTPUT: string representation @@ -533,7 +534,7 @@ def _repr_(self): return 'Link with {} component{} represented by {} crossings'.format(number_of_components, plural, pd_len) def __eq__(self, other): - """ + r""" Check equality. TESTS:: @@ -558,7 +559,7 @@ def __eq__(self, other): return self.braid() == other.braid() def __hash__(self): - """ + r""" Return the hash of ``self``. EXAMPLES:: @@ -570,7 +571,7 @@ def __hash__(self): return hash(self.braid()) def __ne__(self, other): - """ + r""" Check inequality. TESTS:: @@ -588,7 +589,7 @@ def __ne__(self, other): def braid(self): - """ + r""" Return a braid representation of ``self``. OUTPUT: an element in the braid group @@ -1214,7 +1215,7 @@ def khovanov_homology(self, ring=ZZ, height=None, degree=None): return homologies def oriented_gauss_code(self): - """ + r""" Return the oriented Gauss code of ``self``. The oriented Gauss code has two parts: @@ -1301,7 +1302,7 @@ def oriented_gauss_code(self): return self._oriented_gauss_code def pd_code(self): - """ + r""" Return the planar diagram code of ``self``. The planar diagram is returned in the following format. @@ -1409,7 +1410,7 @@ def pd_code(self): raise AssertionError("invalid state") def gauss_code(self): - """ + r""" Return the Gauss code of ``self``. The Gauss code is generated by the following procedure: @@ -1436,7 +1437,7 @@ def gauss_code(self): return self.oriented_gauss_code()[0] def dowker_notation(self): - """ + r""" Return the Dowker notation of ``self``. Similar to the PD code we number the components, so every crossing @@ -1470,7 +1471,7 @@ def dowker_notation(self): return dn def _braid_word_components(self): - """ + r""" Return the disjoint braid components, if any, else return the braid of ``self``. @@ -1519,7 +1520,7 @@ def _braid_word_components(self): return tuple([a for a in x if a]) def _braid_word_components_vector(self): - """ + r""" The list from the :meth:`_braid_word_components` is flattened to give out the vector form. @@ -1586,7 +1587,7 @@ def _homology_generators(self): @cached_method def seifert_matrix(self): - """ + r""" Return the Seifert matrix associated with ``self``. ALGORITHM: @@ -1641,7 +1642,7 @@ def seifert_matrix(self): @cached_method def number_of_components(self): - """ + r""" Return the number of connected components of ``self``. OUTPUT: number of connected components @@ -1674,7 +1675,7 @@ def number_of_components(self): return G.connected_components_number() def is_knot(self): - """ + r""" Return ``True`` if ``self`` is a knot. Every knot is a link but the converse is not true. @@ -1693,7 +1694,7 @@ def is_knot(self): return self.number_of_components() == 1 def genus(self): - """ + r""" Return the genus of ``self``. EXAMPLES:: @@ -1746,7 +1747,7 @@ def genus(self): return sum(g, ZZ.zero()) def signature(self): - """ + r""" Return the signature of ``self``. This is defined as the signature of the symmetric matrix @@ -1816,7 +1817,7 @@ def omega_signature(self, omega): return ZZ.sum(j.real().sign() for j in m.eigenvalues()) def alexander_polynomial(self, var='t'): - """ + r""" Return the Alexander polynomial of ``self``. INPUT: @@ -1891,7 +1892,7 @@ def alexander_polynomial(self, var='t'): return f def determinant(self): - """ + r""" Return the determinant of ``self``. EXAMPLES:: @@ -1922,9 +1923,8 @@ def determinant(self): raise NotImplementedError("determinant implemented only for knots") def is_alternating(self): - """ - Return ``True`` if the given knot diagram is alternating else - returns ``False``. + r""" + Return whether the given knot diagram is alternating. Alternating diagram implies every overcross is followed by an undercross or the vice-versa. @@ -2022,7 +2022,7 @@ def orientation(self): return orientation def seifert_circles(self): - """ + r""" Return the Seifert circles from the link diagram of ``self``. Seifert circles are the circles obtained by smoothing all crossings @@ -2092,7 +2092,7 @@ def seifert_circles(self): return result def regions(self): - """ + r""" Return the regions from the link diagram of ``self``. Regions are obtained always turning left at each crossing. @@ -2264,7 +2264,7 @@ def mirror_image(self): return type(self)(pd) def writhe(self): - """ + r""" Return the writhe of ``self``. EXAMPLES:: diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 9bbc314b3b0..69cc9026bb9 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -1,4 +1,4 @@ -""" +r""" Local Generic Superclass for `p`-adic and power series rings. @@ -8,7 +8,7 @@ - David Roe """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # @@ -16,8 +16,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from copy import copy from sage.rings.ring import CommutativeRing @@ -30,8 +30,8 @@ class LocalGeneric(CommutativeRing): def __init__(self, base, prec, names, element_class, category=None): - """ - Initializes self. + r""" + Initialize ``self``. EXAMPLES:: @@ -75,7 +75,7 @@ def __init__(self, base, prec, names, element_class, category=None): Parent.__init__(self, base, names=(names,), normalize=False, category=category) def is_capped_relative(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a capped relative fashion. @@ -100,7 +100,7 @@ def is_capped_relative(self): return False def is_capped_absolute(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a capped absolute fashion. @@ -125,7 +125,7 @@ def is_capped_absolute(self): return False def is_fixed_mod(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a fixed modulus fashion. @@ -152,7 +152,7 @@ def is_fixed_mod(self): return False def is_floating_point(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a floating point fashion. @@ -177,7 +177,7 @@ def is_floating_point(self): return False def is_lattice_prec(self): - """ + r""" Return whether this `p`-adic ring bounds precision using a lattice model. @@ -206,7 +206,7 @@ def is_lattice_prec(self): return False def is_relaxed(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a relaxed fashion. @@ -456,7 +456,7 @@ def get_unramified_modulus(q, res_name): functor.extras['names'] = kwds.pop('names') elif functor.extras['names'][0] == curpstr: functor.extras['names'] = (str(p),) - # labels for lattice precision + # Labels for lattice precision if 'label' in kwds: functor.extras['label'] = kwds.pop('label') elif 'label' in functor.extras and functor.type not in ['lattice-cap','lattice-float']: @@ -590,7 +590,7 @@ def residue_characteristic(self): OUTPUT: - - integer -- the characteristic of the residue field. + The characteristic of the residue field. EXAMPLES:: @@ -646,7 +646,7 @@ def ground_ring(self): OUTPUT: - - the ground ring of ``self``, i.e., itself + The ground ring of ``self``, i.e., itself. EXAMPLES:: @@ -671,7 +671,7 @@ def ground_ring_of_tower(self): OUTPUT: - - the ground ring of the tower for ``self``, i.e., itself + The ground ring of the tower for ``self``, i.e., itself. EXAMPLES:: @@ -683,8 +683,8 @@ def ground_ring_of_tower(self): def absolute_degree(self): - """ - Return the degree of this extension over the prime p-adic field/ring + r""" + Return the degree of this extension over the prime p-adic field/ring. EXAMPLES:: @@ -699,8 +699,8 @@ def absolute_degree(self): return self.absolute_e() * self.absolute_f() def relative_degree(self): - """ - Return the degree of this extension over its base field/ring + r""" + Return the degree of this extension over its base field/ring. EXAMPLES:: @@ -715,7 +715,7 @@ def relative_degree(self): return self.absolute_degree() // self.base_ring().absolute_degree() def degree(self): - """ + r""" Return the degree of this extension. Raise an error if the base ring/field is itself an extension. @@ -737,8 +737,8 @@ def degree(self): def absolute_e(self): - """ - Return the absolute ramification index of this ring/field + r""" + Return the absolute ramification index of this ring/field. EXAMPLES:: @@ -757,8 +757,8 @@ def absolute_e(self): return self.base_ring().absolute_e() def absolute_ramification_index(self): - """ - Return the absolute ramification index of this ring/field + r""" + Return the absolute ramification index of this ring/field. EXAMPLES:: @@ -773,8 +773,8 @@ def absolute_ramification_index(self): return self.absolute_e() def relative_e(self): - """ - Return the ramification index of this extension over its base ring/field + r""" + Return the ramification index of this extension over its base ring/field. EXAMPLES:: @@ -789,8 +789,8 @@ def relative_e(self): return self.absolute_e() // self.base_ring().absolute_e() def relative_ramification_index(self): - """ - Return the ramification index of this extension over its base ring/field + r""" + Return the ramification index of this extension over its base ring/field. EXAMPLES:: @@ -805,7 +805,7 @@ def relative_ramification_index(self): return self.relative_e() def e(self): - """ + r""" Return the ramification index of this extension. Raise an error if the base ring/field is itself an extension. @@ -826,7 +826,7 @@ def e(self): raise NotImplementedError("For a relative p-adic ring or field you must use relative_e or absolute_e as appropriate") def ramification_index(self): - """ + r""" Return the ramification index of this extension. Raise an error if the base ring/field is itself an extension. @@ -845,9 +845,9 @@ def ramification_index(self): def absolute_f(self): - """ + r""" Return the degree of the residue field of this ring/field - over its prime subfield + over its prime subfield. EXAMPLES:: @@ -866,9 +866,9 @@ def absolute_f(self): return self.base_ring().absolute_f() def absolute_inertia_degree(self): - """ + r""" Return the degree of the residue field of this ring/field - over its prime subfield + over its prime subfield. EXAMPLES:: @@ -883,8 +883,8 @@ def absolute_inertia_degree(self): return self.absolute_f() def relative_f(self): - """ - Return the degree of the residual extension over its base ring/field + r""" + Return the degree of the residual extension over its base ring/field. EXAMPLES:: @@ -899,8 +899,8 @@ def relative_f(self): return self.absolute_f() // self.base_ring().absolute_f() def relative_inertia_degree(self): - """ - Return the degree of the residual extension over its base ring/field + r""" + Return the degree of the residual extension over its base ring/field. EXAMPLES:: @@ -915,7 +915,7 @@ def relative_inertia_degree(self): return self.relative_f() def f(self): - """ + r""" Return the degree of the residual extension. Raise an error if the base ring/field is itself an extension. @@ -936,7 +936,7 @@ def f(self): raise NotImplementedError("For a relative p-adic ring or field you must use relative_f or absolute_f as appropriate") def inertia_degree(self): - """ + r""" Return the degree of the residual extension. Raise an error if the base ring/field is itself an extension. @@ -1000,7 +1000,7 @@ def maximal_unramified_subextension(self): # raise NotImplementedError def uniformiser(self): - """ + r""" Return a uniformiser for ``self``, ie a generator for the unique maximal ideal. EXAMPLES:: @@ -1017,7 +1017,7 @@ def uniformiser(self): return self.uniformizer() def uniformiser_pow(self, n): - """ + r""" Return the `n`th power of the uniformiser of ``self`` (as an element of ``self``). EXAMPLES:: @@ -1029,8 +1029,8 @@ def uniformiser_pow(self, n): return self.uniformizer_pow(n) def ext(self, *args, **kwds): - """ - Constructs an extension of self. See ``extension`` for more details. + r""" + Construct an extension of self. See :meth:`extension` for more details. EXAMPLES:: @@ -1120,7 +1120,7 @@ def _test_residue(self, **options): tester.assertEqual(x, z) def _matrix_flatten_precision(self, M): - """ + r""" Rescale rows and columns of ``M`` so that the minimal absolute precision of each row and column is equal to the cap. diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 5c81ce18e77..d2d52ee3fed 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -1,4 +1,4 @@ -""" +r""" p-Adic Generic A generic superclass for all p-adic parents. @@ -9,10 +9,9 @@ - Genya Zaytman: documentation - David Harvey: doctests - Julian Rueth (2013-03-16): test methods for basic arithmetic - """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # Julian Rueth @@ -21,8 +20,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.misc import some_tuples from copy import copy @@ -44,20 +43,20 @@ class pAdicGeneric(PrincipalIdealDomain, LocalGeneric): def __init__(self, base, p, prec, print_mode, names, element_class, category=None): - """ - Initialization. + r""" + Initialize ``self``. INPUT: - - base -- Base ring. - - p -- prime - - print_mode -- dictionary of print options - - names -- how to print the uniformizer - - element_class -- the class for elements of this ring + - ``base`` -- Base ring. + - ``p`` -- prime + - ``print_mode`` -- dictionary of print options + - ``names`` -- how to print the uniformizer + - ``element_class`` -- the class for elements of this ring EXAMPLES:: - sage: R = Zp(17) #indirect doctest + sage: R = Zp(17) # indirect doctest """ if category is None: if self.is_field(): @@ -71,7 +70,7 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non def some_elements(self): r""" - Returns a list of elements in this ring. + Return a list of elements in this ring. This is typically used for running generic tests (see :class:`TestSuite`). @@ -91,14 +90,16 @@ def some_elements(self): return L def _modified_print_mode(self, print_mode): - """ - Returns a dictionary of print options, starting with self's + r""" + Return a dictionary of print options, starting with self's print options but modified by the options in the dictionary print_mode. INPUT: - - print_mode -- dictionary with keys in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet'] + - ``print_mode`` -- dictionary with keys in ['mode', 'pos', 'ram_name', + 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', + 'max_terse_terms', 'sep', 'alphabet'] EXAMPLES:: @@ -110,14 +111,16 @@ def _modified_print_mode(self, print_mode): print_mode = {} elif isinstance(print_mode, str): print_mode = {'mode': print_mode} - for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec']: + for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', + 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', + 'sep', 'alphabet', 'show_prec']: if option not in print_mode: print_mode[option] = self._printer.dict()[option] return print_mode def ngens(self): - """ - Returns the number of generators of self. + r""" + Return the number of generators of self. We conventionally define this as 1: for base rings, we take a uniformizer as the generator; for extension rings, we take a @@ -133,8 +136,8 @@ def ngens(self): return 1 def gens(self): - """ - Returns a list of generators. + r""" + Return a list of generators. EXAMPLES:: @@ -148,7 +151,7 @@ def gens(self): return [self.gen()] def __richcmp__(self, other, op): - """ + r""" Return 0 if self == other, and 1 or -1 otherwise. We consider two p-adic rings or fields to be equal if they are @@ -182,25 +185,25 @@ def __richcmp__(self, other, op): return self._printer.richcmp_modes(other._printer, op) - #def ngens(self): - # return 1 + # def ngens(self): + # return 1 - #def gen(self, n = 0): - # if n != 0: - # raise IndexError, "only one generator" - # return self(self.prime()) + # def gen(self, n = 0): + # if n != 0: + # raise IndexError, "only one generator" + # return self(self.prime()) def print_mode(self): r""" - Returns the current print mode as a string. + Return the current print mode as a string. INPUT: - self -- a p-adic field + - ``self`` -- a p-adic field OUTPUT: - string -- self's print mode + The print mode for this p-adic field, as a string. EXAMPLES:: @@ -212,15 +215,13 @@ def print_mode(self): def characteristic(self): r""" - Returns the characteristic of self, which is always 0. + Return the characteristic of self, which is always 0. INPUT: - self -- a p-adic parent + - ``self`` -- a p-adic parent - OUTPUT: - - integer -- self's characteristic, i.e., 0 + OUTPUT: self's characteristic, i.e., 0. EXAMPLES:: @@ -230,16 +231,16 @@ def characteristic(self): return Integer(0) def prime(self): - """ - Returns the prime, ie the characteristic of the residue field. + r""" + Return the prime, ie the characteristic of the residue field. INPUT: - self -- a p-adic parent + - ``self`` -- a p-adic parent OUTPUT: - integer -- the characteristic of the residue field + The characteristic of the residue field. EXAMPLES:: @@ -250,10 +251,10 @@ def prime(self): return self.prime_pow._prime() def uniformizer_pow(self, n): - """ - Returns p^n, as an element of self. + r""" + Return `p^n`, as an element of ``self``. - If n is infinity, returns 0. + If `n` is infinity, returns 0. EXAMPLES:: @@ -268,8 +269,9 @@ def uniformizer_pow(self, n): return self(self.prime_pow.pow_Integer_Integer(n)) def _unram_print(self): - """ - For printing. Will be None if the unramified subextension of self is of degree 1 over Z_p or Q_p. + r""" + For printing. Will be ``None`` if the unramified subextension + of self is of degree 1 over `\ZZ_p` or `\QQ_p`. EXAMPLES:: @@ -278,12 +280,12 @@ def _unram_print(self): return None def residue_characteristic(self): - """ + r""" Return the prime, i.e., the characteristic of the residue field. OUTPUT: - integer -- the characteristic of the residue field + The characteristic of the residue field. EXAMPLES:: @@ -294,16 +296,16 @@ def residue_characteristic(self): return self.prime() def residue_class_field(self): - """ - Returns the residue class field. + r""" + Return the residue class field. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - the residue field + The residue field. EXAMPLES:: @@ -316,16 +318,16 @@ def residue_class_field(self): return GF(self.prime()) def residue_field(self): - """ - Returns the residue class field. + r""" + Return the residue class field. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - the residue field + The residue field. EXAMPLES:: @@ -337,8 +339,8 @@ def residue_field(self): return self.residue_class_field() def residue_ring(self, n): - """ - Returns the quotient of the ring of integers by the nth power of the maximal ideal. + r""" + Return the quotient of the ring of integers by the nth power of the maximal ideal. EXAMPLES:: @@ -350,16 +352,16 @@ def residue_ring(self, n): return Zmod(self.prime()**n) def residue_system(self): - """ - Returns a list of elements representing all the residue classes. + r""" + Return a list of elements representing all the residue classes. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - list of elements -- a list of elements representing all the residue classes + A list of elements representing all the residue classes. EXAMPLES:: @@ -370,8 +372,9 @@ def residue_system(self): return [self(i) for i in self.residue_class_field()] def _fraction_field_key(self, print_mode=None): - """ - Changes print_mode from a dictionary to a tuple and raises a deprecation warning if it is present. + r""" + Change print_mode from a dictionary to a tuple, + raising a deprecation warning if it is present. EXAMPLES:: @@ -391,7 +394,7 @@ def _fraction_field_key(self, print_mode=None): @cached_method(key=_fraction_field_key) def fraction_field(self, print_mode=None): r""" - Returns the fraction field of this ring or field. + Return the fraction field of this ring or field. For `\ZZ_p`, this is the `p`-adic field with the same options, and for extensions, it is just the extension of the fraction @@ -407,7 +410,7 @@ def fraction_field(self, print_mode=None): OUTPUT: - - the fraction field of this ring. + The fraction field of this ring. EXAMPLES:: @@ -454,7 +457,7 @@ def fraction_field(self, print_mode=None): def integer_ring(self, print_mode=None): r""" - Returns the ring of integers of this ring or field. + Return the ring of integers of this ring or field. For `\QQ_p`, this is the `p`-adic ring with the same options, and for extensions, it is just the extension of the ring @@ -467,7 +470,7 @@ def integer_ring(self, print_mode=None): OUTPUT: - - the ring of elements of this field with nonnegative valuation. + The ring of elements of this field with nonnegative valuation. EXAMPLES:: @@ -507,7 +510,9 @@ def integer_ring(self, print_mode=None): sage: R.integer_ring({'mode':'series'}) is R True """ - #Currently does not support fields with non integral defining polynomials. This should change when the padic_general_extension framework gets worked out. + # Currently does not support fields with non integral defining + # polynomials. This should change when the padic_general_extension + # framework gets worked out. if not self.is_field() and print_mode is None: return self if print_mode is None: @@ -519,16 +524,16 @@ def integer_ring(self, print_mode=None): def teichmuller(self, x, prec = None): r""" - Returns the teichmuller representative of x. + Return the Teichmüller representative of `x`. INPUT: - - self -- a p-adic ring - - x -- something that can be cast into self + - ``self`` -- a p-adic ring + - ``x`` -- something that can be cast into ``self`` OUTPUT: - - element -- the teichmuller lift of x + The Teichmüller lift of `x`. EXAMPLES:: @@ -567,10 +572,10 @@ def teichmuller(self, x, prec = None): AUTHORS: - Initial version: David Roe - - Quadratic time version: Kiran Kedlaya (3/27/07) + - Quadratic time version: Kiran Kedlaya (2007-03-27) """ ans = self(x) if prec is None else self(x, prec) - # Since teichmuller representatives are defined at infinite precision, + # Since Teichmüller representatives are defined at infinite precision, # we can lift to precision prec, as long as the absolute precision of ans is positive. if ans.precision_absolute() <= 0: raise ValueError("Not enough precision to determine Teichmuller representative") @@ -584,15 +589,17 @@ def teichmuller(self, x, prec = None): def teichmuller_system(self): r""" - Returns a set of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ`. + Return a set of Teichmüller representatives for the invertible + elements of `\ZZ / p\ZZ`. INPUT: - - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - - list of elements -- a list of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ` + A list of Teichmüller representatives for the invertible elements + of `\ZZ / p\ZZ`. EXAMPLES:: @@ -636,7 +643,7 @@ def teichmuller_system(self): # raise NotImplementedError def extension(self, modulus, prec = None, names = None, print_mode = None, implementation='FLINT', **kwds): - """ + r""" Create an extension of this p-adic ring. EXAMPLES:: @@ -689,7 +696,7 @@ def extension(self, modulus, prec = None, names = None, print_mode = None, imple return ExtensionFactory(base=self, modulus=modulus, prec=prec, names=names, check = True, implementation=implementation, **print_mode) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): - """ + r""" Check whether the given images and map on the base ring determine a valid homomorphism to the codomain. @@ -733,7 +740,7 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): return f(im_gens[0]) == 0 def _test_add(self, **options): - """ + r""" Test addition of elements of this ring. INPUT: @@ -747,7 +754,6 @@ def _test_add(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) elements = tester.some_elements() @@ -773,7 +779,7 @@ def _test_add(self, **options): tester.assertTrue(x.is_equal_to(z-y,zprec)) def _test_sub(self, **options): - """ + r""" Test subtraction on elements of this ring. INPUT: @@ -787,7 +793,6 @@ def _test_sub(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -852,7 +857,7 @@ def _test_invert(self, **options): tester.assertEqual(y.valuation(), -x.valuation()) def _test_mul(self, **options): - """ + r""" Test multiplication of elements of this ring. INPUT: @@ -866,7 +871,6 @@ def _test_mul(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -882,7 +886,7 @@ def _test_mul(self, **options): tester.assertEqual(z.valuation(), x.valuation() + y.valuation()) def _test_div(self, **options): - """ + r""" Test division of elements of this ring. INPUT: @@ -896,7 +900,6 @@ def _test_div(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -924,7 +927,7 @@ def _test_div(self, **options): tester.assertEqual(xx, x) def _test_neg(self, **options): - """ + r""" Test the negation operator on elements of this ring. INPUT: @@ -997,7 +1000,7 @@ def _test_shift(self, **options): def _test_log(self, **options): - """ + r""" Test the log operator on elements of this ring. INPUT: @@ -1050,8 +1053,8 @@ def _test_log(self, **options): tester.assertEqual(1+a, c) def _test_teichmuller(self, **options): - """ - Test Teichmuller lifts. + r""" + Test Teichmüller lifts. INPUT: @@ -1139,7 +1142,10 @@ def _log_unit_part_p(self): return self(self.prime()).unit_part().log() def frobenius_endomorphism(self, n=1): - """ + r""" + Return the `n`-th power of the absolute arithmetic Frobeninus + endomorphism on this field. + INPUT: - ``n`` -- an integer (default: 1) @@ -1153,19 +1159,22 @@ def frobenius_endomorphism(self, n=1): sage: K.
= Qq(3^5) sage: Frob = K.frobenius_endomorphism(); Frob - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^3 on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^3 on the residue field sage: Frob(a) == a.frobenius() True We can specify a power:: sage: K.frobenius_endomorphism(2) - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^(3^2) on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^(3^2) on the residue field The result is simplified if possible:: sage: K.frobenius_endomorphism(6) - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^3 on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^3 on the residue field sage: K.frobenius_endomorphism(5) Identity endomorphism of 3-adic Unramified Extension ... @@ -1178,9 +1187,11 @@ def frobenius_endomorphism(self, n=1): return FrobeniusEndomorphism_padics(self, n) def _test_elements_eq_transitive(self, **options): - """ - The operator ``==`` is not transitive for `p`-adic numbers. We disable - the check of the category framework by overriding this method. + r""" + The operator ``==`` is not transitive for `p`-adic numbers. + + We disable the check of the category framework by overriding + this method. EXAMPLES:: @@ -1229,13 +1240,12 @@ def valuation(self): :meth:`NumberField_generic.valuation() `, :meth:`Order.valuation() ` - """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self) def _primitive_qth_root_of_unity(self, exponent): - """ + r""" Compute the ``p^exponent``-th roots of unity in this ring. INPUT: @@ -1244,13 +1254,11 @@ def _primitive_qth_root_of_unity(self, exponent): OUTPUT: - A triple ``(zeta,n,nextzeta)`` where + A triple ``(zeta, n, nextzeta)`` where - ``zeta`` is a generator of the group of ``p^exponent``-th roots of unity in this ring, and - - ``p^n`` is the order of ``zeta``. - - ``nextzeta`` is the result of ``zeta._inverse_pth_root()`` if ``n`` is positive and ``None`` otherwise @@ -1310,7 +1318,7 @@ def _primitive_qth_root_of_unity(self, exponent): return self._qth_roots_of_unity[-2][0], n-2, self._qth_roots_of_unity[-1] def primitive_root_of_unity(self, n=None, order=False): - """ + r""" Return a generator of the group of ``n``-th roots of unity in this ring. @@ -1358,7 +1366,6 @@ def primitive_root_of_unity(self, n=None, order=False): 6 sage: zeta.multiplicative_order() 6 - """ p = self.prime() k = self.residue_field() @@ -1393,7 +1400,7 @@ def primitive_root_of_unity(self, n=None, order=False): return zeta def roots_of_unity(self, n=None): - """ + r""" Return all the ``n``-th roots of unity in this ring. INPUT: @@ -1414,7 +1421,7 @@ def roots_of_unity(self, n=None): [1 + O(5^10), 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + O(5^10)] - In this case, the roots of unity are the Teichmuller representatives:: + In this case, the roots of unity are the Teichmüller representatives:: sage: R.teichmuller_system() [1 + O(5^10), @@ -1445,8 +1452,8 @@ def roots_of_unity(self, n=None): We check that the logarithm of each root of unity vanishes:: sage: for root in roots: - ....: if root.log() != 0: raise ValueError - + ....: if root.log() != 0: + ....: raise ValueError """ zeta, order = self.primitive_root_of_unity(n, order=True) return [ zeta**i for i in range(order) ] @@ -1467,7 +1474,7 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: ``None``); Sage provides an implementation for any extension of - `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented + `\QQ_p` whereas only roots of polynomials over `\QQ_p` is implemented in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, ``"sage"`` otherwise. @@ -1604,7 +1611,6 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur Traceback (most recent call last): ... ArithmeticError: factorization of 0 is not defined - """ if P.is_zero(): raise ArithmeticError("factorization of 0 is not defined") @@ -1635,7 +1641,7 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur class ResidueReductionMap(Morphism): - """ + r""" Reduction map from a p-adic ring or field to its residue field or ring. These maps must be created using the :meth:`_create_` method in order @@ -1652,7 +1658,7 @@ class ResidueReductionMap(Morphism): """ @staticmethod def _create_(R, k): - """ + r""" Initialization. We have to implement this as a static method in order to call ``__make_element_class__``. @@ -1691,7 +1697,7 @@ def _create_(R, k): return f def is_surjective(self): - """ + r""" The reduction map is surjective. EXAMPLES:: @@ -1702,7 +1708,7 @@ def is_surjective(self): return True def is_injective(self): - """ + r""" The reduction map is far from injective. EXAMPLES:: @@ -1713,7 +1719,7 @@ def is_injective(self): return False def _call_(self, x): - """ + r""" Evaluate this morphism. EXAMPLES:: @@ -1733,7 +1739,7 @@ def _call_(self, x): return x.residue(self._n, field=self._field, check_prec=self._field) def section(self): - """ + r""" Returns the section from the residue ring or field back to the p-adic ring or field. @@ -1747,7 +1753,7 @@ def section(self): return ResidueLiftingMap._create_(self.codomain(), self.domain()) def _repr_type(self): - """ + r""" Type of morphism, for printing. EXAMPLES:: @@ -1775,10 +1781,10 @@ def _richcmp_(self, other, op): return NotImplemented return richcmp((self.domain(), self.codomain()), (other.domain(), other.codomain()), op) -# A class for the Teichmuller lift would also be reasonable.... +# A class for the Teichmüller lift would also be reasonable.... class ResidueLiftingMap(Morphism): - """ + r""" Lifting map to a p-adic ring or field from its residue field or ring. These maps must be created using the :meth:`_create_` method in order @@ -1795,7 +1801,7 @@ class ResidueLiftingMap(Morphism): """ @staticmethod def _create_(k, R): - """ + r""" Initialization. We have to implement this as a static method in order to call ``__make_element_class__``. @@ -1823,7 +1829,7 @@ def _create_(k, R): return f def _call_(self, x): - """ + r""" Evaluate this morphism. EXAMPLES:: @@ -1853,7 +1859,7 @@ def _call_(self, x): raise NotImplementedError def _call_with_args(self, x, args=(), kwds={}): - """ + r""" Evaluate this morphism with extra arguments. EXAMPLES:: @@ -1880,7 +1886,7 @@ def _call_with_args(self, x, args=(), kwds={}): raise NotImplementedError def _repr_type(self): - """ + r""" Type of morphism, for printing. EXAMPLES:: @@ -1924,7 +1930,7 @@ def local_print_mode(obj, print_options, pos = None, ram_name = None): .. NOTE:: - For more documentation see ``localvars`` in parent_gens.pyx + For more documentation see :class:`sage.structure.parent_gens.localvars`. """ if isinstance(print_options, str): print_options = {'mode': print_options} From b17b67463aaa3c0fa4d1e8d54711aeccabda3327 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 13 Jun 2021 14:02:30 -0700 Subject: [PATCH 624/706] trac 31948: implement "sage --docbuild reference_top FORMAT" to build top-level reference manual document. --- build/make/Makefile.in | 6 +- src/sage_docbuild/__init__.py | 280 +++++++++++++++++++--------------- 2 files changed, 164 insertions(+), 122 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 4f235654375..0936f51a9ea 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -351,7 +351,7 @@ doc-inventory-references: $(DOC_DEPENDENCIES) $(eval other_docs = $(wordlist 2, 100, $(DOCS))) +$(MAKE_REC) doc-inventory--$(subst /,-,$(biblio)) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-inventory--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference_top doc-html-references: doc-inventory-references $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) @@ -359,7 +359,7 @@ doc-html-references: doc-inventory-references $(eval other_docs = $(wordlist 2, 100, $(DOCS))) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--$(subst /,-,$(biblio)) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-html--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference_top doc-html-other: $(DOC_DEPENDENCIES) doc-html-references +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-html--$(subst /,-,$(doc))) @@ -389,7 +389,7 @@ doc-pdf: doc-inventory-references $(eval other_docs = $(wordlist 2, 100, $(DOCS))) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--$(subst /,-,$(biblio)) +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-pdf--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--reference + +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--reference_top +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-pdf--$(subst /,-,$(doc))) doc-clean: doc-src-clean doc-output-clean diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index 2e31a1f0a11..199a6e2a9fe 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -516,7 +516,7 @@ def __init__(self, name, lang='en'): self.name = doc[0] self.lang = lang - def _output_dir(self, type, lang='en'): + def _output_dir(self, type, lang=None): """ Return the directory where the output of type ``type`` is stored. @@ -530,152 +530,63 @@ def _output_dir(self, type, lang='en'): sage: b._output_dir('html') '.../html/en/reference' """ + if lang is None: + lang = self.lang d = os.path.join(SAGE_DOC, type, lang, self.name) sage_makedirs(d) return d - def _refdir(self, lang): - return os.path.join(SAGE_DOC_SRC, lang, self.name) + def _refdir(self): + return os.path.join(SAGE_DOC_SRC, self.lang, self.name) - def _build_bibliography(self, lang, format, *args, **kwds): + def _build_bibliography(self, format, *args, **kwds): """ Build the bibliography only The bibliography references.aux is referenced by the other manuals and needs to be built first. """ - refdir = self._refdir(lang) + refdir = self._refdir() references = [ - (doc, lang, format, kwds) + args for doc in self.get_all_documents(refdir) + (doc, self.lang, format, kwds) + args for doc in self.get_all_documents(refdir) if doc == 'reference/references' ] build_many(build_ref_doc, references) - def _build_everything_except_bibliography(self, lang, format, *args, **kwds): + def _build_everything_except_bibliography(self, format, *args, **kwds): """ Build the entire reference manual except the bibliography """ - refdir = self._refdir(lang) + refdir = self._refdir() non_references = [ - (doc, lang, format, kwds) + args for doc in self.get_all_documents(refdir) + (doc, self.lang, format, kwds) + args for doc in self.get_all_documents(refdir) if doc != 'reference/references' ] build_many(build_ref_doc, non_references) + def _build_top_level(self, format, *args, **kwds): + """ + Build top-level document. + """ + getattr(ReferenceTopBuilder(), format)(*args, **kwds) + def _wrapper(self, format, *args, **kwds): """ - Builds reference manuals. For each language, it builds the + Builds reference manuals: build the top-level document and its components. """ - for lang in LANGUAGES: - refdir = self._refdir(lang) - if not os.path.exists(refdir): - continue - logger.info('Building bibliography') - self._build_bibliography(lang, format, *args, **kwds) - logger.info('Bibliography finished, building dependent manuals') - self._build_everything_except_bibliography(lang, format, *args, **kwds) - # The html refman must be build at the end to ensure correct - # merging of indexes and inventories. - # Sphinx is run here in the current process (not in a - # subprocess) and the IntersphinxCache gets populated to be - # used for the second pass of the reference manual and for - # the other documents. - getattr(DocBuilder(self.name, lang), format)(*args, **kwds) - - # PDF: we need to build master index file which lists all - # of the PDF file. So we create an html file, based on - # the file index.html from the "website" target. - if format == 'pdf': - # First build the website page. This only takes a few seconds. - getattr(get_builder('website'), 'html')() - - website_dir = os.path.join(SAGE_DOC, 'html', 'en', 'website') - output_dir = self._output_dir(format, lang) - - # Install in output_dir a symlink to the directory containing static files. - try: - os.symlink(os.path.join(website_dir, '_static'), os.path.join(output_dir, '_static')) - except FileExistsError: - pass - - # Now modify website's index.html page and write it to - # output_dir. - with open(os.path.join(website_dir, 'index.html')) as f: - html = f.read().replace('Documentation', 'Reference') - html_output_dir = os.path.dirname(website_dir) - html = html.replace('http://www.sagemath.org', - os.path.join(html_output_dir, 'index.html')) - # From index.html, we want the preamble and the tail. - html_end_preamble = html.find('

Sage Reference') - html_bottom = html.rfind('') + len('') - - # For the content, we modify doc/en/reference/index.rst, which - # has two parts: the body and the table of contents. - with open(os.path.join(SAGE_DOC_SRC, lang, 'reference', 'index.rst')) as f: - rst = f.read() - # Get rid of todolist and miscellaneous rst markup. - rst = rst.replace('.. _reference-manual:\n\n', '') - rst = re.sub(r'\\\\', r'\\', rst) - # Replace rst links with html links. There are three forms: - # - # `blah`__ followed by __ LINK - # - # `blah `_ - # - # :doc:`blah ` - # - # Change the first and the second forms to - # - # blah - # - # Change the third form to - # - # blah - # - rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)', - r'\1.', rst) - rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_', - r'\1', rst) - rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`', - r'\1 ', rst) - # Body: add paragraph

markup. - start = rst.rfind('*\n') + 1 - end = rst.find('\nUser Interfaces') - rst_body = rst[start:end] - rst_body = rst_body.replace('\n\n', '

\n

') - # TOC: don't include the indices - start = rst.find('\nUser Interfaces') - end = rst.find('Indices and Tables') - rst_toc = rst[start:end] - # change * to

  • ; change rst headers to html headers - rst_toc = re.sub(r'\*(.*)\n', - r'
  • \1
  • \n', rst_toc) - rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[=]*\n', - r'\n\n\n

    \1

    \n\n
      \n', rst_toc) - rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[-]*\n', - r'
    \n\n\n

    \1

    \n\n
      \n', rst_toc) - # 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(rst_body) - new_index.write('
        ') - new_index.write(rst_toc) - new_index.write('
      \n\n') - new_index.write(html[html_bottom:]) - logger.warning(''' -PDF documents have been created in subdirectories of - - %s - -Alternatively, you can open - - %s - -for a webpage listing all of the documents.''' % (output_dir, - os.path.join(output_dir, - 'index.html'))) + refdir = self._refdir() + logger.info('Building bibliography') + self._build_bibliography(format, *args, **kwds) + logger.info('Bibliography finished, building dependent manuals') + self._build_everything_except_bibliography(format, *args, **kwds) + # The html refman must be build at the end to ensure correct + # merging of indexes and inventories. + # Sphinx is run here in the current process (not in a + # subprocess) and the IntersphinxCache gets populated to be + # used for the second pass of the reference manual and for + # the other documents. + self._build_top_level(format, *args, **kwds) def get_all_documents(self, refdir): """ @@ -708,6 +619,135 @@ def get_all_documents(self, refdir): return [ doc[1] for doc in sorted(documents) ] +class ReferenceTopBuilder(DocBuilder): + """ + This class builds the top-level page of the reference manual. + """ + def __init__(self, *args, **kwds): + DocBuilder.__init__(self, *args, **kwds) + self.name = 'reference' + self.lang = 'en' + + def _output_dir(self, type, lang=None): + """ + Return the directory where the output of type ``type`` is stored. + + If the directory does not exist, then it will automatically be + created. + + EXAMPLES:: + + sage: from sage_docbuild import ReferenceTopBuilder + sage: b = ReferenceTopBuilder() + sage: b._output_dir('html') + '.../html/en/reference' + """ + if lang is None: + lang = self.lang + d = os.path.join(SAGE_DOC, type, lang, self.name) + sage_makedirs(d) + return d + + def _wrapper(self, format, *args, **kwds): + """ + Build top-level document. + """ + getattr(DocBuilder(self.name, self.lang), format)(*args, **kwds) + # PDF: we need to build master index file which lists all + # of the PDF file. So we create an html file, based on + # the file index.html from the "website" target. + if format == 'pdf': + # First build the website page. This only takes a few seconds. + getattr(get_builder('website'), 'html')() + + website_dir = os.path.join(SAGE_DOC, 'html', 'en', 'website') + output_dir = self._output_dir(format) + + # Install in output_dir a symlink to the directory containing static files. + try: + os.symlink(os.path.join(website_dir, '_static'), os.path.join(output_dir, '_static')) + except FileExistsError: + pass + + # Now modify website's index.html page and write it to + # output_dir. + with open(os.path.join(website_dir, 'index.html')) as f: + html = f.read().replace('Documentation', 'Reference') + html_output_dir = os.path.dirname(website_dir) + html = html.replace('http://www.sagemath.org', + os.path.join(html_output_dir, 'index.html')) + # From index.html, we want the preamble and the tail. + html_end_preamble = html.find('

      Sage Reference') + html_bottom = html.rfind('') + len('') + + # For the content, we modify doc/en/reference/index.rst, which + # has two parts: the body and the table of contents. + with open(os.path.join(SAGE_DOC_SRC, self.lang, 'reference', 'index.rst')) as f: + rst = f.read() + # Get rid of todolist and miscellaneous rst markup. + rst = rst.replace('.. _reference-manual:\n\n', '') + rst = re.sub(r'\\\\', r'\\', rst) + # Replace rst links with html links. There are three forms: + # + # `blah`__ followed by __ LINK + # + # `blah `_ + # + # :doc:`blah ` + # + # Change the first and the second forms to + # + # blah + # + # Change the third form to + # + # blah + # + rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)', + r'\1.', rst) + rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_', + r'\1', rst) + rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`', + r'\1 ', rst) + # Body: add paragraph

      markup. + start = rst.rfind('*\n') + 1 + end = rst.find('\nUser Interfaces') + rst_body = rst[start:end] + rst_body = rst_body.replace('\n\n', '

      \n

      ') + # TOC: don't include the indices + start = rst.find('\nUser Interfaces') + end = rst.find('Indices and Tables') + rst_toc = rst[start:end] + # change * to

    • ; change rst headers to html headers + rst_toc = re.sub(r'\*(.*)\n', + r'
    • \1
    • \n', rst_toc) + rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[=]*\n', + r'

    \n\n\n

    \1

    \n\n
      \n', rst_toc) + rst_toc = re.sub(r'\n([A-Z][a-zA-Z, ]*)\n[-]*\n', + r'
    \n\n\n

    \1

    \n\n
      \n', rst_toc) + # 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(rst_body) + new_index.write('
        ') + new_index.write(rst_toc) + new_index.write('
      \n\n') + new_index.write(html[html_bottom:]) + logger.warning(''' +PDF documents have been created in subdirectories of + + %s + +Alternatively, you can open + + %s + +for a webpage listing all of the documents.''' % (output_dir, + os.path.join(output_dir, + 'index.html'))) + + class ReferenceSubBuilder(DocBuilder): """ This class builds sub-components of the reference manual. It is @@ -1278,6 +1318,8 @@ def get_builder(name): deprecation(31948, 'avoid using "sage --docbuild all html" and "sage --docbuild all pdf"; ' 'use "make doc" and "make doc-pdf" instead, if available.') return AllBuilder() + elif name == 'reference_top': + return ReferenceTopBuilder('reference') elif name.endswith('reference'): return ReferenceBuilder(name) elif 'reference' in name and os.path.exists(os.path.join(SAGE_DOC_SRC, 'en', name)): From 06dfb9097e82dd6d1c95a0f7412f53967d21c217 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 13 Jun 2021 19:31:53 -0700 Subject: [PATCH 625/706] trac 31948: fix "help_documents" --- src/sage_docbuild/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index 199a6e2a9fe..c628aca850c 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -1421,7 +1421,9 @@ def help_documents(s=""): documentation builder. """ docs = get_documents() - s += "DOCUMENTs:\n\n" + s += "DOCUMENTs:\n" + s += format_columns(docs) + s += "\n" if 'reference' in docs: s+= "Other valid document names take the form 'reference/DIR', where\n" s+= "DIR is a subdirectory of SAGE_DOC_SRC/en/reference/.\n" From 8930551946875bc8eefd855c2b8cb88828f93a24 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 13 Jun 2021 22:08:44 -0700 Subject: [PATCH 626/706] trac 31948: repair "sage --docbuild all html" --- src/sage_docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index c628aca850c..deee8ef7781 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -568,7 +568,7 @@ def _build_top_level(self, format, *args, **kwds): """ Build top-level document. """ - getattr(ReferenceTopBuilder(), format)(*args, **kwds) + getattr(ReferenceTopBuilder('reference'), format)(*args, **kwds) def _wrapper(self, format, *args, **kwds): """ From 2d59e5bad611d8ca3344817a197d3f6fa53e1582 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Jun 2021 10:13:14 +0200 Subject: [PATCH 627/706] [ ] -> [] --- src/sage/rings/padics/generic_nodes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 640eef1ca04..4b09b5678c1 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -643,9 +643,9 @@ def convert_multiple(self, *elts): p = self.prime() # We sort elements by precision lattice - elt_by_prec = { } - elt_other = [ ] - indices = { } + elt_by_prec = {} + elt_other = [] + indices = {} for i in range(len(elts)): x = elts[i] idx = id(x) @@ -683,7 +683,7 @@ def convert_multiple(self, *elts): raise NotImplementedError("multiple conversion of a set of variables for which the module precision is not a lattice is not implemented yet") for j in range(len(L)): x = L[j] - dx = [ ] + dx = [] for i in range(j): dx.append([L[i], lattice[i,j]]) prec = lattice[j,j].valuation(p) From 06e892e20709a632c65215ac17ca4a2220e175d8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Jun 2021 13:04:41 +0200 Subject: [PATCH 628/706] [ ] -> [] --- src/sage/rings/padics/lattice_precision.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index ade4a6950d0..9c8b8b55a11 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -595,7 +595,7 @@ def list(self, prec): return [] p = self.p x = ZZ(self.x * p**(self.exponent - val)) - l = [ ] + l = [] for _ in range(val, prec): x, digit = x.quo_rem(p) l.append(digit) From 7323b10d6c628c32adfeb62025879f909f707c61 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 14 Jun 2021 15:50:57 -0700 Subject: [PATCH 629/706] ConvexSet_base._test_contains: Only test extension to AA for exact base rings --- src/sage/geometry/convex_set.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8e672a94c68..d256ae1671b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -505,10 +505,11 @@ def _test_contains(self, tester=None, **options): if ambient_point is not None: tester.assertEqual(contains_space_point, self.contains(ambient_point)) tester.assertEqual(contains_space_point, self.contains(space_coords)) - from sage.rings.qqbar import AA - ext_space = self.ambient_vector_space(AA) - ext_space_point = ext_space(space_point) - tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + if space.base_ring().is_exact(): + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) from sage.symbolic.ring import SR symbolic_space = self.ambient_vector_space(SR) symbolic_space_point = symbolic_space(space_point) From 3d90c59461f5d1c656e6b972809c89468c92a334 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Tue, 15 Jun 2021 13:18:59 -0700 Subject: [PATCH 630/706] trac 31948: use src/doc/Makefile for docbuilding commands --- build/make/Makefile.in | 48 ++++---------------------- src/doc/Makefile | 78 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 0936f51a9ea..966ed94646f 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -337,60 +337,24 @@ DOC_DEPENDENCIES = sagelib sage_docbuild $(inst_sphinx) \ doc: doc-html -# Matches doc-inventory--reference-manifolds etc. -doc-inventory--%: - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory--,,$@)) inventory $(SAGE_DOCBUILD_OPTS)" logs/dochtml.log - -# Matches doc-html--developer, doc-html--reference-manifolds etc. -doc-html--%: - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS)" logs/dochtml.log - -doc-inventory-references: $(DOC_DEPENDENCIES) - $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) - $(eval biblio = $(firstword $(DOCS))) - $(eval other_docs = $(wordlist 2, 100, $(DOCS))) - +$(MAKE_REC) doc-inventory--$(subst /,-,$(biblio)) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-inventory--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference_top - -doc-html-references: doc-inventory-references - $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) - $(eval biblio = $(firstword $(DOCS))) - $(eval other_docs = $(wordlist 2, 100, $(DOCS))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--$(subst /,-,$(biblio)) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-html--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference_top - -doc-html-other: $(DOC_DEPENDENCIES) doc-html-references - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-html--$(subst /,-,$(doc))) - -doc-html: doc-html-references doc-html-other +doc-html: $(DOC_DEPENDENCIES) + cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-html" $(SAGE_ROOT)/logs/dochtml.log # 'doc-html-no-plot': build docs without building the graphics coming # from the '.. plot' directive, in case you want to save a few # megabytes of disk space. 'doc-clean' is a prerequisite because the # presence of graphics is cached in src/doc/output. doc-html-no-plot: doc-clean - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-plot" doc-html + sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) --no-plot\" doc-html" $(SAGE_ROOT)/logs/dochtml.log doc-html-mathjax: - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) -j" doc-html + sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) -j\" doc-html" $(SAGE_ROOT)/logs/dochtml.log # Keep target 'doc-html-jsmath' for backwards compatibility. doc-html-jsmath: doc-html-mathjax -# Matches doc-pdf--developer, doc-pdf--reference-manifolds etc. -doc-pdf--%: - $(AM_V_at)cd $(SAGE_ROOT) && sage-logger -p "./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-pdf--,,$@)) pdf $(SAGE_DOCBUILD_OPTS)" logs/docpdf.log - -doc-pdf: doc-inventory-references - $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) - $(eval biblio = $(firstword $(DOCS))) - $(eval other_docs = $(wordlist 2, 100, $(DOCS))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--$(subst /,-,$(biblio)) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(other_docs), doc-pdf--$(subst /,-,$(doc))) - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--reference_top - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-pdf--$(subst /,-,$(doc))) +doc-pdf: $(DOC_DEPENDENCIES) + cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-pdf" $(SAGE_ROOT)/logs/docpdf.log doc-clean: doc-src-clean doc-output-clean diff --git a/src/doc/Makefile b/src/doc/Makefile index de8689b56ea..ac2578843a1 100644 --- a/src/doc/Makefile +++ b/src/doc/Makefile @@ -1,7 +1,79 @@ -all: - @echo "Please build the doc using either 'make doc' from SAGE_ROOT, or" - @echo "'sage -docbuild all html'. See 'sage -docbuild help' for more informations." +# Silent rules +# https://www.gnu.org/software/automake/manual/html_node/Automake-Silent-Rules.html +ifeq ($(V), 0) +AM_V_at = @ +else +AM_V_at = +endif + +######################################################## +# +# 'make doc-html' (synonym for 'make' and 'make all') builds the html documentation. +# 'make doc-pdf' builds the PDF documentation. +# +# SAGE_ROOT must be defined for these to work, so these commands +# should be called by build/make/Makefile. +# +# 'make clean' removes build artifacts; SAGE_ROOT need not be defined for this to work. + +all: doc-html + clean: rm -rf en/reference/*/sage rm -rf en/reference/sage rm -f common/*.pyc + +# Matches doc-inventory--reference-manifolds etc. +doc-inventory--%: + $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory--,,$@)) inventory $(SAGE_DOCBUILD_OPTS) + +# Matches doc-html--developer, doc-html--reference-manifolds etc. +doc-html--%: + $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS) + +# reference manual, inventory +ifndef SAGE_ROOT +doc-inventory-reference: + $(error SAGE_ROOT undefined. This Makefile needs to be invoked by build/make/install) +else +doc-inventory-reference: + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval BIBLIO = $(firstword $(DOCS))) + $(eval OTHER_DOCS = $(wordlist 2, 100, $(DOCS))) + $(MAKE) doc-inventory--$(subst /,-,$(BIBLIO)) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(OTHER_DOCS), doc-inventory--$(subst /,-,$(doc))) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-inventory--reference_top +endif + +# reference manual, html +doc-html-reference: doc-inventory-reference + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval BIBLIO = $(firstword $(DOCS))) + $(eval OTHER_DOCS = $(wordlist 2, 100, $(DOCS))) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--$(subst /,-,$(BIBLIO)) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(OTHER_DOCS), doc-html--$(subst /,-,$(doc))) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-html--reference_top + +# other documentation, html +doc-html-other: doc-html-reference + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-html--$(subst /,-,$(doc))) + +doc-html: doc-html-reference doc-html-other + +# Matches doc-pdf--developer, doc-pdf--reference-manifolds etc. +doc-pdf--%: + $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-pdf--,,$@)) pdf $(SAGE_DOCBUILD_OPTS) + +doc-pdf: doc-inventory-reference + $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) + $(eval BIBLIO = $(firstword $(DOCS))) + $(eval OTHER_DOCS = $(wordlist 2, 100, $(DOCS))) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--$(subst /,-,$(BIBLIO)) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(OTHER_DOCS), doc-pdf--$(subst /,-,$(doc))) + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" doc-pdf--reference_top + $(MAKE) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-prune-empty-dirs" $(foreach doc, $(wordlist 2, 100, $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents all)), doc-pdf--$(subst /,-,$(doc))) + +.PHONY: all clean \ + doc-html doc-pdf \ + doc-inventory-reference doc-html-reference \ + doc-html-other From fafed318b79fd1fdba1b61aa1b794e39cc20c991 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 15 Jun 2021 16:21:29 -0500 Subject: [PATCH 631/706] Add _mul_ to allow for algebras and add ability for Representation class to take category as a keyword argument. --- src/sage/modules/with_basis/representation.py | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 3057de93f6c..097e5c5ca9d 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -152,7 +152,7 @@ class Representation(Representation_abstract): - :wikipedia:`Group_representation` """ - def __init__(self, semigroup, module, on_basis, side="left"): + def __init__(self, semigroup, module, on_basis, side="left", **kwargs): """ Initialize ``self``. @@ -164,18 +164,31 @@ def __init__(self, semigroup, module, on_basis, side="left"): sage: on_basis = lambda g,m: M.term(m, g.sign()) sage: R = Representation(G, M, on_basis) sage: R._test_representation() + + sage: G = SymmetricGroup(4) + sage: A = SymmetricGroup(4).algebra(QQ) + sage: from sage.categories.algebras import Algebras + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: A.monomial(g*x) + sage: category = Algebras(QQ).WithBasis().FiniteDimensional() + sage: R = Representation(G, A, action, 'left', category=category) + sage: r = R.an_element() + sage: r^2 + 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) + """ + + category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') self._left_repr = (side == "left") self._on_basis = on_basis self._module = module indices = module.basis().keys() - cat = Modules(module.base_ring()).WithBasis() if 'FiniteDimensional' in module.category().axioms(): - cat = cat.FiniteDimensional() + category = category.FiniteDimensional() Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, - category=cat, **module.print_options()) + category=category, **module.print_options()) def _test_representation(self, **options): """ @@ -369,10 +382,45 @@ def _acted_upon_(self, scalar, self_on_left=False): ret += P.linear_combination(((P._on_basis(ms, m), cs*c) for m,c in self), not self_on_left) return ret + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) _rmul_ = _lmul_ = _acted_upon_ + def _mul_(self, other): + """ + EXAMPLES:: + + sage: G = SymmetricGroup(4) + sage: A = G.algebra(QQ) + sage: from sage.categories.algebras import Algebras + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: A.monomial(g*x) + sage: category = Algebras(QQ).WithBasis().FiniteDimensional() + sage: R = Representation(G, A, action, 'left', category=category) + sage: r = R.an_element() + sage: r^2 + 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) + + TESTS:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['a','b','c','d']) + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: M.monomial( g(x.to_vector())) + sage: R = Representation(G, M, action, 'left') + sage: r = R.an_element() + sage: r*r + Traceback (most recent call last): + ... + AttributeError: 'CombinatorialFreeModule_with_category' object has no attribute '_product_from_product_on_basis_multiply' + """ + + if self.parent() is not other.parent(): + return NotImplemented + + return self.parent()._module._product_from_product_on_basis_multiply(self,other) + class RegularRepresentation(Representation): r""" The regular representation of a semigroup. From 54e68e068e64d7a8650c474cf5b24af91c0baad5 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Tue, 15 Jun 2021 19:03:42 -0700 Subject: [PATCH 632/706] trac 31948: move some "$(AM_V_at)"s around --- build/make/Makefile.in | 8 ++++---- src/doc/Makefile | 14 +++----------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 966ed94646f..8ba89d78c74 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -338,23 +338,23 @@ DOC_DEPENDENCIES = sagelib sage_docbuild $(inst_sphinx) \ doc: doc-html doc-html: $(DOC_DEPENDENCIES) - cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-html" $(SAGE_ROOT)/logs/dochtml.log + $(AM_V_at)cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-html" $(SAGE_ROOT)/logs/dochtml.log # 'doc-html-no-plot': build docs without building the graphics coming # from the '.. plot' directive, in case you want to save a few # megabytes of disk space. 'doc-clean' is a prerequisite because the # presence of graphics is cached in src/doc/output. doc-html-no-plot: doc-clean - sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) --no-plot\" doc-html" $(SAGE_ROOT)/logs/dochtml.log + $(AM_V_at)sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) --no-plot\" doc-html" $(SAGE_ROOT)/logs/dochtml.log doc-html-mathjax: - sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) -j\" doc-html" $(SAGE_ROOT)/logs/dochtml.log + $(AM_V_at)sage-logger -p "$(MAKE) SAGE_DOCBUILD_OPTS=\"$(SAGE_DOCBUILD_OPTS) -j\" doc-html" $(SAGE_ROOT)/logs/dochtml.log # Keep target 'doc-html-jsmath' for backwards compatibility. doc-html-jsmath: doc-html-mathjax doc-pdf: $(DOC_DEPENDENCIES) - cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-pdf" $(SAGE_ROOT)/logs/docpdf.log + $(AM_V_at)cd "$(SAGE_SRC)/doc" && sage-logger -p "$(MAKE) doc-pdf" $(SAGE_ROOT)/logs/docpdf.log doc-clean: doc-src-clean doc-output-clean diff --git a/src/doc/Makefile b/src/doc/Makefile index ac2578843a1..b10ae80a147 100644 --- a/src/doc/Makefile +++ b/src/doc/Makefile @@ -1,11 +1,3 @@ -# Silent rules -# https://www.gnu.org/software/automake/manual/html_node/Automake-Silent-Rules.html -ifeq ($(V), 0) -AM_V_at = @ -else -AM_V_at = -endif - ######################################################## # # 'make doc-html' (synonym for 'make' and 'make all') builds the html documentation. @@ -25,11 +17,11 @@ clean: # Matches doc-inventory--reference-manifolds etc. doc-inventory--%: - $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory--,,$@)) inventory $(SAGE_DOCBUILD_OPTS) + cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-inventory--,,$@)) inventory $(SAGE_DOCBUILD_OPTS) # Matches doc-html--developer, doc-html--reference-manifolds etc. doc-html--%: - $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS) + cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-html--,,$@)) html $(SAGE_DOCBUILD_OPTS) # reference manual, inventory ifndef SAGE_ROOT @@ -62,7 +54,7 @@ doc-html: doc-html-reference doc-html-other # Matches doc-pdf--developer, doc-pdf--reference-manifolds etc. doc-pdf--%: - $(AM_V_at)cd $(SAGE_ROOT) && ./sage --docbuild --no-pdf-links $(subst -,/,$(subst doc-pdf--,,$@)) pdf $(SAGE_DOCBUILD_OPTS) + cd $(SAGE_ROOT) && ./sage --docbuild $(subst -,/,$(subst doc-pdf--,,$@)) pdf $(SAGE_DOCBUILD_OPTS) doc-pdf: doc-inventory-reference $(eval DOCS = $(shell cd $(SAGE_ROOT) && ./sage --docbuild --all-documents reference)) From b150e7191f877f9a04bc9f7cd4cea4b0b71cb76f Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Wed, 16 Jun 2021 08:10:07 +0200 Subject: [PATCH 633/706] 31775: correction of a typo --- src/sage/interfaces/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 38d17c8105e..7610a8e63ba 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -333,7 +333,7 @@ def _coerce_from_special_method(self, x): def _coerce_impl(self, x, use_special=True): r""" - Coerce pur Python types via corresponding Sage objects. + Coerce pure Python types via corresponding Sage objects. TESTS: From 94e68582fde0f5bb8b2c57e5f7aa56dbaa5e825a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:21:21 -0700 Subject: [PATCH 634/706] RelativeInterior.ambient, ambient_vector_space, is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3fd2f6c6b54..c714faabcb9 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -69,6 +69,34 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient(self): + r""" + Return the ambient convex set or space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient() + """ + return self._polyhedron.ambient() + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_vector_space() + """ + return self._polyhedron.ambient_vector_space(base_field=base_field) + def ambient_dim(self): r""" Return the dimension of the ambient space. From 0c9bc945a59ffbf38c59d3679036bf4c90a516fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 16 Jun 2021 09:16:27 -0700 Subject: [PATCH 635/706] ConvexSet_base: Add default implementations of ambient, ambient_dim; add doctests --- src/sage/geometry/convex_set.py | 45 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d256ae1671b..c6dc7967e4d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,8 @@ def dim(self): r""" Return the dimension of ``self``. + Subclasses must provide an implementation of this method. + TESTS:: sage: from sage.geometry.convex_set import ConvexSet_base @@ -94,28 +96,55 @@ def dimension(self): def ambient_vector_space(self, base_field=None): r""" Return the ambient vector space. + + Subclasses must provide an implementation of this method. + + The default implementations of :meth:`ambient`, :meth:`ambient_dim`, + :meth:`ambient_dimension` use this method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_vector_space() + Traceback (most recent call last): + ... + NotImplementedError: """ - @abstract_method def ambient(self): r""" Return the ambient convex set or space. + + The default implementation delegates to :meth:`ambient_vector_space`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_vector_space(self, base_field=None): + ....: return (base_field or QQ)^2001 + sage: ExampleSet().ambient_dim() + 2001 """ + return self.ambient_vector_space() - @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. - TESTS:: + The default implementation obtains it from :meth:`ambient`. + + EXAMPLES:: sage: from sage.geometry.convex_set import ConvexSet_base - sage: C = ConvexSet_base() - sage: C.ambient_dim() - Traceback (most recent call last): - ... - NotImplementedError: + sage: class ExampleSet(ConvexSet_base): + ....: def ambient(self): + ....: return QQ^7 + sage: ExampleSet().ambient_dim() + 7 """ + return self.ambient().dimension() def ambient_dimension(self): r""" From 53f260c1b355fc623cf8feed858614a87133028d Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 16 Jun 2021 16:32:54 -0700 Subject: [PATCH 636/706] trac 31948: fix doctests --- src/sage_docbuild/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index deee8ef7781..b63e53539ed 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -638,7 +638,7 @@ def _output_dir(self, type, lang=None): EXAMPLES:: sage: from sage_docbuild import ReferenceTopBuilder - sage: b = ReferenceTopBuilder() + sage: b = ReferenceTopBuilder('reference') sage: b._output_dir('html') '.../html/en/reference' """ From e767248a97138680b8a25a971880cd8fb1cecfc5 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 16 Jun 2021 22:54:40 -0500 Subject: [PATCH 637/706] Fix multiplication and add tests --- src/sage/modules/with_basis/representation.py | 100 ++++++++++++------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 097e5c5ca9d..09eb8cd6748 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -165,6 +165,17 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: R = Representation(G, M, on_basis) sage: R._test_representation() + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: E.monomial(tuple(g(i+1)-1 for i in m[0])) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) + sage: r = R.an_element(); r + 1 + 2*x0 + x0*x1 + 3*x1 + sage: r*r + 1 + 4*x0 + 2*x0*x1 + 6*x1 + sage: G = SymmetricGroup(4) sage: A = SymmetricGroup(4).algebra(QQ) sage: from sage.categories.algebras import Algebras @@ -177,16 +188,25 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) """ + try: + self.product_on_basis = module.product_on_basis + except AttributeError: + pass category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) + if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') + self._left_repr = (side == "left") self._on_basis = on_basis self._module = module + indices = module.basis().keys() + if 'FiniteDimensional' in module.category().axioms(): category = category.FiniteDimensional() + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, category=category, **module.print_options()) @@ -286,6 +306,51 @@ def _element_constructor_(self, x): return self._from_dict(x.monomial_coefficients(copy=False), remove_zeros=False) return super(Representation, self)._element_constructor_(x) + def product_by_coercion(self, left, right): + """ + Return the product of ``left`` and ``right`` by passing to ``self._module`` + and then building a new element of ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.KleinFour() + sage: E = algebras.Exterior(QQ,'e',4) + sage: on_basis = lambda g,m: E.monomial(m) # the trivial representation + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, E, on_basis) + sage: r = R.an_element(); r + 1 + 2*e0 + 3*e1 + e1*e2 + sage: g = G.an_element(); + sage: g*r == r + True + sage: r*r + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: + 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and + 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' + + sage: from sage.categories.algebras import Algebras + sage: category = Algebras(QQ).FiniteDimensional().WithBasis() + sage: T = Representation(G, E, on_basis, category = category) + sage: t = T.an_element(); t + 1 + 2*e0 + 3*e1 + e1*e2 + sage: g*t == t + True + sage: t*t + 1 + 4*e0 + 4*e0*e1*e2 + 6*e1 + 2*e1*e2 + + """ + M = self._module + + # Multiply in self._module + p = M._from_dict(left._monomial_coefficients, False, False) * M._from_dict(right._monomial_coefficients, False, False) + + # Convert from a term in self._module to a term in self + return self._from_dict(p.monomial_coefficients(copy=False), False, False) + def side(self): """ Return whether ``self`` is a left or a right representation. @@ -306,6 +371,7 @@ def side(self): """ return "left" if self._left_repr else "right" + class Element(CombinatorialFreeModule.Element): def _acted_upon_(self, scalar, self_on_left=False): """ @@ -387,40 +453,6 @@ def _acted_upon_(self, scalar, self_on_left=False): _rmul_ = _lmul_ = _acted_upon_ - def _mul_(self, other): - """ - EXAMPLES:: - - sage: G = SymmetricGroup(4) - sage: A = G.algebra(QQ) - sage: from sage.categories.algebras import Algebras - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda g,x: A.monomial(g*x) - sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category=category) - sage: r = R.an_element() - sage: r^2 - 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) - - TESTS:: - - sage: G = SymmetricGroup(4) - sage: M = CombinatorialFreeModule(QQ, ['a','b','c','d']) - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda g,x: M.monomial( g(x.to_vector())) - sage: R = Representation(G, M, action, 'left') - sage: r = R.an_element() - sage: r*r - Traceback (most recent call last): - ... - AttributeError: 'CombinatorialFreeModule_with_category' object has no attribute '_product_from_product_on_basis_multiply' - """ - - if self.parent() is not other.parent(): - return NotImplemented - - return self.parent()._module._product_from_product_on_basis_multiply(self,other) - class RegularRepresentation(Representation): r""" The regular representation of a semigroup. From ce91e44231c897ad00c4d838d2e4f081afcc6ff9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 14 Jun 2021 20:08:10 -0700 Subject: [PATCH 638/706] src/sage/geometry/relative_interior.py: Fix doctest output --- src/sage/geometry/relative_interior.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index c714faabcb9..9e49420a5e4 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -80,6 +80,7 @@ def ambient(self): Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient() + Vector space of dimension 2 over Rational Field """ return self._polyhedron.ambient() @@ -94,6 +95,7 @@ def ambient_vector_space(self, base_field=None): Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient_vector_space() + Vector space of dimension 2 over Rational Field """ return self._polyhedron.ambient_vector_space(base_field=base_field) From f0e7c5864eeea17190b0657accf9534fcafa0c89 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 17 Jun 2021 05:35:13 -0700 Subject: [PATCH 639/706] ambient_vector_space docstring: Fix bad blocks --- src/sage/geometry/cone.py | 2 +- src/sage/geometry/lattice_polytope.py | 2 +- src/sage/geometry/polyhedron/base.py | 2 +- src/sage/geometry/polyhedron/face.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index fd2074676c7..78d0cb0444f 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -987,7 +987,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient lattice (:meth:`lattice`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the rationals) a field. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index bde2f0a6be7..9d42d71f275 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2623,7 +2623,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient lattice (:meth:`lattice`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the rationals) a field. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 82af7e683fc..b9c45c511d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2487,7 +2487,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient free module (:meth:`Vrepresentation_space`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the fraction field of the base ring) a field. diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 56305d035e8..ede09a3e1d9 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -684,7 +684,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient free module of the containing polyhedron tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the fraction field of the base ring) a field. From 200d967982e2d4c600658354ef80a15f1ed0ccd8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 17 Jun 2021 05:58:51 -0700 Subject: [PATCH 640/706] ConvexSet_base.ambient doctest: Actually test the method --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index c6dc7967e4d..d43ba7dbfff 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -124,8 +124,8 @@ def ambient(self): sage: class ExampleSet(ConvexSet_base): ....: def ambient_vector_space(self, base_field=None): ....: return (base_field or QQ)^2001 - sage: ExampleSet().ambient_dim() - 2001 + sage: ExampleSet().ambient() + Vector space of dimension 2001 over Rational Field """ return self.ambient_vector_space() From 1f65c2d903775ded4e91cfd630d17a1b86cc2592 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 17 Jun 2021 16:13:09 +0100 Subject: [PATCH 641/706] Documentation edited and integrate_vector changed As per trac ticket 30698, the documentation was edited to include an example at the beginning. Moreover, the integrate_vector method was changed to allow the use to give the desired number of nodes rather than a desirec error bound. This change was reflected in the documentation. --- src/sage/numerical/gauss_legendre.pyx | 86 +++++++++++++++++---------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index d03280070df..f236b3968a4 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -1,18 +1,31 @@ r""" -Gauss-Legendre integration for vector-valued functions +Gauss-Legendre Integration for Vector-Valued Functions Routine to perform Gauss-Legendre integration for vector-functions. -EXAMPLES:: +EXAMPLES: + +We verify that `\int_0^1 n x^{n-1} \, dx = 1` for `n=1, \dots, 4`:: + + sage: from sage.numerical.gauss_legendre import integrate_vector + sage: prec = 100 + sage: K = RealField(prec) + sage: N = 4 + sage: V = VectorSpace(K,N) + sage: f = lambda x: V([(n+1)*x^n for n in range(N)]) + sage: I = integrate_vector(f,prec) + sage: max([c.abs() for c in I-V(N*[1])]) + 0.00000000000000000000000000000 AUTHORS: - Nils Bruin (2017-06-06): initial version + - Linden Disney-Hogg (2021-06-17): documentation and integrate_vector method changes -NOTE: +NOTE:: -The code here is directly based on mpmath (see http://mpmath.org), but has a highly -optimized routine to compute the nodes. + The code here is directly based on mpmath (see http://mpmath.org), but has a highly + optimized routine to compute the nodes. """ # **************************************************************************** @@ -39,9 +52,9 @@ from sage.misc.cachefunc import cached_function from sage.rings.real_mpfr cimport RealNumber, RealField_class @cached_function -def nodes(degree,prec): +def nodes(degree, prec): r""" - Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme. + Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme using a version of the REC algorithm. INPUT: @@ -53,7 +66,7 @@ def nodes(degree,prec): A list of (node,weight) pairs. - EXAMPLES:: + EXAMPLES: The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. The weights can be computed by a straightforward formula (note that evaluating @@ -61,10 +74,10 @@ def nodes(degree,prec): from this routine are actually more accurate than what the values the closed formula produces):: sage: from sage.numerical.gauss_legendre import nodes - sage: L1=nodes(24,53) - sage: P=RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) - sage: Pdif=P.diff() - sage: L2=[( (r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] + sage: L1 = nodes(24,53) + sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) + sage: Pdif = P.diff() + sage: L2 = [((r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] sage: all((a[0]-b[0]).abs() < 10^-15 and (a[1]-b[1]).abs() < 10^-9 for a,b in zip(L1,L2)) True """ @@ -121,7 +134,7 @@ def nodes(degree,prec): mpfr_clear(v) return nodes -def estimate_error(results,prec,epsilon): +def estimate_error(results, prec, epsilon): r""" Routine to estimate the error in a list of quadrature approximations. @@ -174,12 +187,12 @@ def estimate_error(results,prec,epsilon): e.append(D4.exp()) return max(e) -def integrate_vector(f,prec,epsilon=None): +def integrate_vector(f, prec, N=None, epsilon=None): r""" Integrate a one-argument vector-valued function numerically using Gauss-Legendre. This function uses the Gauss-Legendre quadrature scheme to approximate - the integral of f(t) for t=0..1. + the integral `\int_0^1 f(t) \, dt`. INPUT: @@ -187,7 +200,9 @@ def integrate_vector(f,prec,epsilon=None): - ``prec`` -- integer. Binary precision to be used. - - ``epsilon`` -- Multiprecision float. Target error bound. + - ``N`` -- integer. Number of nodes to use. If specificed, the target error ``epsilon`` is ignored. + + - ``epsilon`` -- multiprecision float (default: `2^{(-\text{prec}+3)}`). Target error bound. OUTPUT: @@ -196,38 +211,45 @@ def integrate_vector(f,prec,epsilon=None): EXAMPLES:: sage: from sage.numerical.gauss_legendre import integrate_vector - sage: prec=200 - sage: K=RealField(prec) - sage: V=VectorSpace(K,2) - sage: epsilon=K(2^(-prec+4)) - sage: f=lambda t:V((1+t^2,1/(1+t^2))) - sage: I=integrate_vector(f,prec,epsilon) - sage: J=V((4/3,pi/4)) + sage: prec = 200 + sage: K = RealField(prec) + sage: V = VectorSpace(K,2) + sage: epsilon = K(2^(-prec+4)) + sage: f = lambda t:V((1+t^2,1/(1+t^2))) + sage: I = integrate_vector(f, prec, epsilon) + sage: J = V((4/3,pi/4)) sage: max(c.abs() for c in (I-J)) < epsilon True We can also use complex-valued integrands:: - sage: prec=200 - sage: Kreal=RealField(prec) - sage: K=ComplexField(prec) - sage: V=VectorSpace(K,2) - sage: epsilon=Kreal(2^(-prec+4)) - sage: f=lambda t: V((t,K(exp(2*pi*t*K.0)))) - sage: I=integrate_vector(f,prec,epsilon) - sage: J=V((1/2,0)) + sage: prec = 200 + sage: Kreal = RealField(prec) + sage: K = ComplexField(prec) + sage: V = VectorSpace(K,2) + sage: epsilon = Kreal(2^(-prec+4)) + sage: f = lambda t: V((t,K(exp(2*pi*t*K.0)))) + sage: I = integrate_vector(f, prec, epsilon) + sage: J = V((1/2,0)) sage: max(c.abs() for c in (I-J)) < epsilon True """ results = [] cdef long degree = 3 Rout = RealField(prec) + if N is not None: + nodelist = nodes(N,prec) + I = nodelist[0][1]*f(nodelist[0][0]) + for i in range(1,len(nodelist)): + I += nodelist[i][1]*f(nodelist[i][0]) + return I + if epsilon is None: epsilon = Rout(2)**(-prec+3) while True: nodelist = nodes(degree,prec) I = nodelist[0][1]*f(nodelist[0][0]) - for i in xrange(1,len(nodelist)): + for i in range(1,len(nodelist)): I += nodelist[i][1]*f(nodelist[i][0]) results.append(I) if degree > 3: From 374207fcc1597e811b9cc9b94790f56d17d36d97 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 17 Jun 2021 20:57:00 -0500 Subject: [PATCH 642/706] Fix cyclic action on generators of ExteriorAlgebra, add tests for the multiplication of group elements on the group algebra as a representation --- src/sage/modules/with_basis/representation.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 09eb8cd6748..96fabf36c6e 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -168,13 +168,24 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: E.monomial(tuple(g(i+1)-1 for i in m[0])) #cyclically permute generators + sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators sage: from sage.categories.algebras import Algebras sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) sage: r = R.an_element(); r 1 + 2*x0 + x0*x1 + 3*x1 sage: r*r 1 + 4*x0 + 2*x0*x1 + 6*x1 + sage: x0, x1, x2 = M.gens() + sage: s = R(x0*x1) + sage: g = G.an_element() + sage: g*s + x1*x2 + sage: g*R(x1*x2) + -x0*x2 + sage: g*r + 1 + 2*x1 + x1*x2 + 3*x2 + sage: g^2*r + 1 + 3*x0 - x0*x2 + 2*x2 sage: G = SymmetricGroup(4) sage: A = SymmetricGroup(4).algebra(QQ) @@ -182,11 +193,15 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g,x: A.monomial(g*x) sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category=category) - sage: r = R.an_element() + sage: R = Representation(G, A, action, 'left', category = category) + sage: r = R.an_element(); r + () + (2,3,4) + 2*(1,3)(2,4) + 3*(1,4)(2,3) sage: r^2 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) - + sage: g = G.an_element(); g + (2,3,4) + sage: g*r + (2,3,4) + (2,4,3) + 2*(1,3,2) + 3*(1,4,3) """ try: self.product_on_basis = module.product_on_basis From f02ca284d4c7b886f5b185db5e6b6d6a8bc4a039 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 18 Jun 2021 10:45:30 -0700 Subject: [PATCH 643/706] src/sage/geometry/polyhedron/face.py: Remove unused import --- src/sage/geometry/polyhedron/face.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index ede09a3e1d9..61644b896f4 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -73,7 +73,6 @@ # http://www.gnu.org/licenses/ ######################################################################## -from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp from sage.misc.all import cached_method from sage.modules.free_module_element import vector From bc84af8c795b7da433d2000afc3626ee65ba28b8 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 18 Jun 2021 14:37:48 -0700 Subject: [PATCH 644/706] trac 31696: fixes for Japanese documentation, PDF build --- src/doc/ja/a_tour_of_sage/conf.py | 10 ---------- src/doc/ja/tutorial/conf.py | 13 ------------- 2 files changed, 23 deletions(-) diff --git a/src/doc/ja/a_tour_of_sage/conf.py b/src/doc/ja/a_tour_of_sage/conf.py index edcad0cf018..ab57a150cbb 100644 --- a/src/doc/ja/a_tour_of_sage/conf.py +++ b/src/doc/ja/a_tour_of_sage/conf.py @@ -37,13 +37,3 @@ ('index', name + '.tex', project, 'The Sage Group', 'manual'), ] - -# LaTeX の docclass 設定 -latex_docclass = {'manual': 'jsbook'} - -# Additional LaTeX stuff for the French version -#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - -# the definition of \\at in the standard preamble of the sphinx doc -# conflicts with that in babel/french[b] -latex_elements['preamble'] += '\\let\\at\\undefined' diff --git a/src/doc/ja/tutorial/conf.py b/src/doc/ja/tutorial/conf.py index 141629139f0..520f4cc46f3 100644 --- a/src/doc/ja/tutorial/conf.py +++ b/src/doc/ja/tutorial/conf.py @@ -37,16 +37,3 @@ ('index', name + '.tex', project, 'The Sage Group', 'manual'), ] - -# LaTeX の docclass 設定 -latex_docclass = {'manual': 'jsbook'} - -# Additional LaTeX stuff for the French version -#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - -# the definition of \\at in the standard preamble of the sphinx doc -# conflicts with that in babel/french[b] -latex_elements['preamble'] += '\\let\\at\\undefined' - -# -# html_use_smartypants = False From 5351ebbfdd14067d008ae30b9acb76ba8bead936 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 18 Jun 2021 17:53:45 -0500 Subject: [PATCH 645/706] Fix PEP8 spacing issue, simplify tuple --- src/sage/modules/with_basis/representation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 96fabf36c6e..27fffed163d 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -168,9 +168,9 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) sage: r = R.an_element(); r 1 + 2*x0 + x0*x1 + 3*x1 sage: r*r @@ -193,7 +193,7 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g,x: A.monomial(g*x) sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category = category) + sage: R = Representation(G, A, action, 'left', category=category) sage: r = R.an_element(); r () + (2,3,4) + 2*(1,3)(2,4) + 3*(1,4)(2,3) sage: r^2 @@ -349,7 +349,7 @@ def product_by_coercion(self, left, right): sage: from sage.categories.algebras import Algebras sage: category = Algebras(QQ).FiniteDimensional().WithBasis() - sage: T = Representation(G, E, on_basis, category = category) + sage: T = Representation(G, E, on_basis, category=category) sage: t = T.an_element(); t 1 + 2*e0 + 3*e1 + e1*e2 sage: g*t == t From 4c0a4ae74c28680e48080c39c5e3b171a79a9ad3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 19 Jun 2021 13:04:17 +0200 Subject: [PATCH 646/706] initialize do_f_vector --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 8f2864f77af..54f9c66460b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -3004,7 +3004,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef size_t current_length = 1 # dynamically enlarge **edges cdef int output_dim_init = 1 if do_edges else dim - 2 - cdef bint do_f_vector + cdef bint do_f_vector = False cdef size_t* f_vector if dim == 1 and (do_edges or self.n_facets() > 1): From 46c5c53b4dea44da56f0ba2f72c839ad6243bd0e Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sat, 19 Jun 2021 18:07:57 +0200 Subject: [PATCH 647/706] Updated SageMath version to 9.4.beta2 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index e567adc1a12..4047e93f4c2 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.4.beta1", - "version": "9.4.beta1", + "title": "sagemath/sage: 9.4.beta2", + "version": "9.4.beta2", "upload_type": "software", - "publication_date": "2021-06-06", + "publication_date": "2021-06-19", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.4.beta1", + "identifier": "https://github.com/sagemath/sage/tree/9.4.beta2", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 032560869f7..079eef3212d 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.4.beta1, Release Date: 2021-06-06 +SageMath version 9.4.beta2, Release Date: 2021-06-19 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index e04af335405..c736d90d325 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=99cc85673ad9849560b64a2bdda5105cad8d005c -md5=8be851ed8a493cb8860872afd7d64a20 -cksum=1816015983 +sha1=9dab15e2209bd5515c3822aeb6beb911dc9a6084 +md5=e76f8e7f8be26c9729dd010f1a93c245 +cksum=641170247 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 202fa5ca0ed..55d4006c1a2 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -e2f8dfb096197ef2e7ab351d40a2cb8a41fe4a4b +8b70b5bba76ffd6bd0f1c2441afc249f5046a612 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index dd1b37884f3..bd33b958bae 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.4.beta1 +9.4.beta2 diff --git a/src/VERSION.txt b/src/VERSION.txt index dd1b37884f3..bd33b958bae 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.4.beta1 +9.4.beta2 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 8b31e9a4d7a..a7af79ec572 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.4.beta1' -SAGE_RELEASE_DATE='2021-06-06' -SAGE_VERSION_BANNER='SageMath version 9.4.beta1, Release Date: 2021-06-06' +SAGE_VERSION='9.4.beta2' +SAGE_RELEASE_DATE='2021-06-19' +SAGE_VERSION_BANNER='SageMath version 9.4.beta2, Release Date: 2021-06-19' diff --git a/src/sage/version.py b/src/sage/version.py index bf5f5872b81..c1c207a1ee9 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.4.beta1' -date = '2021-06-06' -banner = 'SageMath version 9.4.beta1, Release Date: 2021-06-06' +version = '9.4.beta2' +date = '2021-06-19' +banner = 'SageMath version 9.4.beta2, Release Date: 2021-06-19' From cfe09c6a3fff933d0e0a0a8d4f3a5ab3c950f2d5 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 31 May 2021 15:01:42 -0500 Subject: [PATCH 648/706] Include submodules in find_namespace_packages --- src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup.py b/src/setup.py index df8242c71a0..b5738a11dd5 100755 --- a/src/setup.py +++ b/src/setup.py @@ -76,7 +76,7 @@ log.debug(f"files_to_exclude = {files_to_exclude}") -python_packages = find_namespace_packages(where=SAGE_SRC, include=['sage', 'sage_setup']) +python_packages = find_namespace_packages(where=SAGE_SRC, include=['sage', 'sage_setup', 'sage.*', 'sage_setup.*']) log.debug(f"python_packages = {python_packages}") log.info(f"Discovered Python/Cython sources, time: {(time.time() - t):.2f} seconds.") From 24a1ac21968d99172d7ac99dcf630437818763f7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Jun 2021 12:30:36 -0700 Subject: [PATCH 649/706] src/setup.py: Add comment --- src/setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/setup.py b/src/setup.py index b5738a11dd5..497d0468230 100755 --- a/src/setup.py +++ b/src/setup.py @@ -1,5 +1,11 @@ #!/usr/bin/env python +## This version of setup.py is used by the Sage distribution +## only when configure --enable-editable has been used. +## +## Distribution packaging should use build/pkgs/sagelib/src/setup.py +## instead. + from __future__ import print_function import os From cca587d298600ba6cc2a0172788bce2805540f1d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Jun 2021 12:31:02 -0700 Subject: [PATCH 650/706] src/setup.py: Do not just catch exceptions and continue --- src/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup.py b/src/setup.py index 497d0468230..f75900f6a0e 100755 --- a/src/setup.py +++ b/src/setup.py @@ -111,7 +111,7 @@ nthreads=4) except Exception as exception: log.warn(f"Exception while generating and cythonizing source files: {exception}") - extensions = None + raise # ######################################################## # ## Distutils From 297792afd93108f916b60674e7a748bad2609087 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 20 Jun 2021 01:16:58 +0200 Subject: [PATCH 651/706] equalities -> equations for #31821 as well --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 530ee8dea22..0dc365fe999 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2785,7 +2785,7 @@ cdef class CombinatorialPolyhedron(SageObject): return (self.n_facets() == other.n_facets() and self.Vrepresentation() == other.Vrepresentation() and self.facet_names() == other_C.facet_names() - and self.equalities() == other_C.equalities() + and self.equations() == other_C.equations() and self.dimension() == other.dimension() and self.far_face_tuple() == other_C.far_face_tuple() and self.incidence_matrix() == other.incidence_matrix()) From e1b5b5e178cc887bbad14daaafcd417aeef988be Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:05:41 +0200 Subject: [PATCH 652/706] join_of_Vrep and meet_of_facets for face iterator --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 426 +++++++++++++++++- 2 files changed, 425 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 25a7014c33e..2bfd0d5f114 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -69,6 +69,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int find_face(self, face_t face) except -1 @cython.final cdef class FaceIterator(FaceIterator_base): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 3e83ff5edb9..2ce3dc7f513 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -176,7 +176,7 @@ AUTHOR: from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check -from .conversions cimport bit_rep_to_Vrep_list +from .conversions cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron @@ -482,6 +482,391 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() + def meet_of_facets(self, *indices): + r""" + Construct the meet of the facets indicated by the indices. + + This is the largest face contained in all facets with the given indices. + + The iterator must be reseted if not newly initialized. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.meet_of_facets(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 3) + sage: it.meet_of_facets(1,5).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.meet_of_facets().ambient_H_indices() + () + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 2, 3, 4) + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,6).ambient_H_indices() + (1, 6) + sage: it.meet_of_facets(1,2,6).ambient_H_indices() + (1, 2, 6, 7) + sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + + sage: s = cones.schur(4) + sage: C = CombinatorialPolyhedron(s) + sage: it = C.face_iter() + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,2,3).ambient_H_indices() + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.meet_of_facets(9,11) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.meet_of_facets(9,11).ambient_H_indices() + (9, 11) + + """ + if self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def join_of_Vrep(self, *indices): + r""" + Construct the join of the Vrepresentatives indicated by the indices. + + This is the smallest face containing all Vrepresentatives with the given indices. + + The iterator must be reseted if not newly initialized. + + .. NOTE:: + + In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + may not te well defined. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (0, 1, 2, 3) + sage: it.join_of_Vrep(1,5).ambient_V_indices() + (0, 1, 5, 6) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.join_of_Vrep().ambient_V_indices() + () + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (1, 3) + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,6).ambient_V_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + sage: it.join_of_Vrep(8) + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.join_of_Vrep(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.join_of_Vrep(1,10).ambient_V_indices() + (1, 10) + + In case of an unbounded polyhedron, we try to make sense of the input:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0, 1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: it.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + + sage: P = Polyhedron(rays=[[1,0], [0,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + """ + if not self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def _meet_of_coatoms(self, *indices): + r""" + Construct the meet of the coatoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In non-dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._meet_of_coatoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._meet_of_coatoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._meet_of_coatoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._meet_of_coatoms(-1) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to size_t + sage: it._meet_of_coatoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + + The empty face is detected correctly, even with lines or rays:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(1,2,4,5) + A -1-dimensional face of a Polyhedron in ZZ^4 + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(0) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(1, 2) + A -1-dimensional face of a Polyhedron in QQ^2 + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef size_t i + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + for i in indices: + assert 0 <= i < n_coatoms, "index out of range" + face_intersection(face, face, coatoms.data.faces[i]) + + if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The meet is contained in the far face and therefore is the empty face. + face_clear(face) + + self.find_face(face) + output = self.current() + self.reset() + return output + + def _join_of_atoms(self, *indices): + r""" + Construct the join of atoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._join_of_atoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In non-dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._join_of_atoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it._join_of_atoms(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it._join_of_atoms(1,10).ambient_V_indices() + (1, 10) + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._join_of_atoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._join_of_atoms(-1) + Traceback (most recent call last): + ... + AssertionError: index out of range + sage: it._join_of_atoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef face_t pseudo_face = face_mem.data.faces[1] + cdef size_t i + + assert all(i in range(n_atoms) for i in indices), "index out of range" + + # Initialize a pseudo_face as indicated by the indices. + for i in indices: + face_add_atom(pseudo_face, i) + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + # Now we intersect all faces that contain our pseudo_face. + for i in range(n_coatoms): + if face_issubset(pseudo_face, coatoms.data.faces[i]): + face_intersection(face, face, coatoms.data.faces[i]) + + if not indices: + # The neutral element of the join. + face_clear(face) + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The join is not well-defined. + # We allow for unbounded polyhedra to compute the join, even with rays. + # However, the result is not necesarrily well-defined. + raise ValueError("the join is not well-defined") + + self.find_face(face) + output = self.current() + self.reset() + return output + cdef int ignore_subsets(self) except -1: r""" Ignore sub-/supfaces of the current face. @@ -573,6 +958,43 @@ cdef class FaceIterator_base(SageObject): """ return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep) + cdef int find_face(self, face_t face) except -1: + """ + Iterate until the current face is ``face``. + + The value can then be obtained with :meth:`current`. + """ + cdef size_t n_atoms = face_len_atoms(face) + + if n_atoms == self.coatoms.n_atoms(): + # The face is the universe. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = self.structure.dimension + return 0 + elif n_atoms == 0: + # The face is the empty face. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = -1 + return 0 + + cdef int d = self.next_dimension() + while self.structure.current_dimension != self.structure.dimension: + if face_issubset(face, self.structure.face): + if face_issubset(self.structure.face, face): + # Found our face. + return 0 + else: + # The face is not a subface/supface of the current face. + self.ignore_subsets() + + d = self.next_dimension() + + raise ValueError("the face appears to be incorrect") + + + cdef class FaceIterator(FaceIterator_base): r""" A class to iterate over all combinatorial faces of a polyhedron. @@ -1037,7 +1459,7 @@ cdef class FaceIterator_geom(FaceIterator_base): .. SEEALSO:: - See :class:`FaceIterator`. + :class:`FaceIterator_base`. """ def __init__(self, P, dual=None, output_dimension=None): r""" From 99a1d1645cce1728dfa2c8fec3a30a5dc48d1a5c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:21:02 +0200 Subject: [PATCH 653/706] expose in combinatorial_polyhedron --- .../combinatorial_polyhedron/base.pyx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0dc365fe999..18b84d148f1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2262,6 +2262,56 @@ cdef class CombinatorialPolyhedron(SageObject): return (False, None) return False + def join_of_Vrep(self, *indices): + r""" + Return the smallest face containing all Vrepresentatives indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.join_of_Vrep`. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(4) + sage: C = CombinatorialPolyhedron(P) + sage: C.join_of_Vrep(0,1) + A 1-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.join_of_Vrep(0,3).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(8).ambient_V_indices() + (8,) + sage: C.join_of_Vrep().ambient_V_indices() + () + """ + return self.face_iter().join_of_Vrep(*indices) + + def meet_of_facets(self, *indices): + r""" + Return the largest face contained all facets indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + + EXAMPLES:: + + sage: P = polytopes.dodecahedron() + sage: C = CombinatorialPolyhedron(P) + sage: C.meet_of_facets(0) + A 2-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.meet_of_facets(0).ambient_H_indices() + (0,) + sage: C.meet_of_facets(0,1).ambient_H_indices() + (0, 1) + sage: C.meet_of_facets(0,3).ambient_H_indices() + (0, 3) + sage: C.meet_of_facets(0,2,3).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + sage: C.meet_of_facets().ambient_H_indices() + () + """ + return self.face_iter().meet_of_facets(*indices) + def face_iter(self, dimension=None, dual=None): r""" Iterator over all proper faces of specified dimension. From cc9f594650a2f6ed8ff1057cec46e5baf1b1adb3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 13:44:48 +0200 Subject: [PATCH 654/706] raise index error for index error; fix doctests --- src/sage/geometry/polyhedron/base.py | 6 ++++++ .../polyhedron/combinatorial_polyhedron/base.pyx | 8 ++++---- .../combinatorial_polyhedron/face_iterator.pyx | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e082c73d111..999b8f91bce 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6937,6 +6937,12 @@ def facets(self): return () return self.faces(self.dimension()-1) + def join_of_Vrep(self, *Vrepresentatives): + return self.face_generator().join_of_Vrep(*Vrepresentatives) + + def meet_of_facets(self, *facets): + return self.face_generator().meet_of_facets(*facets) + @cached_method(do_pickle=True) def f_vector(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 18b84d148f1..0ff427befd5 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2276,8 +2276,8 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.join_of_Vrep(0,1) A 1-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.join_of_Vrep(0,3).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(0,11).ambient_V_indices() + (0, 1, 10, 11, 12, 13) sage: C.join_of_Vrep(8).ambient_V_indices() (8,) sage: C.join_of_Vrep().ambient_V_indices() @@ -2303,8 +2303,8 @@ cdef class CombinatorialPolyhedron(SageObject): (0,) sage: C.meet_of_facets(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,3).ambient_H_indices() - (0, 3) + sage: C.meet_of_facets(0,2).ambient_H_indices() + (0, 2) sage: C.meet_of_facets(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: C.meet_of_facets().ambient_H_indices() diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 2ce3dc7f513..045abf26f89 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -526,7 +526,7 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_facets(1,2,3).ambient_H_indices() Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -588,7 +588,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(8) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -700,7 +700,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range The empty face is detected correctly, even with lines or rays:: @@ -738,7 +738,8 @@ cdef class FaceIterator_base(SageObject): face_add_atom(face, i) for i in indices: - assert 0 <= i < n_coatoms, "index out of range" + if not 0 <= i < n_coatoms: + raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): @@ -818,11 +819,11 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(-1) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range sage: it._join_of_atoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range """ if unlikely(self.structure.face_status != 0): raise ValueError("please reset the face iterator") @@ -838,7 +839,8 @@ cdef class FaceIterator_base(SageObject): cdef face_t pseudo_face = face_mem.data.faces[1] cdef size_t i - assert all(i in range(n_atoms) for i in indices), "index out of range" + if not all(i in range(n_atoms) for i in indices): + raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. for i in indices: From 800ad8dcfac5e8813106349fa16d8257848cb168 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 14:24:53 +0200 Subject: [PATCH 655/706] expose in Polyhedron_base --- .../geometry/polyhedra_quickref.rst | 2 + src/sage/geometry/polyhedron/base.py | 136 +++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index d684c689c3e..968800e3925 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,6 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 999b8f91bce..81a4923ec6f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6938,10 +6938,142 @@ def facets(self): return self.faces(self.dimension()-1) def join_of_Vrep(self, *Vrepresentatives): - return self.face_generator().join_of_Vrep(*Vrepresentatives) + r""" + Return the smallest face that contains in ``Vrepresentatives``. + + INPUT: + + - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + .. NOTE:: + + In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^5 + sage: P.join_of_Vrep(1,3,4).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + + The input is flexible:: + + sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) + A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + + In case of an unbounded polyhedron, the join may not be well-defined:: + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep(0,1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: P.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(1,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + """ + from sage.geometry.polyhedron.representation import Vrepresentation + from sage.geometry.polyhedron.face import PolyhedronFace + + new_indices = [0]*len(Vrepresentatives) + for i, v in enumerate(Vrepresentatives): + if isinstance(v, PolyhedronFace) and facet.dim() == 0: + v = v.ambient_V_indices()[0] + + if v in ZZ: + new_indices[i] = v + elif isinstance(v, Vrepresentation): + new_indices[i] = v.index() + else: + raise ValueError("{} is not a Vrepresentative".format(v)) + + return self.face_generator().join_of_Vrep(*new_indices) def meet_of_facets(self, *facets): - return self.face_generator().meet_of_facets(*facets) + r""" + Return the largest face that is contained in ``facets``. + + INPUT: + + - ``facets`` -- facets or indices of facets; + the indices are assumed to be the indices of the Hrepresentation + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.meet_of_facets() + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices + sage: P.meet_of_facets(1) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices + sage: P.meet_of_facets(2) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices + sage: P.meet_of_facets(2,3,4) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(2,3,4).ambient_H_indices() + (0, 2, 3, 4) + + The indices are the indices of the Hrepresentation:: + + sage: P.meet_of_facets(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a facet + + The input is flexible:: + + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + + TESTS: + + The offset is taken correctly:: + + sage: P = polytopes.permutahedron(3, backend='field') + sage: P.Hrepresentation() + (An inequality (1, 1, 0) x - 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0, + An inequality (0, 2, 0) x - 2 >= 0, + An inequality (-4, -4, 0) x + 20 >= 0, + An inequality (-8, 0, 0) x + 24 >= 0, + An equation (-1/6, -1/6, -1/6) x + 1 == 0) + sage: P.meet_of_facets(0) + A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + """ + from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.face import PolyhedronFace + + # Equations are ignored by combinatorial polyhedron for indexing. + offset = 0 + if self.n_equations() and self.Hrepresentation(0).is_equation(): + offset = self.n_equations() + + new_indices = [0]*len(facets) + for i, facet in enumerate(facets): + if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + H_indices = facet.ambient_H_indices() + facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] + + if facet in ZZ and facet >= offset: + new_indices[i] = facet - offset + elif isinstance(facet, Inequality): + new_indices[i] = facet.index() - offset + else: + raise ValueError("{} is not a facet".format(facet)) + + return self.face_generator().meet_of_facets(*new_indices) @cached_method(do_pickle=True) def f_vector(self): From 510bbc6ede25c83a0591572c04f289761486d9a0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 7 Sep 2020 15:36:08 +0200 Subject: [PATCH 656/706] fix doctests --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 81a4923ec6f..c23f4afc820 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6958,8 +6958,8 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^5 - sage: P.join_of_Vrep(1,3,4).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: P.join_of_Vrep(0,12,13).ambient_V_indices() + (0, 12, 13, 68) The input is flexible:: @@ -7017,12 +7017,12 @@ def meet_of_facets(self, *facets): A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices sage: P.meet_of_facets(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(2) + sage: P.meet_of_facets(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(2,3,4) + sage: P.meet_of_facets(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(2,3,4).ambient_H_indices() - (0, 2, 3, 4) + sage: P.meet_of_facets(1,3,7).ambient_H_indices() + (0, 1, 3, 7) The indices are the indices of the Hrepresentation:: @@ -7033,7 +7033,7 @@ def meet_of_facets(self, *facets): The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices TESTS: @@ -7042,13 +7042,13 @@ def meet_of_facets(self, *facets): sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() - (An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (0, 0, 1) x - 1 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (0, 1, 1) x - 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0, - An inequality (0, 2, 0) x - 2 >= 0, - An inequality (-4, -4, 0) x + 20 >= 0, - An inequality (-8, 0, 0) x + 24 >= 0, - An equation (-1/6, -1/6, -1/6) x + 1 == 0) + An inequality (1, 0, 1) x - 3 >= 0, + An inequality (1, 1, 0) x - 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_facets(0) A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices """ From bacc3e88dde6cea6f99d4dbd631acb8d0bb55a94 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 16 Jan 2021 21:33:30 +0100 Subject: [PATCH 657/706] improved documentation --- src/sage/geometry/polyhedron/base.py | 19 ++++++++++++++++--- .../face_iterator.pyx | 18 +++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c23f4afc820..4a6f84ebb96 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7038,7 +7038,8 @@ def meet_of_facets(self, *facets): TESTS: - The offset is taken correctly:: + Equations are not considered by the combinatorial polyhedron. + We check that the index corresponds to the Hrepresentation index:: sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() @@ -7049,8 +7050,20 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0) - A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(0).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + + sage: P = polytopes.permutahedron(3, backend='ppl') + sage: P.Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, + An inequality (1, 1, 0) x - 3 >= 0, + An inequality (-1, -1, 0) x + 5 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (-1, 0, 0) x + 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0) + sage: P.meet_of_facets(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) """ from sage.geometry.polyhedron.representation import Inequality from sage.geometry.polyhedron.face import PolyhedronFace diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 045abf26f89..ccf31c6ee9e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -488,7 +488,7 @@ cdef class FaceIterator_base(SageObject): This is the largest face contained in all facets with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. EXAMPLES:: @@ -528,7 +528,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -555,11 +555,11 @@ cdef class FaceIterator_base(SageObject): This is the smallest face containing all Vrepresentatives with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. NOTE:: - In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives may not te well defined. EXAMPLES:: @@ -590,7 +590,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -605,7 +605,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(1,10).ambient_V_indices() (1, 10) - In case of an unbounded polyhedron, we try to make sense of the input:: + In the case of an unbounded polyhedron, we try to make sense of the input:: sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) sage: it = P.face_generator() @@ -647,7 +647,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the meet of the coatoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -755,7 +755,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the join of atoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -786,7 +786,7 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(1) A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() From 9cbd0159a5567fab7078494a53ffe935542a217f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 20:38:18 +0200 Subject: [PATCH 658/706] fix a variable name and add a doctest --- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4a6f84ebb96..d6cdf34120f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6966,6 +6966,13 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + :: + + sage: P = polytopes.cube() + sage: a, b = P.faces(0)[:2] + sage: P.join_of_Vrep(a, b) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + In case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) @@ -6987,7 +6994,7 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): - if isinstance(v, PolyhedronFace) and facet.dim() == 0: + if isinstance(v, PolyhedronFace) and v.dim() == 0: v = v.ambient_V_indices()[0] if v in ZZ: From 4612fe5b59faeac318e42211839d7df408aed8b9 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 21:06:37 +0200 Subject: [PATCH 659/706] typos --- src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst | 2 +- src/sage/geometry/polyhedron/base.py | 4 ++-- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index 968800e3925..9a5a113d0c9 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -173,7 +173,7 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained in specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d6cdf34120f..6be46ff330c 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6949,7 +6949,7 @@ def join_of_Vrep(self, *Vrepresentatives): .. NOTE:: - In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + In the case of unbounded polyhedra, the join of rays etc. may not be well-defined. EXAMPLES:: @@ -6973,7 +6973,7 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(a, b) A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices - In case of an unbounded polyhedron, the join may not be well-defined:: + In the case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) sage: P.join_of_Vrep(0) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index ccf31c6ee9e..445d79ea54a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -560,7 +560,7 @@ cdef class FaceIterator_base(SageObject): .. NOTE:: In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives - may not te well defined. + may not be well defined. EXAMPLES:: From 3b09b688994db6c116595d9f54b7a846723ccc77 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 21:08:17 +0200 Subject: [PATCH 660/706] another typo --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6be46ff330c..d5df6d2e380 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6939,7 +6939,7 @@ def facets(self): def join_of_Vrep(self, *Vrepresentatives): r""" - Return the smallest face that contains in ``Vrepresentatives``. + Return the smallest face that contains ``Vrepresentatives``. INPUT: From fe428aa3f3935c89b70e8e41512c99e47ad1db34 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 17 May 2021 12:07:13 +0200 Subject: [PATCH 661/706] improved documentation and check for elements to be of the correct polyhedron --- src/sage/geometry/polyhedron/base.py | 62 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d5df6d2e380..cab134f220e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6943,7 +6943,7 @@ def join_of_Vrep(self, *Vrepresentatives): INPUT: - - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + - ``Vrepresentatives`` -- vertices/rays/lines of ``self`` or indices of such OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -6988,6 +6988,25 @@ def join_of_Vrep(self, *Vrepresentatives): Traceback (most recent call last): ... ValueError: the join is not well-defined + + The ``Vrepresentatives`` must be of ``self``:: + + sage: P = polytopes.cube(backend='ppl') + sage: Q = polytopes.cube(backend='field') + sage: v = P.vertices()[0] + sage: P.join_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: Q.join_of_Vrep(v) + Traceback (most recent call last): + ... + ValueError: not a Vrepresentative of ``self`` + sage: f = P.faces(0)[0] + sage: P.join_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: Q.join_of_Vrep(v) + Traceback (most recent call last): + ... + ValueError: not a Vrepresentative of ``self`` """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -6995,11 +7014,14 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): if isinstance(v, PolyhedronFace) and v.dim() == 0: - v = v.ambient_V_indices()[0] - - if v in ZZ: + if v.polyhedron() is not self: + raise ValueError("not a Vrepresentative of ``self``") + new_indices[i] = v.ambient_V_indices()[0] + elif v in ZZ: new_indices[i] = v elif isinstance(v, Vrepresentation): + if v.polyhedron() is not self: + raise ValueError("not a Vrepresentative of ``self``") new_indices[i] = v.index() else: raise ValueError("{} is not a Vrepresentative".format(v)) @@ -7012,7 +7034,7 @@ def meet_of_facets(self, *facets): INPUT: - - ``facets`` -- facets or indices of facets; + - ``facets`` -- facets or indices of facets of ``self``; the indices are assumed to be the indices of the Hrepresentation OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -7031,18 +7053,38 @@ def meet_of_facets(self, *facets): sage: P.meet_of_facets(1,3,7).ambient_H_indices() (0, 1, 3, 7) - The indices are the indices of the Hrepresentation:: + The indices are the indices of the Hrepresentation. + ``0`` corresponds to an equation and is not permitted as input:: sage: P.meet_of_facets(0) Traceback (most recent call last): ... - ValueError: 0 is not a facet + ValueError: 0 is the index of an equation and not of an inequality The input is flexible:: sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + The facets must be facets of ``self``:: + + sage: P = polytopes.cube(backend='ppl') + sage: Q = polytopes.cube(backend='field') + sage: f = P.facets()[0] + sage: P.meet_of_facets(f) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: Q.meet_of_facets(f) + Traceback (most recent call last): + ... + ValueError: not a facet of ``self`` + sage: f = P.inequalities()[0] + sage: P.meet_of_facets(f) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: Q.meet_of_facets(f) + Traceback (most recent call last): + ... + ValueError: not a facet of ``self`` + TESTS: Equations are not considered by the combinatorial polyhedron. @@ -7083,15 +7125,19 @@ def meet_of_facets(self, *facets): new_indices = [0]*len(facets) for i, facet in enumerate(facets): if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + if facet.polyhedron() is not self: + raise ValueError("not a facet of ``self``") H_indices = facet.ambient_H_indices() facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] if facet in ZZ and facet >= offset: new_indices[i] = facet - offset elif isinstance(facet, Inequality): + if facet.polyhedron() is not self: + raise ValueError("not a facet of ``self``") new_indices[i] = facet.index() - offset else: - raise ValueError("{} is not a facet".format(facet)) + raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) return self.face_generator().meet_of_facets(*new_indices) From dd79e1c91067a981f22fee01c1e6f1cca5e3df51 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 17 May 2021 13:01:52 +0200 Subject: [PATCH 662/706] typo --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0ff427befd5..9e24a4cd683 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2287,7 +2287,7 @@ cdef class CombinatorialPolyhedron(SageObject): def meet_of_facets(self, *indices): r""" - Return the largest face contained all facets indicated by the indices. + Return the largest face contained in all facets indicated by the indices. .. SEEALSO:: From 56c66a4439323f647687848ea6a00c4243efc098 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 22:06:23 +0200 Subject: [PATCH 663/706] ignore equations; make aliases --- .../geometry/polyhedra_quickref.rst | 4 +- src/sage/geometry/polyhedron/base.py | 78 ++++++++++++------- .../combinatorial_polyhedron/base.pyx | 18 ++--- .../combinatorial_face.pyx | 11 +-- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 42 ++++++---- 6 files changed, 95 insertions(+), 59 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index 9a5a113d0c9..c8f6b40b171 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,8 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained in specified facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep`, :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.least_common_superface_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_Hrep`, :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.greatest_common_subface_of_Hrep` | largest face contained in specified Hrepresentatives :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cab134f220e..1533a8bcb15 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7007,6 +7007,15 @@ def join_of_Vrep(self, *Vrepresentatives): Traceback (most recent call last): ... ValueError: not a Vrepresentative of ``self`` + + TESTS: + + ``least_common_superface_of_Vrep`` is an alias:: + + sage: P.least_common_superface_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: P.least_common_superface_of_Vrep == P.join_of_Vrep + True """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7028,13 +7037,15 @@ def join_of_Vrep(self, *Vrepresentatives): return self.face_generator().join_of_Vrep(*new_indices) - def meet_of_facets(self, *facets): + least_common_superface_of_Vrep = join_of_Vrep + + def meet_of_Hrep(self, *Hrepresentatives): r""" Return the largest face that is contained in ``facets``. INPUT: - - ``facets`` -- facets or indices of facets of ``self``; + - ``Hrepresentatives`` -- facets or indices of Hrepresentatives; the indices are assumed to be the indices of the Hrepresentation OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -7042,45 +7053,43 @@ def meet_of_facets(self, *facets): EXAMPLES:: sage: P = polytopes.permutahedron(5) - sage: P.meet_of_facets() + sage: P.meet_of_Hrep() A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices - sage: P.meet_of_facets(1) + sage: P.meet_of_Hrep(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(4) + sage: P.meet_of_Hrep(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(1,3,7) + sage: P.meet_of_Hrep(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(1,3,7).ambient_H_indices() + sage: P.meet_of_Hrep(1,3,7).ambient_H_indices() (0, 1, 3, 7) The indices are the indices of the Hrepresentation. - ``0`` corresponds to an equation and is not permitted as input:: + ``0`` corresponds to an equation and is ignored:: - sage: P.meet_of_facets(0) - Traceback (most recent call last): - ... - ValueError: 0 is the index of an equation and not of an inequality + sage: P.meet_of_Hrep(0) + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) + sage: P.meet_of_Hrep(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - The facets must be facets of ``self``:: + The ``Hrepresentatives`` must belong to ``self``:: sage: P = polytopes.cube(backend='ppl') sage: Q = polytopes.cube(backend='field') sage: f = P.facets()[0] - sage: P.meet_of_facets(f) + sage: P.meet_of_Hrep(f) A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices - sage: Q.meet_of_facets(f) + sage: Q.meet_of_Hrep(f) Traceback (most recent call last): ... ValueError: not a facet of ``self`` sage: f = P.inequalities()[0] - sage: P.meet_of_facets(f) + sage: P.meet_of_Hrep(f) A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices - sage: Q.meet_of_facets(f) + sage: Q.meet_of_Hrep(f) Traceback (most recent call last): ... ValueError: not a facet of ``self`` @@ -7099,7 +7108,7 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0).ambient_Hrepresentation() + sage: P.meet_of_Hrep(0).ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) sage: P = polytopes.permutahedron(3, backend='ppl') @@ -7111,10 +7120,17 @@ def meet_of_facets(self, *facets): An inequality (-1, 0, 0) x + 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, An inequality (0, -1, 0) x + 3 >= 0) - sage: P.meet_of_facets(1).ambient_Hrepresentation() + sage: P.meet_of_Hrep(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) + + ``greatest_common_subface_of_Hrep`` is an alias:: + + sage: P.greatest_common_subface_of_Hrep(1).ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) + sage: P.greatest_common_subface_of_Hrep == P.meet_of_Hrep + True """ - from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.representation import Inequality, Equation from sage.geometry.polyhedron.face import PolyhedronFace # Equations are ignored by combinatorial polyhedron for indexing. @@ -7122,8 +7138,8 @@ def meet_of_facets(self, *facets): if self.n_equations() and self.Hrepresentation(0).is_equation(): offset = self.n_equations() - new_indices = [0]*len(facets) - for i, facet in enumerate(facets): + new_indices = [] + for i, facet in enumerate(Hrepresentatives): if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): if facet.polyhedron() is not self: raise ValueError("not a facet of ``self``") @@ -7131,15 +7147,25 @@ def meet_of_facets(self, *facets): facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] if facet in ZZ and facet >= offset: - new_indices[i] = facet - offset + # Note that ``CombinatorialPolyhedron`` ignores indices of equations + # and has equations last. + new_indices.append(facet - offset) elif isinstance(facet, Inequality): if facet.polyhedron() is not self: raise ValueError("not a facet of ``self``") - new_indices[i] = facet.index() - offset + new_indices.append(facet.index() - offset) + elif isinstance(facet, Equation): + # Ignore equations. + continue + elif facet in ZZ and 0 <= facet < offset: + # Ignore equations. + continue else: raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) - return self.face_generator().meet_of_facets(*new_indices) + return self.face_generator().meet_of_Hrep(*new_indices) + + greatest_common_subface_of_Hrep = meet_of_Hrep @cached_method(do_pickle=True) def f_vector(self): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 9e24a4cd683..a8e184f17c9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2285,32 +2285,32 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self.face_iter().join_of_Vrep(*indices) - def meet_of_facets(self, *indices): + def meet_of_Hrep(self, *indices): r""" Return the largest face contained in all facets indicated by the indices. .. SEEALSO:: - :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_Hrep`. EXAMPLES:: sage: P = polytopes.dodecahedron() sage: C = CombinatorialPolyhedron(P) - sage: C.meet_of_facets(0) + sage: C.meet_of_Hrep(0) A 2-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.meet_of_facets(0).ambient_H_indices() + sage: C.meet_of_Hrep(0).ambient_H_indices() (0,) - sage: C.meet_of_facets(0,1).ambient_H_indices() + sage: C.meet_of_Hrep(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,2).ambient_H_indices() + sage: C.meet_of_Hrep(0,2).ambient_H_indices() (0, 2) - sage: C.meet_of_facets(0,2,3).ambient_H_indices() + sage: C.meet_of_Hrep(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) - sage: C.meet_of_facets().ambient_H_indices() + sage: C.meet_of_Hrep().ambient_H_indices() () """ - return self.face_iter().meet_of_facets(*indices) + return self.face_iter().meet_of_Hrep(*indices) def face_iter(self, dimension=None, dual=None): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 439a6f7d7bf..f9f3798ca21 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -181,8 +181,9 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = it.structure.dimension self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names + self._n_ambient_facets = it._n_facets self._equations = it._equations - self._n_equations = len(self._equations) if self._equations else 0 + self._n_equations = it._n_equations self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -209,6 +210,10 @@ cdef class CombinatorialFace(SageObject): self._ambient_facets = all_faces._facet_names self._equations = all_faces._equations self._n_equations = len(self._equations) if self._equations else 0 + if self._dual: + self._n_ambient_facets = self.atoms.n_faces() + else: + self._n_ambient_facets = self.coatoms.n_faces() self._initialized_from_face_lattice = True @@ -228,10 +233,6 @@ cdef class CombinatorialFace(SageObject): # Reverse the hash index in dual mode to respect inclusion of faces. self._hash_index = -self._hash_index - 1 - self._n_ambient_facets = self.atoms.n_faces() - else: - self._n_ambient_facets = self.coatoms.n_faces() - def _repr_(self): r""" Return a description of the combinatorial face. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 2bfd0d5f114..a72ceb45c57 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -56,6 +56,7 @@ cdef class FaceIterator_base(SageObject): # some copies from ``CombinatorialPolyhedron`` cdef tuple _Vrep, _facet_names, _equations + cdef size_t _n_equations, _n_facets cdef bint _bounded # Atoms and coatoms are the vertices/facets of the Polyedron. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 445d79ea54a..3ab044dae17 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -252,7 +252,12 @@ cdef class FaceIterator_base(SageObject): self.atoms = C.bitrep_Vrep() self._Vrep = C.Vrep() self._facet_names = C.facet_names() + self._n_facets = C.bitrep_facets().n_faces() self._equations = C.equations() + if self._equations: + self._n_equations = len(self._equations) + else: + self._n_equations = 0 self._bounded = C.is_bounded() self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) @@ -482,7 +487,7 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() - def meet_of_facets(self, *indices): + def meet_of_Hrep(self, *indices): r""" Construct the meet of the facets indicated by the indices. @@ -494,36 +499,36 @@ cdef class FaceIterator_base(SageObject): sage: P = polytopes.cube() sage: it = P.face_generator() - sage: it.meet_of_facets(1,2) + sage: it.meet_of_Hrep(1,2) A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,3).ambient_H_indices() (1, 3) - sage: it.meet_of_facets(1,5).ambient_H_indices() + sage: it.meet_of_Hrep(1,5).ambient_H_indices() (0, 1, 2, 3, 4, 5) sage: P = polytopes.cross_polytope(4) sage: it = P.face_generator() - sage: it.meet_of_facets().ambient_H_indices() + sage: it.meet_of_Hrep().ambient_H_indices() () - sage: it.meet_of_facets(1,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,3).ambient_H_indices() (1, 2, 3, 4) - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,6).ambient_H_indices() (1, 6) - sage: it.meet_of_facets(1,2,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,6).ambient_H_indices() (1, 2, 6, 7) - sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,5,6).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7) sage: s = cones.schur(4) sage: C = CombinatorialPolyhedron(s) sage: it = C.face_iter() - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,2,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,3).ambient_H_indices() Traceback (most recent call last): ... IndexError: coatoms out of range @@ -535,15 +540,18 @@ cdef class FaceIterator_base(SageObject): sage: _ = next(it), next(it) sage: next(it).ambient_V_indices() (15, 16, 17, 18, 19) - sage: it.meet_of_facets(9,11) + sage: it.meet_of_Hrep(9,11) Traceback (most recent call last): ... ValueError: please reset the face iterator sage: it.reset() - sage: it.meet_of_facets(9,11).ambient_H_indices() + sage: it.meet_of_Hrep(9,11).ambient_H_indices() (9, 11) """ + # Ignore equations. + indices = [i for i in indices + if not (self._n_facets <= i < self._n_facets + self._n_equations)] if self.dual: return self._join_of_atoms(*indices) else: @@ -651,7 +659,7 @@ cdef class FaceIterator_base(SageObject): .. SEEALSO:: - :meth:`meet_of_facets`, + :meth:`meet_of_Hrep`, :meth:`join_of_Vrep`. EXAMPLES: @@ -759,7 +767,7 @@ cdef class FaceIterator_base(SageObject): .. SEEALSO:: - :meth:`meet_of_facets`, + :meth:`meet_of_Hrep`, :meth:`join_of_Vrep`. EXAMPLES: From 80c80e54e725b5f677534bfe121c8beee7a11ede Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 21:47:28 +0200 Subject: [PATCH 664/706] small fixes --- src/sage/geometry/polyhedron/base.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1533a8bcb15..6efab021a0f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7016,6 +7016,13 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex sage: P.least_common_superface_of_Vrep == P.join_of_Vrep True + + Error message for invalid input:: + + sage: P.join_of_Vrep('foo') + Traceback (most recent call last): + ... + ValueError: foo is not a Vrepresentative """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7041,7 +7048,7 @@ def join_of_Vrep(self, *Vrepresentatives): def meet_of_Hrep(self, *Hrepresentatives): r""" - Return the largest face that is contained in ``facets``. + Return the largest face that is contained in ``Hrepresentatives``. INPUT: @@ -7109,7 +7116,7 @@ def meet_of_Hrep(self, *Hrepresentatives): An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_Hrep(0).ambient_Hrepresentation() - (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + (An inequality (0, 0, 1) x - 1 >= 0, An equation (1, 1, 1) x - 6 == 0) sage: P = polytopes.permutahedron(3, backend='ppl') sage: P.Hrepresentation() @@ -7129,6 +7136,13 @@ def meet_of_Hrep(self, *Hrepresentatives): (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) sage: P.greatest_common_subface_of_Hrep == P.meet_of_Hrep True + + Error message for invalid input:: + + sage: P.meet_of_Hrep('foo') + Traceback (most recent call last): + ... + ValueError: foo is not a Hrepresentative """ from sage.geometry.polyhedron.representation import Inequality, Equation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7161,7 +7175,7 @@ def meet_of_Hrep(self, *Hrepresentatives): # Ignore equations. continue else: - raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) + raise ValueError("{} is not a Hrepresentative".format(facet)) return self.face_generator().meet_of_Hrep(*new_indices) From 9afb2761d7459190701d5d896c922c23397c8dc5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 23:03:23 +0200 Subject: [PATCH 665/706] fix segementation fault --- .../face_iterator.pyx | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 3ab044dae17..462246147ed 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -548,6 +548,26 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_Hrep(9,11).ambient_H_indices() (9, 11) + TESTS: + + Check that things work fine, if the face iterator was never properly initialized:: + + sage: P = Polyhedron() + sage: P.meet_of_Hrep() + A -1-dimensional face of a Polyhedron in ZZ^0 + sage: P = Polyhedron([[0,0]]) + sage: P.meet_of_Hrep() + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P.meet_of_Hrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P = Polyhedron(lines=[[1]]) + sage: P.meet_of_Hrep() + A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line + sage: P = Polyhedron(lines=[[1, 1]]) + sage: P.meet_of_Hrep() + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P.meet_of_Hrep(0) + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line """ # Ignore equations. indices = [i for i in indices @@ -645,6 +665,33 @@ cdef class FaceIterator_base(SageObject): A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex sage: it.join_of_Vrep(1,2) A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + + TESTS: + + Check that things work fine, if the face iterator was never properly initialized:: + + sage: P = Polyhedron() + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^0 + sage: P = Polyhedron([[0,0]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P = Polyhedron(lines=[[1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^1 + sage: P.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: P = Polyhedron(lines=[[1, 1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined """ if not self.dual: return self._join_of_atoms(*indices) @@ -750,7 +797,10 @@ cdef class FaceIterator_base(SageObject): raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) - if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + if not self.coatoms.n_faces(): + # Prevent segmentation fault. + pass + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): # The meet is contained in the far face and therefore is the empty face. face_clear(face) @@ -866,6 +916,11 @@ cdef class FaceIterator_base(SageObject): if not indices: # The neutral element of the join. face_clear(face) + elif not self._bounded and not self.coatoms.n_faces(): + # Note: It is important to catch this and not to run ``face_issubset`` to prevent a segmentation fault. + if face_len_atoms(face): + # Contained in the far face, if it contains anything. + raise ValueError("the join is not well-defined") elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): # The join is not well-defined. # We allow for unbounded polyhedra to compute the join, even with rays. From ef5c95e4a73e7f31a53befa1adc0f6a61573ea8e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 01:06:41 +0200 Subject: [PATCH 666/706] check with a nice error for negative indices --- .../combinatorial_polyhedron/face_iterator.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 462246147ed..68d1606c598 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -751,7 +751,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(-1) Traceback (most recent call last): ... - OverflowError: can't convert negative value to size_t + IndexError: coatoms out of range sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... @@ -786,11 +786,12 @@ cdef class FaceIterator_base(SageObject): cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) cdef face_t face = face_mem.data.faces[0] - cdef size_t i + cdef int i + cdef size_t j # Initialize the full polyhedron. - for i in range(n_atoms): - face_add_atom(face, i) + for j in range(n_atoms): + face_add_atom(face, j) for i in indices: if not 0 <= i < n_coatoms: @@ -895,9 +896,10 @@ cdef class FaceIterator_base(SageObject): cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) cdef face_t face = face_mem.data.faces[0] cdef face_t pseudo_face = face_mem.data.faces[1] + cdef int j cdef size_t i - if not all(i in range(n_atoms) for i in indices): + if not all(0 <= j < n_atoms for j in indices): raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. From 99ddc2f635e0546be6ee4f9813fea85486c79817 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:15:20 +0200 Subject: [PATCH 667/706] better check for the far face containment --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index a72ceb45c57..a1a21d6dde4 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -58,6 +58,7 @@ cdef class FaceIterator_base(SageObject): cdef tuple _Vrep, _facet_names, _equations cdef size_t _n_equations, _n_facets cdef bint _bounded + cdef face_t _far_face # Atoms and coatoms are the vertices/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 68d1606c598..53a8c09a73b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -259,6 +259,7 @@ cdef class FaceIterator_base(SageObject): else: self._n_equations = 0 self._bounded = C.is_bounded() + self._far_face[0] = C._far_face[0] self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) self.structure.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) @@ -304,7 +305,7 @@ cdef class FaceIterator_base(SageObject): # needs to be at most ``n_facets - 1``. # Hence it is fine to use the first entry already for the far face, # as ``self.visited_all`` holds ``n_facets`` pointers. - add_face_shallow(self.structure.visited_all[self.structure.dimension-1], C._far_face) + add_face_shallow(self.structure.visited_all[self.structure.dimension-1], self._far_face) # Initialize ``first_time``. self.structure.first_time = self._mem.allocarray(self.structure.dimension, sizeof(bint)) @@ -682,16 +683,25 @@ cdef class FaceIterator_base(SageObject): sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^1 sage: P.join_of_Vrep(0) - Traceback (most recent call last): - ... - ValueError: the join is not well-defined + A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line sage: P = Polyhedron(lines=[[1, 1]]) sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.Vrepresentation() + (A line in the direction (1, 1), A vertex at (0, 0)) sage: P.join_of_Vrep(0) - Traceback (most recent call last): - ... - ValueError: the join is not well-defined + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P = Polyhedron(lines=[[1, 0], [0, 1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: P.join_of_Vrep(0, 1) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: P.join_of_Vrep(0, 1, 2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines """ if not self.dual: return self._join_of_atoms(*indices) @@ -798,10 +808,7 @@ cdef class FaceIterator_base(SageObject): raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) - if not self.coatoms.n_faces(): - # Prevent segmentation fault. - pass - elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + if not self._bounded and face_issubset(face, self._far_face): # The meet is contained in the far face and therefore is the empty face. face_clear(face) @@ -918,12 +925,7 @@ cdef class FaceIterator_base(SageObject): if not indices: # The neutral element of the join. face_clear(face) - elif not self._bounded and not self.coatoms.n_faces(): - # Note: It is important to catch this and not to run ``face_issubset`` to prevent a segmentation fault. - if face_len_atoms(face): - # Contained in the far face, if it contains anything. - raise ValueError("the join is not well-defined") - elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + elif not self._bounded and face_issubset(face, self._far_face): # The join is not well-defined. # We allow for unbounded polyhedra to compute the join, even with rays. # However, the result is not necesarrily well-defined. From 0f78d2a6c05e0460cab8dea296c5d32ac25d2835 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:19:34 +0200 Subject: [PATCH 668/706] implement only subfaces/supfaces for face iterator --- .../face_data_structure.pxd | 3 + .../face_iterator.pxd | 2 + .../face_iterator.pyx | 187 +++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index 4270885d515..627d8dd550a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -186,3 +186,6 @@ cdef inline void swap_faces(face_t a, face_t b) nogil: tmp[0] = a[0] a[0] = b[0] b[0] = tmp[0] + +cdef inline bint faces_are_identical(face_t a, face_t b) nogil: + return a.atoms.limbs == b.atoms.limbs diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index a1a21d6dde4..d53ac95fe28 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -16,6 +16,7 @@ cdef struct iter_s: int dimension # dimension of the polyhedron int output_dimension # only faces of this (dual?) dimension are considered int lowest_dimension # don't consider faces below this (dual?) dimension + int highest_dimension # don't consider faces above this (dual?) dimension size_t _index # this counts the number of seen faces, useful for hasing the faces # ``visited_all`` points to faces, of which we have visited all faces already. @@ -71,6 +72,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int only_subsets(self) except -1 cdef int find_face(self, face_t face) except -1 @cython.final diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 53a8c09a73b..e4b02fae715 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -223,7 +223,8 @@ cdef class FaceIterator_base(SageObject): self.structure.dual = dual self.structure.face_status = 0 self.structure.dimension = C.dimension() - self.structure.current_dimension = self.structure.dimension -1 + self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self._mem = MemoryAllocator() # We will not yield the empty face. @@ -353,6 +354,7 @@ cdef class FaceIterator_base(SageObject): self.structure.face_status = 0 self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces() self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self.structure.first_time[self.structure.dimension - 1] = True self.structure.yet_to_visit = self.coatoms.n_faces() @@ -457,6 +459,33 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_non_simplex_faces 127 + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``ignore_subfaces`` after ``only_subfaces:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.only_subfaces() + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot ignore a face after setting iterator to only visit subsets + + Face iterator must be set to a face first:: + + sage: it = C.face_iter(dual=False) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") @@ -464,9 +493,9 @@ cdef class FaceIterator_base(SageObject): def ignore_supfaces(self): r""" - The iterator will not visit any faces of the current face. + The iterator will not visit any faces containing the current face. - Only possible when not in dual mode. + Only possible when in dual mode. EXAMPLES:: @@ -483,6 +512,15 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_faces_with_non_simplex_quotient 4845 + + Face iterator must be in dual mode:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.ignore_supfaces() + Traceback (most recent call last): + ... + ValueError: only possible when in dual mode """ if unlikely(not self.dual): raise ValueError("only possible when in dual mode") @@ -948,6 +986,8 @@ cdef class FaceIterator_base(SageObject): """ if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 3): + raise ValueError("cannot ignore a face after setting iterator to only visit subsets") if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 @@ -959,13 +999,144 @@ cdef class FaceIterator_base(SageObject): add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face) self.structure.face_status = 2 + def only_subfaces(self): + r""" + The iterator will visit all (remaining) subfaces of the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: next(it).ambient_H_indices() + () + sage: next(it).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_H_indices() + (5,) + sage: next(it).ambient_H_indices() + (4,) + sage: it.only_subfaces() + sage: list(f.ambient_H_indices() for f in it) + [(4, 5), (3, 4), (1, 4), (0, 4), (3, 4, 5), (0, 4, 5), (1, 3, 4), (0, 1, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: next(it).ambient_H_indices() + (15,) + sage: next(it).ambient_H_indices() + (14,) + sage: it.only_subfaces() + sage: all(14 in f.ambient_H_indices() for f in it) + True + + Face iterator needs to be set to a face first:: + + sage: it = C.face_iter() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``only_subfaces`` after ``ignore_subfaces:: + + sage: it = C.face_iter() + sage: _ = next(it) + sage: it.ignore_subfaces() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot visit subsets after ignoring a face + """ + if unlikely(self.dual): + raise ValueError("only possible when not in dual mode") + self.only_subsets() + + def only_supfaces(self): + r""" + The iterator will visit all (remaining) faces + containing the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cross_polytope(3) + sage: it = P.face_generator() + sage: next(it).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_V_indices() + () + sage: next(it).ambient_V_indices() + (5,) + sage: next(it).ambient_V_indices() + (4,) + sage: it.only_supfaces() + sage: list(f.ambient_V_indices() for f in it) + [(4, 5), (3, 4), (2, 4), (0, 4), (3, 4, 5), (2, 4, 5), (0, 3, 4), (0, 2, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=True) + sage: next(it).ambient_V_indices() + (23,) + sage: next(it).ambient_V_indices() + (22,) + sage: it.only_supfaces() + sage: all(22 in f.ambient_V_indices() for f in it) + True + """ + if unlikely(not self.dual): + raise ValueError("only possible when in dual mode") + self.only_subsets() + + cdef int only_subsets(self) except -1: + r""" + Only visit sub-/supfaces of the current face and then + terminate. + + See :meth:`FaceIterator_base.only_subfaces` and + :meth:`FaceIterator_base.only_supfaces`. + """ + if unlikely(self.structure.face_status == 0): + raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 2): + raise ValueError("cannot only visit subsets after ignoring a face") + + cdef face_list_t* faces = &self.structure.new_faces[self.structure.current_dimension] + cdef size_t yet_to_visit = self.structure.yet_to_visit + + if unlikely(yet_to_visit >= faces[0].n_faces + or not faces_are_identical(faces[0].faces[yet_to_visit], self.structure.face)): + raise ValueError("iterator is not set to the correct face") + + swap_faces(faces[0].faces[yet_to_visit], faces[0].faces[faces[0].n_faces - 1]) + + self.structure.face_status = 3 + self.structure.yet_to_visit = 0 + # This will work: + # ``next_dimension`` will first call ``next_face_loop`` and then check + # for the dimension. By this time the current dimension has changed. + self.structure.highest_dimension = self.structure.current_dimension - 1 + cdef inline CombinatorialFace next_face(self): r""" Set attribute ``face`` to the next face and return it as :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): return None return CombinatorialFace(self) @@ -1363,7 +1534,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return face @@ -1656,7 +1827,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return self.current() @@ -1684,9 +1855,9 @@ cdef inline int next_dimension(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ - cdef int dim = structure.dimension + cdef int max_dim = structure.highest_dimension structure.face_status = 0 - while (not next_face_loop(structure)) and (structure.current_dimension < dim): + while (not next_face_loop(structure)) and (structure.current_dimension <= max_dim): sig_check() structure._index += 1 return structure.current_dimension From 6c88068c4c78450513ac5a84d482f3ef33c8ba4e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:26 +0200 Subject: [PATCH 669/706] fix bug revealed by a_maximal_chain --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index e4b02fae715..57dae081101 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1136,7 +1136,7 @@ cdef class FaceIterator_base(SageObject): :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): return None return CombinatorialFace(self) @@ -1534,7 +1534,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return face @@ -1827,7 +1827,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return self.current() From b72578bee43e31df4edf99cd1f140a607c8d714c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:47 +0200 Subject: [PATCH 670/706] fix error message in doctest --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 57dae081101..858a9311406 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1057,7 +1057,7 @@ cdef class FaceIterator_base(SageObject): sage: it.only_subfaces() Traceback (most recent call last): ... - ValueError: cannot visit subsets after ignoring a face + ValueError: cannot only visit subsets after ignoring a face """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") From ad75abb5acdf3ac6fbd6e652781e1fd555a5e6c2 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 16:49:56 +0200 Subject: [PATCH 671/706] fix the order of the coatoms when resetting the face iterator --- .../face_iterator.pyx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 858a9311406..7a433794b65 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -339,6 +339,25 @@ cdef class FaceIterator_base(SageObject): sage: it.reset() sage: next(it).ambient_V_indices() (0, 3, 4, 5) + + TESTS: + + Resetting will fix the order of the coatoms after ``only_subsets``:: + + sage: P = polytopes.Birkhoff_polytope(3) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=False) + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) + sage: face = next(it) + sage: face.ambient_H_indices() + (7,) + sage: it.only_subfaces() + sage: it.reset() + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, @@ -360,6 +379,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + # ``only_subsets`` might have messed up the coatoms. + face_list_shallow_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data) + def __next__(self): r""" Must be implemented by a derived class. From 3d158adfbc8eaa1fdc88559ea68a1debb37a317e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:35:23 +0200 Subject: [PATCH 672/706] do not display equations for doctests illustrating how the iterator works --- .../combinatorial_polyhedron/face_iterator.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 7a433794b65..8af94d6966a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -348,15 +348,15 @@ cdef class FaceIterator_base(SageObject): sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter(dual=False) sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (8,) sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (7,) sage: it.only_subfaces() sage: it.reset() sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: @@ -1046,9 +1046,9 @@ cdef class FaceIterator_base(SageObject): sage: P = polytopes.Birkhoff_polytope(4) sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter() - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (15,) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (14,) sage: it.only_subfaces() sage: all(14 in f.ambient_H_indices() for f in it) From 353313c3189b8d4da69980d0eba1cb9fcbc9ad3e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:38:03 +0200 Subject: [PATCH 673/706] update face status --- .../polyhedron/combinatorial_polyhedron/face_iterator.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index d53ac95fe28..f99e71b7013 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -9,7 +9,7 @@ from .combinatorial_face cimport CombinatorialFace cdef struct iter_s: bint dual # if 1, then iterate over dual Polyhedron face_t face # the current face of the iterator - int face_status # 0 not initialized, 1 initialized, 2 added to visited_all + int face_status # 0 not initialized, 1 initialized, 2 added to visited_all, 3 only visit subsets size_t *atom_rep # a place where atom-representaion of face will be stored size_t *coatom_rep # a place where coatom-representaion of face will be stored int current_dimension # dimension of current face, dual dimension if ``dual`` From 842fb8efea3c6c694a6469d19a52a5799a114d2c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:44:06 +0200 Subject: [PATCH 674/706] consume the iterator if running ignore_subsets after only_subsets --- .../combinatorial_polyhedron/face_iterator.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 8af94d6966a..9a525bbb5a9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -491,15 +491,14 @@ cdef class FaceIterator_base(SageObject): ... ValueError: only possible when not in dual mode - Cannot run ``ignore_subfaces`` after ``only_subfaces:: + Ignoring the same face as was requested to visit only consumes the iterator:: sage: it = C.face_iter(dual=False) sage: _ = next(it) sage: it.only_subfaces() sage: it.ignore_subfaces() - Traceback (most recent call last): - ... - ValueError: cannot ignore a face after setting iterator to only visit subsets + sage: list(it) + [] Face iterator must be set to a face first:: @@ -1009,7 +1008,10 @@ cdef class FaceIterator_base(SageObject): if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") if unlikely(self.structure.face_status == 3): - raise ValueError("cannot ignore a face after setting iterator to only visit subsets") + # The iterator is consumed, if it was just set to visit only subsets + # next thing to ignore subsets. + self.structure.current_dimension = self.structure.dimension + return 0 if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 From 250f0e41942ef94855d4d4391e7467cd191d2754 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:53:01 +0200 Subject: [PATCH 675/706] specify for `find_face` that we assume the iterator to be clean --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9a525bbb5a9..2757ac73c41 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1227,6 +1227,10 @@ cdef class FaceIterator_base(SageObject): Iterate until the current face is ``face``. The value can then be obtained with :meth:`current`. + + The iterator is assumed to be newly initialized or reset. + See :meth:`FaceIterator_base._join_of_atoms` and + :meth:`FaceIterator_base._meet_of_coatoms`. """ cdef size_t n_atoms = face_len_atoms(face) From 3f1d44a5e7ef3f97b1cfce97c6413dd64bd837d3 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:05:04 +0200 Subject: [PATCH 676/706] Use different names in conversion of derivatives of symbolic functions These are less likely to clash with user-defined variables and lead to bugs such as sage: var('x t0') sage: function('f') sage: diff(f(x,1 + t0),x).subs({x:t0}).simplify_full() D[0](f)(t0, t0 + 1) + 4*D[1](f)(t0, t0 + 1) Note that we cannot use newly generated unique names here (such as those obtained from SR.symbol()), because multiple conversions of the same expression must be equal. --- src/sage/interfaces/maxima_abstract.py | 2 +- src/sage/interfaces/maxima_lib.py | 10 +++++--- src/sage/symbolic/expression_conversions.py | 28 +++++++++++++-------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 1200c2fa0c2..d8102801ba8 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1766,7 +1766,7 @@ def _latex_(self): sage: y,d = var('y,d') sage: f = function('f') sage: latex(maxima(derivative(f(x*y), x))) - \left(\left.{{{\it \partial}}\over{{\it \partial}\, {\it t}_{0}}}\,f\left({\it t}_{0}\right) \right|_{{\it t}_{0}={\it x}\, {\it y}}\right)\,{\it y} + \left(\left.{{{\it \partial}}\over{{\it \partial}\, {\it \_symbol}_{0}}}\,f\left( {\it \_symbol}_{0}\right)\right|_{ {\it \_symbol}_{0}={\it x}\, {\it y}}\right)\,{\it y} sage: latex(maxima(derivative(f(x,y,d), d,x,x,y))) {{{\it \partial}^4}\over{{\it \partial}\,{\it d}\, {\it \partial}\,{\it x}^2\,{\it \partial}\, {\it y}}}\,f\left({\it x} , {\it y} , {\it d}\right) sage: latex(maxima(d/(d-2))) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 40367c52426..7066b10da66 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1581,10 +1581,12 @@ def sr_to_max(expr): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it # like one. So, we replace the argument `1` with a - # temporary variable e.g. `t0` and then evaluate the - # derivative f'(t0) symbolically at t0=1. See trac - # #12796. - temp_args = [SR.var("t%s"%i) for i in range(len(args))] + # temporary variable e.g. `_symbol0` and then evaluate + # the derivative f'(_symbol0) symbolically at + # _symbol0=1. See trac #12796. Note that we cannot use + # SR.temp_var here since two conversions of the same + # expression have to be equal. + temp_args = [SR.symbol("_symbol%s"%i) for i in range(len(args))] f = sr_to_max(op.function()(*temp_args)) params = op.parameter_set() deriv_max = [[mdiff],f] diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 9cf695317fc..8168b86bc7f 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -509,7 +509,7 @@ def derivative(self, ex, operator): sage: t D[0](f)(x*y) sage: m.derivative(t, t.operator()) - "at(diff('f(_SAGE_VAR_t0), _SAGE_VAR_t0, 1), [_SAGE_VAR_t0 = (_SAGE_VAR_x)*(_SAGE_VAR_y)])" + "at(diff('f(_SAGE_VAR__symbol0), _SAGE_VAR__symbol0, 1), [_SAGE_VAR__symbol0 = (_SAGE_VAR_x)*(_SAGE_VAR_y)])" TESTS: @@ -533,7 +533,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=exp(x)); a D[0](f)(e^x) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0),_SAGE_VAR_t0,1),_SAGE_VAR_t0=%e^_SAGE_VAR_x) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=%e^_SAGE_VAR_x) sage: bool(b.sage() == a) True @@ -542,7 +542,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=4); a D[0](f)(4) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -562,7 +562,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4); a D[0](f)(4, y) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0,_SAGE_VAR_y),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0,_SAGE_VAR_y),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -571,7 +571,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4).subs(y=8); a D[0](f)(4, 8) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0,8),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0,8),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -594,9 +594,12 @@ def derivative(self, ex, operator): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it like # one. So, we replace the argument `1` with a temporary - # variable e.g. `t0` and then evaluate the derivative - # f'(t0) symbolically at t0=1. See trac #12796. - temp_args = [SR.var("t%s"%i) for i in range(len(args))] + # variable e.g. `_symbol0` and then evaluate the + # derivative f'(_symbol0) symbolically at _symbol0=1. See + # trac #12796. Note that we cannot use SR.temp_var here + # since two conversions of the same expression have to be + # equal. + temp_args = [SR.symbol("_symbol%s"%i) for i in range(len(args))] f = operator.function()(*temp_args) params = operator.parameter_set() params = ["%s, %s"%(temp_args[i]._maxima_init_(), params.count(i)) for i in set(params)] @@ -1002,9 +1005,12 @@ def derivative(self, ex, operator): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it like # one. So, we replace the argument `1` with a temporary - # variable e.g. `t0` and then evaluate the derivative - # f'(t0) symbolically at t0=1. See trac #12796. - temp_args = [SR.var("t%s" % i) for i in range(len(args))] + # variable e.g. `_symbol0` and then evaluate the + # derivative f'(_symbol0) symbolically at _symbol0=1. See + # trac #12796. Note that we cannot use SR.temp_var here + # since two conversions of the same expression have to be + # equal. + temp_args = [SR.symbol("_symbol%s" % i) for i in range(len(args))] f = operator.function()(*temp_args) vars = ",".join(temp_args[i]._fricas_init_() for i in params_set) subs = ",".join("%s = %s" % (t._fricas_init_(), a._fricas_init_()) From 30c17886739e1277e5d7a117c8b41529b27c10d6 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:12:35 +0200 Subject: [PATCH 677/706] Introduce a mechanism for creating temporary variables with unique names In various places in sagemath, temporary symbolic variables are needed. Previously, these variables were created in an ad-hoc manner and could clash with user-defined variables. This commit introduces a unified mechanism for creating these variables and cleaning up afterwards (removing the variable from the list of symbols in the symbolic ring and forgetting any assumptions about it). --- src/sage/calculus/desolvers.py | 203 +++++++++--------- .../complex_dynamics/mandel_julia_helper.pyx | 7 +- src/sage/functions/piecewise.py | 22 +- .../parametrized_surface3d.py | 46 ++-- src/sage/libs/pynac/pynac.pxd | 1 + src/sage/manifolds/chart.py | 11 +- src/sage/manifolds/continuous_map.py | 3 +- .../differentiable/characteristic_class.py | 40 ++-- ...otics_multivariate_generating_functions.py | 7 +- src/sage/symbolic/expression.pyx | 61 ++++-- src/sage/symbolic/operators.py | 2 +- src/sage/symbolic/ring.pyx | 111 +++++++++- 12 files changed, 323 insertions(+), 191 deletions(-) diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index ea0688c80c0..b180e504016 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -78,7 +78,7 @@ from sage.interfaces.maxima import Maxima from sage.plot.all import line from sage.symbolic.expression import is_SymbolicEquation -from sage.symbolic.ring import is_SymbolicVariable +from sage.symbolic.ring import SR, is_SymbolicVariable from sage.calculus.functional import diff from sage.misc.functional import N from sage.rings.real_mpfr import RealField @@ -1351,6 +1351,9 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output if ics is None: raise ValueError("No initial conditions, specify with ics=[x0,y0].") + if output not in ['list', 'plot', 'slope_field']: + raise ValueError("Option output should be 'list', 'plot' or 'slope_field'.") + if ivar is None: ivars = de.variables() ivars = [t for t in ivars if t != dvar] @@ -1358,66 +1361,65 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output raise ValueError("Unable to determine independent variable, please specify.") ivar = ivars[0] + step=abs(step) + + def desolve_rk4_inner(de, dvar): + de0=de._maxima_() + maxima("load('dynamics)") + lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points) + sol_1, sol_2 = [],[] + if lower_boundics[0]: + cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ + "%(de0.str(),'_SAGE_VAR_'+str(dvar),str(ics[1]),'_SAGE_VAR_'+str(ivar),str(ics[0]),upper_bound,step) + sol_2=maxima(cmd).sage() + sol_2.pop(0) + sol=sol_1 + sol.extend([[ics[0],ics[1]]]) + sol.extend(sol_2) + + if output == 'list': + return sol + from sage.plot.plot import list_plot + from sage.plot.plot_field import plot_slope_field + R = list_plot(sol, plotjoined=True, **kwds) + if output == 'plot': + return R + if output == 'slope_field': + XMIN = sol[0][0] + YMIN = sol[0][1] + XMAX = XMIN + YMAX = YMIN + for s, t in sol: + if s > XMAX: + XMAX = s + if s < XMIN: + XMIN = s + if t > YMAX: + YMAX = t + if t < YMIN: + YMIN = t + return plot_slope_field(de, (ivar,XMIN,XMAX), (dvar,YMIN,YMAX))+R + if not is_SymbolicVariable(dvar): from sage.symbolic.ring import SR from sage.calculus.all import diff from sage.symbolic.relation import solve if is_SymbolicEquation(de): de = de.lhs() - de.rhs() - dummy_dvar = SR.var('dummy_dvar') # consider to add warning if the solution is not unique de=solve(de,diff(dvar,ivar),solution_dict=True) if len(de) != 1: raise NotImplementedError("Sorry, cannot find explicit formula for right-hand side of the ODE.") - de=de[0][diff(dvar,ivar)].subs(dvar==dummy_dvar) + with SR.temp_var() as dummy_dvar: + return desolve_rk4_inner(de[0][diff(dvar,ivar)].subs({dvar:dummy_dvar}), dummy_dvar) else: - dummy_dvar=dvar - - step=abs(step) - de0=de._maxima_() - maxima("load('dynamics)") - lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points) - sol_1, sol_2 = [],[] - if lower_boundics[0]: - cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ - "%(de0.str(),'_SAGE_VAR_'+str(dummy_dvar),str(ics[1]),'_SAGE_VAR_'+str(ivar),str(ics[0]),upper_bound,step) - sol_2=maxima(cmd).sage() - sol_2.pop(0) - sol=sol_1 - sol.extend([[ics[0],ics[1]]]) - sol.extend(sol_2) - - if output == 'list': - return sol - from sage.plot.plot import list_plot - from sage.plot.plot_field import plot_slope_field - R = list_plot(sol, plotjoined=True, **kwds) - if output == 'plot': - return R - if output == 'slope_field': - XMIN = sol[0][0] - YMIN = sol[0][1] - XMAX = XMIN - YMAX = YMIN - for s, t in sol: - if s > XMAX: - XMAX = s - if s < XMIN: - XMIN = s - if t > YMAX: - YMAX = t - if t < YMIN: - YMIN = t - return plot_slope_field(de, (ivar,XMIN,XMAX), (dummy_dvar,YMIN,YMAX))+R - - raise ValueError("Option output should be 'list', 'plot' or 'slope_field'.") - + return desolve_rk4_inner(de, dvar) def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1): r""" @@ -1655,6 +1657,49 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() from sage.ext.fast_eval import fast_float from sage.calculus.functions import jacobian + def desolve_odeint_inner(ivar): + # one-dimensional systems: + if is_SymbolicVariable(dvars): + func = fast_float(des, dvars, ivar) + if not compute_jac: + Dfun = None + else: + J = diff(des, dvars) + J = fast_float(J, dvars, ivar) + + def Dfun(y, t): + return [J(y, t)] + + # n-dimensional systems: + else: + desc = [] + variabs = dvars[:] + variabs.append(ivar) + for de in des: + desc.append(fast_float(de,*variabs)) + + def func(y,t): + v = list(y[:]) + v.append(t) + return [dec(*v) for dec in desc] + + if not compute_jac: + Dfun=None + else: + J = jacobian(des,dvars) + J = [list(v) for v in J] + J = fast_float(J,*variabs) + def Dfun(y,t): + v = list(y[:]) + v.append(t) + return [[element(*v) for element in row] for row in J] + + + sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol, + tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep, + mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg) + return sol + if ivar is None: if len(dvars)==0 or len(dvars)==1: if len(dvars)==1: @@ -1671,59 +1716,17 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() ivars = all_vars - set(dvars) if len(ivars)==1: - ivar = ivars.pop() + return desolve_odeint_inner(ivars[0]) elif not ivars: - try: - safe_names = [ 't_' + str(dvar) for dvar in dvars ] - except TypeError: # not iterable - safe_names = [ 't_' + str(dvars) ] - from sage.symbolic.ring import SR - ivar = [SR.var(name) for name in safe_names] + if is_SymbolicVariable(dvars): + with SR.temp_var() as ivar: + return desolve_odeint_inner(ivar) + else: + with SR.temp_var(n=len(dvars)) as ivar: + return desolve_odeint_inner(ivar) else: raise ValueError("Unable to determine independent variable, please specify.") - - # one-dimensional systems: - if is_SymbolicVariable(dvars): - func = fast_float(des, dvars, ivar) - if not compute_jac: - Dfun = None - else: - J = diff(des, dvars) - J = fast_float(J, dvars, ivar) - - def Dfun(y, t): - return [J(y, t)] - - # n-dimensional systems: - else: - desc = [] - variabs = dvars[:] - variabs.append(ivar) - for de in des: - desc.append(fast_float(de,*variabs)) - - def func(y,t): - v = list(y[:]) - v.append(t) - return [dec(*v) for dec in desc] - - if not compute_jac: - Dfun=None - else: - J = jacobian(des,dvars) - J = [list(v) for v in J] - J = fast_float(J,*variabs) - def Dfun(y,t): - v = list(y[:]) - v.append(t) - return [[element(*v) for element in row] for row in J] - - - sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol, - tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep, - mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg) - - return sol + return desolve_odeint_inner(ivar) def desolve_mintides(f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-16): r""" diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx index 7cff9ac6347..7a6ee5aa51a 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +++ b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx @@ -34,6 +34,7 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.ext.fast_callable import fast_callable from sage.calculus.all import symbolic_expression +from sage.symbolic.ring import SR from sage.calculus.var import var from sage.rings.fraction_field import is_FractionField from sage.categories.function_fields import FunctionFields @@ -843,9 +844,9 @@ cpdef polynomial_mandelbrot(f, parameter=None, double x_center=0, # critical points for each c. else: # Solve for critical points symbollically. - w = var('w') - df = f.derivative(z).polynomial(z).subs({z:w}) - critical_pts = solve(symbolic_expression(df)==0, w) + with SR.temp_var() as w: + df = f.derivative(z).polynomial(z).subs({z:w}) + critical_pts = solve(symbolic_expression(df)==0, w) c_pts = [] for pt in critical_pts: c_pts.append(fast_callable(pt.rhs(), vars=[c], domain=CDF)) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..37b2e2fc727 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -946,8 +946,6 @@ def convolution(self, parameters, variable, other): g = other if len(f.end_points())*len(g.end_points()) == 0: raise ValueError('one of the piecewise functions is nowhere defined') - tt = SR.var('tt') - uu = SR.var('uu') fd, f0 = parameters[0] gd, g0 = next(other.items()) if len(f)==1 and len(g)==1: @@ -957,12 +955,14 @@ def convolution(self, parameters, variable, other): a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() - i1 = f0.subs({variable: uu}) - i2 = g0.subs({variable: tt-uu}) - fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs(tt = variable) - fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs(tt = variable) - fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs(tt = variable) - fg4 = definite_integral(i1*i2, uu, a1, a2).subs(tt = variable) + with SR.temp_var() as tt: + with SR.temp_var() as uu: + i1 = f0.subs({variable: uu}) + i2 = g0.subs({variable: tt-uu}) + fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs({tt:variable}) + fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs({tt:variable}) + fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs({tt:variable}) + fg4 = definite_integral(i1*i2, uu, a1, a2).subs({tt:variable}) if a1-b1": pass diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 5a2b7070867..56b49bba55f 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -3162,7 +3162,6 @@ def inverse(self): """ from sage.symbolic.relation import solve - from sage.symbolic.assumptions import assumptions if self._inverse is not None: return self._inverse # The computation is necessary: @@ -3186,8 +3185,7 @@ def inverse(self): for i in range(n2): if x2[i].is_positive(): coord_domain[i] = 'positive' - xp2 = [ SR.var('xxxx' + str(i), domain=coord_domain[i]) - for i in range(n2) ] + xp2 = [ SR.temp_var(domain=coord_domain[i]) for i in range(n2) ] xx2 = self._transf.expr() equations = [xp2[i] == xx2[i] for i in range(n2)] try: @@ -3240,12 +3238,7 @@ def inverse(self): "manually") x2_to_x1 = list_x2_to_x1[0] self._inverse = type(self)(self._chart2, self._chart1, *x2_to_x1) - # Some cleaning: the local symbolic variables (xxxx0, xxxx1, ...) are - # removed from the list of assumptions - for asm in assumptions(): - for xxxx in xp2: - if asm.has(xxxx): - asm.forget() + SR.cleanup_var(xp2) return self._inverse def set_inverse(self, *transformations, **kwds): diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index 7991191b900..04a29d9f4f0 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -1963,7 +1963,7 @@ def __invert__(self): n2 = len(chart2._xx) # New symbolic variables (different from chart2._xx to allow for a # correct solution even when chart2 = chart1): - x2 = [SR.var('xxxx' + str(i)) for i in range(n2)] + x2 = SR.temp_var(n=n2) equations = [x2[i] == coord_map._functions[i].expr() for i in range(n2)] solutions = solve(equations, chart1._xx, solution_dict=True) @@ -1983,6 +1983,7 @@ def __invert__(self): except AttributeError: pass coord_functions[(chart2, chart1)] = inv_functions + SR.cleanup_var(x2) if self._name is None: name = None else: diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index f4155b2577c..dfa68faa604 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -498,28 +498,28 @@ def _get_coeff_list(self, distinct_real=True): pow_range = self._base_space._dim // 2 def_var = self._func.default_variable() # Use a complex variable without affecting the old one: - new_var = SR.symbol('x_char_class_', domain='complex') - if self._vbundle._field_type == 'real' and distinct_real: - if self._class_type == 'additive': - func = self._func.subs({def_var: new_var ** 2}) / 2 - elif self._class_type == 'multiplicative': - # This could case problems in the real domain, where sqrt(x^2) - # is simplified to |x|. However, the variable must be complex - # anyway. - func = self._func.subs({def_var : new_var**2}).sqrt() - elif self._class_type == 'Pfaffian': - # There are no canonical Pfaffian classes, however, consider the - # projection onto the odd part of the function to keep the - # matrices skew: - func = (self._func.subs({def_var: new_var}) - - self._func.subs({def_var: -new_var})) / 2 - else: - func = self._func.subs({def_var: new_var}) + with SR.temp_var(domain='complex') as new_var: + if self._vbundle._field_type == 'real' and distinct_real: + if self._class_type == 'additive': + func = self._func.subs({def_var: new_var ** 2}) / 2 + elif self._class_type == 'multiplicative': + # This could case problems in the real domain, where sqrt(x^2) + # is simplified to |x|. However, the variable must be complex + # anyway. + func = self._func.subs({def_var : new_var**2}).sqrt() + elif self._class_type == 'Pfaffian': + # There are no canonical Pfaffian classes, however, consider the + # projection onto the odd part of the function to keep the + # matrices skew: + func = (self._func.subs({def_var: new_var}) - + self._func.subs({def_var: -new_var})) / 2 + else: + func = self._func.subs({def_var: new_var}) - if self._vbundle._field_type == 'real' and not distinct_real: - pow_range = pow_range // 2 + if self._vbundle._field_type == 'real' and not distinct_real: + pow_range = pow_range // 2 - return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) + return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) def _init_derived(self): r""" diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 1975374e2ac..9bd8b03df28 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -3681,6 +3681,7 @@ def diff_prod(f_derivs, u, g, X, interval, end, uderivs, atc): D = {} rhs = [] lhs = [] + new_vars = [] for t in combinations_with_replacement(X, l): t = list(t) s = t + end @@ -3689,13 +3690,15 @@ def diff_prod(f_derivs, u, g, X, interval, end, uderivs, atc): # Since Sage's solve command can't take derivatives as variable # names, make new variables based on t to stand in for # diff(u, t) and store them in D. - D[diff(u, t).subs(atc)] = var('zing' + - ''.join(str(x) for x in t)) + new_var = SR.temp_var() + new_vars.append(new_var) + D[diff(u, t).subs(atc)] = new_var eqns = [lhs[i] == rhs[i].subs(uderivs).subs(D) for i in range(len(lhs))] variables = D.values() sol = solve(eqns, *variables, solution_dict=True) uderivs.update(subs_all(D, sol[ZZ.zero()])) + SR.cleanup_var(new_vars) return uderivs diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f81c4ff9717..d43f288a262 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -767,6 +767,29 @@ cdef class Expression(CommutativeRingElement): """ return new_Expression_from_GEx(self._parent, self._gobj) + def __enter__(self): + """ + Method used by temporary variables with Python `with` to + automatically clean up after themselves. + """ + return self + + def __exit__(self, *args): + """ + Method used by temporary variables with Python `with` to + automatically clean up after themselves. + + TESTS:: + + sage: symbols_copy = SR.symbols.copy() + sage: with SR.temp_var() as t: pass + sage: symbols_copy == SR.symbols + True + + """ + SR.cleanup_var(self) + return False + def _repr_(self): r""" Return string representation of this symbolic expression. @@ -12885,25 +12908,25 @@ cdef class Expression(CommutativeRingElement): if not self.has(Y): raise ValueError("Expression {} contains no {} terms".format(self, Y)) - x = SR.symbol() - yy = SR.symbol() - y = SymbolicFunction('y', 1)(x) - f = SymbolicFunction('f', 2)(x, yy) - Fx = f.diff(x) - Fy = f.diff(yy) - G = -(Fx/Fy) - G = G.subs({yy: y}) - di = {y.diff(x): -self.diff(X)/self.diff(Y)} - R = G - S = G.diff(x, n - 1) - for i in range(n + 1): - di[y.diff(x, i + 1).subs({x: x})] = R - S = S.subs(di) - R = G.diff(x, i) - for j in range(n + 1 - i): - di[f.diff(x, i, yy, j).subs({x: x, yy: y})] = self.diff(X, i, Y, j) - S = S.subs(di) - return S + with SR.temp_var() as x: + with SR.temp_var() as yy: + y = SymbolicFunction('y', 1)(x) + f = SymbolicFunction('f', 2)(x, yy) + Fx = f.diff(x) + Fy = f.diff(yy) + G = -(Fx/Fy) + G = G.subs({yy: y}) + di = {y.diff(x): -self.diff(X)/self.diff(Y)} + R = G + S = G.diff(x, n - 1) + for i in range(n + 1): + di[y.diff(x, i + 1).subs({x: x})] = R + S = S.subs(di) + R = G.diff(x, i) + for j in range(n + 1 - i): + di[f.diff(x, i, yy, j).subs({x: x, yy: y})] = self.diff(X, i, Y, j) + S = S.subs(di) + return S def solve_diophantine(f, *args, **kwds): """ diff --git a/src/sage/symbolic/operators.py b/src/sage/symbolic/operators.py index 368d6f68f89..2fa839149dc 100644 --- a/src/sage/symbolic/operators.py +++ b/src/sage/symbolic/operators.py @@ -114,7 +114,7 @@ def __call__(self, *args): # temporary variable e.g. `t0` and then evaluate the # derivative f'(t0) symbolically at t0=1. See trac # #12796. - temp_args=[SR.var("t%s"%i) for i in range(len(args))] + temp_args=SR.temp_var(n=len(args)) vars=[temp_args[i] for i in self._parameter_set] return self._f(*temp_args).diff(*vars).function(*temp_args)(*args) vars = [args[i] for i in self._parameter_set] diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 692266db00c..47c2fe55d99 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -730,6 +730,7 @@ cdef class SymbolicRing(CommutativeRing): if name is None: # Check if we need a temporary anonymous new symbol symb = ginac_new_symbol() + name = symb.get_name().decode('ascii') if domain is not None: symb.set_domain(sage_domain_to_ginac_domain(domain)) else: @@ -741,14 +742,102 @@ cdef class SymbolicRing(CommutativeRing): ginac_domain = domain_complex symb = ginac_symbol(str_to_bytes(name), str_to_bytes(latex_name), ginac_domain) - self.symbols[name] = e e._gobj = GEx(symb) + self.symbols[name] = e if domain is not None: send_sage_domain_to_maxima(e, domain) return e + def temp_var(self, n=None, domain=None): + """ + Return one or multiple new unique symbolic variables as an element + of the symbolic ring. Use this instead of SR.var() if there is a + possibility of name clashes occuring. Call SR.cleanup_var() once + the variables are no longer needed or use a `with SR.temp_var() + as ...` construct. + + INPUT: + + - ``n`` -- (optional) positive integer; number of symbolic variables + + - ``domain`` -- (optional) specify the domain of the variable(s); + + EXAMPLES: + + Simple definition of a functional derivative:: + + sage: def functional_derivative(expr,f,x): + ....: with SR.temp_var() as a: + ....: return expr.subs({f(x):a}).diff(a).subs({a:f(x)}) + sage: f = function('f') + sage: a = var('a') + sage: functional_derivative(f(a)^2+a,f,a) + 2*f(a) + + Contrast this to a similar implementation using SR.var(), + which gives a wrong result in our example:: + + sage: def functional_derivative(expr,f,x): + ....: a = SR.var('a') + ....: return expr.subs({f(x):a}).diff(a).subs({a:f(x)}) + sage: f = function('f') + sage: a = var('a') + sage: functional_derivative(f(a)^2+a,f,a) + 2*f(a) + 1 + + TESTS: + + sage: x = SR.temp_var() + sage: y = SR.temp_var() + sage: bool(x == x) + True + sage: bool(x == y) + False + sage: bool(x.parent()(x._maxima_()) == x) + True + + """ + if (n == None): + return self.symbol(None, domain=domain) + return TemporaryVariables([self.temp_var(domain=domain) for i in range(n)]) + + def cleanup_var(self, symbol): + """ + Cleans up a variable, removing assumptions about the + variable and allowing for it to be garbage collected + + INPUT: + + - ``symbol`` -- a variable or a list of variables + + TESTS: + + sage: from sage.symbolic.assumptions import assumptions + sage: symbols_copy = SR.symbols.copy() + sage: assumptions_copy = assumptions().copy() + sage: x = SR.temp_var(domain='real') + sage: SR.cleanup_var(x) + sage: symbols_copy == SR.symbols + True + sage: assumptions_copy == assumptions() + True + """ + from sage.symbolic.assumptions import assumptions + if isinstance(symbol,list) or isinstance(symbol,tuple): + for s in symbol: + self.cleanup_var(s) + else: + try: + name = self._repr_element_(symbol) + del self.symbols[name] + except KeyError: + pass + for asm in assumptions(): + if asm.has(symbol): + asm.forget() + def var(self, name, latex_name=None, n=None, domain=None): """ Return a symbolic variable as an element of the symbolic ring. @@ -1424,3 +1513,23 @@ def isidentifier(x): if x in KEYWORDS: return False return x.isidentifier() + +class TemporaryVariables(tuple): + """ + Instances of this class can be used with Python `with` to + automatically clean up after themselves. + """ + def __enter__(self): + return self + + def __exit__(self, *args): + """ + TESTS:: + + sage: symbols_copy = SR.symbols.copy() + sage: with SR.temp_var(n=2) as temp_vars: pass + sage: symbols_copy == SR.symbols + True + """ + SR.cleanup_var(self) + return False From efc463a31d42afffcf3585f872d03630d22324fc Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:35:48 +0200 Subject: [PATCH 678/706] Warn on potential name clashes in SR.var() --- src/sage/symbolic/ring.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 47c2fe55d99..337e11e192a 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -977,6 +977,15 @@ cdef class SymbolicRing(CommutativeRing): for s in names_list: if not isidentifier(s): raise ValueError(f'The name "{s}" is not a valid Python identifier.') + # warn on bad symbol names, but only once + # symbol... names are temporary variables created with + # SR.temp_var + # _symbol... names are used in the conversion of + # derivatives of symbolic functions to maxima and other + # external libraries + if self.symbols.get(s) is None and ((s.startswith('symbol') and s[6:].isdigit()) or (s.startswith('_symbol') and s[7:].isdigit())): + import warnings + warnings.warn(f'The name "{name}" may clash with names used internally in sagemath. It is recommended to choose a different name for your variable.') formatted_latex_name = None if latex_name is not None and n is None: From 011f59a15f0d7f59e92c91655a8855aafa3c2b97 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 08:23:30 -0700 Subject: [PATCH 679/706] src/sage/docs/conf.py: Add more \DeclareUnicodeCharacter --- src/sage/docs/conf.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index d249f2cdafe..04056fef012 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -400,18 +400,29 @@ def set_intersphinx_mappings(app, config): \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}} \DeclareUnicodeCharacter{2113}{\ell} - \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} - \DeclareUnicodeCharacter{2264}{\leq} - \DeclareUnicodeCharacter{2265}{\geq} - \DeclareUnicodeCharacter{221E}{\infty} - \DeclareUnicodeCharacter{2211}{\sum} + \DeclareUnicodeCharacter{2148}{\id} + \DeclareUnicodeCharacter{2202}{\partial} + \DeclareUnicodeCharacter{2205}{\ensuremath{\emptyset}} \DeclareUnicodeCharacter{2208}{\in} \DeclareUnicodeCharacter{2209}{\notin} - \DeclareUnicodeCharacter{2202}{\partial} + \DeclareUnicodeCharacter{2211}{\sum} + \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} + \DeclareUnicodeCharacter{221E}{\infty} + \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} + \DeclareUnicodeCharacter{2228}{\ensuremath{\vee}} + \DeclareUnicodeCharacter{2229}{\ensuremath{\cap}} + \DeclareUnicodeCharacter{222A}{\ensuremath{\cup}} \DeclareUnicodeCharacter{222B}{\ensuremath{\int}} - \DeclareUnicodeCharacter{2148}{\id} \DeclareUnicodeCharacter{2248}{\approx} \DeclareUnicodeCharacter{2260}{\neq} + \DeclareUnicodeCharacter{2264}{\leq} + \DeclareUnicodeCharacter{2265}{\geq} + \DeclareUnicodeCharacter{2293}{\ensuremath{\sqcap}} + \DeclareUnicodeCharacter{2294}{\ensuremath{\sqcup}} + \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} + \DeclareUnicodeCharacter{22C1}{\ensuremath{\bigvee}} + \DeclareUnicodeCharacter{22C2}{\ensuremath{\bigcap}} + \DeclareUnicodeCharacter{22C3}{\ensuremath{\bigcup}} \DeclareUnicodeCharacter{00B1}{\pm} \DeclareUnicodeCharacter{2A02}{\otimes} \DeclareUnicodeCharacter{2A01}{\oplus} @@ -449,6 +460,8 @@ def set_intersphinx_mappings(app, config): \DeclareUnicodeCharacter{2089}{\ensuremath{{}_9}} \DeclareUnicodeCharacter{208A}{\ensuremath{{}_+}} \DeclareUnicodeCharacter{208B}{\ensuremath{{}_-}} + \DeclareUnicodeCharacter{1D62}{\ensuremath{{}_i}} + \DeclareUnicodeCharacter{2C7C}{\ensuremath{{}_j}} \newcommand{\sageMexSymbol}[1] {{\fontencoding{OMX}\fontfamily{cmex}\selectfont\raisebox{0.75em}{\symbol{#1}}}} From c985f42cd1184fa1227a38dbdd8353ce392b3285 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sun, 20 Jun 2021 19:15:28 -0700 Subject: [PATCH 680/706] Changed is_easy_sn_an to descriptive name --- .../rings/polynomial/polynomial_rational_flint.pyx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index f9724ea41d3..55cfd8a4819 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2513,7 +2513,7 @@ cdef class Polynomial_rational_flint(Polynomial): # Alias for discriminant disc = discriminant - def is_easy_sn_an(self, num_trials=50, assume_irreducible=False): + def galois_group_davenport_smith_test(self, num_trials=50, assume_irreducible=False): """ Use the Davenport-Smith test to attempt to certify that `f` has Galois group A_n or S_n. @@ -2521,18 +2521,20 @@ cdef class Polynomial_rational_flint(Polynomial): By default, we first check that `f` is irreducible. For extra efficiency, one can override this by specifying `assume_irreducible=True`; this yields undefined results if `f` is not irreducible. + + A corresponding function in Magma is `IsEasySnAn`. EXAMPLES:: sage: P. = QQ[] sage: u = x^7 + x + 1 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 1 sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 2 sage: u = x^7 - 2 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 0 """ From b55bec31bbfed772def9b95ed4e1921f06c9ce9f Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 13:32:21 +0100 Subject: [PATCH 681/706] Format changes As per a comment by slelivere, some pep8 formatting of the examples was done. --- src/sage/numerical/gauss_legendre.pyx | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index f236b3968a4..010247b3a49 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -11,9 +11,9 @@ We verify that `\int_0^1 n x^{n-1} \, dx = 1` for `n=1, \dots, 4`:: sage: prec = 100 sage: K = RealField(prec) sage: N = 4 - sage: V = VectorSpace(K,N) + sage: V = VectorSpace(K, N) sage: f = lambda x: V([(n+1)*x^n for n in range(N)]) - sage: I = integrate_vector(f,prec) + sage: I = integrate_vector(f, prec) sage: max([c.abs() for c in I-V(N*[1])]) 0.00000000000000000000000000000 @@ -22,7 +22,7 @@ AUTHORS: - Nils Bruin (2017-06-06): initial version - Linden Disney-Hogg (2021-06-17): documentation and integrate_vector method changes -NOTE:: +.. NOTE:: The code here is directly based on mpmath (see http://mpmath.org), but has a highly optimized routine to compute the nodes. @@ -74,11 +74,13 @@ def nodes(degree, prec): from this routine are actually more accurate than what the values the closed formula produces):: sage: from sage.numerical.gauss_legendre import nodes - sage: L1 = nodes(24,53) - sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) + sage: L1 = nodes(24, 53) + sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24, x)) sage: Pdif = P.diff() - sage: L2 = [((r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] - sage: all((a[0]-b[0]).abs() < 10^-15 and (a[1]-b[1]).abs() < 10^-9 for a,b in zip(L1,L2)) + sage: L2 = [((r + 1)/2, 1/(1 - r^2)/Pdif(r)^2) + for r, _ in RR['x'](P).roots()] + sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 + for a, b in zip(L1, L2)) True """ cdef long j,j1,n @@ -163,11 +165,11 @@ def estimate_error(results, prec, epsilon): sage: from sage.numerical.gauss_legendre import estimate_error sage: prec = 200 sage: K = RealField(prec) - sage: V = VectorSpace(K,2) - sage: a = V([1,-1]) - sage: b = V([1,1/2]) - sage: L = [a+2^(-2^i)*b for i in [0..5]] - sage: estimate_error(L,prec,K(2^(-prec))) + sage: V = VectorSpace(K, 2) + sage: a = V([1, -1]) + sage: b = V([1, 1/2]) + sage: L = [a + 2^(-2^i)*b for i in [0..5]] + sage: estimate_error(L, prec, K(2^(-prec))) 2.328235...e-10 """ if len(results)==2: @@ -213,12 +215,12 @@ def integrate_vector(f, prec, N=None, epsilon=None): sage: from sage.numerical.gauss_legendre import integrate_vector sage: prec = 200 sage: K = RealField(prec) - sage: V = VectorSpace(K,2) - sage: epsilon = K(2^(-prec+4)) - sage: f = lambda t:V((1+t^2,1/(1+t^2))) - sage: I = integrate_vector(f, prec, epsilon) - sage: J = V((4/3,pi/4)) - sage: max(c.abs() for c in (I-J)) < epsilon + sage: V = VectorSpace(K, 2) + sage: epsilon = K(2^(-prec + 4)) + sage: f = lambda t:V((1 + t^2, 1/(1 + t^2))) + sage: I = integrate_vector(f, prec, epsilon=epsilon) + sage: J = V((4/3, pi/4)) + sage: max(c.abs() for c in (I - J)) < epsilon True We can also use complex-valued integrands:: @@ -226,12 +228,12 @@ def integrate_vector(f, prec, N=None, epsilon=None): sage: prec = 200 sage: Kreal = RealField(prec) sage: K = ComplexField(prec) - sage: V = VectorSpace(K,2) - sage: epsilon = Kreal(2^(-prec+4)) - sage: f = lambda t: V((t,K(exp(2*pi*t*K.0)))) - sage: I = integrate_vector(f, prec, epsilon) - sage: J = V((1/2,0)) - sage: max(c.abs() for c in (I-J)) < epsilon + sage: V = VectorSpace(K, 2) + sage: epsilon = Kreal(2^(-prec + 4)) + sage: f = lambda t: V((t, K(exp(2*pi*t*K.0)))) + sage: I = integrate_vector(f, prec, epsilon=epsilon) + sage: J = V((1/2, 0)) + sage: max(c.abs() for c in (I - J)) < epsilon True """ results = [] From 87886ccda6687881688947100019df24d8e06d48 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 14:08:51 +0100 Subject: [PATCH 682/706] Added reference for REC algorithm The REC algorithm is an algorithm for computing the weights and nodes of the Gauss-Legendre scheme, described in Neu2018. This references is added to the references index as well as to the GL documentation. --- src/doc/en/reference/references/index.rst | 5 +++++ src/sage/numerical/gauss_legendre.pyx | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index befb73990a5..e678a0fdad7 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4348,6 +4348,11 @@ REFERENCES: Proceedings of the National Academy of Sciences 36.1 (1950): 48-49. +.. [Neu2018] Christian Neurohr, *Efficient Integration on Riemann Surfaces & + Applications*, + PhD Thesis, Carl von Ossietzky Universität Oldenburg + http://oops.uni-oldenburg.de/3607. + .. [New2003] Newman, M.E.J. *The Structure and function of complex networks*, SIAM Review vol. 45, no. 2 (2003), pp. 167-256. :doi:`10.1137/S003614450342480`. diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 010247b3a49..2e1ab03859c 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -54,17 +54,22 @@ from sage.rings.real_mpfr cimport RealNumber, RealField_class @cached_function def nodes(degree, prec): r""" - Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme using a version of the REC algorithm. + Compute the integration nodes and weights for the Gauss-Legendre quadrature + scheme + + We use the recurrence relations for Legendre polynomials to compute their values. + This is a version of the algorithm that in [Neu2018]_ is called the REC algorithm. INPUT: - ``degree`` -- integer. The number of nodes. Must be 3 or even. - - ``prec`` -- integer (minimal value 53). Binary precision with which the nodes and weights are computed. + - ``prec`` -- integer (minimal value 53). Binary precision with which the + nodes and weights are computed. OUTPUT: - A list of (node,weight) pairs. + A list of (node, weight) pairs. EXAMPLES: From f8c19a22f0cf7029f24d5b0e29c27c5fcf6f1119 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 14:50:45 +0100 Subject: [PATCH 683/706] Added TODO for nodes method The Arb library has code for calculating the nodes and weights. The added TODO suggests investigating if this code is faster. --- src/sage/numerical/gauss_legendre.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 2e1ab03859c..f7bbc6d2317 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -87,6 +87,12 @@ def nodes(degree, prec): sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 for a, b in zip(L1, L2)) True + + .. TODO:: + + It may be worth testing if using the Arb algorithm for finding the + nodes and weights in ``arb/acb_calc/integrate_gl_auto_deg.c`` has better + performance. """ cdef long j,j1,n cdef RealNumber r,t1,t2,t3,t4,a,w From 53438dd4cd04f9392220515b1bca87ff6ad48499 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 08:58:32 -0700 Subject: [PATCH 684/706] build/pkgs/pip: Update to 21.1.2 --- build/pkgs/pip/checksums.ini | 6 +++--- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index fabc83e4fa0..858cd7a1023 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=85a0cca517b854373dfcf43c3cac4ff726120c7f -md5=7fe80e6b3f94b5350284ed536d5b3a23 -cksum=3136634190 +sha1=b26e4afd5524f5613edf5b1b94dd1b6e9a2e1f87 +md5=a867fd51eacfd5293f5b7e0c2e7867a7 +cksum=2004742197 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 2d978e312b5..9fcf356b68f 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -21.1.1 +21.1.2 From a60179ab6b642246ee54120e43fdf9663afe5638 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Mon, 21 Jun 2021 23:28:40 +0200 Subject: [PATCH 685/706] Updated SageMath version to 9.4.beta3 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 4047e93f4c2..e1658133ddd 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.4.beta2", - "version": "9.4.beta2", + "title": "sagemath/sage: 9.4.beta3", + "version": "9.4.beta3", "upload_type": "software", - "publication_date": "2021-06-19", + "publication_date": "2021-06-21", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.4.beta2", + "identifier": "https://github.com/sagemath/sage/tree/9.4.beta3", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 079eef3212d..1ad0a4e8860 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.4.beta2, Release Date: 2021-06-19 +SageMath version 9.4.beta3, Release Date: 2021-06-21 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index c675d02ccd1..58884b88ca8 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=22ba093f620a2ab9a16516da4389f0b526e40e9b -md5=f687583786b11cab5a9e05df2db70151 -cksum=1560728026 +sha1=ec569cd49d5143432ec4315b54e5771a69f5c6cf +md5=1560d6f3eaf69380aa9e7110e9db9eb7 +cksum=707354543 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 5147162c126..bbd627519a1 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -791df42abf2b27544de92a290e19d9d3b88e85dd +fcefe993200b34e7ca722800ace83e70d0159932 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index bd33b958bae..f9ffedae371 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.4.beta2 +9.4.beta3 diff --git a/src/VERSION.txt b/src/VERSION.txt index bd33b958bae..f9ffedae371 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.4.beta2 +9.4.beta3 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index a7af79ec572..72225680ccb 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.4.beta2' -SAGE_RELEASE_DATE='2021-06-19' -SAGE_VERSION_BANNER='SageMath version 9.4.beta2, Release Date: 2021-06-19' +SAGE_VERSION='9.4.beta3' +SAGE_RELEASE_DATE='2021-06-21' +SAGE_VERSION_BANNER='SageMath version 9.4.beta3, Release Date: 2021-06-21' diff --git a/src/sage/version.py b/src/sage/version.py index c1c207a1ee9..6bde12d24e0 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.4.beta2' -date = '2021-06-19' -banner = 'SageMath version 9.4.beta2, Release Date: 2021-06-19' +version = '9.4.beta3' +date = '2021-06-21' +banner = 'SageMath version 9.4.beta3, Release Date: 2021-06-21' From a56b3c1333bce409e672b2f7de09d65591acf800 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Tue, 22 Jun 2021 12:48:54 +0100 Subject: [PATCH 686/706] Undid changes to integrate_vector Previous edits made to integrate_vector which are now the remit of ticket #32014 have been removed. This should (hopefully) allow the two tickets to not conflict. --- src/sage/numerical/gauss_legendre.pyx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index f7bbc6d2317..27bcb8b8b71 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -200,7 +200,7 @@ def estimate_error(results, prec, epsilon): e.append(D4.exp()) return max(e) -def integrate_vector(f, prec, N=None, epsilon=None): +def integrate_vector(f, prec, epsilon=None): r""" Integrate a one-argument vector-valued function numerically using Gauss-Legendre. @@ -213,8 +213,6 @@ def integrate_vector(f, prec, N=None, epsilon=None): - ``prec`` -- integer. Binary precision to be used. - - ``N`` -- integer. Number of nodes to use. If specificed, the target error ``epsilon`` is ignored. - - ``epsilon`` -- multiprecision float (default: `2^{(-\text{prec}+3)}`). Target error bound. OUTPUT: @@ -250,12 +248,6 @@ def integrate_vector(f, prec, N=None, epsilon=None): results = [] cdef long degree = 3 Rout = RealField(prec) - if N is not None: - nodelist = nodes(N,prec) - I = nodelist[0][1]*f(nodelist[0][0]) - for i in range(1,len(nodelist)): - I += nodelist[i][1]*f(nodelist[i][0]) - return I if epsilon is None: epsilon = Rout(2)**(-prec+3) From d0319dc9c0336bfed5f3d811f06fc74d5f54e48c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 07:06:46 -0700 Subject: [PATCH 687/706] build/pkgs/pip/patches/10029.patch: New --- build/pkgs/pip/patches/10029.patch | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 build/pkgs/pip/patches/10029.patch diff --git a/build/pkgs/pip/patches/10029.patch b/build/pkgs/pip/patches/10029.patch new file mode 100644 index 00000000000..cfd93403298 --- /dev/null +++ b/build/pkgs/pip/patches/10029.patch @@ -0,0 +1,71 @@ +From b8f1fcf863081fde0b9d558759c0e3c46ce09a12 Mon Sep 17 00:00:00 2001 +From: Ben Darnell <> +Date: Fri, 28 May 2021 16:01:41 +0000 +Subject: [PATCH] Avoid importing a non-vendored version of Tornado + +Code depending on this conditional import could break if an old +version of Tornado is present in the environment, rendering pip +unusable. +--- + news/10020.bugfix.rst | 1 + + src/pip/_vendor/tenacity/__init__.py | 10 ++++++---- + tools/vendoring/patches/tenacity.patch | 21 +++++++++++++++++++++ + 3 files changed, 28 insertions(+), 4 deletions(-) + create mode 100644 news/10020.bugfix.rst + create mode 100644 tools/vendoring/patches/tenacity.patch + +diff --git a/news/10020.bugfix.rst b/news/10020.bugfix.rst +new file mode 100644 +index 00000000000..9425626fb07 +--- /dev/null ++++ b/news/10020.bugfix.rst +@@ -0,0 +1 @@ ++Remove unused optional ``tornado`` import in vendored ``tenacity`` to prevent old versions of Tornado from breaking pip. +diff --git a/src/pip/_vendor/tenacity/__init__.py b/src/pip/_vendor/tenacity/__init__.py +index 5f8cb505896..42e9d8940b1 100644 +--- a/src/pip/_vendor/tenacity/__init__.py ++++ b/src/pip/_vendor/tenacity/__init__.py +@@ -22,10 +22,12 @@ + except ImportError: + iscoroutinefunction = None + +-try: +- import tornado +-except ImportError: +- tornado = None ++# Replace a conditional import with a hard-coded None so that pip does ++# not attempt to use tornado even if it is present in the environment. ++# If tornado is non-None, tenacity will attempt to execute some code ++# that is sensitive to the version of tornado, which could break pip ++# if an old version is found. ++tornado = None + + import sys + import threading +diff --git a/tools/vendoring/patches/tenacity.patch b/tools/vendoring/patches/tenacity.patch +new file mode 100644 +index 00000000000..006588b3653 +--- /dev/null ++++ b/tools/vendoring/patches/tenacity.patch +@@ -0,0 +1,21 @@ ++diff --git a/src/pip/_vendor/tenacity/__init__.py b/src/pip/_vendor/tenacity/__init__.py ++index 5f8cb5058..42e9d8940 100644 ++--- a/src/pip/_vendor/tenacity/__init__.py +++++ b/src/pip/_vendor/tenacity/__init__.py ++@@ -22,10 +22,12 @@ try: ++ except ImportError: ++ iscoroutinefunction = None ++ ++-try: ++- import tornado ++-except ImportError: ++- tornado = None +++# Replace a conditional import with a hard-coded None so that pip does +++# not attempt to use tornado even if it is present in the environment. +++# If tornado is non-None, tenacity will attempt to execute some code +++# that is sensitive to the version of tornado, which could break pip +++# if an old version is found. +++tornado = None ++ ++ import sys ++ import threading From f307b11af55cd9ee7302d8983773a4d71703b6ff Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 11:42:47 -0700 Subject: [PATCH 688/706] build/pkgs/setuptools/spkg-install.in: Do not nuke packages in site-packages before install --- build/pkgs/setuptools/spkg-install.in | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/build/pkgs/setuptools/spkg-install.in b/build/pkgs/setuptools/spkg-install.in index efc63964f5c..09a8b8f65ce 100644 --- a/build/pkgs/setuptools/spkg-install.in +++ b/build/pkgs/setuptools/spkg-install.in @@ -1,19 +1,5 @@ -# distribute doesn't allow itself to be replaced by setuptools -# so we manually have to delete it -# (pip actually can uninstall setuptools but we may not have pip -# install yet) -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/setuptools* -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/distribute* - -export PYTHON_EGG_CACHE="$DOT_SAGE/.python-eggs" - cd src - -versions=3 - -# Prevent setuptools from installing itself with easy_install -for vers in $versions; do - python${vers} setup.py --no-user-cfg install \ +# Use --single-version-externally-managed to prevent setuptools from installing itself with easy_install +python3 setup.py --no-user-cfg install \ --single-version-externally-managed --root="$SAGE_DESTDIR" || \ sdh_die "Error building / installing setuptools for Python ${vers}" -done From b3a3c6dc6101cc70fb7d5c7b4383d937a48d0c95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 19:56:43 -0700 Subject: [PATCH 689/706] tox.ini, .github/workflows/tox.yml: Add ubuntu-impish, linuxmint-20.2, fedora-35 --- .github/workflows/tox.yml | 2 +- tox.ini | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 767a12e279d..f14be3ee2e0 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -38,7 +38,7 @@ jobs: fail-fast: false max-parallel: 20 matrix: - tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-focal, ubuntu-groovy, ubuntu-hirsute, debian-jessie, debian-stretch, debian-buster, debian-bullseye, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, linuxmint-20.1, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, centos-7, centos-8, gentoo, gentoo-python3.7, archlinux-latest, opensuse-15, opensuse-15.3, opensuse-tumbleweed, slackware-14.2, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386] + tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-focal, ubuntu-groovy, ubuntu-hirsute, ubuntu-impish, debian-jessie, debian-stretch, debian-buster, debian-bullseye, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, centos-7, centos-8, gentoo, gentoo-python3.7, archlinux-latest, opensuse-15, opensuse-15.3, opensuse-tumbleweed, slackware-14.2, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386] tox_packages_factor: [minimal, standard] env: TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} diff --git a/tox.ini b/tox.ini index 26e36e582c3..21e342350d5 100644 --- a/tox.ini +++ b/tox.ini @@ -153,7 +153,7 @@ setenv = docker: BASE_TAG=latest # # https://hub.docker.com/_/ubuntu?tab=description - # as of 2020-11, latest=focal=20.04, rolling=groovy=20.10, devel=hirsute=21.04 + # as of 2021-06, latest=focal=20.04, rolling=hirsute=21.04, impish=devel=21.10 # ubuntu: SYSTEM=debian ubuntu: BASE_IMAGE=ubuntu @@ -167,6 +167,9 @@ setenv = ubuntu-groovy: BASE_TAG=groovy ubuntu-groovy: IGNORE_MISSING_SYSTEM_PACKAGES=no ubuntu-hirsute: BASE_TAG=hirsute + ubuntu-hirsute: IGNORE_MISSING_SYSTEM_PACKAGES=no + ubuntu-impish: BASE_TAG=impish + ubuntu-impish: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/_/debian # debian-bullseye does not have libgiac-dev @@ -194,9 +197,10 @@ setenv = linuxmint-19.3: BASE_IMAGE=linuxmintd/mint19.3 linuxmint-20: BASE_IMAGE=linuxmintd/mint20 linuxmint-20.1: BASE_IMAGE=linuxmintd/mint20.1 + linuxmint-20.2: BASE_IMAGE=linuxmintd/mint20.2 # # https://hub.docker.com/_/fedora - # as of 2020-11, latest=33, rawhide=34 + # as of 2021-06, latest=34, rawhide=35 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora-26: BASE_TAG=26 @@ -213,6 +217,9 @@ setenv = fedora-33: BASE_TAG=33 fedora-33: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-34: BASE_TAG=34 + fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no + fedora-35: BASE_TAG=35 + fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/scientificlinux/sl # From de25ce550477a3c8efeb4e50c1b9c230c3908bbd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 23 Jun 2021 10:48:31 -0700 Subject: [PATCH 690/706] build/pkgs/python3/spkg-build.in, spkg-configure.m4: Do not check for the readline module --- build/pkgs/python3/spkg-build.in | 3 ++- build/pkgs/python3/spkg-configure.m4 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 8c7aee8ae3f..161b9199ee2 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -110,7 +110,8 @@ fi echo "Testing importing of various modules..." import_errors=false -test_modules="ctypes math hashlib crypt readline socket zlib sqlite3" +# Trac #31160: We no longer check for readline here. +test_modules="ctypes math hashlib crypt socket zlib sqlite3" if [ "$UNAME" = "Darwin" ]; then test_modules="$test_modules _scproxy" fi diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index a76f27e3cb9..1a835b37a0a 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -22,7 +22,8 @@ SAGE_SPKG_CONFIGURE([python3], [ SAGE_SPKG_DEPCHECK([bzip2 xz libffi], [ dnl Check if we can do venv with a system python3 dnl instead of building our own copy. - check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" + dnl Trac #31160: We no longer check for readline here. + check_modules="sqlite3, ctypes, math, hashlib, crypt, socket, zlib, distutils.core" AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python AC_MSG_RESULT([]) From 346e492ec95b0bcdb2e712163dfbb5dfdf2c8b85 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 24 Jun 2021 10:03:59 +0200 Subject: [PATCH 691/706] document that coordinate vector is not unique unless reduced --- src/sage/modules/fg_pid/fgp_module.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 8213af301ed..db8fd8cabb7 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1271,8 +1271,11 @@ def coordinate_vector(self, x, reduce=False): sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W; Q Finitely generated module V/W over Integer Ring with invariants (4, 12) - sage: Q.coordinate_vector(Q.0 - Q.1) - (1, -1) + sage: Q.coordinate_vector(Q.0 - Q.1, reduce=True) + (1, 11) + sage: a, b = Q.coordinate_vector(Q.0 - Q.1) + sage: (a % 4, b % 12) + (1, 11) sage: O, X = Q.optimized() sage: O.V() @@ -1285,14 +1288,16 @@ def coordinate_vector(self, x, reduce=False): (0, 8) sage: Q.coordinate_vector(x, reduce=True) (0, 8) - sage: Q.coordinate_vector(-x, reduce=False) # random - (0, -8) + sage: a, b = Q.coordinate_vector(-x, reduce=False) + sage: (a % 4, b % 12) + (0, 4) sage: x == 8*Q.1 True sage: x = Q(V.1); x (0, 11) - sage: Q.coordinate_vector(x) - (0, -1) + sage: a, b = Q.coordinate_vector(x) + sage: (a % 4, b % 12) + (0, 11) sage: x == -Q.1 True sage: x = Q(V.2); x From 7bed91d083c7a6b97b37a4af18579be9cedb80e2 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 24 Jun 2021 11:06:51 +0100 Subject: [PATCH 692/706] allow 32-bit boxes to complain --- src/sage/libs/eclib/interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index a163cc31093..de2c48f307d 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -553,7 +553,9 @@ def saturate(self, bound=-1, lower=2): sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() - [[-1001107, -4004428, 1]] + [[-1001107, -4004428, 1]] # 64-bit + ... # 32-bit + [[-1001107, -4004428, 1]] # 32-bit sage: E.saturate() sage: E.gens() [[-1001107, -4004428, 1]] From a9c3c18820b8c96cd2336b90dc480417eaefd6d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 12 Mar 2021 10:32:09 -0800 Subject: [PATCH 693/706] build/pkgs/openblas/spkg-install.in: Save the configuration, reuse it in spkg-check.in --- build/pkgs/openblas/spkg-check.in | 33 +++++------------------------ build/pkgs/openblas/spkg-install.in | 4 ++++ 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/build/pkgs/openblas/spkg-check.in b/build/pkgs/openblas/spkg-check.in index 3032da767f5..de401dfc0af 100644 --- a/build/pkgs/openblas/spkg-check.in +++ b/build/pkgs/openblas/spkg-check.in @@ -1,33 +1,10 @@ -cd src - -# OpenBlas has no proper configure script +# OpenBLAS has no proper configure script # Options are directly passed to make # And the name static library archive produced depends on them # And the tests directly link to that archive rather than through a symlink -# Therefore the following is copied from spkg-install -# We could also patch the Makefile to use a generic symlink pointing -# to the archive with a specific name +# So we use the saved configuration from spkg-install.in +. ./set_openblas_configure || sdh_die "Saved configuration not found" -if [ `sage-bootstrap-python -c "from __future__ import print_function; import platform; print(platform.architecture()[0])"` = "32bit" ]; then - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE BINARY=32" -fi - -if [ "x$SAGE_FAT_BINARY" = "xyes" ]; then - # See https://github.com/xianyi/OpenBLAS/issues/510 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=PRESCOTT" -fi +cd src -${MAKE:-make} tests $OPENBLAS_CONFIGURE -if [ $? -ne 0 ]; then - # First make sure we already didn't set a target - if [[ $OPENBLAS_CONFIGURE == *"TARGET"* ]]; then - sdh_die "Failures while running the OpenBLAS testsuite ... exiting" - else - # The recommended TARGET is ATOM if CPU fails - # See https://github.com/xianyi/OpenBLAS/issues/1204 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=ATOM" - echo "Failures while testing the OpenBLAS testsuite" - echo "Retrying the OpenBLAS testsuite with TARGET=ATOM" - ${MAKE:-make} tests $OPENBLAS_CONFIGURE - fi -fi +${MAKE:-make} tests $OPENBLAS_CONFIGURE || sdh_die "'make tests' failed" diff --git a/build/pkgs/openblas/spkg-install.in b/build/pkgs/openblas/spkg-install.in index 70ad7b5d189..b06a55d3130 100644 --- a/build/pkgs/openblas/spkg-install.in +++ b/build/pkgs/openblas/spkg-install.in @@ -45,6 +45,10 @@ sdh_make_install PREFIX="$SAGE_LOCAL" NO_STATIC=1 $OPENBLAS_CONFIGURE cd .. ./write_pc_file.py +# Save configuration for spkg-check +echo >&2 "Writing configuration to $(pwd)/set_openblas_configure" +echo OPENBLAS_CONFIGURE=\'"$OPENBLAS_CONFIGURE"\' > set_openblas_configure + # OpenBLAS's Makefile has a bug w.r.t. calling install_name_tool when # DESTDIR is set. It should *not* include the DESTDIR in the library's # install_name; we set the correct install_name here From 8d319830fd177b18aaefdd9a22c92696fef5cc73 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 24 Jun 2021 14:57:45 -0700 Subject: [PATCH 694/706] build/make/Makefile.in: Uninstall setuptools before reinstalling python3 --- build/make/Makefile.in | 5 +++++ build/pkgs/python3/spkg-build.in | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 8ba89d78c74..a249cc3c546 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -283,6 +283,11 @@ all-toolchain: base-toolchain # given as a prerequisite to any pip-installed packages PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel setuptools_wheel +# Trac #32056: Avoid installed setuptools leaking into the build of python3 by uninstalling it. +# It will have to be reinstalled anyway because of its dependency on $(PYTHON). +python3-SAGE_LOCAL-no-deps: setuptools-clean +python3-SAGE_VENV-no-deps: setuptools-clean + # Everything needed to start up Sage using "./sage". Of course, not # every part of Sage will work. It does not include Maxima for example. SAGERUNTIME = sagelib $(inst_ipython) $(inst_pexpect) \ diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 8c7aee8ae3f..e15072fd8da 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -72,15 +72,11 @@ rm -f "$SAGE_LOCAL/lib/python" rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"* -if [ "$PKG_BASE" = "python2" ]; then - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --enable-unicode=ucs4" -else # Note: --without-ensurepip ensures that setuptools+pip are *not* installed # automatically when installing python3. They will be installed instead by # the separate setuptools and pip packages; see # https://trac.sagemath.org/ticket/23398 PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-ensurepip" -fi sdh_configure --enable-shared $PYTHON_CONFIGURE From 789550ca04c94acfb1e803251538996a34962038 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 25 Jun 2021 13:50:28 +0100 Subject: [PATCH 695/706] 31443 updated for eclib version 20210625 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- src/sage/libs/eclib/interface.py | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 8342f43b6da..a76b8f40fde 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=b8e1bd10a28341fcd8146cf12fc38a12c798c555 -md5=d54562480e1ebd73ae3f343b949dfa66 -cksum=2657346485 +sha1=e25f1aa6b7450f17bcff643fac9473d326012e29 +md5=b1288dc5eb981d45db1db0e11987468a +cksum=2713659223 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 891293d2df1..ed06b627c4d 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210503 +20210625 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 4bced5f44ed..e49e0b74ae3 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210503"]) + m4_pushdef([SAGE_ECLIB_VER],["20210625"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index de2c48f307d..a163cc31093 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -553,9 +553,7 @@ def saturate(self, bound=-1, lower=2): sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() - [[-1001107, -4004428, 1]] # 64-bit - ... # 32-bit - [[-1001107, -4004428, 1]] # 32-bit + [[-1001107, -4004428, 1]] sage: E.saturate() sage: E.gens() [[-1001107, -4004428, 1]] From 4550eb6a753930c5818b63049f2eec197470ed1f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Feb 2021 15:59:40 -0800 Subject: [PATCH 696/706] sage.env.sage_include_directories: Do not fail if numpy cannot be imported --- src/sage/env.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/env.py b/src/sage/env.py index e792ca80450..ccf92b70e1a 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -317,7 +317,7 @@ def _get_shared_lib_path(*libnames: str) -> Optional[str]: GAP_SO = var("GAP_SO", _get_shared_lib_path("gap", "")) # post process -if ' ' in DOT_SAGE: +if DOT_SAGE is not None and ' ' in DOT_SAGE: if UNAME[:6] == 'CYGWIN': # on windows/cygwin it is typical for the home directory # to have a space in it. Fortunately, users also have @@ -378,14 +378,18 @@ def sage_include_directories(use_sources=False): sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True """ - import numpy import distutils.sysconfig TOP = SAGE_SRC if use_sources else SAGE_LIB - return [TOP, - distutils.sysconfig.get_python_inc(), - numpy.get_include()] + dirs = [TOP, + distutils.sysconfig.get_python_inc()] + try: + import numpy + dirs.append(numpy.get_include()) + except ModuleNotFoundError: + pass + return dirs def get_cblas_pc_module_name() -> str: """ From 0075b33914e6d4d571da9164e6392dc25bc11c28 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 27 Jun 2021 15:49:24 -0700 Subject: [PATCH 697/706] build/pkgs/{scipy,sphinx,sympy}/install-requires.txt: Update version ranges to include the version in package-version.txt --- build/pkgs/scipy/install-requires.txt | 2 +- build/pkgs/sphinx/install-requires.txt | 3 +-- build/pkgs/sympy/install-requires.txt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/install-requires.txt b/build/pkgs/scipy/install-requires.txt index 2e7f4c30acc..375bf360da9 100644 --- a/build/pkgs/scipy/install-requires.txt +++ b/build/pkgs/scipy/install-requires.txt @@ -1 +1 @@ -scipy >=1.5, <1.6 +scipy >=1.5, <1.8 diff --git a/build/pkgs/sphinx/install-requires.txt b/build/pkgs/sphinx/install-requires.txt index d7680fc6a37..2d146f2fe48 100644 --- a/build/pkgs/sphinx/install-requires.txt +++ b/build/pkgs/sphinx/install-requires.txt @@ -1,2 +1 @@ -# gentoo uses 3.2.1 -sphinx >=3, <3.3 +sphinx >=4, <4.2 diff --git a/build/pkgs/sympy/install-requires.txt b/build/pkgs/sympy/install-requires.txt index 9c1c610c7c0..207de33a893 100644 --- a/build/pkgs/sympy/install-requires.txt +++ b/build/pkgs/sympy/install-requires.txt @@ -1 +1 @@ -sympy >=1.6, <1.8 +sympy >=1.6, <2.0 From 6df6144cd06af7691315a78550e1e1ff57f09820 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 27 Jun 2021 23:46:58 -0700 Subject: [PATCH 698/706] src/sage/doctest/forker.py: Do not crash if readline cannot be imported --- src/sage/doctest/forker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 9dcc1d6b549..6bc3518983a 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -231,7 +231,11 @@ def init_sage(controller=None): # We import readline before forking, otherwise Pdb doesn't work # on OS X: http://trac.sagemath.org/14289 - import readline + try: + import readline + except ModuleNotFoundError: + # Do not require readline for running doctests (Trac #31160). + pass try: import sympy From e74a32b3a68b78bde03c6e15608436dee3012bd0 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 28 Jun 2021 10:43:09 +0100 Subject: [PATCH 699/706] Added continuation prompts Continuation prompts were missing from an example, and this error was rectified. --- src/sage/numerical/gauss_legendre.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 27bcb8b8b71..1234405acaa 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -83,9 +83,9 @@ def nodes(degree, prec): sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24, x)) sage: Pdif = P.diff() sage: L2 = [((r + 1)/2, 1/(1 - r^2)/Pdif(r)^2) - for r, _ in RR['x'](P).roots()] + ....: for r, _ in RR['x'](P).roots()] sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 - for a, b in zip(L1, L2)) + ....: for a, b in zip(L1, L2)) True .. TODO:: From 473cd41f19ec23df7e207391cfb0cf41c7c4ef46 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Thu, 1 Jul 2021 22:47:24 +0200 Subject: [PATCH 700/706] Updated SageMath version to 9.4.beta4 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index e1658133ddd..c40fda4109b 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.4.beta3", - "version": "9.4.beta3", + "title": "sagemath/sage: 9.4.beta4", + "version": "9.4.beta4", "upload_type": "software", - "publication_date": "2021-06-21", + "publication_date": "2021-07-01", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.4.beta3", + "identifier": "https://github.com/sagemath/sage/tree/9.4.beta4", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 1ad0a4e8860..2f40e816f49 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.4.beta3, Release Date: 2021-06-21 +SageMath version 9.4.beta4, Release Date: 2021-07-01 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 5644d267bbb..8dfcdb4fa2a 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=6c57f29a40da0d59930469bb1f42d4bc5f597ce7 -md5=8071fb87383998629b18fd416b6be631 -cksum=1343198753 +sha1=7b88d740d7ed702c15cb4048ae08c2faf2e9852c +md5=6252f2920838a7ea42b17845aa9782a2 +cksum=2536572108 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index fc1fc1f6be6..e457c20289b 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -f325f588ed6fe34ecacae25cde4dd8ace2551c79 +06ec5d949d4ac37fffbebba5ab2ad5294cb141e1 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index f9ffedae371..0c2ffe14533 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.4.beta3 +9.4.beta4 diff --git a/src/VERSION.txt b/src/VERSION.txt index f9ffedae371..0c2ffe14533 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.4.beta3 +9.4.beta4 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 72225680ccb..f9935624875 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.4.beta3' -SAGE_RELEASE_DATE='2021-06-21' -SAGE_VERSION_BANNER='SageMath version 9.4.beta3, Release Date: 2021-06-21' +SAGE_VERSION='9.4.beta4' +SAGE_RELEASE_DATE='2021-07-01' +SAGE_VERSION_BANNER='SageMath version 9.4.beta4, Release Date: 2021-07-01' diff --git a/src/sage/version.py b/src/sage/version.py index 6bde12d24e0..6cc84191475 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.4.beta3' -date = '2021-06-21' -banner = 'SageMath version 9.4.beta3, Release Date: 2021-06-21' +version = '9.4.beta4' +date = '2021-07-01' +banner = 'SageMath version 9.4.beta4, Release Date: 2021-07-01' From af7ca87f87075d79b9c7ccd2e59f8fcb8205e1e8 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Wed, 7 Jul 2021 18:39:03 +0200 Subject: [PATCH 701/706] Refactor ExactTerm._repr_ to TermWithCoefficient._product_repr_ --- src/sage/rings/asymptotic/term_monoid.py | 95 ++++++++++++------------ 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 81e40c4e61e..047da7db3c2 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1771,7 +1771,7 @@ def _element_constructor_(self, data, coefficient=None): sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t1 = T(x^2, 5); t1 # indirect doctest - Term with coefficient 5 and growth x^2 + Term with coefficient 5*x^2 TESTS:: @@ -1782,23 +1782,23 @@ def _element_constructor_(self, data, coefficient=None): :: sage: T(G.gen()^10) - Term with coefficient 1 and growth x^10 + Term with coefficient x^10 sage: T(G.gen()^10, coefficient=10) - Term with coefficient 10 and growth x^10 + Term with coefficient 10*x^10 sage: T(x^123) - Term with coefficient 1 and growth x^123 + Term with coefficient x^123 :: sage: T(x) - Term with coefficient 1 and growth x + Term with coefficient x :: sage: G_log = GrowthGroup('log(x)^ZZ') sage: T_log = TermWithCoefficientMonoid(TermMonoid, G_log, ZZ) sage: T_log(log(x)) - Term with coefficient 1 and growth log(x) + Term with coefficient log(x) """ if isinstance(data, self.element_class) and data.parent() == self: @@ -2818,9 +2818,9 @@ class TermWithCoefficient(GenericTerm): sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) sage: CT_ZZ(x^2, 5) - Term with coefficient 5 and growth x^2 + Term with coefficient 5*x^2 sage: CT_QQ(x^3, 3/8) - Term with coefficient 3/8 and growth x^3 + Term with coefficient 3/8*x^3 """ def __init__(self, parent, growth, coefficient): @@ -2848,7 +2848,7 @@ def __init__(self, parent, growth, coefficient): ValueError: 1/2 is not a coefficient in Generic Term Monoid x^ZZ with (implicit) coefficients in Integer Ring. sage: t = CT_QQ(x, 1/2); t - Term with coefficient 1/2 and growth x + Term with coefficient 1/2*x For technical reasons, the coefficient 0 is not allowed:: @@ -2864,7 +2864,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring sage: CT_ZZ(x^42, 42) - Term with coefficient 42 and growth x^42 + Term with coefficient 42*x^42 """ try: coefficient = parent.coefficient_ring(coefficient) @@ -2901,10 +2901,38 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: T(x^2, 5)._repr_() - 'Term with coefficient 5 and growth x^2' + 'Term with coefficient 5*x^2' """ - return 'Term with coefficient %s and growth %s' % \ - (self.coefficient, self.growth) + return f'Term with coefficient {self._product_repr_()}' + + def _product_repr_(self, latex=False): + if latex: + from sage.misc.latex import latex as latex_repr + f = latex_repr + else: + f = repr + + g = f(self.growth) + c = f(self.coefficient) + + if g == '1': + return c + elif c == '1': + return '{g}'.format(g=g) + elif c == '-1': + return '-{g}'.format(g=g) + elif self.coefficient._is_atomic() or (-self.coefficient)._is_atomic(): + # note that -pi/2 is not atomic, but -5 is. As subtractions are handled + # in the asymptotic ring, we ignore such non-atomicity. + s = '{c} {g}' if latex else '{c}*{g}' + else: + s = r'\left({c}\right) {g}' if latex else '({c})*{g}' + s = s.format(c=c, g=g) + + if latex: + import re + s = re.sub(r'([0-9])\s+([0-9])', r'\1 \\cdot \2', s) + return s def _mul_(self, other): r""" @@ -2941,7 +2969,7 @@ def _mul_(self, other): sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) sage: t1 * t2 - Term with coefficient 6 and growth x^5 + Term with coefficient 6*x^5 And now, an example for exact terms:: @@ -2975,11 +3003,11 @@ def _calculate_pow_(self, exponent): sage: G = GrowthGroup('z^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t = T('2*z'); t - Term with coefficient 2 and growth z + Term with coefficient 2*z sage: t._calculate_pow_(3) - Term with coefficient 8 and growth z^3 + Term with coefficient 8*z^3 sage: t._calculate_pow_(-2) - Term with coefficient 1/4 and growth z^(-2) + Term with coefficient 1/4*z^(-2) :: @@ -3088,7 +3116,7 @@ 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 + Term with coefficient x sage: t == T(x, 1) True sage: t == T(x, 2) @@ -3188,7 +3216,7 @@ def _an_element_(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ') sage: TermWithCoefficientMonoid(TermMonoid, G, ZZ).an_element() # indirect doctest - Term with coefficient 1 and growth x + Term with coefficient x sage: TermMonoid('exact', G, ZZ).an_element() # indirect doctest x sage: TermMonoid('exact', G, QQ).an_element() # indirect doctest @@ -3337,34 +3365,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - if latex: - from sage.misc.latex import latex as latex_repr - f = latex_repr - else: - f = repr - - g = f(self.growth) - c = f(self.coefficient) - - if g == '1': - return c - elif c == '1': - return '{g}'.format(g=g) - elif c == '-1': - return '-{g}'.format(g=g) - elif self.coefficient._is_atomic() or (-self.coefficient)._is_atomic(): - # note that -pi/2 is not atomic, but -5 is. As subtractions are handled - # in the asymptotic ring, we ignore such non-atomicity. - s = '{c} {g}' if latex else '{c}*{g}' - else: - s = r'\left({c}\right) {g}' if latex else '({c})*{g}' - s = s.format(c=c, g=g) - - if latex: - import re - s = re.sub(r'([0-9])\s+([0-9])', r'\1 \\cdot \2', s) - - return s + return self._product_repr_(latex) def _latex_(self): r""" From f3b1c46239e44d3bbadd58980056fe8797684e8a Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Thu, 8 Jul 2021 20:15:16 +0200 Subject: [PATCH 702/706] Revert TermWithCoefficient._repr_ to be more descriptive. --- src/sage/rings/asymptotic/term_monoid.py | 37 ++++++++++++------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 047da7db3c2..1d0e5968b04 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1771,7 +1771,7 @@ def _element_constructor_(self, data, coefficient=None): sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t1 = T(x^2, 5); t1 # indirect doctest - Term with coefficient 5*x^2 + Term with coefficient 5 and growth x^2 TESTS:: @@ -1782,23 +1782,23 @@ def _element_constructor_(self, data, coefficient=None): :: sage: T(G.gen()^10) - Term with coefficient x^10 + Term with coefficient 1 and growth x^10 sage: T(G.gen()^10, coefficient=10) - Term with coefficient 10*x^10 + Term with coefficient 10 and growth x^10 sage: T(x^123) - Term with coefficient x^123 + Term with coefficient 1 and growth x^123 :: sage: T(x) - Term with coefficient x + Term with coefficient 1 and growth x :: sage: G_log = GrowthGroup('log(x)^ZZ') sage: T_log = TermWithCoefficientMonoid(TermMonoid, G_log, ZZ) sage: T_log(log(x)) - Term with coefficient log(x) + Term with coefficient 1 and growth log(x) """ if isinstance(data, self.element_class) and data.parent() == self: @@ -2818,9 +2818,9 @@ class TermWithCoefficient(GenericTerm): sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) sage: CT_ZZ(x^2, 5) - Term with coefficient 5*x^2 + Term with coefficient 5 and growth x^2 sage: CT_QQ(x^3, 3/8) - Term with coefficient 3/8*x^3 + Term with coefficient 3/8 and growth x^3 """ def __init__(self, parent, growth, coefficient): @@ -2848,7 +2848,7 @@ def __init__(self, parent, growth, coefficient): ValueError: 1/2 is not a coefficient in Generic Term Monoid x^ZZ with (implicit) coefficients in Integer Ring. sage: t = CT_QQ(x, 1/2); t - Term with coefficient 1/2*x + Term with coefficient 1/2 and growth x For technical reasons, the coefficient 0 is not allowed:: @@ -2864,7 +2864,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring sage: CT_ZZ(x^42, 42) - Term with coefficient 42*x^42 + Term with coefficient 42 and growth x^42 """ try: coefficient = parent.coefficient_ring(coefficient) @@ -2901,9 +2901,10 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: T(x^2, 5)._repr_() - 'Term with coefficient 5*x^2' + 'Term with coefficient 5 and growth x^2' """ - return f'Term with coefficient {self._product_repr_()}' + return 'Term with coefficient %s and growth %s' % \ + (self.coefficient, self.growth) def _product_repr_(self, latex=False): if latex: @@ -2969,7 +2970,7 @@ def _mul_(self, other): sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) sage: t1 * t2 - Term with coefficient 6*x^5 + Term with coefficient 6 and growth x^5 And now, an example for exact terms:: @@ -3003,11 +3004,11 @@ def _calculate_pow_(self, exponent): sage: G = GrowthGroup('z^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: t = T('2*z'); t - Term with coefficient 2*z + Term with coefficient 2 and growth z sage: t._calculate_pow_(3) - Term with coefficient 8*z^3 + Term with coefficient 8 and growth z^3 sage: t._calculate_pow_(-2) - Term with coefficient 1/4*z^(-2) + Term with coefficient 1/4 and growth z^(-2) :: @@ -3116,7 +3117,7 @@ def _eq_(self, other): sage: T = TermWithCoefficientMonoid(TermMonoid, GrowthGroup('x^ZZ'), ZZ) sage: t = T.an_element(); t - Term with coefficient x + Term with coefficient 1 and growth x sage: t == T(x, 1) True sage: t == T(x, 2) @@ -3216,7 +3217,7 @@ def _an_element_(self): sage: TermMonoid = TermMonoidFactory('__main__.TermMonoid') sage: G = GrowthGroup('x^ZZ') sage: TermWithCoefficientMonoid(TermMonoid, G, ZZ).an_element() # indirect doctest - Term with coefficient x + Term with coefficient 1 and growth x sage: TermMonoid('exact', G, ZZ).an_element() # indirect doctest x sage: TermMonoid('exact', G, QQ).an_element() # indirect doctest From 7fb17cef5a32e71fa908e85c660be7a220b30e5a Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:38:52 +0200 Subject: [PATCH 703/706] rename _product_repr_ to _repr_product_ --- src/sage/rings/asymptotic/term_monoid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 1d0e5968b04..067b4a0e54b 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2906,7 +2906,7 @@ def _repr_(self): return 'Term with coefficient %s and growth %s' % \ (self.coefficient, self.growth) - def _product_repr_(self, latex=False): + def _repr_product_(self, latex=False): if latex: from sage.misc.latex import latex as latex_repr f = latex_repr @@ -3366,7 +3366,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - return self._product_repr_(latex) + return self._repr_product_(latex) def _latex_(self): r""" From 7af743f3cdab60639801f81a5b376a1bc59ecce2 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:44:18 +0200 Subject: [PATCH 704/706] added docstring to TermWithCoefficient._repr_product_ --- src/sage/rings/asymptotic/term_monoid.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 067b4a0e54b..f380d663b91 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2907,6 +2907,30 @@ def _repr_(self): (self.coefficient, self.growth) def _repr_product_(self, latex=False): + r""" + A representation string for this term with coefficient as a product. + + INPUT: + + - ``latex`` -- (default: ``False``) a boolean. If set, then + LaTeX-output is returned + + OUTPUT: + + A string + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import 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: G = GrowthGroup('x^ZZ'); x = G.gen() + sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) + sage: T(x^2, 5)._repr_product_() + '5*x^2' + """ if latex: from sage.misc.latex import latex as latex_repr f = latex_repr From 1db8a42d0288607f913c1ea279e295034e00c3d0 Mon Sep 17 00:00:00 2001 From: thhagelmayer Date: Sun, 11 Jul 2021 09:46:41 +0200 Subject: [PATCH 705/706] Changed the call to the product representation in ExactTerm._repr_ to self._repr_product_(latex=latex) --- src/sage/rings/asymptotic/term_monoid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index f380d663b91..435478f6444 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -3390,7 +3390,7 @@ def _repr_(self, latex=False): sage: (1+a)/n (a + 1)*n^(-1) """ - return self._repr_product_(latex) + return self._repr_product_(latex=latex) def _latex_(self): r""" From c11b2f46051c9438d90bf45c69d7437c421b3b48 Mon Sep 17 00:00:00 2001 From: Daniel Krenn Date: Sun, 11 Jul 2021 19:32:43 +0200 Subject: [PATCH 706/706] Trac #32153: fix missing full stop --- src/sage/rings/asymptotic/term_monoid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 435478f6444..51ddd16dd1d 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -2913,7 +2913,7 @@ def _repr_product_(self, latex=False): INPUT: - ``latex`` -- (default: ``False``) a boolean. If set, then - LaTeX-output is returned + LaTeX-output is returned. OUTPUT: