From 99d9b6cbb562c87e87ca0fc2a6465b387e486157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 7 Jul 2020 16:42:49 +0200 Subject: [PATCH 001/454] convert some .inverse to .__invert__ and make a global alias in magmas cat --- .../hecke_algebras/ariki_koike_algebra.py | 6 ++-- src/sage/algebras/iwahori_hecke_algebra.py | 6 ++-- src/sage/algebras/yokonuma_hecke_algebra.py | 7 ++--- ...eflection_or_generalized_coxeter_groups.py | 6 ++-- src/sage/categories/magmas.py | 30 +++++++++++++++++++ src/sage/combinat/affine_permutation.py | 8 ++--- src/sage/combinat/colored_permutations.py | 12 +++----- .../combinat/root_system/fundamental_group.py | 6 ++-- .../hyperbolic_space/hyperbolic_isometry.py | 8 ++--- src/sage/groups/abelian_gps/element_base.py | 14 ++++----- src/sage/groups/abelian_gps/values.py | 9 ++---- src/sage/groups/affine_gps/group_element.py | 10 +++---- src/sage/groups/group_exp.py | 6 ++-- src/sage/groups/group_semidirect_product.py | 11 +++---- src/sage/modular/pollack_stevens/sigma0.py | 8 +++-- src/sage/structure/element.pyx | 7 +---- 16 files changed, 75 insertions(+), 79 deletions(-) diff --git a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py index b78b7691e7b..2e4ddb6a4bd 100644 --- a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py +++ b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py @@ -1126,7 +1126,7 @@ def inverse_T(self, i): return self._from_dict({m: ~self._q, self.one_basis(): c}) class Element(CombinatorialFreeModule.Element): - def inverse(self): + def __invert__(self): r""" Return the inverse if ``self`` is a basis element. @@ -1135,7 +1135,7 @@ def inverse(self): sage: LT = algebras.ArikiKoike(3, 4).LT() sage: t = LT.T(1) * LT.T(2) * LT.T(3); t T[1,2,3] - sage: t.inverse() + sage: t.inverse() # indirect doctest (q^-3-3*q^-2+3*q^-1-1) + (q^-3-2*q^-2+q^-1)*T[3] + (q^-3-2*q^-2+q^-1)*T[2] + (q^-3-q^-2)*T[3,2] + (q^-3-2*q^-2+q^-1)*T[1] + (q^-3-q^-2)*T[1,3] @@ -1149,8 +1149,6 @@ def inverse(self): H = self.parent() return ~self[l,w] * H.prod(H.inverse_T(i) for i in reversed(w.reduced_word())) - __invert__ = inverse - class T(_Basis): r""" The basis of the Ariki-Koike algebra given by monomials of the diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index ae50b39a3e0..b779269b2f8 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -1727,7 +1727,7 @@ class Element(CombinatorialFreeModule.Element): sage: T1.parent() Iwahori-Hecke algebra of type A2 in 1,-1 over Integer Ring in the T-basis """ - def inverse(self): + def __invert__(self): r""" Return the inverse if ``self`` is a basis element. @@ -1741,7 +1741,7 @@ def inverse(self): sage: R. = LaurentPolynomialRing(QQ) sage: H = IwahoriHeckeAlgebra("A2", q).T() sage: [T1,T2] = H.algebra_generators() - sage: x = (T1*T2).inverse(); x + sage: x = (T1*T2).inverse(); x # indirect doctest (q^-2)*T[2,1] + (q^-2-q^-1)*T[1] + (q^-2-q^-1)*T[2] + (q^-2-2*q^-1+1) sage: x*T1*T2 1 @@ -1766,8 +1766,6 @@ def inverse(self): return H.prod(H.inverse_generator(i) for i in reversed(w.reduced_word())) - __invert__ = inverse - standard = T class _KLHeckeBasis(_Basis): diff --git a/src/sage/algebras/yokonuma_hecke_algebra.py b/src/sage/algebras/yokonuma_hecke_algebra.py index dde7772e227..a8c74e25f0b 100644 --- a/src/sage/algebras/yokonuma_hecke_algebra.py +++ b/src/sage/algebras/yokonuma_hecke_algebra.py @@ -454,7 +454,7 @@ def inverse_g(self, i): return self.g(i) + (~self._q + self._q) * self.e(i) class Element(CombinatorialFreeModule.Element): - def inverse(self): + def __invert__(self): r""" Return the inverse if ``self`` is a basis element. @@ -463,7 +463,7 @@ def inverse(self): sage: Y = algebras.YokonumaHecke(3, 3) sage: t = prod(Y.t()); t t1*t2*t3 - sage: ~t + sage: t.inverse() # indirect doctest t1^2*t2^2*t3^2 sage: [3*~(t*g) for g in Y.g()] [(q^-1+q)*t2*t3^2 + (q^-1+q)*t1*t3^2 @@ -494,6 +494,3 @@ def inverse(self): c = ~self.coefficients()[0] telt = H.monomial( (tuple((H._d - e) % H._d for e in t), H._Pn.one()) ) return c * telt * H.prod(H.inverse_g(i) for i in reversed(w.reduced_word())) - - __invert__ = inverse - diff --git a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py index 720d0097632..daa36275cf9 100644 --- a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py +++ b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py @@ -1141,7 +1141,7 @@ def _mul_(self, other): """ return self.apply_simple_reflections(other.reduced_word()) - def inverse(self): + def __invert__(self): """ Return the inverse of ``self``. @@ -1149,7 +1149,7 @@ def inverse(self): sage: W = WeylGroup(['B',7]) sage: w = W.an_element() - sage: u = w.inverse() + sage: u = w.inverse() # indirect doctest sage: u == ~w True sage: u * w == w * u @@ -1165,8 +1165,6 @@ def inverse(self): """ return self.parent().one().apply_simple_reflections(self.reduced_word_reverse_iterator()) - __invert__ = inverse - def apply_conjugation_by_simple_reflection(self, i): r""" Conjugate ``self`` by the ``i``-th simple reflection. diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 8073599c0d3..7631a4fcd37 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -589,6 +589,36 @@ def _div_(left, right): """ return left * ~right + def __invert__(self): + r""" + Return the inverse of ``self``. + + The default implementation is to divide ``self.one()``. + + EXAMPLES:: + + sage: A = Matrix([[1, 0], [1, 1]]) + sage: ~A + [ 1 0] + [-1 1] + """ + return self._parent.one()._div_(self) + + def inverse(self): + """ + Return the inverse of ``self``. + + This an alias for inversion, defined in ``  invert__``. + + Element classes should implement ``  invert__`` only. + + EXAMPLES:: + + sage: AA(sqrt(~2)).inverse() + 1.414213562373095? + """ + return self.__invert__() + class SubcategoryMethods: @cached_method diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index f550c1413e0..33d24cc7c5f 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -161,21 +161,19 @@ def __mul__(self, q): return self.__rmul__(q) @cached_method - def inverse(self): + def __invert__(self): r""" Return the inverse affine permutation. EXAMPLES:: sage: p = AffinePermutationGroup(['A',7,1])([3, -1, 0, 6, 5, 4, 10, 9]) - sage: p.inverse() + sage: p.inverse() # indirect doctest Type A affine permutation with window [0, -1, 1, 6, 5, 4, 10, 11] """ - inv = [self.position(i) for i in range(1,len(self)+1)] + inv = [self.position(i) for i in range(1, len(self) + 1)] return type(self)(self.parent(), inv, check=False) - __invert__=inverse - def apply_simple_reflection(self, i, side='right'): r""" Apply a simple reflection. diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 2227ee6f293..f0f7f58c023 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -97,7 +97,7 @@ def _mul_(self, other): p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) - def inverse(self): + def __invert__(self): """ Return the inverse of ``self``. @@ -105,7 +105,7 @@ def inverse(self): sage: C = ColoredPermutations(4, 3) sage: s1,s2,t = C.gens() - sage: ~t + sage: ~t # indirect doctest [[0, 0, 3], [1, 2, 3]] sage: all(x * ~x == C.one() for x in C.gens()) True @@ -115,8 +115,6 @@ def inverse(self): tuple([-self._colors[i - 1] for i in ip]), # -1 for indexing ip) - __invert__ = inverse - def __eq__(self, other): """ Check equality. @@ -982,7 +980,7 @@ def _mul_(self, other): p = self._perm._left_to_right_multiply_on_right(other._perm) return self.__class__(self.parent(), colors, p) - def inverse(self): + def __invert__(self): """ Return the inverse of ``self``. @@ -991,7 +989,7 @@ def inverse(self): sage: S = SignedPermutations(4) sage: s1,s2,s3,s4 = S.gens() sage: x = s4*s1*s2*s3*s4 - sage: ~x + sage: ~x # indirect doctest [2, 3, -4, -1] sage: x * ~x == S.one() True @@ -1001,8 +999,6 @@ def inverse(self): tuple([self._colors[i - 1] for i in ip]), # -1 for indexing ip) - __invert__ = inverse - def __iter__(self): """ Iterate over ``self``. diff --git a/src/sage/combinat/root_system/fundamental_group.py b/src/sage/combinat/root_system/fundamental_group.py index 7c097ad7fe3..53117f4213c 100644 --- a/src/sage/combinat/root_system/fundamental_group.py +++ b/src/sage/combinat/root_system/fundamental_group.py @@ -249,7 +249,7 @@ def _repr_(self): """ return self.parent()._prefix + "[" + repr(self.value()) + "]" - def inverse(self): + def __invert__(self): r""" Return the inverse element of ``self``. @@ -257,7 +257,7 @@ def inverse(self): sage: from sage.combinat.root_system.fundamental_group import FundamentalGroupOfExtendedAffineWeylGroup sage: F = FundamentalGroupOfExtendedAffineWeylGroup(['A',3,1]) - sage: F(1).inverse() + sage: F(1).inverse() # indirect doctest pi[3] sage: F = FundamentalGroupOfExtendedAffineWeylGroup(['E',6,1], prefix="f") sage: F(1).inverse() @@ -266,8 +266,6 @@ def inverse(self): par = self.parent() return self.__class__(par, par.dual_node(self.value())) - __invert__ = inverse - def _richcmp_(self, x, op): r""" Compare ``self`` with `x`. diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index 418e3eccbc5..bcdbde6063d 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -323,7 +323,7 @@ def matrix(self): """ return self._matrix - def inverse(self): + def __invert__(self): r""" Return the inverse of the isometry ``self``. @@ -331,13 +331,11 @@ def inverse(self): sage: UHP = HyperbolicPlane().UHP() sage: A = UHP.get_isometry(matrix(2,[4,1,3,2])) - sage: B = A.inverse() + sage: B = A.inverse() # indirect doctest sage: A*B == UHP.get_isometry(identity_matrix(2)) True """ - return self.__class__(self.domain(), self.matrix().inverse()) - - __invert__ = inverse + return self.__class__(self.domain(), self.matrix().__invert__()) def is_identity(self): """ diff --git a/src/sage/groups/abelian_gps/element_base.py b/src/sage/groups/abelian_gps/element_base.py index d5b93a3f918..c1eec7ecb1f 100644 --- a/src/sage/groups/abelian_gps/element_base.py +++ b/src/sage/groups/abelian_gps/element_base.py @@ -15,7 +15,7 @@ # Copyright (C) 2012 Volker Braun # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ########################################################################### from sage.structure.element import MultiplicativeGroupElement @@ -298,14 +298,14 @@ def __pow__(self, n): for e,order in zip(self._exponents, G.gens_orders()) ] return G.element_class(G, exponents) - def inverse(self): + def __invert__(self): """ - Returns the inverse element. + Return the inverse element. EXAMPLES:: sage: G. = AbelianGroup([0,5]) - sage: a.inverse() + sage: a.inverse() # indirect doctest a^-1 sage: a.__invert__() a^-1 @@ -319,12 +319,10 @@ def inverse(self): (-1, 4) """ G = self.parent() - exponents = [ (-e)%order if order!=0 else -e - for e,order in zip(self._exponents, G.gens_orders()) ] + exponents = [(-e) % order if order != 0 else -e + for e, order in zip(self._exponents, G.gens_orders())] return G.element_class(G, exponents) - __invert__ = inverse - def is_trivial(self): """ Test whether ``self`` is the trivial group element ``1``. diff --git a/src/sage/groups/abelian_gps/values.py b/src/sage/groups/abelian_gps/values.py index 288fb95b93c..e3d681af428 100644 --- a/src/sage/groups/abelian_gps/values.py +++ b/src/sage/groups/abelian_gps/values.py @@ -330,14 +330,14 @@ def __pow__(self, n): pow_self._value = pow(self.value(), m) return pow_self - def inverse(self): + def __invert__(self): """ Return the inverse element. EXAMPLES:: sage: G. = AbelianGroupWithValues([2,-1], [0,4]) - sage: a.inverse() + sage: a.inverse() # indirect doctest a^-1 sage: a.inverse().value() 1/2 @@ -350,13 +350,10 @@ def inverse(self): sage: (a*b).inverse().value() -1/2 """ - m = AbelianGroupElement.inverse(self) + m = AbelianGroupElement.__invert__(self) m._value = ~self.value() return m - __invert__ = inverse - - class AbelianGroupWithValues_class(AbelianGroup_class): """ diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index bc8257b5471..5b107cbc194 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -381,7 +381,7 @@ def _act_on_(self, x, self_on_left): if self_on_left: return self(x) - def inverse(self): + def __invert__(self): """ Return the inverse group element. @@ -399,19 +399,17 @@ def inverse(self): sage: ~g [1 1] [1] x |-> [0 1] x + [0] - sage: g * g.inverse() + sage: g * g.inverse() # indirect doctest [1 0] [0] x |-> [0 1] x + [0] sage: g * g.inverse() == g.inverse() * g == G(1) True """ parent = self.parent() - A = parent.matrix_space()(self._A.inverse()) - b = -A*self.b() + A = parent.matrix_space()(~self._A) + b = -A * self.b() return parent.element_class(parent, A, b, check=False) - __invert__ = inverse - def _richcmp_(self, other, op): """ Compare ``self`` with ``other``. diff --git a/src/sage/groups/group_exp.py b/src/sage/groups/group_exp.py index bf0db4aa837..3ffa13a6ebf 100644 --- a/src/sage/groups/group_exp.py +++ b/src/sage/groups/group_exp.py @@ -211,20 +211,18 @@ def __init__(self, parent, x): raise ValueError("%s is not an element of %s" % (x, parent._G)) ElementWrapper.__init__(self, parent, x) - def inverse(self): + def __invert__(self): r""" Invert the element ``self``. EXAMPLES:: sage: EZ = GroupExp()(ZZ) - sage: EZ(-3).inverse() + sage: EZ(-3).inverse() # indirect doctest 3 """ return GroupExpElement(self.parent(), -self.value) - __invert__ = inverse - def __mul__(self, x): r""" Multiply ``self`` by `x`. diff --git a/src/sage/groups/group_semidirect_product.py b/src/sage/groups/group_semidirect_product.py index d5b8b6b62de..cb99b637b91 100644 --- a/src/sage/groups/group_semidirect_product.py +++ b/src/sage/groups/group_semidirect_product.py @@ -58,9 +58,9 @@ def wrapper(prefix, s): return gstr return gstr + " * " + hstr - def inverse(self): + def __invert__(self): r""" - The inverse of ``self``. + Return the inverse of ``self``. EXAMPLES:: @@ -75,19 +75,16 @@ def inverse(self): s1*s2 * t[2*alpha[1] + 2*alpha[2]] sage: g.inverse() s2*s1 * t[2*alpha[1]] - """ par = self.parent() g = self.cartesian_projection(0) h = self.cartesian_projection(1) if par.act_to_right(): - return self.__class__(par,(~g, par._twist(g,~h))) + return self.__class__(par, (~g, par._twist(g, ~h))) else: hi = ~h - return self.__class__(par,(par._twist(hi,~g),hi)) - - __invert__ = inverse + return self.__class__(par, (par._twist(hi, ~g), hi)) def to_opposite(self): r""" diff --git a/src/sage/modular/pollack_stevens/sigma0.py b/src/sage/modular/pollack_stevens/sigma0.py index 6ff9b24b551..eed9e244cd2 100644 --- a/src/sage/modular/pollack_stevens/sigma0.py +++ b/src/sage/modular/pollack_stevens/sigma0.py @@ -295,15 +295,17 @@ def matrix(self): """ return self._mat - def inverse(self): + def __invert__(self): r""" - Return the inverse of self. This will raise an error if the result is not in the monoid. + Return the inverse of ``self``. + + This will raise an error if the result is not in the monoid. EXAMPLES:: sage: from sage.modular.pollack_stevens.sigma0 import Sigma0 sage: s = Sigma0(3)([1,4,3,13]) - sage: s.inverse() + sage: s.inverse() # indirect doctest [13 -4] [-3 1] sage: Sigma0(3)([1, 0, 0, 3]).inverse() diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index b1a64b9cbba..2e2f3d574b7 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -2555,12 +2555,6 @@ cdef class MultiplicativeGroupElement(MonoidElement): """ return self * ~right - def __invert__(self): - r""" - Return the inverse of ``self``. - """ - return self._parent.one() / self - def is_RingElement(x): """ @@ -2568,6 +2562,7 @@ def is_RingElement(x): """ return isinstance(x, RingElement) + cdef class RingElement(ModuleElement): cpdef _mul_(self, other): """ From 1a1ec14fcae40bc98284b74ef3a2d3d5e55461a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 7 Jul 2020 19:26:45 +0200 Subject: [PATCH 002/454] fix some details --- src/sage/categories/magmas.py | 4 ++-- src/sage/rings/number_field/class_group.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 7631a4fcd37..9dbf5a25f25 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -608,9 +608,9 @@ def inverse(self): """ Return the inverse of ``self``. - This an alias for inversion, defined in ``  invert__``. + This an alias for inversion, defined in ``__invert__``. - Element classes should implement ``  invert__`` only. + Element classes should implement ``__invert__`` only. EXAMPLES:: diff --git a/src/sage/rings/number_field/class_group.py b/src/sage/rings/number_field/class_group.py index 46d0ca8c9d3..04e1b06983a 100644 --- a/src/sage/rings/number_field/class_group.py +++ b/src/sage/rings/number_field/class_group.py @@ -184,7 +184,7 @@ def inverse(self): sage: ~G(2, a) Fractional ideal class (2, a^2 + 2*a - 1) """ - m = AbelianGroupElement.inverse(self) + m = AbelianGroupElement.__invert__(self) m._value = (~self.ideal()).reduce_equiv() return m From 9d5692d26c4bf8ad4aa8eb1a9f2cbaab5f35b842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 7 Jul 2020 21:35:39 +0200 Subject: [PATCH 003/454] fix some doctests in polynomial quotient rings --- src/sage/rings/polynomial/polynomial_quotient_ring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 05e3ff50a53..cdc294cff2d 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -320,7 +320,7 @@ class of the category, and store the current class of the quotient sage: isinstance(Q.an_element(),Q.element_class) True sage: [s for s in dir(Q.category().element_class) if not s.startswith('_')] - ['cartesian_product', 'inverse_of_unit', 'is_idempotent', 'is_one', 'is_unit', 'lift', 'powers'] + ['cartesian_product', 'inverse', 'inverse_of_unit', 'is_idempotent', 'is_one', 'is_unit', 'lift', 'powers'] sage: first_class = Q.__class__ We try to find out whether `Q` is a field. Indeed it is, and thus its category, @@ -338,6 +338,7 @@ class of the category, and store the current class of the quotient 'euclidean_degree', 'factor', 'gcd', + 'inverse', 'inverse_of_unit', 'is_idempotent', 'is_one', From fbc929ef8a01b017641838c47287e141ea96f8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 8 Jul 2020 08:31:38 +0200 Subject: [PATCH 004/454] some pycodestyle details (breaking some lines after : and ;) --- src/sage/combinat/affine_permutation.py | 23 ++++++++++++------- .../polynomial/polynomial_quotient_ring.py | 9 +++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 33d24cc7c5f..0f67a89fa81 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -708,10 +708,14 @@ def maximal_cyclic_factor(self, typ='decreasing', side='right', verbose=False): T = [i] j = i for count in range(1,self.k): - if (typ[0],side[0]) == ('d','r'): j=(j+1)%(k+1) - if (typ[0],side[0]) == ('i','r'): j=(j-1)%(k+1) - if (typ[0],side[0]) == ('d','l'): j=(j-1)%(k+1) - if (typ[0],side[0]) == ('i','l'): j=(j+1)%(k+1) + if (typ[0],side[0]) == ('d','r'): + j=(j+1)%(k+1) + if (typ[0],side[0]) == ('i','r'): + j=(j-1)%(k+1) + if (typ[0],side[0]) == ('d','l'): + j=(j-1)%(k+1) + if (typ[0],side[0]) == ('i','l'): + j=(j+1)%(k+1) if y.has_descent(j, side): y=y.apply_simple_reflection(j,side) T.append(j%(k+1)) @@ -849,9 +853,10 @@ def to_lehmer_code(self, typ='decreasing', side='right'): a=self(i) for j in range(i-self.k, i): b=self(j) - #A small rotation is necessary for the reduced word from - #the lehmer code to match the element. - if a Date: Wed, 8 Jul 2020 08:37:23 +0200 Subject: [PATCH 005/454] fix one wrong use of parent --- src/sage/categories/magmas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 9dbf5a25f25..58ccb1d2476 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -602,7 +602,7 @@ def __invert__(self): [ 1 0] [-1 1] """ - return self._parent.one()._div_(self) + return self.parent().one()._div_(self) def inverse(self): """ From 5e7e19afded220ad7e88b6362e7f98448a46df04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 19 Aug 2020 10:08:36 +0200 Subject: [PATCH 006/454] tentative fix, move inversion to monoids --- src/sage/categories/magmas.py | 30 ------------------------- src/sage/categories/monoids.py | 30 +++++++++++++++++++++++++ src/sage/monoids/free_monoid_element.py | 7 ++++-- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index ba6a5b7135f..89af2269084 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -589,36 +589,6 @@ def _div_(left, right): """ return left * ~right - def __invert__(self): - r""" - Return the inverse of ``self``. - - The default implementation is to divide ``self.one()``. - - EXAMPLES:: - - sage: A = Matrix([[1, 0], [1, 1]]) - sage: ~A - [ 1 0] - [-1 1] - """ - return self.parent().one()._div_(self) - - def inverse(self): - """ - Return the inverse of ``self``. - - This an alias for inversion, defined in ``__invert__``. - - Element classes should implement ``__invert__`` only. - - EXAMPLES:: - - sage: AA(sqrt(~2)).inverse() - 1.414213562373095? - """ - return self.__invert__() - class SubcategoryMethods: @cached_method diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 42b55b8dda4..d598a6cecb3 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -313,6 +313,36 @@ def powers(self, n): l.append(x) return l + def __invert__(self): + r""" + Return the inverse of ``self``. + + The default implementation is to divide ``self.one()``. + + EXAMPLES:: + + sage: A = Matrix([[1, 0], [1, 1]]) + sage: ~A + [ 1 0] + [-1 1] + """ + return self.parent().one()._div_(self) + + def inverse(self): + """ + Return the inverse of ``self``. + + This an alias for inversion, defined in ``__invert__``. + + Element classes should implement ``__invert__`` only. + + EXAMPLES:: + + sage: AA(sqrt(~2)).inverse() + 1.414213562373095? + """ + return self.__invert__() + class Commutative(CategoryWithAxiom): r""" Category of commutative (abelian) monoids. diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index 549f9381e94..0da37bb9adf 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -21,7 +21,7 @@ # See the GNU General Public License for more details; the full text # is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ #***************************************************************************** from sage.rings.integer import Integer @@ -102,7 +102,7 @@ def __hash__(self): def __iter__(self): """ - Returns an iterator which yields tuples of variable and exponent. + Return an iterator which yields tuples of variable and exponent. EXAMPLES:: @@ -257,6 +257,9 @@ def _mul_(self, y): z._element_list = x_elt[:k] + [ m ] + y_elt[1:] return z + def __invert__(self): + raise NotImplementedError + def __len__(self): """ Return the degree of the monoid element ``self``, where each From bc7fe1e67d9788991fdb5f1487d14c3ac44a4f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Nov 2020 10:17:19 +0100 Subject: [PATCH 007/454] add doctests in free_monoid_element --- src/sage/monoids/free_monoid_element.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index 7345a251464..9bdb03802df 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -48,7 +48,7 @@ class FreeMonoidElement(MonoidElement): sage: x**(-1) Traceback (most recent call last): ... - TypeError: bad operand type for unary ~: 'FreeMonoid_with_category.element_class' + NotimplementedError """ def __init__(self, F, x, check=True): """ @@ -255,6 +255,16 @@ def _mul_(self, y): return z def __invert__(self): + """ + EXAMPLES:: + + sage: a = FreeMonoid(5, 'a').gens() + sage: x = a[0]*a[1]*a[4]**3 + sage: x**(-1) + Traceback (most recent call last): + ... + NotimplementedError + """ raise NotImplementedError def __len__(self): From 0e35336b8ab50524db307c772f930ce144889672 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Tue, 10 Aug 2021 14:59:47 +0100 Subject: [PATCH 008/454] Addition of abel_jacobi methods and edits to architecture. This commit contains a large amount of work, combined together. 1) The structure of many method that use upstairs_edge have been changed to allow for the flexibilty that now an edge need not correspond to an edge of the voronoi diagram, but can instead be specified by a tuple (z_start, z_end). This mean that methods such as simple_vector_line_integral can now integrate along paths that are not part of the skeleton. 2) Methods for the calculation of the Abel-Jacobi map, namely _integrate_differentials_iteratively, _aj_based, abel_jacobi and reparameterise_differential_minpoly, have been added. 3) __init__ has been changed to now include the branchpoints of the output of cohomology_basis() in self.branch_locus. The branch locus of just self.f is now stored in self._f_branch locus. This is useful when calculating the Abel-Jacobi map. As a result the voronoi diagram for a given curve may now be different, and this is reflected in the examples and tests for certain methods. 4) A method for the upstairs graph has been added, which is useful when calculating the Abel-Jacobi map. 5) Matrix of integral values now writes the output of line integrals to self._integral_dict if the differentials are the cohomology basis. This saves on recalculation when calling the Abel-Jacobi map, which can be useful when calculating these integrals is slow. 6) Methods to help with the manipulation of divisors have been added. Namely the methods places_at_branch_locus, strong_approximation, and divisor_to_divisor_list. 7) A method reduce_over_period_lattice has been added to help with the interpretation of results from abel_jacobi, as these will be path dependent. --- .../riemann_surfaces/riemann_surface.py | 1271 +++++++++++++++-- 1 file changed, 1155 insertions(+), 116 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index a4ec7484d72..744e0163b13 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -79,8 +79,10 @@ from sage.matrix.special import block_matrix from sage.misc.cachefunc import cached_method from sage.misc.flatten import flatten +from sage.misc.functional import numerical_approx from sage.misc.misc_c import prod from sage.modules.free_module import VectorSpace +from sage.modules.free_module_integer import IntegerLattice from sage.numerical.gauss_legendre import integrate_vector, integrate_vector_N from sage.rings.complex_mpfr import ComplexField, CDF from sage.rings.integer_ring import ZZ @@ -90,7 +92,6 @@ from sage.rings.real_mpfr import RealField import sage.libs.mpmath.all as mpall - def voronoi_ghost(cpoints, n=6, CC=CDF): r""" Convert a set of complex points to a list of real tuples `(x,y)`, and @@ -322,6 +323,103 @@ def differential_basis_baker(f): if P.interior_contains(a)] +def find_closest_element(item, List): + r""" + Return the index of the closest element of a list. + + Given ``List`` and ``item``, return the index of the element ``l`` of ``List`` + which minimises ``(item-l).abs()``. If there are multiple such elements, the + first is returned. + + INPUT: + + - ``item`` -- value to minimise the distance to over the list. + + - ``List`` -- list. List to look for closest element in. + + EXAMPLES:: + + sage: i = 5 + sage: l = list(range(10)) + sage: i == find_closest_element(i, l) + + Note that this method does no checks on the input, but will fail for inputs + where the absolute value or subtraction do not make sense. + """ + dists = [(item-l).abs() for l in List] + return dists.index(min(dists)) + + +def reparameterise_differential_minpoly(minpoly, z0): + r""" + Rewrites a minimal polynomial to write is around `z_0`. + + Given a minimal polynomial `m(z,g)`, where `g` corresponds to a differential + on the surface (that is, it is represented as a rational function, and + implicitly carries a factor `dz`), we rewrite the minpoly in terms of + variables `\bar{z}, \bar{g}` s.t now `\bar{z}=0 \Leftrightarrow z=z_0`. + + INPUT: + + - ``minpoly`` -- a polynomial in two variables, where the first variables + corresponds to the base coordinate on the Riemann surface. + + - ``z0`` -- complex number of infinity. The point about which to + reparameterise. + + OUTPUT: + + A polynomial in two variables giving the reparameterise minimal polynomial. + + EXAMPLES: + + On the curve given by `w^2-z^3+1=0`, we have differential + `\frac{dz}{2w} = \frac{dz}{2\sqrt{z^3-1}}` + with minimal polynomial `g^2(z^3-1)-1/4=0`. We can make the substitution + `\bar{z}=z^{-1}` to parameterise the differential about `z=\Infty` as + `\frac{-\bar{z}^{-2} d\bar{z}}{2\sqrt{\bar{z}^{-3}-1}} = \frac{-d\bar{z}}{2\sqrt{\bar{z}(1-\bar{z}^3)}}`. + Hence the transformed differential should have minimal polynomial + `\bar{g}^2\bar{z}(1-\bar{z}^3)-1/4=0`, and we can check this:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(w^2-z^3+1) + sage: minpoly = S._cohomology_basis_bounding_data[1][0][2] + sage: z0 = Infinity + sage: S.reparameterise_differential_minpoly(minpoly, z0) + -zbar^4*gbar^2 + zbar*gbar^2 - 1/4 + + We can further check that reparameterising about `0` is the identity + operation:: + + sage: S.reparameterise_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly + True + + .. NOTE:: + + As part of the routine, when reparameterising about infinity, a + rational function is reduced and then the numerator is taken. Over + an inexact ring this is numerically unstable, and so it is advisable + to only reparameterise about infinty over an exact ring. + """ + P = minpoly.parent() + F = PolynomialRing(P.base_ring(), [str(v)+"bar" for v in P.gens()]) + + try: + Inf = bool(z0==z0.parent()(Infinity)) + except TypeError: + Inf = False + + if Inf: + F = F.fraction_field() + mt = F(minpoly(F.gen(0)**(-1),-F.gen(0)**(+2)*F.gen(1))) + mt.reduce() + mt = mt.numerator() + else: + mt = minpoly(F.gen(0)+z0,F.gen(1)) + return mt + + class RiemannSurface(object): r""" Construct a Riemann Surface. This is specified by the zeroes of a bivariate @@ -379,11 +477,11 @@ class RiemannSurface(object): sage: K. = NumberField(t^2-t+3,embedding=CC(0.5+1.6*I)) sage: R. = K[] sage: f = y^2+y-(x^3+(1-a)*x^2-(2+a)*x-2) - sage: S = RiemannSurface(f,prec=100,differentials=[1]) + sage: S = RiemannSurface(f, prec=100, differentials=[1]) sage: A = S.endomorphism_basis() sage: len(A) 2 - sage: all( len(T.minpoly().roots(K)) > 0 for T in A) + sage: all(len(T.minpoly().roots(K)) > 0 for T in A) True TESTS: @@ -418,6 +516,8 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati # Initializations. self._prec = prec self._certification = certification + if not (integration_method=="heuristic" or integration_method=="rigorous"): + raise ValueError("Invalid integration method") self._integration_method = integration_method self._R = f.parent() if len(self._R.gens()) != 2: @@ -456,16 +556,55 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati # Compute the branch locus. Takes the square-free part of the discriminant # because of numerical issues. self.branch_locus = [] - for x in self._discriminant.factor(): - self.branch_locus += self._CCz(x[0](self._CCz.gen(), 0)).roots(multiplicities=False) + existing_factors = [x[0] for x in self._discriminant.factor()] + for fac in existing_factors: + self.branch_locus += self._CCz(fac(self._CCz.gen(), 0)).roots(multiplicities=False) + self._f_branch_locus = self.branch_locus + self._cohomology_basis_bounding_data = self._bounding_data(self.cohomology_basis(), + exact=True) + RBzg, bounding_data_list = self._cohomology_basis_bounding_data + minpoly_list = [bd[2] for bd in bounding_data_list] + # We now want to calculate the additional branchpoints associated to + # the differentials. + discriminants = [RBzg(self._discriminant(*RBzg.gens()))] + for minpoly in minpoly_list: + F = RBzg(minpoly) + dF = F.derivative(RBzg.gen(1)) + discriminants += [F.resultant(dF, RBzg.gen(1))] + combined_discriminant = lcm(discriminants)(*self._R.gens()) + self._differentials_branch_locus = [] + for x in combined_discriminant.factor(): + if not x[0] in existing_factors: + self._differentials_branch_locus += self._CCz(x[0](self._CCz.gen(), + 0)).roots(multiplicities=False) + # We add these branchpoints to the existing. + self.branch_locus = self.branch_locus+self._differentials_branch_locus + # We now want to also check whether Infinity is a branch point of any + # of the differentials. + # This will be useful when calculating the Abel-Jacobi map. + minpoly_list = [reparameterise_differential_minpoly(mp, Infinity) + for mp in minpoly_list] + discriminants = [] + for minpoly in minpoly_list: + F = RBzg(minpoly) + dF = F.derivative(RBzg.gen(1)) + discriminants += [F.resultant(dF, RBzg.gen(1))] + discriminant_about_infinity = RBzg(lcm(discriminants)) + if discriminant_about_infinity(0,0)==0: + self._differentials_branch_locus.append(self._CC(Infinity)) + # Voronoi diagram and the important points associated with it self.voronoi_diagram = Voronoi(voronoi_ghost(self.branch_locus, CC=self._CC)) self._vertices = [self._CC(x0, y0) for x0, y0 in self.voronoi_diagram.vertices] self._wvalues = [self.w_values(z0) for z0 in self._vertices] + # We arbitrarily, but sensibly, set the basepoint to be the rightmost vertex + self._basepoint = (self._vertices.index(sorted(self._vertices, + key=lambda z:z.real())[-1]), 0) self._Sn = SymmetricGroup(range(self.degree)) self._L = {} + self._integral_dict = {} self._fastcall_f = fast_callable(f, domain=self._CC) self._fastcall_dfdw = fast_callable(self._dfdw, domain=self._CC) self._fastcall_dfdz = fast_callable(self._dfdz, domain=self._CC) @@ -508,6 +647,12 @@ def w_values(self, z0): sage: S.w_values(0) # abs tol 1e-14 [-1.00000000000000*I, 1.00000000000000*I] + + Note that typically the method returns a list of length ``self.degree``, + but that at ramification points, this may no longer be true:: + + sage: S.w_values(1) # abs tol 1e-14 + [0.000000000000000] """ return self.f(z0,self._CCw.gen(0)).roots(multiplicities=False) @@ -568,7 +713,7 @@ def downstairs_graph(self): Return the Voronoi decomposition as a planar graph. The result of this routine can be useful to interpret the labelling of - the vertices. + the vertices. See also :meth:`upstairs_graph`. OUTPUT: @@ -582,22 +727,38 @@ def downstairs_graph(self): sage: S = RiemannSurface(f) sage: S.downstairs_graph() Graph on 11 vertices + """ + G = Graph(self.downstairs_edges()) + G.set_pos(dict(enumerate(list(v) for v in self._vertices))) + return G + + def upstairs_graph(self): + r""" + Return the graph of the upstairs edges. + + This method can be useful for generating paths in the surface between points labelled + by upstairs vertices, and verifying that a homology basis is likely computed correctly. + See also :meth:`downstairs_graph`. - Similarly one can form the graph of the upstairs edges, which is - visually rather less attractive but can be instructive to verify that a - homology basis is likely correctly computed.:: + OUTPUT: - sage: G = Graph(S.upstairs_edges()); G + The homotopy-continued Voronoi decomposition as a graph, with appropriate 3D embedding. + + EXAMPLES:: + + sage: R. = QQ[] + sage: S = Curve(w^2-z^4+1).riemann_surface() + sage: G = S.upstairs_graph(); G Graph on 22 vertices - sage: G.is_planar() - False sage: G.genus() 1 sage: G.is_connected() True """ - G = Graph(self.downstairs_edges()) - G.set_pos(dict(enumerate(list(v) for v in self._vertices))) + G = Graph(self.upstairs_edges()) + G.set_pos({(i,j): [self._vertices[i].real(), self._vertices[i].imag(), + self.w_values(self._vertices[i])[j].imag()] + for i in range(len(self._vertices)) for j in range(self.degree)}, dim=3) return G def _compute_delta(self, z1, epsilon, wvalues=None): @@ -653,7 +814,7 @@ def _compute_delta(self, z1, epsilon, wvalues=None): if wvalues is None: wvalues = self.w_values(z1) # For computation of rho. Need the branch locus + roots of a0. - badpoints = self.branch_locus + self._a0roots + badpoints = self._f_branch_locus + self._a0roots rho = min(abs(z1 - z) for z in badpoints) / 2 Y = max(abs(self._fastcall_dfdz(z1, wi)/self._fastcall_dfdw(z1, wi)) for wi in wvalues) @@ -674,7 +835,7 @@ def _compute_delta(self, z1, epsilon, wvalues=None): else: # Instead, we just compute the minimum distance between branch # points and the point in question. - return min(abs(b - z1) for b in self.branch_locus) / 2 + return min(abs(b - z1) for b in self._f_branch_locus) / 2 def homotopy_continuation(self, edge): r""" @@ -683,15 +844,18 @@ def homotopy_continuation(self, edge): INPUT: - - ``edge`` -- a tuple of integers indicating an edge of the Voronoi - diagram + - ``edge`` -- a tuple ``(z_start, z_end)`` indicating the straight line + over which to perform the homotopy continutation. OUTPUT: - A list of complex numbers corresponding to the points which are reached - when traversing along the direction of the edge. The ordering of these - points indicates how they have been permuted due to the weaving of the - curve. + A list containing the initialised continuation data. Each entry in the + list contains: the `t` values that entry corresponds to, a list of + complex numbers corresponding to the points which are reached when + continued along the edge when traversing along the direction of the + edge, and a value ``epsilon`` giving the minimumdistance between the + fibre values divided by 3. The ordering of these points indicates how + they have been permuted due to the weaving of the curve. EXAMPLES: @@ -707,15 +871,15 @@ def homotopy_continuation(self, edge): sage: sigma = S.edge_permutations()[edge1] sage: continued_values = S.homotopy_continuation(edge1) sage: stored_values = S.w_values(S._vertices[edge1[1]]) - sage: all( abs(continued_values[i]-stored_values[sigma(i)]) < 1e-8 for i in range(3)) + sage: all(abs(continued_values[i]-stored_values[sigma(i)]) < 1e-8 for i in range(3)) True """ - i0, i1 = edge + z_start, z_end = edge + z_start = self._CC(z_start) + z_end = self._CC(z_end) ZERO = self._RR.zero() ONE = self._RR.one() datastorage = [] - z_start = self._CC(self._vertices[i0]) - z_end = self._CC(self._vertices[i1]) path_length = abs(z_end - z_start) def path(t): @@ -744,9 +908,8 @@ def path(t): break currw = neww epsilon = min([abs(currw[i] - currw[j]) for i in range(1,n) for j in range(i)])/3 - datastorage += [(T,currw,epsilon)] - self._L[edge] = datastorage - return currw + datastorage += [(T, currw, epsilon)] + return datastorage def _determine_new_w(self, z0, oldw, epsilon): r""" @@ -808,6 +971,11 @@ def _determine_new_w(self, z0, oldw, epsilon): [-0.562337685361648 + 0.151166007149998*I, 0.640201585779414 - 1.48567225836436*I, -0.0778639004177661 + 1.33450625121437*I] + + .. NOTE:: + + Algorithmically, this method is nearly identical to :meth:`_newton_iteration`, + but this method takes a list of `w` values. """ # Tools of Newton iteration. F = self._fastcall_f @@ -948,10 +1116,13 @@ def upstairs_edges(self): # Lifts each edge individually. for e in self.downstairs_edges(): i0, i1 = e + d_edge = (self._vertices[i0], self._vertices[i1]) # Epsilon for checking w-value later. - epsilon = min([abs(self._wvalues[i1][i] - self._wvalues[i1][n-j-1]) for i in range(n) for j in range(n-i-1)])/3 + epsilon = min([abs(self._wvalues[i1][i] - self._wvalues[i1][n-j-1]) + for i in range(n) for j in range(n-i-1)])/3 # Homotopy continuation along e. - homotopycont = self.homotopy_continuation(e) + self._L[e] = self.homotopy_continuation(d_edge) + homotopycont = self._L[e][-1][1] for i in range(len(homotopycont)): # Checks over the w-values of the next point to check which it is. for j in range(len(self._wvalues[i1])): @@ -1030,17 +1201,17 @@ def edge_permutations(self) -> dict: (1, 2): (), (1, 3): (0,1), (1, 6): (), - (2, 0): (), - (2, 1): (), (2, 5): (0,1), - (3, 1): (0,1), (3, 4): (), - (4, 0): (), - (4, 3): (), - (5, 2): (0,1), (5, 7): (), - (6, 1): (), (6, 7): (), + (2, 0): (), + (4, 0): (), + (2, 1): (), + (3, 1): (0,1), + (6, 1): (), + (5, 2): (0,1), + (4, 3): (), (7, 5): (), (7, 6): ()} """ @@ -1073,21 +1244,28 @@ def monodromy_group(self): sage: f = z^3*w + w^3 + z sage: S = RiemannSurface(f) sage: G = S.monodromy_group(); G - [(0,1,2), (0,1), (0,2), (1,2), (1,2), (1,2), (0,1), (0,2), (0,2)] + [(0,2,1), (0,1), (1,2), (0,2), (0,2), (1,2), (0,2), (0,1), (), (), (), (), (), (), (), (1,2)] The permutations give the local monodromy generators for the branch points:: sage: list(zip(S.branch_locus + [unsigned_infinity], G)) #abs tol 0.0000001 - [(0.000000000000000, (0,1,2)), + [(0.000000000000000, (0,2,1)), (-1.31362670141929, (0,1)), - (-0.819032851784253 - 1.02703471138023*I, (0,2)), - (-0.819032851784253 + 1.02703471138023*I, (1,2)), - (0.292309440469772 - 1.28069133740100*I, (1,2)), + (-0.819032851784253 - 1.02703471138023*I, (1,2)), + (-0.819032851784253 + 1.02703471138023*I, (0,2)), + (0.292309440469772 - 1.28069133740100*I, (0,2)), (0.292309440469772 + 1.28069133740100*I, (1,2)), - (1.18353676202412 - 0.569961265016465*I, (0,1)), - (1.18353676202412 + 0.569961265016465*I, (0,2)), - (Infinity, (0,2))] + (1.18353676202412 - 0.569961265016465*I, (0,2)), + (1.18353676202412 + 0.569961265016465*I, (0,1)), + (-1.45036146591896, ()), + (-0.904285583009352 - 1.13393825501392*I, ()), + (-0.904285583009352 + 1.13393825501392*I, ()), + (0.322735787970535 - 1.41399787587734*I, ()), + (0.322735787970535 + 1.41399787587734*I, ()), + (1.30673052799829 - 0.629288255904939*I, ()), + (1.30673052799829 + 0.629288255904939*I, ()), + (Infinity, (1,2))] We can check the ramification by looking at the cycle lengths and verify it agrees with the Riemann-Hurwitz formula:: @@ -1308,21 +1486,25 @@ def direction(center, neighbour): bcycles[i] += [(P[self.genus + i][j], [x for x in cycles[j]] + [cycles[j][0]])] return acycles + bcycles - def make_zw_interpolator(self, upstairs_edge): + def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): r""" - Given an upstairs edge for which continuation data has been stored, - return a function that computes `z(t),w(t)` , where `t` in `[0,1]` is a + Given a downstairs edge for which continuation data has been initialised, + return a function that computes `z(t), w(t)` , where `t` in `[0,1]` is a parametrization of the edge. INPUT: - - ``upstairs_edge`` -- a pair of integer tuples indicating an edge on - the upstairs graph of the surface + - ``upstairs_edge`` -- tuple. ``((z_start, sb), (z_end,))`` giving the + start and end values of the base coordinate along the straight-line + path and the starting branch. + + - ``initial_continuation`` -- list (default: None). Output of + ``homotopy_continuation`` initialising the continuation data. OUTPUT: - A tuple (g, d), where g is the function that computes the interpolation - along the edge and d is the difference of the z-values of the end and + A tuple ``(g, d)``, where ``g`` is the function that computes the interpolation + along the edge and ``d`` is the difference of the z-values of the end and start point. EXAMPLES:: @@ -1332,17 +1514,30 @@ def make_zw_interpolator(self, upstairs_edge): sage: f = w^2 - z^4 + 1 sage: S = RiemannSurface(f) sage: _ = S.homology_basis() - sage: g,d = S.make_zw_interpolator([(0,0),(1,0)]); - sage: all(f(*g(i*0.1)).abs() < 1e-13for i in range(10)) + sage: u_edge = [(0, 0), (1, 0)] + sage: d_edge = tuple(u[0] for u in u_edge) + sage: u_edge = [(S._vertices[i], j) for i, j in u_edge] + sage: initial_continuation = S._L[d_edge] + sage: g, d = S.make_zw_interpolator(u_edge, initial_continuation); + sage: all(f(*g(i*0.1)).abs() < 1e-13 for i in range(10)) True sage: abs((g(1)[0]-g(0)[0]) - d) < 1e-13 True + + .. NOTE:: + + The interpolator returned by this method can effectively hang if + either ``z_start`` or ``z_end`` are branchpoints. In these situations + it is better to take a different approach rather than continue to use + the interpolator. """ - eindex = tuple(u[0] for u in upstairs_edge) - i0, i1 = eindex - z_start = self._vertices[i0] - z_end = self._vertices[i1] - currL = self._L[eindex] + downstairs_edge = tuple(u[0] for u in upstairs_edge) + z_start, z_end = downstairs_edge + z_start = self._CC(z_start) + z_end = self._CC(z_end) + if initial_continuation==None: + initial_continuation = self.homotopy_continuation(downstairs_edge) + currL = initial_continuation windex = upstairs_edge[0][1] def w_interpolate(t): @@ -1370,7 +1565,7 @@ def w_interpolate(t): tnew = t while True: tnew = (t1 + tnew) / 2 - znew = (1-tnew)*self._vertices[i0]+tnew*self._vertices[i1] + znew = (1-tnew)*z_start+tnew*z_end try: neww1 = self._determine_new_w(znew, currL[i][1], epsilon) except ConvergenceError: @@ -1380,7 +1575,7 @@ def w_interpolate(t): break # once the loop has succeeded we insert our new value t1 = tnew - self._L[eindex].insert(i + 1, (t1, neww1, epsilon)) + currL.insert(i + 1, (t1, neww1, epsilon)) return w_interpolate, (z_end - z_start) def simple_vector_line_integral(self, upstairs_edge, differentials): @@ -1389,8 +1584,10 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): INPUT: - - ``upstairs_edge`` -- a pair of integer tuples corresponding to an edge - of the upstairs graph. + - ``upstairs_edge`` -- tuple. Either a pair of integer tuples + corresponding to an edge of the upstairs graph, or a tuple + ``((z_start, sb), (z_end, ))`` as in the input of + ``make_zw_interpolator``. - ``differentials`` -- a list of polynomials; a polynomial `g` represents the differential `g(z,w)/(df/dw) dz` where `f(z,w)=0` is @@ -1418,9 +1615,21 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): .. NOTE:: - Uses data that ``homology_basis`` initializes. + Uses data that :meth:`homology_basis` initializes, and may give incorrect + values if :meth:`homology_basis` has not initialized them. In practice + it is more efficient to set ``differentials`` to a fast-callable version + of differentials to speed up execution. """ - w_of_t, Delta_z = self.make_zw_interpolator(upstairs_edge) + d_edge = tuple(u[0] for u in upstairs_edge) + # Using a try-catch here allows us to retain a certain amount of back compatibility + # for users. + try: + initial_continuation = self._L[d_edge] + upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), + (self._vertices[d_edge[1]], )) + except KeyError: + initial_continuation = self.homotopy_continuation(d_edge) + w_of_t, Delta_z = self.make_zw_interpolator(upstairs_edge, initial_continuation) V = VectorSpace(self._CC, len(differentials)) def integrand(t): @@ -1459,7 +1668,7 @@ def cohomology_basis(self, option=1): """ if self.genus == 0: self._differentials = [] - return self._differentials[0] + return self._differentials #[0] if self._differentials is None: # Computes differentials from the adjointIdeal using Singular # First we homogenize @@ -1495,7 +1704,7 @@ def cohomology_basis(self, option=1): self._differentials = generators return self._differentials - def _bounding_data(self, differentials): + def _bounding_data(self, differentials, exact=False): r""" Compute the data required to bound a differential on a circle. @@ -1504,7 +1713,12 @@ def _bounding_data(self, differentials): INPUT: - - ``differentials`` -- The differentials for which to compute the bounding data. + - ``differentials`` -- list. The differentials for which to compute the + bounding data. + + - ``exact`` -- logical (default: False). Whether to return the minimal + polynomials over the exact base ring, or whether to return them over + ``self._CC``. OUTPUT: @@ -1512,7 +1726,7 @@ def _bounding_data(self, differentials): as well as a list containing the differential, its derivative, the minimal polynomial, and ``a0_info`` for each differential. Here ``a0_info`` is the leading coefficient and roots of the leading order - term of the minimal polynomial. + term of the minimal polynomial, calculated in ``self._CC``. EXAMPLES:: @@ -1530,8 +1744,21 @@ def _bounding_data(self, differentials): -0.500000000000000 - 0.866025403784439*I, -0.500000000000000 + 0.866025403784439*I]))]) + Note that ``self._bounding_data(self._cohomology_basis(), exact=True)`` + is stored in ``self._cohomology_basis_bounding_data``:: + + sage: S._cohomology_basis_bounding_data + (Multivariate Polynomial Ring in z, g over Rational Field, + [(1/(2*y), + (-3*z^2*g)/(2*z^3 - 2), + z^3*g^2 - g^2 - 1/4, + (1.00000000000000, + [1.00000000000000, + -0.500000000000000 - 0.866025403784439*I, + -0.500000000000000 + 0.866025403784439*I]))]) """ - # This copies previous work by NB, outputting the zipped list required for a certified line integral. + # This copies previous work by NB, outputting the zipped list required + # for a certified line integral. RB = self._R.base_ring() P = PolynomialRing(RB, 'Z') k = P.fraction_field() @@ -1540,13 +1767,14 @@ def _bounding_data(self, differentials): L = k.extension(fZW, 'Wb') dfdw_L = self._dfdw(P.gen(0), L.gen(0)) integrand_list = [h/self._dfdw for h in differentials] - # minpoly_univ gives the minimal polynomial for h, in variable x, with coefficients given by polynomials - # with coefficients in P (i.e. rational polynomials in Z). + # minpoly_univ gives the minimal polynomial for h, in variable x, with + # coefficients given by polynomials with coefficients in P (i.e. + # rational polynomials in Z). minpoly_univ = [(h(P.gen(0), L.gen(0))/dfdw_L).minpoly().numerator() for h in differentials] RBzg = PolynomialRing(RB, ['z', 'g']) - # The following line changes the variables in these minimal polynomials as Z -> z, x -> G, then evaluates - # at G = QQzg.gens(1) ( = g ) + # The following line changes the variables in these minimal polynomials + # as Z -> z, x -> G, then evaluates at G = QQzg.gens(1) ( = g ) RBzgG = PolynomialRing(RBzg, 'G') minpoly_list = [RBzgG([c(RBzg.gen(0)) for c in list(h)])(RBzg.gen(1)) for h in minpoly_univ] @@ -1558,7 +1786,6 @@ def _bounding_data(self, differentials): CCminpoly_list = [CCzg(h) for h in minpoly_list] a0_list = [P(h.leading_coefficient()) for h in minpoly_univ] - #CCa0_list = [self._CCz(a0) for a0 in a0_list] # Note that because the field over which the Riemann surface is defined # is embedded into CC, it has characteristic 0, and so we know the # irreducible factors are all separable, i.e. the roots have multiplicity @@ -1567,12 +1794,10 @@ def _bounding_data(self, differentials): flatten([self._CCz(F).roots(multiplicities=False)*m for F, m in a0.factor()])) for a0 in a0_list] - - #a0_list = [self._CCz(h.leading_coefficient()) for h in minpoly_univ] - #a0_info = [(a0.leading_coefficient(), flatten([[r]*n for r, n in a0.roots()])) for a0 in a0_list] - # Note that we are assuming here that the root finder is working correctly, which will need - # some thought. - return CCzg, list(zip(integrand_list, dgdz_list, CCminpoly_list, a0_info)) + if exact: + return RBzg, list(zip(integrand_list, dgdz_list, minpoly_list, a0_info)) + else: + return CCzg, list(zip(integrand_list, dgdz_list, CCminpoly_list, a0_info)) def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): r""" @@ -1585,8 +1810,10 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): INPUT: - - ``upstairs_edge`` -- a pair of integer tuples corresponding to an edge - of the upstairs graph. + - ``upstairs_edge`` -- tuple. Either a pair of integer tuples + corresponding to an edge of the upstairs graph, or a tuple + ``((z_start, sb), (z_end, ))`` as in the input of + ``make_zw_interpolator``. - ``differentials`` -- a list of polynomials; a polynomial `g` represents the differential `g(z,w)/(df/dw) dz` where `f(z,w)=0` is @@ -1594,7 +1821,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): - ``bounding_data`` -- tuple containing the data required for bounding the integrands. This should be in the form of the output from - ``_bounding_data``. + :meth:`_bounding_data`. OUTPUT: @@ -1620,8 +1847,9 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): .. NOTE:: - Uses data that ``homology_basis`` initializes. In practice is it is - more efficient to set ``differentials`` to a fast-callable version + Uses data that :meth:`homology_basis` initializes, and may give incorrect + values if :meth:`homology_basis` has not initialized them. In practice + it is more efficient to set ``differentials`` to a fast-callable version of differentials to speed up execution. .. TODO:: @@ -1643,17 +1871,28 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): entire line with one ellipse, then bounds along that ellipse with multiple circles. """ - # Note that this, in it's current formalism, makes no check that bounding data at all corresponds to - # the differentials given. The onus is then on the design of other funcions which use it. + # Note that this, in it's current formalism, makes no check that bounding + # data at all corresponds to the differentials given. The onus is then on + # the design of other funcions which use it. - # CCzg is required to be known as we need to know the ring which the minpolys lie in. + # CCzg is required to be known as we need to know the ring which the minpolys + # lie in. CCzg, bounding_data_list = bounding_data + + d_edge = tuple(u[0] for u in upstairs_edge) + # Using a try-catch here allows us to retain a certain amount of back + # compatibility for users. + try: + initial_continuation = self._L[d_edge] + upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), + (self._vertices[d_edge[1]], )) + except KeyError: + initial_continuation = self.homotopy_continuation(d_edge) - i0, _ = upstairs_edge[0] - i1, _ = upstairs_edge[1] - z0 = self._vertices[i0] - z1 = self._vertices[i1] - zwt, z1_minus_z0 = self.make_zw_interpolator(upstairs_edge) + zwt, z1_minus_z0 = self.make_zw_interpolator(upstairs_edge, + initial_continuation) + z0 = zwt(0)[0] + z1 = zwt(1)[0] # list of (centre, radius) pairs that still need to be processed ball_stack = [(self._RR(0.5), self._RR(0.5))] @@ -1672,30 +1911,34 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): while ball_stack: ct, rt = ball_stack.pop() cz = (1-ct)*z0+ct*z1 # This is the central z-value of our ball. - distances = [(cz-b).abs() for b in self.branch_locus] # Distance to the discriminant points + # Distance to the discriminant points + distances = [(cz-b).abs() for b in self.branch_locus] rho_z = min(distances) rho_t = rho_z/(z1-z0).abs() if rho_t > rt: rho_t = alpha*rho_t+(1-alpha)*rt # sqrt(rho_t*rt) could also work rho_z = rho_t*(z1-z0).abs() delta_z = (alpha*rho_t+(1-alpha)*rt)*(z1-z0).abs() - expr = rho_t/rt+((rho_t/rt)**2-1).sqrt() # Note this is really exp(arcosh(rho_t/rt)) + # Note this is really exp(arcosh(rho_t/rt)) + expr = rho_t/rt+((rho_t/rt)**2-1).sqrt() N = 3 cw = zwt(ct)[1] for g, dgdz, minpoly,(a0lc,a0roots) in bounding_data_list: z_1 = a0lc.abs()*prod((cz-r).abs()-rho_z for r in a0roots) n = minpoly.degree(CCzg.gen(1)) - # Note the structure of the code is currently s.t 'z' has to be the variable in - # the minpolys. - ai_new = [(minpoly.coefficient({CCzg.gen(1):i}))(z=cz+self._CCz.gen(0)) for i - in range(n)] - ai_pos = [ self._RRz([c.abs() for c in h.list()]) for h in ai_new] + # Note the structure of the code is currently s.t 'z' has to + # be the variable in the minpolys. + ai_new = [(minpoly.coefficient({CCzg.gen(1):i}))(z=cz+self._CCz.gen(0)) + for i in range(n)] + ai_pos = [self._RRz([c.abs() for c in h.list()]) + for h in ai_new] m = [a(rho_z)/z_1 for a in ai_pos] l = len(m) - M_tilde = 2*max((m[i].abs())**(1/self._RR(l-i)) for i in range(l)) + M_tilde = 2*max((m[i].abs())**(1/self._RR(l-i)) + for i in range(l)) cg = g(cz,cw) cdgdz = dgdz(cz,cg) - Delta = delta_z*cdgdz.abs()+ (delta_z**2)*M_tilde/(rho_z*(rho_z-delta_z)) + Delta = delta_z*cdgdz.abs() + (delta_z**2)*M_tilde/(rho_z*(rho_z-delta_z)) M = Delta #+ abs(cg) N_required = ((64*M/(15*(1-1/expr)*E_global)).log()/(2*expr.log())).ceil() N = max(N,N_required) @@ -1703,9 +1946,8 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): N = (K*(self._RR(N).sqrt()/K).ceil())**2 # Rounding is sensible as it allows the cache of nodes in # sage.numerical.gauss_legendre to be used. - # Quadratic rounding can be shown to be a sensible choice through the - # basic argument that nodes is quadratic in N - + # Quadratic rounding can be shown to be a sensible choice through + # the basic argument that nodes is quadratic in N ct_minus_rt = ct-rt two_rt = 2*rt def integrand(t): @@ -1755,6 +1997,13 @@ def matrix_of_integral_values(self, differentials, integration_method="heuristic sage: (m[0,0]/m[0,1]).algdep(3).degree() #curve is CM, so the period is quadratic 2 + .. NOTE:: + + If ``differentials is self.cohomology_basis()``, the calculations + of the integrals along the edges are written to `self._integral_dict``. + This is as this data will be required when computing the Abel-Jacobi + map, and so it is helpful to have is stored rather than recomputing. + """ cycles = self.homology_basis() @@ -1778,20 +2027,21 @@ def normalize_pairs(L): fcd = [fast_callable(omega, domain=self._CC) for omega in differentials] if integration_method=="heuristic": - line_int = lambda edge: self.simple_vector_line_integral(edge,fcd) + line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) elif integration_method=="rigorous": bd = self._bounding_data(differentials) - line_int = lambda edge: self.rigorous_line_integral(edge,fcd,bd) + line_int = lambda edge: self.rigorous_line_integral(edge, fcd, bd) else: raise ValueError("Invalid integration method") - #integral_dict = {upstairs_edge: - # self.simple_vector_line_integral(upstairs_edge, - # differentials) - # for upstairs_edge in occurring_edges} - integral_dict = dict() - for upstairs_edge in occurring_edges: - integral_dict[upstairs_edge] = line_int(upstairs_edge) + if differentials is self.cohomology_basis(): + integral_dict = self._integral_dict + for upstairs_edge in occurring_edges: + if not upstairs_edge in integral_dict.keys(): + integral_dict[upstairs_edge] = line_int(upstairs_edge) + else: + integral_dict = {upstairs_edge: lint_int(upstairs_edge) + for upstairs_edge in occurring_edges} rows = [] for cycle in cycles: @@ -2338,6 +2588,795 @@ def __add__(self, other): """ return RiemannSurfaceSum([self, other]) + def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individually=False, raise_errors=True, prec=None): + r""" + Integrate the cohomology basis along a straight line edge. + + The cohomology basis is integrated along a straight line using a version + of the double exponential quadrature. This method of integrating the + cohomology basis is especially useful when integrating out to infinity, + or near roots of `self._dfdw`. In order to aid with convergence of the + method, two main modification to a standard integrator are made, most + importantly of which is the truncation of the integral near branch points, + where the first term in the Puiseux series of the integrands are used to + approximately bound the integral. The ``cutoff_individually`` parameter + allows the user to set whether that truncation is uniform over all the + integrands, which improves the complexity of the algorithm, but loses + the ability to gain benefits where integrands vanish to a high order at + the branchpoint. + + INPUT: + + - ``upstairs_edge`` -- tuple. A tuple of complex numbers of the form + ``((z_start, w_start), z_end)`` specifying the path to integrate + along, where ``z_start`` may be infinite, in which case ``w_start`` + must be an integer specifying the branch. + + - ``cutoff_individually`` -- boolean (default: False). Whether to truncate + the integrand uniformly or not. If ``None``, then no truncation is + applied. + + - ``raise_errors`` -- boolean (default: True). By default the code uses + convergence errors to ensure any answers returned are accurate. This + can be turned off to return answers faster that are not necessarily + correct. + + - ``prec`` -- integer (default: ``self._prec``). The precision to try + and achieve, defined as `2^{-\text{prec}+3}`. + + OUTPUT: + + Tuple ``(I, gs)`` where ``I`` is the vector of integrals, and ``gs`` are + the values of the differentials at ``z_end``. + + EXAMPLES: + + We know that for the surface given by `w^2-z^4-1` a cohomology basis is + given by `\frac{dz}{2w}`. One can verify analytically that + `\int_0^1 frac{dt}{\sqrt{1-t^4}}=\frac{\sqrt{\pi}\Gamma(5/4)}{\Gamma(3/4)}`, + and we check this with the integrator, being careful with signs:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(w^2+z^4-1, prec=100) + sage: branch = 0 + sage: eps = S._RR(2)**(-S._prec) + sage: z_start = 1-eps + sage: z_end = 0 + sage: w_start = S.w_values(z_start)[0] + sage: s = sign(w_start) + sage: u_edge = ((z_start, w_start), z_end) + sage: J, _ = S._integrate_differentials_iteratively(u_edge) + sage: bool(J[0]+s*S._RR(sqrt(pi)*gamma(5/4)/gamma(3/4)/2)<1e-10) + True + + .. NOTE:: + + The cutoff methodology is calculating the first term in the Puiseux + series of the differentials about z_start. In future it may be + desirable to extend this further and use the truncated Puiseux series + entirely to integrate the differentials. + """ + (z_start, w_start), z_end = upstairs_edge + z_start = self._CC(z_start) + z_end = self._CC(z_end) + + if z_end==self._CC(Infinity): + raise NotImplementedError + + _, bounding_data_list = self._cohomology_basis_bounding_data + mp_list = [bd[2] for bd in bounding_data_list] + + # Parameterise so zbar=0 corresponds to z=z_start + mp_list = [reparameterise_differential_minpoly(mp, z_start) + for mp in mp_list] + + if z_start==self._CC(Infinity): + CCzg = PolynomialRing(self._CC, ['zbar','gbar']) + mp_list = [CCzg(mp) for mp in mp_list] + J = 1/z_end + endscale = -z_end**(-2) + def initialise(z, i): + DF = ComplexField(2*self._prec) + DFw = PolynomialRing(DF,'wbar') + z = DF(z) + R = DF(z**(-1)) + wR = DFw(self.f(R, DFw.gen(0))).roots(multiplicities=False)[w_start] + newg = -R**2*self.cohomology_basis()[i](R, wR)/self._dfdw(R, wR) + err = mp_list[i](z,newg).abs() + if err>tau: + rs = mp_list[i](z, DFw.gen(0)).roots(multiplicities=False) + sb = find_closest_element(newg, rs) + newg = rs[sb] + return newg + else: + CCzg = mp_list[0].parent() + J = z_end-z_start + endscale = 1 + def initialise(z, i): + newg = self.cohomology_basis()[i](z_start, w_start)/self._dfdw(z_start, w_start) + err = mp_list[i](z, newg).abs() + if err>tau: + rs = mp_list[i](z, self._CCw.gen(0)).roots(multiplicities=False) + sb = find_closest_element(newg, rs) + newg = rs[sb] + return newg + + fc_mp_list = [fast_callable(mp, domain=self._CC) for mp in mp_list] + fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) + for mp in mp_list] + + if prec==None: + prec = self._prec + # tau here is playing the role of the desired error. + tau = self._RR(2)**(-prec+3) + ONE = self._RR(1) + LAMBDA = self._RR.pi()/2 + + if cutoff_individually==None: + cutoffs = [0] + cutoff_individually = False + else: + cutoffs = [] + A = PolynomialRing(self._CC,'xyz') + aes = [] + for mp in mp_list: + d = mp.dict() + mp = sum([d[k]*CCzg.gen(0)**k[0]*CCzg.gen(1)**k[1] + for k in d.keys() if d[k].abs()>tau]) + cst = min([iz for (iz, ig) in d.keys() if ig==0]) + a = QQ(max([(cst-iz)/ig for (iz,ig) in d.keys() if ig>0])) + sum_coeffs = sum([d[k]*A.gen(0)**k[1] for k in d.keys() + if ((k[1]==0 and k[0]==cst) or k[1]*a+k[0]-cst==0)]) + G = max([r.abs() for r in sum_coeffs.roots(multiplicities=False)]) + cutoffs.append(((a+1)*tau/G)**(1/self._CC(a+1))/J.abs()) + aes.append(a) + cutoff_individually = bool(not all(ai<=0 for ai in aes) and cutoff_individually) + + if raise_errors: + n_steps = self._prec-1 + def error_handle(out): + raise ConvergenceError("Newton iteration fails to converge") + else: + n_steps = 15 + def error_handle(out): + return out + + V = VectorSpace(self._CC, self.genus) + h = ONE + Nh = (-lambert_w(-1,-tau/2)/LAMBDA).log().ceil() + h0 = Nh*h + + if cutoff_individually: + z_fc_list = list(zip(fc_mp_list, fc_dmp_list)) + + def fv(hj, previous_estimate_and_validity): + u2 = LAMBDA*hj.sinh() + t = 1/(2*u2.exp()*u2.cosh()) + z0 = J*t + outg = [] + valid = self.genus*[True] + previous_estimate, validity = previous_estimate_and_validity + for i in range(self.genus): + co = cutoffs[i] + pv = validity[i] + if t=Ndelta and + (Ndelta.sign_mantissa_exponent()[2] + +self._prec) < newg.norm().sign_mantissa_exponent()[2]): + outg.append(newg) + break + delta = new_delta + Ndelta = Nnew_delta + newg-=delta + if j==99: + outg.append(error_handle(newg)) + fj = V(outg) + u1 = LAMBDA*hj.cosh() + w = u1/(2*u2.cosh()**2) + return (fj, valid), w*fj + + f0, v0 = fv(h0, (self.genus*[0], self.genus*[False])) + else: + cutoffs.append(1) + cutoff = min(cutoffs) + cutoff_z = J*cutoff + J -= cutoff_z + + def fv(hj, previous_estimate): + u2 = LAMBDA*hj.sinh() + t = 1/(2*u2.exp()*u2.cosh()) + z0 = cutoff_z+J*t + outg = [] + for F, dF, oldg in zip(fc_mp_list, fc_dmp_list, previous_estimate): + delta = F(z0, oldg) / dF(z0, oldg) + Ndelta = delta.norm() + newg = oldg - delta + for j in range(100): + new_delta = F(z0, newg) / dF(z0, newg) + Nnew_delta = new_delta.norm() + if (new_delta == 0) or (Nnew_delta>=Ndelta and + (Ndelta.sign_mantissa_exponent()[2] + +self._prec) < newg.norm().sign_mantissa_exponent()[2]): + outg.append(newg) + break + delta = new_delta + Ndelta = Nnew_delta + newg-=delta + if j==99: + outg.append(error_handle(newg)) + fj = V(outg) + u1 = LAMBDA*hj.cosh() + w = u1/(2*u2.cosh()**2) + return fj, w*fj + + u1, u2 = (LAMBDA*h0.cosh(),LAMBDA*h0.sinh()) + y, w = (1/(2*u2.exp()*u2.cosh()), u1/(2*u2.cosh()**2)) + z0 = cutoff_z+J*y + f0 = [initialise(z0, i) for i in range(self.genus)] + f0 = V(f0) + v0 = w*f0 + + D3_over_tau = v0.norm(Infinity) + D4 = D3_over_tau + results = [] + + for k in range(n_steps): + hj = h0 + val = v0 + fj = f0 + for j in range(2*Nh): + hj -= h + try: + fj, v = fv(hj, fj) + except ConvergenceError: + break + D3_over_tau = max(v.norm(Infinity), D3_over_tau) + val += v + if j==2*Nh-1: + results.append(h*val) + D4 = max(D4, v.norm(Infinity)) + if len(results)>2: + if results[-1] == results[-2] or results[2] == results[-3]: + D = tau + else: + D1 = (results[-1]-results[-2]).norm(Infinity) + D2 = (results[-1]-results[-3]).norm(Infinity) + D = min(ONE,max(D1**(D1.log()/D2.log()),D2**2,tau*D3_over_tau,D4,tau)) + + if D <= tau: + if cutoff_individually: + fj = fj[0] + return J*results[-1], endscale*fj + h /= 2 + Nh *= 2 + return error_handle((J*results[-1], endscale*fj)) + + def _aj_based(self, P): + r""" + Return the Abel-Jacobi map to ``P`` from ``self._basepoint``. + + Computes a representative of the Abel-Jacobi map from ``self._basepoint`` + to ``P`` via a well-chosen vertex ``V``. The representative given will be + dependent on the path chosen. + + INPUT: + + - ``P`` -- tuple. A pair giving the endpoint of the integral, either in + the form ``(z, w)`` or ``(Infinity, branch)``, where in the latter case + we are using the conventing that the `w` value over `\infty` is given by + the limit as ``z`` tends to `\infty` of ``self.w_values(z)[branch]``. + + OUTPUT: + + A vector of length ``self.genus``. + + EXAMPLES: + + As the output of ``_aj_based`` is difficult to intepret due to its path + dependency, we look at the output of :meth:`abel_jacobi`. We check for + two hyperelliptic curves that the Abel-Jacobi map between two branch + points is a 2-torsion point over the lattice:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(y^2-x^3+1) + sage: divisor = [(-1, (Infinity, 0)), (1, (1, 0))] + sage: AJ = S.abel_jacobi(divisor) + sage: AJx2 = [2*z for z in AJ] + sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) + True + sage: S = RiemannSurface(y^2-x^4+1) + sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] + sage: AJ = S.abel_jacobi(divisor) + sage: AJx2 = [2*z for z in AJ] + sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) + True + + """ + ##### + fcd = [fast_callable(h, domain = self._CC) for h in self.cohomology_basis()] + + if self._integration_method=="heuristic": + line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) + else: + bd = self._cohomology_basis_bounding_data + line_int = lambda edge: self.rigorous_line_integral(edge, fcd, bd) + ##### + B = self._basepoint + zP, wP = P + + try: + Inf = bool(zP==zP.parent()(Infinity)) + except TypeError: + Inf = False + + if Inf: + zV = self._vertices[B[0]] + if zV==0: + zV += 1 + upstairs_edge = (P, zV) + ci = bool(self._CC(Infinity) in self._differentials_branch_locus) + AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, + cutoff_individually=ci) + AJ = -AJ + g0e = endgs[0] + ws = self.w_values(zV) + g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] + W_index = find_closest_element(g0e, g0s) + if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision requried.") + V_index = B[0] + else: + zP = self._CC(zP) + wP = self._CC(wP) + V_index = find_closest_element(zP, self._vertices) + b_index = find_closest_element(zP, self.branch_locus) + b = self.branch_locus[b_index] + + d1 = self._CC(1e-2)*max(b.abs() for b in self.branch_locus) + + # We choose the first vertex we want to go to. + # If the closest vertex is closer than the nearest branch point, just take that vertex + # otherwise we need something smarter. + delta = self._RR(2)**(-self._prec+1) + if not ((zP-self._vertices[V_index]).abs() < (zP-b).abs() or (zP-b).abs()<=delta): + region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] + args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] + suitable_vertex_indices = [region[i] + for i in range(len(region)) if args[i].abs()>self._RR.pi()/2] + suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] + if suitable_vertices==[]: + raise ValueError("There is no satisfactory choice of V for zP={}".format(zP)) + V_index = suitable_vertex_indices[find_closest_element(zP, suitable_vertices)] + ##### + zV = self._vertices[V_index] + d_edge = (zP, zV) + + if (zP-b).abs() >= d1 or b in self._differentials_branch_locus: + wP_index = find_closest_element(wP, self.w_values(zP)) + u_edge = ((zP, wP_index), (zV, )) + initial_continuation = self.homotopy_continuation(d_edge) + AJ = -line_int(u_edge) + + w_end = initial_continuation[-1][1][wP_index] + W_index = find_closest_element(w_end, self._wvalues[V_index]) + else: + zs = zP + ws = wP + + while self._dfdw(zs, ws).abs()==0: + zs = zs+delta*(zV-zs)/(zV-zs).abs()/2 + ws_list = self.w_values(zs) + wP_index = find_closest_element(ws, ws_list) + ws = ws_list[wP_index] + upstairs_edge = ((zs, ws), zV) + AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, + cutoff_individually=False) + AJ = -AJ + g0e = endgs[0] + + ws = self.w_values(zV) + g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] + W_index = find_closest_element(g0e, g0s) + if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision requried.") + + uV_index = (V_index, W_index) + ##### + G = self.upstairs_graph() + path = G.shortest_path(B, uV_index) + edges = [(path[i],path[i+1]) for i in range(len(path)-1)] + ##### + for e in edges: + if e[1][0]>e[0][0]: + s = 1 + else: + s = -1 + e = tuple(reversed(e)) + try: + AJ += s*self._integral_dict[e] + except KeyError: + Ie = line_int(e) + self._integral_dict[e] = Ie + AJ += s*Ie + return AJ + + def abel_jacobi(self, divisor, verbose=False): + r""" + Return the Abel-Jacobi map of ``divisor``. + + Returns a representative of the Abel-Jacobi map of a divisor with basepoint + ``self._basepoint``. + + INPUT: + + - ``divisor`` -- list. A list with each entry a tuple of the form ``(v, P)``, + where ``v`` is the valuation of the divisor at point ``P``, ``P`` as per + the input to :meth:`_aj_based`. + + - `` verbose`` -- logical (default: False). Whether to report the progress + of the computation, in terms of how many elements of the list ``divisor`` + have been completed. + + OUTPUT: + + A vector of length ``self.genus``. + + EXAMPLES: + + We can test that the Abel-Jacobi map between two branchpoints of a + superelliptic curve of degree `p` is a `p`-torsion point in the Jacobian:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: p = 4 + sage: S = RiemannSurface(y^p-x^4+1, prec=100) + sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] + sage: AJ = S.abel_jacobi(divisor) # long time (15 seconds) + sage: AJxp = [p*z for z in AJ] # long time + sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-7) # long time + True + """ + ans = 0 + n = len(divisor) + for i in range(n): + v, p = divisor[i] + if verbose: + print("starting computation for p = {}".format(p)) + ans += v*self._aj_based(p) + if verbose: + print("Done, {}% complete".format(numerical_approx(100*(i+1)/n, 11))) + return ans + + def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None): + r""" + Reduce a vector over the period lattice. + + Given a vector of length ``self.genus``, this method returns a vector + in the same orbit of the period lattice that is short. There are two + possible methods, ``'svp'`` which returns a certified shortest vector, + but can be much slower for higher genus curves, and ``'ip'``, which is + faster but not guaranteed to return the shortest vector. In general the + latter will perform well when the lattice basis vectors are of similar + size. + + INPUT: + + - ``vector`` -- vector. A vector of length ``self.genus`` to reduce over + the lattice. + + - ``method`` -- string (default: ``'ip'``). String specifying the method + to use to reduce the vector. THe options are ``'ip'`` and ``'svp'``. + + - ``b`` -- integer (default provided): as for + :meth:`homomorphism_basis`, and used in its invocation if + (re)calculating said basis. + + - ``r`` -- integer (default: ``b/4``). as for + :meth:`homomorphism_basis`, and used in its invocation if + (re)calculating said basis. + + OUTPUT: + + Complex vector of length ``self.genus`` in the same orbit as ``vector`` + in the lattice. + + EXAMPLES: + + We can check that the lattice basis vectors themselves are reduced to + zero:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(y^2-x^5+1) + sage: epsilon = S._RR(2)^(-S._prec+1) + sage: for vector in S.period_matrix().columns(): + ....: print(bool(S.reduce_over_period_lattice(vector).norm() 2**(self._prec-4): + raise ValueError("insufficient precision for b=%s" % b) + + def C2Z(v): + vR = [(S*z.real_part()).round() for z in v] + vR += [(S*z.imag_part()).round() for z in v] + return vR + + M = Matrix(ZZ, 2*self.genus, 2*self.genus, + [C2Z(c) for c in self.period_matrix().columns()]) + u = C2Z(vector) + L = IntegerLattice(M) + u = VR(u)-VR(L.closest_vector(u)) + reduced = VC([self._CC(u[i]+I*u[i+self.genus])/S for i in range(self.genus)]) + + elif method=="ip": + + def C2R(v): + return VR([z.real_part() for z in v]+[z.imag_part() for z in v]) + + u = C2R(vector) + basis_vecs = [C2R(c) for c in self.period_matrix().columns()] + M = Matrix([[ei.dot_product(ej) for ei in basis_vecs] for ej in basis_vecs]) + v_dot_e = VR([u.dot_product(e) for e in basis_vecs]) + coeffs = M.solve_right(v_dot_e) + u -= sum([t.round()*e for t, e in zip(coeffs, basis_vecs)]) + reduced = VC([self._CC(u[i]+I*u[i+self.genus]) for i in range(self.genus)]) + else: + raise ValueError("Must give a valid method.") + + return reduced + + def places_at_branch_locus(self): + r""" + Return the places above the branch locus. + + Returns a list of the of places above the branch locus. This must be + done over the base ring, and so the places are given in terms of the + factors of the discriminant. Currently, this method only works when + ``self._R.base_ring()==QQ`` as for other rings, the function field + for ``Curve(self.f)`` is not implemented. To go from these divisors to + a divisor list, see :meth:`divisor_to_divisor_list`. + + OUTPUT: + + List of places of the functions field ``Curve(self.f).function_field()``. + + EXAMPLES:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(25*(x^4+y^4+1) - 34*(x^2*y^2+x^2+y^2)) + sage: S.places_at_branch_locus() + [Place (x - 2, (x - 2)*y, y^2 - 17/5, y^3 - 17/5*y), + Place (x + 2, (x + 2)*y, y^2 - 17/5, y^3 - 17/5*y), + Place (x - 1/2, (x - 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), + Place (x + 1/2, (x + 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), + Place (x^4 - 34/25*x^2 + 1, y, y^2, y^3), + Place (x^4 - 34/25*x^2 + 1, (x^4 - 34/25*x^2 + 1)*y, y^2 - 34/25*x^2 - 34/25, y^3 + (-34/25*x^2 - 34/25)*y)] + """ + BP = [] + K = self._R.base_ring() + if not K==QQ: + raise NotImplementedError + C = Curve(self.f) + KC = C.function_field() + g0, g1 = self._R.gens() + Kb = FunctionField(K, str(g0)) + MO = Kb.maximal_order() + BP = [] + for x in self._discriminant.factor(): + fac = x[0](g0, 0) + p0 = MO.ideal(fac).place() + BP += KC.places_above(p0) + return BP + + def strong_approximation(self, divisor, S): + r""" + Apply the method of strong approximation to a divisor. + + As described in [Neu2018]_, apply the method of strong approximation to + ``divisor`` with list of places to avoid ``S``. Currently, this method + only works when ``self._R.base_ring()==QQ`` as for other rings, the function + field for ``Curve(self.f)`` is not implemented. + + INPUT: + + - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` + + - ``S`` -- list. A list of places to avoid. + + OUTPUT: + + A tuple ``(S, B)``, where ``D`` is a new divisor, linearly equivalent + to ``divisor``, but not intersecting ``S``, and ``B`` is a list of tuples + ``(v, b)`` where ``b`` are the functions giving the linear equivalence, + added with multiplicity ``v``. + + EXAMPLES:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(y^2-x^3+1) + sage: avoid = Curve(S.f).places_at_infinity() + sage: D = 1*avoid[0] + sage: S.strong_approximation(D, avoid) + (- Place (x - 2, (x - 2)*y) + + Place (x - 1, y) + + Place (x^2 + x + 1, y), + [(1, (1/(x - 2))*y)]) + """ + # One would standardly expect to run this with + # S = Curve(self.f).places_at_infinity() + # or + # S = Curve(self.f).places_at_infinity()+self.places_at_branch_locus() + # + # To avoid current implementation issues with going between divisors + # and divisor lists, we implement a method that handles only divisors + K = self._R.base_ring() + if not K==QQ: + raise NotImplementedError + C = Curve(self.f) + KC = C.function_field() + g0, g1 = self._R.gens() + Kb = FunctionField(K, str(g0)) + Pz = PolynomialRing(K, g0) + Pw = PolynomialRing(K, g1) + MO = Kb.maximal_order() + + D_base = -sum(S) + + rr = self._vertices[self._basepoint[0]].real() + rr = rr.ceil() + Fac = g0-K(rr) + p0 = MO.ideal(Fac).place() + q0 = KC.places_above(p0)[0] + + new_divisor = divisor + B = [] + for p in divisor.support(): + if p in S: + v = divisor.valuation(p) + i = S.index(p) + Q = S[i] + D = D_base+Q + if D==0: + ios = self.genus + else: + ios = len(D.basis_differential_space()) + while ios>0: + D += q0 + ios = len(D.basis_differential_space()) + LD = D.function_space() + V = LD[0] + a = LD[1] + b = 0 + for s in S: + LDps = (D+s).function_space() + Vps = LDps[0] + ebd = [LDps[2](a(g)) for g in V.gens()] + U = Vps.span(ebd) + Quot = Vps.quotient(U) + bs = LDps[1](Quot.lift(Quot.basis()[0])) + b += bs + B.append((v,b)) + new_divisor += v*b.divisor() + return new_divisor, B + + def divisor_to_divisor_list(self, divisor): + r""" + Turn a divisor into a list for :meth:`abel_jacobi`. + + Given ``divisor`` in ``Curve(self.f).function_field().divisor_group()``, + consisting of places above finite points in the base, return an equivalent + divisor list suitable for input into :meth:`abel_jacboi`. + + INPUT: + + - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` + + OUTPUT: + + A list with elements of the form ``(v, (z, w))`` representing the finite places. + + EXAMPLES:: + + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: R. = QQ[] + sage: S = RiemannSurface(y^2-x^3+1) + sage: D = sum(S.places_at_branch_locus()) + sage: S.divisor_to_divisor_list(D) + [(1, (-1.00000000000000, 0.000000000000000)), + (1, (0.500000000000000 - 0.866025403784439*I, 0.000000000000000)), + (1, (0.500000000000000 + 0.866025403784439*I, 0.000000000000000))] + + .. TODO:: + + Currently this method can only handle places above finite points in + the base. It would be useful to extend this to allow for places at + infinity. + """ + eps = self._RR(2)**(-self._prec+3) + dl = [] + for d in divisor.support(): + v = divisor.valuation(d) + gs = d._prime.gens() + gs = [self._R(gi) for gi in gs] + g0 = gs[0] + gis = gs[1:] + + rs = S._CCz(g0).roots() + rys = [] + + for r, m in rs: + ys = [] + for gi in gis: + ers = [gi(r,y).abs() for y in ys] + try: + ers = min(ers) + except: + ers = 1 + if not ers Date: Thu, 12 Aug 2021 10:20:00 +0100 Subject: [PATCH 009/454] Bug fixes and documentation changes. A collection of small changes from errors highlighted by doctests. --- .../riemann_surfaces/riemann_surface.py | 74 +++++++++++-------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 744e0163b13..951fd10276c 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -30,7 +30,7 @@ sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] sage: f = x^4-x^3*y+2*x^3+2*x^2*y+2*x^2-2*x*y^2+4*x*y-y^3+3*y^2+2*y+1 - sage: S = RiemannSurface(f,prec=100) + sage: S = RiemannSurface(f, prec=100) sage: M = S.riemann_matrix() We test the usual properties, i.e., that the period matrix is symmetric and that @@ -70,8 +70,10 @@ # **************************************************************************** from scipy.spatial import Voronoi +from sage.arith.functions import lcm from sage.arith.misc import GCD, algdep from sage.ext.fast_callable import fast_callable +from sage.functions.log import lambert_w from sage.graphs.graph import Graph from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroup @@ -85,11 +87,14 @@ from sage.modules.free_module_integer import IntegerLattice from sage.numerical.gauss_legendre import integrate_vector, integrate_vector_N from sage.rings.complex_mpfr import ComplexField, CDF +from sage.rings.function_field.constructor import FunctionField +from sage.rings.infinity import Infinity from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.qqbar import number_field_elements_from_algebraics from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField +from sage.schemes.curves.constructor import Curve import sage.libs.mpmath.all as mpall def voronoi_ghost(cpoints, n=6, CC=CDF): @@ -339,9 +344,11 @@ def find_closest_element(item, List): EXAMPLES:: + sage: from sage.schemes.riemann_surfaces.riemann_surface import find_closest_element sage: i = 5 sage: l = list(range(10)) sage: i == find_closest_element(i, l) + True Note that this method does no checks on the input, but will fail for inputs where the absolute value or subtraction do not make sense. @@ -381,18 +388,18 @@ def reparameterise_differential_minpoly(minpoly, z0): Hence the transformed differential should have minimal polynomial `\bar{g}^2\bar{z}(1-\bar{z}^3)-1/4=0`, and we can check this:: - sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface, reparameterise_differential_minpoly sage: R. = QQ[] sage: S = RiemannSurface(w^2-z^3+1) sage: minpoly = S._cohomology_basis_bounding_data[1][0][2] sage: z0 = Infinity - sage: S.reparameterise_differential_minpoly(minpoly, z0) + sage: reparameterise_differential_minpoly(minpoly, z0) -zbar^4*gbar^2 + zbar*gbar^2 - 1/4 We can further check that reparameterising about `0` is the identity operation:: - sage: S.reparameterise_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly + sage: reparameterise_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly True .. NOTE:: @@ -869,7 +876,8 @@ def homotopy_continuation(self, edge): sage: S = RiemannSurface(f) sage: edge1 = sorted(S.edge_permutations())[0] sage: sigma = S.edge_permutations()[edge1] - sage: continued_values = S.homotopy_continuation(edge1) + sage: edge = [S._vertices[i] for i in edge1] + sage: continued_values = S.homotopy_continuation(edge)[-1][1] sage: stored_values = S.w_values(S._vertices[edge1[1]]) sage: all(abs(continued_values[i]-stored_values[sigma(i)]) < 1e-8 for i in range(3)) True @@ -947,7 +955,7 @@ def _determine_new_w(self, z0, oldw, epsilon): sage: z0 = S._vertices[0] sage: epsilon = 0.1 sage: oldw = S.w_values(z0) - sage: neww = S._determine_new_w(z0,oldw,epsilon); neww #abs tol 0.00000001 + sage: neww = S._determine_new_w(z0, oldw, epsilon); neww #abs tol 0.00000001 [-0.934613146929672 + 2.01088055918363*I, 0.934613146929672 - 2.01088055918363*I] @@ -964,13 +972,13 @@ def _determine_new_w(self, z0, oldw, epsilon): sage: g = z^3*w + w^3 + z sage: T = RiemannSurface(g) - sage: z0 = T._vertices[2]*(0.9) - T._vertices[15]*(0.1) + sage: z0 = T._vertices[2]*(0.9) - T._vertices[5]*(0.1) sage: epsilon = 0.5 sage: oldw = T.w_values(T._vertices[2]) - sage: T._determine_new_w(z0,oldw,epsilon) - [-0.562337685361648 + 0.151166007149998*I, - 0.640201585779414 - 1.48567225836436*I, - -0.0778639004177661 + 1.33450625121437*I] + sage: T._determine_new_w(z0, oldw, epsilon) + Traceback (most recent call last): + ... + ConvergenceError: Newton iteration escaped neighbourhood .. NOTE:: @@ -1056,11 +1064,13 @@ def _newton_iteration(self, z0, oldw, epsilon): sage: g = z^3*w + w^3 + z sage: T = RiemannSurface(g) - sage: z0 = T._vertices[2]*(0.9) - T._vertices[15]*(0.1) + sage: z0 = T._vertices[2]*(0.9) - T._vertices[5]*(0.1) sage: epsilon = 0.5 - sage: oldw = T.w_values(T._vertices[2])[0] + sage: oldw = T.w_values(T._vertices[2])[1] sage: T._newton_iteration(z0, oldw, epsilon) - -0.562337685361648 + 0.151166007149998*I + Traceback (most recent call last): + ... + ConvergenceError: Newton iteration escaped neighbourhood """ F = self._fastcall_f dF = self._fastcall_dfdw @@ -1153,12 +1163,12 @@ def _edge_permutation(self, edge): sage: f = z^3*w + w^3 + z sage: S = RiemannSurface(f) - Compute the edge permutation of (1,2) on the Voronoi diagram:: + Compute the edge permutation of (2, 9) on the Voronoi diagram:: - sage: S._edge_permutation((1,2)) - (0,2,1) + sage: S._edge_permutation((2, 9)) + (1,2) - This indicates that while traversing along the direction of `(5,16)`, + This indicates that while traversing along the direction of `(2, 9)`, the 2nd and 3rd layers of the Riemann surface are interchanging. """ if edge in self.downstairs_edges(): @@ -1201,17 +1211,17 @@ def edge_permutations(self) -> dict: (1, 2): (), (1, 3): (0,1), (1, 6): (), - (2, 5): (0,1), - (3, 4): (), - (5, 7): (), - (6, 7): (), (2, 0): (), - (4, 0): (), (2, 1): (), + (2, 5): (0,1), (3, 1): (0,1), - (6, 1): (), - (5, 2): (0,1), + (3, 4): (), + (4, 0): (), (4, 3): (), + (5, 2): (0,1), + (5, 7): (), + (6, 1): (), + (6, 7): (), (7, 5): (), (7, 6): ()} """ @@ -1749,7 +1759,7 @@ def _bounding_data(self, differentials, exact=False): sage: S._cohomology_basis_bounding_data (Multivariate Polynomial Ring in z, g over Rational Field, - [(1/(2*y), + [(1/(2*w), (-3*z^2*g)/(2*z^3 - 2), z^3*g^2 - g^2 - 1/4, (1.00000000000000, @@ -1841,8 +1851,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): sage: _ = S.homology_basis() sage: differentials = S.cohomology_basis() sage: bounding_data = S._bounding_data(differentials) - sage: S.rigorous_line_integral([(0,0), (1,0)], differentials, - bounding_data) + sage: S.rigorous_line_integral([(0,0), (1,0)], differentials, bounding_data) # abs tol 1e-10 (1.80277751848459e-16 - 0.352971844594760*I) .. NOTE:: @@ -2211,7 +2220,8 @@ def path(t): P += line3d([path(t[0])+(t[1][i].imag_part(),) for t in T],color=color,thickness=thickness) for z,ws in zip(self._vertices,self._wvalues): for w in ws: - P += point3d([z.real_part(),z.imag_part(),w.imag_part()],color="purple", size=20) + P += point3d([z.real_part(), z.imag_part(), w.imag_part()], + color="purple", size=20) return P def endomorphism_basis(self, b=None, r=None): @@ -2889,7 +2899,8 @@ def _aj_based(self, P): As the output of ``_aj_based`` is difficult to intepret due to its path dependency, we look at the output of :meth:`abel_jacobi`. We check for two hyperelliptic curves that the Abel-Jacobi map between two branch - points is a 2-torsion point over the lattice:: + points is a 2-torsion point over the lattice. Note we must remember to + reduce over the period lattice, as results are path dependent:: sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] @@ -2897,6 +2908,8 @@ def _aj_based(self, P): sage: divisor = [(-1, (Infinity, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) sage: AJx2 = [2*z for z in AJ] + sage: vector(AJx2).norm() # abs tol 1e-10 + 2.4286506478875809114000865640 sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) True sage: S = RiemannSurface(y^2-x^4+1) @@ -3128,6 +3141,7 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None): VR = VectorSpace(self._RR, 2*self.genus) VC = VectorSpace(self._CC, self.genus) + I = self._CC(0,-1) if method=="svp": H = max(max(z.real_part().abs() for z in vector), From 81164656a2e46b19d23f5db672d819cd3971f77a Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 12 Aug 2021 13:59:01 +0100 Subject: [PATCH 010/454] Document fixes. --- .../schemes/riemann_surfaces/riemann_surface.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 951fd10276c..2f20b79341e 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -739,6 +739,7 @@ def downstairs_graph(self): G.set_pos(dict(enumerate(list(v) for v in self._vertices))) return G + @cached_method def upstairs_graph(self): r""" Return the graph of the upstairs edges. @@ -1379,8 +1380,7 @@ def homology_basis(self): if self.genus == 0: return [] - edgesu = self.upstairs_edges() - cycles = Graph(edgesu).cycle_basis() + cycles = self.upstairs_graph().cycle_basis() # Computing the Gram matrix. cn = len(cycles) # Forming a list of lists of zeroes. @@ -2904,7 +2904,8 @@ def _aj_based(self, P): sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] - sage: S = RiemannSurface(y^2-x^3+1) + sage: p = 100 + sage: S = RiemannSurface(y^2-x^3+1, prec=p) sage: divisor = [(-1, (Infinity, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) sage: AJx2 = [2*z for z in AJ] @@ -2912,7 +2913,7 @@ def _aj_based(self, P): 2.4286506478875809114000865640 sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) True - sage: S = RiemannSurface(y^2-x^4+1) + sage: S = RiemannSurface(y^2-x^4+1, prec=p) sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) sage: AJx2 = [2*z for z in AJ] @@ -3342,9 +3343,9 @@ def divisor_to_divisor_list(self, divisor): sage: S = RiemannSurface(y^2-x^3+1) sage: D = sum(S.places_at_branch_locus()) sage: S.divisor_to_divisor_list(D) - [(1, (-1.00000000000000, 0.000000000000000)), - (1, (0.500000000000000 - 0.866025403784439*I, 0.000000000000000)), - (1, (0.500000000000000 + 0.866025403784439*I, 0.000000000000000))] + [(1, (1.00000000000000, 0.000000000000000)), + (1, (-0.500000000000000 - 0.866025403784439*I, 0.000000000000000)), + (1, (-0.500000000000000 + 0.866025403784439*I, 0.000000000000000))] .. TODO:: @@ -3361,7 +3362,7 @@ def divisor_to_divisor_list(self, divisor): g0 = gs[0] gis = gs[1:] - rs = S._CCz(g0).roots() + rs = self._CCz(g0).roots() rys = [] for r, m in rs: From 6f7f5da36dc34ecfd53389f19025a1520c6ffac3 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 12 Aug 2021 14:30:15 +0100 Subject: [PATCH 011/454] Documentation fixes --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 2f20b79341e..e02ecfedbff 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3042,7 +3042,7 @@ def abel_jacobi(self, divisor, verbose=False): where ``v`` is the valuation of the divisor at point ``P``, ``P`` as per the input to :meth:`_aj_based`. - - `` verbose`` -- logical (default: False). Whether to report the progress + - ``verbose`` -- logical (default: False). Whether to report the progress of the computation, in terms of how many elements of the list ``divisor`` have been completed. From d437a21416845f98ac8138084a3b79c0a74e9344 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Fri, 13 Aug 2021 10:34:01 +0100 Subject: [PATCH 012/454] Documentation and bug fixes. --- .../riemann_surfaces/riemann_surface.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index e02ecfedbff..3b08f78a954 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -55,6 +55,45 @@ sage: all(len(a.minpoly().roots(K)) == a.minpoly().degree() for a in A) True +We can look at an extended example of the Abel-Jacobi functionality. We will +show that the sum of the intersections of a bitangent to a quadratic is a +half-canonical divisor. We will use the Edge quartic as the example, which has +bitangent `x=1/2`. + + sage: f = 25*(x^4+y^4+1) - 34*(x^2*y^2+x^2+y^2) + sage: S = RiemannSurface(f) + sage: BL = S.places_at_branch_locus(); BL + [Place (x - 2, (x - 2)*y, y^2 - 17/5, y^3 - 17/5*y), + Place (x + 2, (x + 2)*y, y^2 - 17/5, y^3 - 17/5*y), + Place (x - 1/2, (x - 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), + Place (x + 1/2, (x + 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), + Place (x^4 - 34/25*x^2 + 1, y, y^2, y^3), + Place (x^4 - 34/25*x^2 + 1, (x^4 - 34/25*x^2 + 1)*y, y^2 - 34/25*x^2 - 34/25, y^3 + (-34/25*x^2 - 34/25)*y)] + +We can read off out the output of ``places_at_branch_locus`` to choose our +divisor, and we can calculate the canonical divisor using curve functionality:: + + sage: D = 1*BL[2] + sage: from sage.schemes.curves.constructor import Curve + sage: C = Curve(f) + sage: F = C.function_field() + sage: K = (F(x).differential()).divisor() + +Note we could check using exact techniques that `2D=K`:: + + sage: Z = K-2*D + sage: (Z.degree()==0, len(Z.basis_differential_space())==S.genus, len(Z.basis_function_space())==1) + (True, True, True) + +We can also check this using our Abel-Jacobi functions:: + + sage: avoid = C.places_at_infinity() + sage: Zeq, _ = S.strong_approximation(Z, avoid) + sage: Zlist = S.divisor_to_divisor_list(Zeq) + sage: AJ = S.abel_jacobi(Zlist) # long time (50 seconds) + sage: S.reduce_over_period_lattice(AJ).norm()<1e-10 # long time + True + REFERENCES: The initial version of this code was developed alongside [BSZ2019]_. @@ -3353,6 +3392,8 @@ def divisor_to_divisor_list(self, divisor): the base. It would be useful to extend this to allow for places at infinity. """ + # If this error bound is too restrictive, this method might fail and + # not return. One might want to change the way this error is handled. eps = self._RR(2)**(-self._prec+3) dl = [] for d in divisor.support(): @@ -3373,7 +3414,7 @@ def divisor_to_divisor_list(self, divisor): ers = min(ers) except: ers = 1 - if not ers Date: Fri, 13 Aug 2021 17:09:32 +0100 Subject: [PATCH 013/454] Documentation changes and slight refactoring Fast-callables of the cohomology basis have been stored to the main object to reduce the time to compute the Abel-Jacobi map (only slightly), and fixes for the documentation. --- .../riemann_surfaces/riemann_surface.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0b1903d2406..a108ac3871a 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -17,6 +17,9 @@ period lattice numerically, by determining integer (near) solutions to the relevant approximate linear equations. +One can also calculate the Abel-Jacobi map on the Riemann surface, and there +is basic functionality to interface with divisors of curves to facilitate this. + AUTHORS: - Alexandre Zotine, Nils Bruin (2017-06-10): initial version @@ -58,7 +61,7 @@ We can look at an extended example of the Abel-Jacobi functionality. We will show that the sum of the intersections of a bitangent to a quadratic is a half-canonical divisor. We will use the Edge quartic as the example, which has -bitangent `x=1/2`. +bitangent `x=1/2`:: sage: f = 25*(x^4+y^4+1) - 34*(x^2*y^2+x^2+y^2) sage: S = RiemannSurface(f) @@ -705,6 +708,8 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati self._fastcall_f = fast_callable(f, domain=self._CC) self._fastcall_dfdw = fast_callable(self._dfdw, domain=self._CC) self._fastcall_dfdz = fast_callable(self._dfdz, domain=self._CC) + self._fastcall_cohomology_basis = [fast_callable(h, domain = self._CC) + for h in self.cohomology_basis()] def __repr__(self): r""" @@ -1866,7 +1871,7 @@ def _bounding_data(self, differentials, exact=False): sage: S._cohomology_basis_bounding_data (Multivariate Polynomial Ring in z, g over Rational Field, - [(1/(2*w), + [(1/(2*y), (-3*z^2*g)/(2*z^3 - 2), z^3*g^2 - g^2 - 1/4, (1.00000000000000, @@ -2165,7 +2170,12 @@ def normalize_pairs(L): occurring_edges.update(*[normalize_pairs(p[1]) for h in cycles for p in h]) - fcd = [fast_callable(omega, domain=self._CC) for omega in differentials] + if differentials is self.cohomology_basis(): + fcd = self._fastcall_cohomology_basis + integral_dict = self._integral_dict + else: + fcd = [fast_callable(omega, domain=self._CC) for omega in differentials] + integral_dict = dict() if integration_method=="heuristic": line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) @@ -2175,14 +2185,9 @@ def normalize_pairs(L): else: raise ValueError("Invalid integration method") - if differentials is self.cohomology_basis(): - integral_dict = self._integral_dict - for upstairs_edge in occurring_edges: - if not upstairs_edge in integral_dict.keys(): - integral_dict[upstairs_edge] = line_int(upstairs_edge) - else: - integral_dict = {upstairs_edge: lint_int(upstairs_edge) - for upstairs_edge in occurring_edges} + for upstairs_edge in occurring_edges: + if not upstairs_edge in integral_dict.keys(): + integral_dict[upstairs_edge] = line_int(upstairs_edge) rows = [] for cycle in cycles: @@ -3054,7 +3059,7 @@ def _aj_based(self, P): """ ##### - fcd = [fast_callable(h, domain = self._CC) for h in self.cohomology_basis()] + fcd = self._fastcall_cohomology_basis if self._integration_method=="heuristic": line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) From 822a47b117cad0f5117e38e8c9f6f37f3871e50c Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 16 Aug 2021 10:35:05 +0100 Subject: [PATCH 014/454] Methodology changes based on comment 11 --- .../riemann_surfaces/riemann_surface.py | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index a108ac3871a..0005252a56e 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -678,7 +678,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati self._differentials_branch_locus += self._CCz(x[0](self._CCz.gen(), 0)).roots(multiplicities=False) # We add these branchpoints to the existing. - self.branch_locus = self.branch_locus+self._differentials_branch_locus + #self.branch_locus = self.branch_locus+self._differentials_branch_locus # We now want to also check whether Infinity is a branch point of any # of the differentials. # This will be useful when calculating the Abel-Jacobi map. @@ -3097,8 +3097,12 @@ def _aj_based(self, P): V_index = find_closest_element(zP, self._vertices) b_index = find_closest_element(zP, self.branch_locus) b = self.branch_locus[b_index] - - d1 = self._CC(1e-2)*max(b.abs() for b in self.branch_locus) + #bl = self.branch_locus+self._differentials_branch_locus + #b_index = find_closest_element(zP, bl) + #b = bl[b_index] + + scale = max(b.abs() for b in self.branch_locus) + d1 = self._CC(1e-2)*scale # We choose the first vertex we want to go to. # If the closest vertex is closer than the nearest branch point, just take that vertex @@ -3108,17 +3112,17 @@ def _aj_based(self, P): region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] suitable_vertex_indices = [region[i] - for i in range(len(region)) if args[i].abs()>self._RR.pi()/2] + for i in range(len(region)) if args[i].abs()-self._RR.pi()/2>=-self._RR(1e-15)] suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] if suitable_vertices==[]: raise ValueError("There is no satisfactory choice of V for zP={}".format(zP)) V_index = suitable_vertex_indices[find_closest_element(zP, suitable_vertices)] ##### zV = self._vertices[V_index] - d_edge = (zP, zV) if (zP-b).abs() >= d1 or b in self._differentials_branch_locus: wP_index = find_closest_element(wP, self.w_values(zP)) + d_edge = (zP, zV) u_edge = ((zP, wP_index), (zV, )) initial_continuation = self.homotopy_continuation(d_edge) AJ = -line_int(u_edge) @@ -3129,6 +3133,23 @@ def _aj_based(self, P): zs = zP ws = wP + ##### + # Here we need a block of code to change the vertex if the path + # from zP to zV would go through a ramification point of the integrands + fl = [c for c in self._differentials_branch_locus if not c==self._CC(Infinity)] + ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 + for c in fl] + ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() + for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] + while (len(ds)>=1 and min(ds)=0 and ts[i]<=1)] + ##### + while self._dfdw(zs, ws).abs()==0: zs = zs+delta*(zV-zs)/(zV-zs).abs()/2 ws_list = self.w_values(zs) From bc92b3c7212f534f1ccceccae7e5565ddbe93aac Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 16 Aug 2021 11:16:38 +0100 Subject: [PATCH 015/454] Documentation changes Examples needed to be fixed given a change in the nature of the code in the last commit. --- .../riemann_surfaces/riemann_surface.py | 81 +++++++++++-------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0005252a56e..8a03dd9e440 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -561,24 +561,44 @@ class RiemannSurface(object): sage: Sr = RiemannSurface(f, prec=p, integration_method='rigorous') sage: from sage.numerical.gauss_legendre import nodes sage: nodes.cache.clear() - sage: %time Rh = Sh.riemann_matrix() # random | long time (2 seconds) - CPU times: user 2.38 s, sys: 36 µs, total: 2.38 s - Wall time: 2.38 s + sage: %time Rh = Sh.riemann_matrix() # random | long time (1 second) + CPU times: user 795 ms, sys: 0 ns, total: 795 ms + Wall time: 799 ms sage: nodes.cache.clear() - sage: %time Rr = Sr.riemann_matrix() # random | long time (3 seconds) - CPU times: user 2.67 s, sys: 66 µs, total: 2.67 s - Wall time: 2.67 s - sage: p = 200 + sage: %time Rr = Sr.riemann_matrix() # random | long time (2 seconds) + CPU times: user 1.75 s, sys: 0 ns, total: 1.75 s + Wall time: 1.75 s + sage: p = 500 sage: Sh = RiemannSurface(f, prec=p, integration_method='heuristic') sage: Sr = RiemannSurface(f, prec=p, integration_method='rigorous') sage: nodes.cache.clear() - sage: %time Rh = Sh.riemann_matrix() # random | long time (7 seconds) - CPU times: user 7.12 s, sys: 4.01 ms, total: 7.13 s - Wall time: 7.13 s + sage: %time Rh = Sh.riemann_matrix() # random | long time (8 seconds) + CPU times: user 8.43 s, sys: 0 ns, total: 8.43 s + Wall time: 8.43 ss sage: nodes.cache.clear() - sage: %time Rr = Sr.riemann_matrix() # random | long time (5 seconds) - CPU times: user 4.91 s, sys: 9 µs, total: 4.91 s - Wall time: 4.91 s + sage: %time Rr = Sr.riemann_matrix() # random | long time (10 seconds) + CPU times: user 9.69 s, sys: 0 ns, total: 9.69 s + Wall time: 9.69 s + + Note that for the above curve, the branch points are evenly distributed, and + hence the implicit assumptions in the heuristic method are more sensible, + meaning that a higher precision is required to see the heuristic method + being significantly slower than the rigorous method. For a worse conditioned + curve, this effect is more pronounced:: + + sage: q = 1/10 + sage: f = y^2-(x^2-2*x+1+q^2)*(x^2+2*x+1+q^2) + sage: p = 500 + sage: Sh = RiemannSurface(f, prec=p, integration_method='heuristic') + sage: Sr = RiemannSurface(f, prec=p, integration_method='rigorous') + sage: nodes.cache.clear() + sage: %time Rh = Sh.riemann_matrix() # random | long time (7 second) + CPU times: user 7.45 s, sys: 0 ns, total: 7.45 s + Wall time: 7.44 s + sage: nodes.cache.clear() + sage: %time Rr = Sr.riemann_matrix() # random | long time (2 second) + CPU times: user 1.5 s, sys: 0 ns, total: 1.5 s + Wall time: 1.5 s This disparity in timings can get increasingly worse, and testing has shown that even for random quadrics the heuristic method can be as bad as 30 times @@ -1068,7 +1088,7 @@ def _determine_new_w(self, z0, oldw, epsilon): sage: g = z^3*w + w^3 + z sage: T = RiemannSurface(g) - sage: z0 = T._vertices[2]*(0.9) - T._vertices[5]*(0.1) + sage: z0 = T._vertices[2]*(0.9) + 0.3*I sage: epsilon = 0.5 sage: oldw = T.w_values(T._vertices[2]) sage: T._determine_new_w(z0, oldw, epsilon) @@ -1160,7 +1180,7 @@ def _newton_iteration(self, z0, oldw, epsilon): sage: g = z^3*w + w^3 + z sage: T = RiemannSurface(g) - sage: z0 = T._vertices[2]*(0.9) - T._vertices[5]*(0.1) + sage: z0 = T._vertices[2]*(0.9) + 0.3*I sage: epsilon = 0.5 sage: oldw = T.w_values(T._vertices[2])[1] sage: T._newton_iteration(z0, oldw, epsilon) @@ -1259,10 +1279,10 @@ def _edge_permutation(self, edge): sage: f = z^3*w + w^3 + z sage: S = RiemannSurface(f) - Compute the edge permutation of (2, 9) on the Voronoi diagram:: + Compute the edge permutation of (1, 2) on the Voronoi diagram:: - sage: S._edge_permutation((2, 9)) - (1,2) + sage: S._edge_permutation((1, 2)) + (0,2,1) This indicates that while traversing along the direction of `(2, 9)`, the 2nd and 3rd layers of the Riemann surface are interchanging. @@ -1350,28 +1370,21 @@ def monodromy_group(self): sage: f = z^3*w + w^3 + z sage: S = RiemannSurface(f) sage: G = S.monodromy_group(); G - [(0,2,1), (0,1), (1,2), (0,2), (0,2), (1,2), (0,2), (0,1), (), (), (), (), (), (), (), (1,2)] + [(0,1,2), (0,1), (0,2), (1,2), (1,2), (1,2), (0,1), (0,2), (0,2)] The permutations give the local monodromy generators for the branch points:: sage: list(zip(S.branch_locus + [unsigned_infinity], G)) #abs tol 0.0000001 - [(0.000000000000000, (0,2,1)), + [(0.000000000000000, (0,1,2)), (-1.31362670141929, (0,1)), - (-0.819032851784253 - 1.02703471138023*I, (1,2)), - (-0.819032851784253 + 1.02703471138023*I, (0,2)), - (0.292309440469772 - 1.28069133740100*I, (0,2)), + (-0.819032851784253 - 1.02703471138023*I, (0,2)), + (-0.819032851784253 + 1.02703471138023*I, (1,2)), + (0.292309440469772 - 1.28069133740100*I, (1,2)), (0.292309440469772 + 1.28069133740100*I, (1,2)), - (1.18353676202412 - 0.569961265016465*I, (0,2)), - (1.18353676202412 + 0.569961265016465*I, (0,1)), - (-1.45036146591896, ()), - (-0.904285583009352 - 1.13393825501392*I, ()), - (-0.904285583009352 + 1.13393825501392*I, ()), - (0.322735787970535 - 1.41399787587734*I, ()), - (0.322735787970535 + 1.41399787587734*I, ()), - (1.30673052799829 - 0.629288255904939*I, ()), - (1.30673052799829 + 0.629288255904939*I, ()), - (Infinity, (1,2))] + (1.18353676202412 - 0.569961265016465*I, (0,1)), + (1.18353676202412 + 0.569961265016465*I, (0,2)), + (Infinity, (0,2))] We can check the ramification by looking at the cycle lengths and verify it agrees with the Riemann-Hurwitz formula:: @@ -1715,7 +1728,7 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): sage: M = S.riemann_matrix() sage: differentials = S.cohomology_basis() - sage: S.simple_vector_line_integral([(0,0),(1,0)], differentials) #abs tol 0.00000001 + sage: S.simple_vector_line_integral([(0, 0), (1, 0)], differentials) #abs tol 0.00000001 (1.14590610929717e-16 - 0.352971844594760*I) .. NOTE:: From 1ef9823e8fdc8c386f77c46bd66a5a2019044581 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sat, 21 Aug 2021 12:32:18 +0100 Subject: [PATCH 016/454] Typo fixes. Fixing typos identified by a run of tox. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 9e34a7af9eb..c2730db86f9 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -449,7 +449,7 @@ def reparameterise_differential_minpoly(minpoly, z0): As part of the routine, when reparameterising about infinity, a rational function is reduced and then the numerator is taken. Over an inexact ring this is numerically unstable, and so it is advisable - to only reparameterise about infinty over an exact ring. + to only reparameterise about infinity over an exact ring. """ P = minpoly.parent() F = PolynomialRing(P.base_ring(), [str(v)+"bar" for v in P.gens()]) @@ -1853,7 +1853,7 @@ def _bounding_data(self, differentials, exact=False): the list corresponds to an element of ``differentials``. Introducing the notation ``RBzg = PolynomialRing(self._R, ['z','g'])`` and ``CCzg = PolynomialRing(self._CC, ['z','g'])``, we have that: - - ``Rzg`` is either ``RBzg`` or ``CCzg`` depending on the vlaue of + - ``Rzg`` is either ``RBzg`` or ``CCzg`` depending on the value of ``exact``, - ``g`` is the full rational function in ``self._R.fraction_field()`` giving the differential, @@ -3044,7 +3044,7 @@ def _aj_based(self, P): EXAMPLES: - As the output of ``_aj_based`` is difficult to intepret due to its path + As the output of ``_aj_based`` is difficult to interpret due to its path dependency, we look at the output of :meth:`abel_jacobi`. We check for two hyperelliptic curves that the Abel-Jacobi map between two branch points is a 2-torsion point over the lattice. Note we must remember to @@ -3100,7 +3100,7 @@ def _aj_based(self, P): g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] W_index = find_closest_element(g0e, g0s) if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision requried.") + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") V_index = B[0] else: zP = self._CC(zP) @@ -3176,7 +3176,7 @@ def _aj_based(self, P): g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] W_index = find_closest_element(g0e, g0s) if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision requried.") + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") uV_index = (V_index, W_index) ##### From fb194e3d18ab30e08f1f56180eb18ad1e525d1c7 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sat, 21 Aug 2021 13:05:18 +0100 Subject: [PATCH 017/454] Minor method change. A small change was made in the rigorous_line_integral to avoid some unnecessary subtractions. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index c2730db86f9..e621dad61cf 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -2083,11 +2083,11 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): # Distance to the discriminant points distances = [(cz-b).abs() for b in self.branch_locus] rho_z = min(distances) - rho_t = rho_z/(z1-z0).abs() + rho_t = rho_z/(z1_minus_z0).abs() if rho_t > rt: rho_t = alpha*rho_t+(1-alpha)*rt # sqrt(rho_t*rt) could also work rho_z = rho_t*(z1-z0).abs() - delta_z = (alpha*rho_t+(1-alpha)*rt)*(z1-z0).abs() + delta_z = (alpha*rho_t+(1-alpha)*rt)*(z1_minus_z0).abs() expr = rho_t/rt+((rho_t/rt)**2-1).sqrt() # Note this is really exp(arcosh(rho_t/rt)) N = 3 cw = zwt(ct)[1] From 068afafbbd26c4e134e810e05c279ceb6900dbc0 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sat, 21 Aug 2021 15:09:27 +0100 Subject: [PATCH 018/454] Minor formatting edit. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index e621dad61cf..ec8320fd9d0 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -2932,7 +2932,7 @@ def fv(hj, previous_estimate_and_validity): for j in range(100): new_delta = F(z0, newg) / dF(z0, newg) Nnew_delta = new_delta.norm() - if (new_delta == 0) or (Nnew_delta>=Ndelta and + if (new_delta == 0) or (Nnew_delta >= Ndelta and (Ndelta.sign_mantissa_exponent()[2] +self._prec) < newg.norm().sign_mantissa_exponent()[2]): outg.append(newg) @@ -2966,7 +2966,7 @@ def fv(hj, previous_estimate): for j in range(100): new_delta = F(z0, newg) / dF(z0, newg) Nnew_delta = new_delta.norm() - if (new_delta == 0) or (Nnew_delta>=Ndelta and + if (new_delta == 0) or (Nnew_delta >= Ndelta and (Ndelta.sign_mantissa_exponent()[2] +self._prec) < newg.norm().sign_mantissa_exponent()[2]): outg.append(newg) From b3c83bdb1c431c91e143af0b64417c2a45c7e139 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sun, 22 Aug 2021 11:31:10 +0100 Subject: [PATCH 019/454] Minor formatting changes --- .../riemann_surfaces/riemann_surface.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index ec8320fd9d0..706f1ff6322 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -93,8 +93,8 @@ sage: avoid = C.places_at_infinity() sage: Zeq, _ = S.strong_approximation(Z, avoid) sage: Zlist = S.divisor_to_divisor_list(Zeq) - sage: AJ = S.abel_jacobi(Zlist) # long time (50 seconds) - sage: S.reduce_over_period_lattice(AJ).norm()<1e-10 # long time + sage: AJ = S.abel_jacobi(Zlist) # long time (50 seconds) + sage: S.reduce_over_period_lattice(AJ).norm() < 1e-10 # long time True REFERENCES: @@ -781,7 +781,7 @@ def w_values(self, z0): Note that typically the method returns a list of length ``self.degree``, but that at ramification points, this may no longer be true:: - sage: S.w_values(1) # abs tol 1e-14 + sage: S.w_values(1) # abs tol 1e-14 [0.000000000000000] """ return self.f(z0,self._CCw.gen(0)).roots(multiplicities=False) @@ -1986,7 +1986,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): sage: _ = S.homology_basis() sage: differentials = S.cohomology_basis() sage: bounding_data = S._bounding_data(differentials) - sage: S.rigorous_line_integral([(0,0), (1,0)], differentials, bounding_data) # abs tol 1e-10 + sage: S.rigorous_line_integral([(0,0), (1,0)], differentials, bounding_data) # abs tol 1e-10 (1.80277751848459e-16 - 0.352971844594760*I) .. NOTE:: @@ -3057,15 +3057,15 @@ def _aj_based(self, P): sage: divisor = [(-1, (Infinity, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) sage: AJx2 = [2*z for z in AJ] - sage: vector(AJx2).norm() # abs tol 1e-10 + sage: vector(AJx2).norm() # abs tol 1e-10 2.4286506478875809114000865640 - sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) + sage: bool(S.reduce_over_period_lattice(AJx2).norm() < 1e-10) True sage: S = RiemannSurface(y^2-x^4+1, prec=p) sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) sage: AJx2 = [2*z for z in AJ] - sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-10) + sage: bool(S.reduce_over_period_lattice(AJx2).norm() < 1e-10) True """ @@ -3229,9 +3229,9 @@ def abel_jacobi(self, divisor, verbose=False): sage: p = 4 sage: S = RiemannSurface(y^p-x^4+1, prec=100) sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] - sage: AJ = S.abel_jacobi(divisor) # long time (15 seconds) - sage: AJxp = [p*z for z in AJ] # long time - sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-7) # long time + sage: AJ = S.abel_jacobi(divisor) # long time (15 seconds) + sage: AJxp = [p*z for z in AJ] # long time + sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-7) # long time True """ ans = 0 From c44941741db4edbf488fa8d72af6a5481dab34b5 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 8 Sep 2021 15:09:44 +0100 Subject: [PATCH 020/454] Error bound improved. Using the tighter error bound from Rabinowitz, " Rough and ready error estimates in Gaussian integration of analytic functions", the value for the N required in the rigorous integration was reduced. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 706f1ff6322..96f6af79fba 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -2108,7 +2108,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): cdgdz = dgdz(cz,cg) Delta = delta_z*cdgdz.abs() + (delta_z**2)*M_tilde/(rho_z*(rho_z-delta_z)) M = Delta - N_required = ((64*M/(15*(1-1/expr)*E_global)).log()/(2*expr.log())).ceil() + N_required = ((M*(self._RR.pi()+64/(15*(expr**2-1)))/E_global).log()/(2*expr.log())).ceil() N = max(N,N_required) N = (K*(self._RR(N).sqrt()/K).ceil())**2 From 7bd64d78134bd7442577e0dbe3eff16d6d8c0171 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Tue, 26 Oct 2021 11:13:04 +0100 Subject: [PATCH 021/454] Typo fixes --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 96f6af79fba..5d03a21f30f 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3035,7 +3035,7 @@ def _aj_based(self, P): - ``P`` -- tuple. A pair giving the endpoint of the integral, either in the form ``(z, w)`` or ``(Infinity, branch)``, where in the latter case - we are using the conventing that the `w` value over `\infty` is given by + we are using the convention that the `w` value over `\infty` is given by the limit as ``z`` tends to `\infty` of ``self.w_values(z)[branch]``. OUTPUT: From d8b063f44a10945597e487017225f7b0cf5b8b62 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 3 Nov 2021 16:11:04 +0000 Subject: [PATCH 022/454] Updates and bug fixes. The divisor_to_divisor_list method was made more general, and the reduce_over_period_lattice was made to allow for the period matrix to be normalised (helpful if calculating the RCV). The _aj_based method was updated to deal with errors caused when the point take the map to was already a vertex. --- .../riemann_surfaces/riemann_surface.py | 171 ++++++++++-------- 1 file changed, 98 insertions(+), 73 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 5d03a21f30f..37eacce9252 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3106,77 +3106,82 @@ def _aj_based(self, P): zP = self._CC(zP) wP = self._CC(wP) V_index = find_closest_element(zP, self._vertices) - b_index = find_closest_element(zP, self.branch_locus) - b = self.branch_locus[b_index] - #bl = self.branch_locus+self._differentials_branch_locus - #b_index = find_closest_element(zP, bl) - #b = bl[b_index] - - scale = max(b.abs() for b in self.branch_locus) - d1 = self._CC(1e-2)*scale - - # We choose the first vertex we want to go to. - # If the closest vertex is closer than the nearest branch point, just take that vertex - # otherwise we need something smarter. - delta = self._RR(2)**(-self._prec+1) - if not ((zP-self._vertices[V_index]).abs() < (zP-b).abs() or (zP-b).abs()<=delta): - region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] - args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] - suitable_vertex_indices = [region[i] - for i in range(len(region)) if args[i].abs()-self._RR.pi()/2>=-self._RR(1e-15)] - suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] - if suitable_vertices==[]: - raise ValueError("There is no satisfactory choice of V for zP={}".format(zP)) - V_index = suitable_vertex_indices[find_closest_element(zP, suitable_vertices)] - ##### - zV = self._vertices[V_index] - - if (zP-b).abs() >= d1 or b in self._differentials_branch_locus: - wP_index = find_closest_element(wP, self.w_values(zP)) - d_edge = (zP, zV) - u_edge = ((zP, wP_index), (zV, )) - initial_continuation = self.homotopy_continuation(d_edge) - AJ = -line_int(u_edge) - - w_end = initial_continuation[-1][1][wP_index] - W_index = find_closest_element(w_end, self._wvalues[V_index]) - else: - zs = zP - ws = wP + if zP==self._vertices[V_index]: + W_index = find_closest_element(wP, self._wvalues[V_index]) + AJ = 0 + else: + b_index = find_closest_element(zP, self.branch_locus) + b = self.branch_locus[b_index] + #bl = self.branch_locus+self._differentials_branch_locus + #b_index = find_closest_element(zP, bl) + #b = bl[b_index] + + scale = max(b.abs() for b in self.branch_locus) + d1 = self._CC(1e-2)*scale + + # We choose the first vertex we want to go to. + # If the closest vertex is closer than the nearest branch point, just take that vertex + # otherwise we need something smarter. + delta = self._RR(2)**(-self._prec+1) + if not ((zP-self._vertices[V_index]).abs() < (zP-b).abs() or (zP-b).abs()<=delta): + region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] + args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] + suitable_vertex_indices = [region[i] + for i in range(len(region)) if args[i].abs()-self._RR.pi()/2>=-self._RR(1e-15)] + suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] + if suitable_vertices==[]: + raise ValueError("There is no satisfactory choice of V for zP={}".format(zP)) + V_index = suitable_vertex_indices[find_closest_element(zP, suitable_vertices)] ##### - # Here we need a block of code to change the vertex if the path - # from zP to zV would go through a ramification point of the integrands - fl = [c for c in self._differentials_branch_locus if not c==self._CC(Infinity)] - ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 - for c in fl] - ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() - for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] - while (len(ds)>=1 and min(ds)= d1 or b in self._differentials_branch_locus: + wP_index = find_closest_element(wP, self.w_values(zP)) + d_edge = (zP, zV) + u_edge = ((zP, wP_index), (zV, )) + initial_continuation = self.homotopy_continuation(d_edge) + AJ = -line_int(u_edge) + + w_end = initial_continuation[-1][1][wP_index] + W_index = find_closest_element(w_end, self._wvalues[V_index]) + else: + zs = zP + ws = wP + + ##### + # Here we need a block of code to change the vertex if the path + # from zP to zV would go through a ramification point of the integrands + fl = [c for c in self._differentials_branch_locus if not c==self._CC(Infinity)] ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 for c in fl] ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] - ##### - - while self._dfdw(zs, ws).abs()==0: - zs = zs+delta*(zV-zs)/(zV-zs).abs()/2 - ws_list = self.w_values(zs) - wP_index = find_closest_element(ws, ws_list) - ws = ws_list[wP_index] - upstairs_edge = ((zs, ws), zV) - AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, - cutoff_individually=False) - AJ = -AJ - g0e = endgs[0] + while (len(ds)>=1 and min(ds)=0 and ts[i]<=1)] + ##### + + while self._dfdw(zs, ws).abs()==0: + zs = zs+delta*(zV-zs)/(zV-zs).abs()/2 + ws_list = self.w_values(zs) + wP_index = find_closest_element(ws, ws_list) + ws = ws_list[wP_index] + upstairs_edge = ((zs, ws), zV) + AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, + cutoff_individually=False) + AJ = -AJ + g0e = endgs[0] - ws = self.w_values(zV) - g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] - W_index = find_closest_element(g0e, g0s) - if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") + ws = self.w_values(zV) + g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] + W_index = find_closest_element(g0e, g0s) + if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") uV_index = (V_index, W_index) ##### @@ -3245,7 +3250,7 @@ def abel_jacobi(self, divisor, verbose=False): print("Done, {}% complete".format(numerical_approx(100*(i+1)/n, 11))) return ans - def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None): + def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normalised=False): r""" Reduce a vector over the period lattice. @@ -3272,6 +3277,10 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None): - ``r`` -- integer (default: ``b/4``). as for :meth:`homomorphism_basis`, and used in its invocation if (re)calculating said basis. + + - ``normalised`` -- logical (default: ``False``). Whether to use the + period matrix with the differentials normalised s.t. the `A`-matrix + is the identity. OUTPUT: @@ -3312,6 +3321,12 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None): VR = VectorSpace(self._RR, 2*self.genus) VC = VectorSpace(self._CC, self.genus) I = self._CC(0,-1) + PM = self.period_matrix() + + if normalised: + AM = PM[:,0:g] + AInv = numerical_inverse(AM) + PM = AInv*PM if method=="svp": H = max(max(z.real_part().abs() for z in vector), @@ -3330,7 +3345,7 @@ def C2Z(v): return vR M = Matrix(ZZ, 2*self.genus, 2*self.genus, - [C2Z(c) for c in self.period_matrix().columns()]) + [C2Z(c) for c in PM.columns()]) u = C2Z(vector) L = IntegerLattice(M) u = VR(u)-VR(L.closest_vector(u)) @@ -3342,7 +3357,7 @@ def C2R(v): return VR([z.real_part() for z in v]+[z.imag_part() for z in v]) u = C2R(vector) - basis_vecs = [C2R(c) for c in self.period_matrix().columns()] + basis_vecs = [C2R(c) for c in PM.columns()] M = Matrix([[ei.dot_product(ej) for ei in basis_vecs] for ej in basis_vecs]) v_dot_e = VR([u.dot_product(e) for e in basis_vecs]) coeffs = M.solve_right(v_dot_e) @@ -3414,7 +3429,7 @@ def strong_approximation(self, divisor, S): OUTPUT: - A tuple ``(S, B)``, where ``D`` is a new divisor, linearly equivalent + A tuple ``(D, B)``, where ``D`` is a new divisor, linearly equivalent to ``divisor``, but not intersecting ``S``, and ``B`` is a list of tuples ``(v, b)`` where ``b`` are the functions giving the linear equivalence, added with multiplicity ``v``. @@ -3526,12 +3541,20 @@ def divisor_to_divisor_list(self, divisor): # not return. One might want to change the way this error is handled. eps = self._RR(2)**(-self._prec+3) dl = [] + + PZ = PolynomialRing(S._R.base(), 'z').fraction_field() + RF = PolynomialRing(PZ, 'w') + for d in divisor.support(): v = divisor.valuation(d) gs = d._prime.gens() - gs = [self._R(gi) for gi in gs] - g0 = gs[0] - gis = gs[1:] + + g0 = self._R(gs[0]) + gis = [sum([PZ(gi.list()[i])*RF.gen()**i + for i in range(len(gi.list()))]) for gi in gs[1:]] + #gs = [self._R(gi) for gi in gs] + #g0 = gs[0] + #gis = gs[1:] rs = self._CCz(g0).roots() rys = [] @@ -3539,13 +3562,15 @@ def divisor_to_divisor_list(self, divisor): for r, m in rs: ys = [] for gi in gis: - ers = [gi(r,y).abs() for y in ys] + ers = [gi(y, r).abs() for y in ys] + #ers = [gi(r,y).abs() for y in ys] try: ers = min(ers) except: ers = 1 if not ers<=eps: - poly = self._CCw(gi(r, self._CCw.gen(0))) + poly = self._CCw(gi(self._CCw.gen(0), r)) + #poly = self._CCw(gi(r, self._CCw.gen(0))) if poly==0: nys = [] else: From 61be2ca9bcef1b01e74cba93f25503c6620b0052 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 2 Dec 2021 12:26:39 +0000 Subject: [PATCH 023/454] Fixed error causing bugs with reduce_over_period_lattice --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 37eacce9252..bbb09e2def6 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3320,7 +3320,7 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal VR = VectorSpace(self._RR, 2*self.genus) VC = VectorSpace(self._CC, self.genus) - I = self._CC(0,-1) + I = self._CC(0, 1) PM = self.period_matrix() if normalised: From 3ba04e72f297b22f4951a90f799a894a2a4adede Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 13:41:34 +0100 Subject: [PATCH 024/454] Trac 32921: add optional keyword-only argument inhomogeneities --- src/sage/combinat/k_regular_sequence.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index b06b55b02fe..ac333ca8203 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -834,6 +834,10 @@ def from_recurrence(self, *args, **kwds): - ``offset`` -- an integer (default: ``0``). See explanation of ``equations`` above. + - ``inhomogeneities`` -- a dictionary mapping integers ``r`` to the + inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. All + inhomogeneities have to be regular sequences from ``self``. + OUTPUT: a :class:`kRegularSequence` EXAMPLES: @@ -1749,7 +1753,7 @@ def parse_direct_arguments(self, M, m, coeffs, initial_values): return (M, m, coeffs, initial_values) - def parameters(self, M, m, coeffs, initial_values, offset=0): + def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}): r""" Determine parameters from recurrence relations as admissible in :meth:`kRegularSequenceSpace.from_recurrence`. @@ -1789,7 +1793,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0): (3, 0): 10, (3, 1): 11}, initial_values={0: 1, 1: 2, 2: 1, 3: 4, 4: 12, 5: 30, 6: 48, 7: 66, 8: 75, 9: 204, 10: 333, 11: 462, 12: 216, 13: 594, -6: 0, -5: 0, -4: 0, -3: 0, -2: 0, -1: 0}, - offset=1, n1=3) + offset=1, n1=3, inhomogeneities={}) .. SEEALSO:: @@ -1815,13 +1819,14 @@ def parameters(self, M, m, coeffs, initial_values, offset=0): sage: RP.parameters(1, 0, {(0, 0): 1}, ....: {0: 1, 1: 0}, 0) recurrence_rules(M=1, m=0, l=0, u=0, ll=0, uu=0, dim=1, - coeffs={(0, 0): 1}, initial_values={0: 1, 1: 0}, offset=0, n1=0) + coeffs={(0, 0): 1}, initial_values={0: 1, 1: 0}, offset=0, n1=0, + inhomogeneities={}) Finally, also for the zero-sequence the output is as expected:: sage: RP.parameters(1, 0, {}, {0: 0}, 0) recurrence_rules(M=1, m=0, l=0, u=0, ll=0, uu=0, dim=1, - coeffs={}, initial_values={0: 0}, offset=0, n1=0) + coeffs={}, initial_values={0: 0}, offset=0, n1=0, inhomogeneities={}) :: @@ -1829,7 +1834,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0): ....: {(0, 0): 0, (1, 1): 0}, {0: 0}, 0) recurrence_rules(M=1, m=0, l=0, u=0, ll=0, uu=0, dim=1, coeffs={(0, 0): 0, (1, 1): 0}, initial_values={0: 0}, - offset=0, n1=0) + offset=0, n1=0, inhomogeneities={}) """ from collections import namedtuple @@ -1875,11 +1880,12 @@ def parameters(self, M, m, coeffs, initial_values, offset=0): recurrence_rules = namedtuple('recurrence_rules', ['M', 'm', 'l', 'u', 'll', 'uu', 'dim', - 'coeffs', 'initial_values', 'offset', 'n1']) + 'coeffs', 'initial_values', 'offset', 'n1', + 'inhomogeneities']) return recurrence_rules(M=M, m=m, l=l, u=u, ll=ll, uu=uu, dim=dim, coeffs=coeffs, initial_values=initial_values, - offset=offset, n1=n1) + offset=offset, n1=n1, inhomogeneities=inhomogeneities) def values(self, *, M, m, l, u, ll, coeffs, initial_values, last_value_needed, offset): From 976ac29663f1d0583c4cb05675840718993c8f48 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 15:06:33 +0100 Subject: [PATCH 025/454] Trac 32921: add inhomogeneities to values-method --- src/sage/combinat/k_regular_sequence.py | 41 ++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ac333ca8203..cd0d9ae912d 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1876,7 +1876,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) initial_values = self.values( M=M, m=m, l=l, u=u, ll=ll, coeffs=coeffs, initial_values=initial_values, last_value_needed=last_value_needed, - offset=offset) + offset=offset, inhomogeneities=inhomogeneities) recurrence_rules = namedtuple('recurrence_rules', ['M', 'm', 'l', 'u', 'll', 'uu', 'dim', @@ -1888,7 +1888,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) offset=offset, n1=n1, inhomogeneities=inhomogeneities) def values(self, *, M, m, l, u, ll, coeffs, - initial_values, last_value_needed, offset): + initial_values, last_value_needed, offset, inhomogeneities): r""" Determine enough values of the corresponding recursive sequence by applying the recurrence relations given in :meth:`kRegularSequenceSpace.from_recurrence` @@ -1913,6 +1913,10 @@ def values(self, *, M, m, l, u, ll, coeffs, - ``last_value_needed`` -- last initial value which is needed to determine the linear representation + - ``inhomogeneities`` -- a dictionary mapping integers ``r`` to the + inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. All + inhomogeneities have to be regular sequences from ``self``. + OUTPUT: A dictionary mapping integers ``n`` to the ``n``-th value of the @@ -1927,7 +1931,7 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, ....: initial_values={0: 0, 1: 1, 2: 1}, last_value_needed=20, - ....: offset=0) + ....: offset=0, inhomogeneities={}) {0: 0, 1: 1, 2: 1, 3: 2, 4: 1, 5: 3, 6: 2, 7: 3, 8: 1, 9: 4, 10: 3, 11: 5, 12: 2, 13: 5, 14: 3, 15: 4, 16: 1, 17: 5, 18: 4, 19: 7, 20: 3} @@ -1942,7 +1946,7 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, ....: initial_values={0: 0, 1: 2}, last_value_needed=20, - ....: offset=0) + ....: offset=0, inhomogeneities={}) {0: 0, 1: 2, 2: 2, 3: 4, 4: 2, 5: 6, 6: 4, 7: 6, 8: 2, 9: 8, 10: 6, 11: 10, 12: 4, 13: 10, 14: 6, 15: 8, 16: 2, 17: 10, 18: 8, 19: 14, 20: 6} @@ -1951,7 +1955,8 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, - ....: initial_values={}, last_value_needed=20, offset=0) + ....: initial_values={}, last_value_needed=20, offset=0, + ....: inhomogeneities={}) Traceback (most recent call last): ... ValueError: Initial values for arguments in [0, 1] are missing. @@ -1960,7 +1965,8 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, - ....: initial_values={0: 0}, last_value_needed=20, offset=0) + ....: initial_values={0: 0}, last_value_needed=20, offset=0, + ....: inhomogeneities={}) Traceback (most recent call last): ... ValueError: Initial values for arguments in [1] are missing. @@ -1970,7 +1976,7 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, ....: initial_values={0: 0, 2: 1}, last_value_needed=20, - ....: offset=0) + ....: offset=0, inhomogeneities={}) Traceback (most recent call last): ... ValueError: Initial values for arguments in [1] are missing. @@ -1980,7 +1986,7 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=1, ll=0, ....: coeffs={(0, 0): 1, (1, 0): 1, (1, 1): 1}, ....: initial_values={0: 0, 1: 2, 2:0}, last_value_needed=20, - ....: offset=0) + ....: offset=0, inhomogeneities={}) Traceback (most recent call last): ... ValueError: Initial value for argument 2 does not match with the given @@ -1991,7 +1997,7 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=-2, u=2, ll=-2, ....: coeffs={(0, -2): 1, (0, 2): 1, (1, -2): 1, (1, 2): 1}, ....: initial_values={0: 0, 1: 2, 2: 4, 3: 3, 4: 2}, - ....: last_value_needed=20, offset=2) + ....: last_value_needed=20, offset=2, inhomogeneities={}) {-2: 0, -1: 0, 0: 0, 1: 2, 2: 4, 3: 3, 4: 2, 5: 2, 6: 4, 7: 4, 8: 8, 9: 8, 10: 7, 11: 7, 12: 10, 13: 10, 14: 10, 15: 10, 16: 11, 17: 11, 18: 11, 19: 11, 20: 18} @@ -2000,14 +2006,14 @@ def values(self, *, M, m, l, u, ll, coeffs, sage: RP.values(M=1, m=0, l=0, u=0, ll=0, ....: coeffs={}, initial_values={}, last_value_needed=10, - ....: offset=0) + ....: offset=0, inhomogeneities={}) {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0} :: sage: RP.values(M=1, m=0, l=0, u=0, ll=0, ....: coeffs={(0, 0): 0, (1, 1): 0}, initial_values={}, - ....: last_value_needed=10, offset=0) + ....: last_value_needed=10, offset=0, inhomogeneities={}) {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0} """ from sage.arith.srange import srange @@ -2027,6 +2033,13 @@ def coeff(r, k): except KeyError: return 0 + @cached_function + def inhomogeneity(r, n): + try: + return inhomogeneities[r][n] + except KeyError: + return 0 + def f(n): f_n = values[n] if f_n is not None and f_n != "pending": @@ -2041,7 +2054,7 @@ def f(n): missing_values.append(n) return sum([coeff(r, j)*f(k**m*q + j) for j in srange(l, u + 1) - if coeff(r, j)]) + if coeff(r, j)]) + inhomogeneity(r, q) for n in srange(last_value_needed + 1): values.update({n: f(n)}) @@ -2053,8 +2066,8 @@ def f(n): for n in keys_initial: q, r = ZZ(n).quo_rem(k**M) if (q >= offset and - values[n] != sum([coeff(r, j)*values[k**m*q + j] - for j in srange(l, u + 1)])): + values[n] != (sum([coeff(r, j)*values[k**m*q + j] + for j in srange(l, u + 1)])) + inhomogeneity(r, q)): raise ValueError("Initial value for argument %s does not match with " "the given recurrence relations." % (n,)) from None From 6837137025804030468632ac713d9e4023169dd2 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 15:55:41 +0100 Subject: [PATCH 026/454] Trac #32921: add inhomogeneities to left-method --- src/sage/combinat/k_regular_sequence.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index cd0d9ae912d..d5dbc4f19e2 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2407,8 +2407,9 @@ def left(self, recurrence_rules): sage: from collections import namedtuple sage: from sage.combinat.k_regular_sequence import RecurrenceParser sage: RP = RecurrenceParser(2, ZZ) - sage: RRD = namedtuple('recurrence_rules_dim', ['dim']) - sage: recurrence_rules = RRD(dim=5) + sage: RRD = namedtuple('recurrence_rules_dim', + ....: ['dim', 'inhomogeneities']) + sage: recurrence_rules = RRD(dim=5, inhomogeneities={}) sage: RP.left(recurrence_rules) (1, 0, 0, 0, 0) @@ -2416,8 +2417,20 @@ def left(self, recurrence_rules): :meth:`kRegularSequenceSpace.from_recurrence` """ + from sage.functions.other import floor from sage.modules.free_module_element import vector + dim = recurrence_rules.dim + inhomogeneities = recurrence_rules.inhomogeneities + + if not all(S.is_trivial_zero() for S in inhomogeneities.values()): + k = self.k + M = recurrence_rules.M + m = recurrence_rules.m + ll = recurrence_rules.ll + uu = recurrence_rules.uu + dim = dim + (floor((k**(M-1) - k**m + uu)/k**M) - floor(ll/k**M) + 1) * \ + sum(S.mu[0].ncols() for S in inhomogeneities.values()) return vector([1] + (dim - 1)*[0]) From 22c5f0bd80da7e4961e404b628348776a11e9d49 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 16:12:43 +0100 Subject: [PATCH 027/454] Trac #32921: add inhomogeneities to right-method --- src/sage/combinat/k_regular_sequence.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index d5dbc4f19e2..5a81d7babe6 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2487,14 +2487,31 @@ def right(self, recurrence_rules): sage: RP.right(UB_rules) (1, 1, 2, 1, 2, 2, 4, 2, 4, 6, 0, 4, 4, 1, 0, 0) """ + from itertools import chain + from sage.arith.srange import srange + from sage.functions.other import floor from sage.modules.free_module_element import vector n1 = recurrence_rules.n1 + inhomogeneities = recurrence_rules.inhomogeneities right = self.v_eval_n(recurrence_rules, 0) if n1 >= 1: right = vector(list(right) + [1] + (n1 - 1)*[0]) + if not all(S.is_trivial_zero() for S in inhomogeneities.values()): + k = self.k + M = recurrence_rules.M + m = recurrence_rules.m + ll = recurrence_rules.ll + uu = recurrence_rules.uu + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + shifted_inhomogeneities = [S.subsequence(1, b) + for S in inhomogeneities.values() + for b in srange(lower, upper + 1)] + right = vector(chain(right, *[S.right for S in shifted_inhomogeneities])) + return right def __call__(self, *args, **kwds): From 53c6b584251663e8c8edc2adeea043c94d1cdeb7 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 21:08:50 +0100 Subject: [PATCH 028/454] Trac #32921: first draft for matrices --- src/sage/combinat/k_regular_sequence.py | 41 +++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 5a81d7babe6..570da8e98d7 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2331,9 +2331,10 @@ def matrix(self, recurrence_rules, rem, correct_offset=True): :meth:`kRegularSequenceSpace.from_recurrence` """ from sage.arith.srange import srange + from sage.functions.other import floor from sage.matrix.constructor import Matrix from sage.matrix.matrix_space import MatrixSpace - from sage.matrix.special import block_matrix, zero_matrix + from sage.matrix.special import block_matrix, block_diagonal_matrix, zero_matrix from sage.modules.free_module_element import vector coefficient_ring = self.coefficient_ring @@ -2347,6 +2348,7 @@ def matrix(self, recurrence_rules, rem, correct_offset=True): n1 = recurrence_rules.n1 dim_without_corr = dim - n1 coeffs = recurrence_rules.coeffs + inhomogeneities = recurrence_rules.inhomogeneities ind = self.ind(M, m, ll, uu) @cached_function @@ -2372,9 +2374,7 @@ def entry(i, kk): mat = Matrix(coefficient_ring, dim_without_corr, dim_without_corr, entry) - if n1 == 0 or not correct_offset: - return mat - else: + if n1 > 0 and correct_offset: W = Matrix(coefficient_ring, dim_without_corr, 0) for i in srange(n1): W = W.augment( @@ -2386,8 +2386,36 @@ def entry(i, kk): J = J.stack(vector([int(j*k == i - rem) for j in srange(n1)])) Mat = MatrixSpace(coefficient_ring, dim, dim) - return Mat(block_matrix([[mat, W], - [zero_matrix(n1, dim_without_corr), J]])) + mat = Mat(block_matrix([[mat, W], + [zero_matrix(n1, dim_without_corr), J]])) + + if not all(S.is_trivial_zero() for S in inhomogeneities.values()): + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + shifted_inhomogeneities = {} + current_row = 0 + for i in inhomogeneities.keys(): + for b in srange(lower, upper + 1): + S_b = inhomogeneities[i].subsequence(1, b) + shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) + current_row += S_b.mu[0].ncols() + + mat = block_diagonal_matrix(mat, *[S[0].mu[rem] + for S in shifted_inhomogeneities.values()]) + + for i in srange(dim): + j, d = ind[i] + if j == M - 1: + rem_d = k**(M-1)*rem + (d%k**M) + dd = d // k**M + if rem_d < k**M and rem_d in inhomogeneities.keys(): + mat[ind[(j, d)], + dim + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 + elif rem_d >= k**M and rem_d - k in inhomogeneities.keys(): + mat[ind[(j, d)], + dim + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 + + return mat def left(self, recurrence_rules): r""" @@ -2488,6 +2516,7 @@ def right(self, recurrence_rules): (1, 1, 2, 1, 2, 2, 4, 2, 4, 6, 0, 4, 4, 1, 0, 0) """ from itertools import chain + from sage.arith.srange import srange from sage.functions.other import floor from sage.modules.free_module_element import vector From 9a3996450ddcbe542dd1d723b629fed5e64f462d Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 21:09:29 +0100 Subject: [PATCH 029/454] Trac #32921: add examples/tests --- src/sage/combinat/k_regular_sequence.py | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 570da8e98d7..0fc919a5d06 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -879,6 +879,43 @@ def from_recurrence(self, *args, **kwds): ....: f(20) == 8, f(21) == 4, f(22) == 4, f(23) == 8], f, n, offset=3) 2-regular sequence 1, 2, 2, 4, 2, 4, 6, 0, 4, 4, ... + Binary sum of digits `S(n)`, characterized by the recurrence relations + `S(4n) = S(2n)`, `S(4n + 1) = S(2n + 1)`, `S(4n + 2) = S(2n + 1)` and + `S(4n + 3) = -S(2n) + 2S(2n + 1)`:: + + sage: S = Seq2.from_recurrence([ + ....: f(4*n) == f(2*n), + ....: f(4*n + 1) == f(2*n + 1), + ....: f(4*n + 2) == f(2*n + 1), + ....: f(4*n + 3) == -f(2*n) + 2*f(2*n + 1), + ....: f(0) == 0, f(1) == 1], f, n) + sage: S + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... + + In order to check if this sequence is indeed the binary sum of digits, + we construct directly via its linear representation and compare it + with ``S``:: + + sage: S2 = Seq2( + ....: (Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [1, 1]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: (S - S2).is_trivial_zero() + True + + Alternatively, we can also use the simpler but inhomogeneous recurrence relations + `S(2n) = S(n)` and `S(2n+1) = S(n) + 1` via direct parameters:: + + sage: one = Seq2((Matrix([[1]]), Matrix([[1]])), + ....: left=vector([1]), right=vector([1])) + sage: S = Seq2.from_recurrence(M=1, m=0, + ....: coeffs={(0, 0): 1, (1, 0): 1}, + ....: initial_values={0: 0, 1: 1}, + ....: inhomogeneities={1: one}) + sage: S + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... + sage: (S - S2).is_trivial_zero() + True + Number of Non-Zero Elements in the Generalized Pascal's Triangle (see [LRS2017]_):: sage: Seq2 = kRegularSequenceSpace(2, QQ) From cee66a6c936867734af47fcd84bbb45d6a144974 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 21:09:49 +0100 Subject: [PATCH 030/454] Trac #32921: add keywords to old test --- src/sage/combinat/k_regular_sequence.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 0fc919a5d06..69bd77ddd09 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -930,12 +930,12 @@ def from_recurrence(self, *args, **kwds): Finally, the same sequence can also be obtained via direct parameters without symbolic equations:: - sage: Seq2.from_recurrence(2, 1, - ....: {(0, 0): 5/3, (0, 1): -1/3, - ....: (1, 0): 4/3, (1, 1): 1/3, - ....: (2, 0): 1/3, (2, 1): 4/3, - ....: (3, 0): -1/3, (3, 1): 5/3}, - ....: {0: 1, 1: 2}) + sage: Seq2.from_recurrence(M=2, m=1, + ....: coeffs={(0, 0): 5/3, (0, 1): -1/3, + ....: (1, 0): 4/3, (1, 1): 1/3, + ....: (2, 0): 1/3, (2, 1): 4/3, + ....: (3, 0): -1/3, (3, 1): 5/3}, + ....: initial_values={0: 1, 1: 2}) 2-regular sequence 1, 2, 3, 3, 4, 5, 5, 4, 5, 7, ... TESTS:: From b1f2b71238369da6398e5fe9ef115a7408ddd16d Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 21:11:50 +0100 Subject: [PATCH 031/454] Trac #32921: use is_trivial_zero in old example --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 69bd77ddd09..c8ce7627fdc 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1007,7 +1007,7 @@ def from_recurrence(self, *args, **kwds): ....: g(22) == 22, g(23) == 23, g(24) == 24, g(25) == 25, ....: g(26) == 26, g(27) == 27, g(28) == 28, g(29) == 29, ....: g(30) == 30, g(31) == 31], g, m, offset=8) - sage: all([S[i] == T[i] for i in srange(1000)]) + sage: (S - T).is_trivial_zero() True Zero-sequence with non-zero initial values:: From 7e4ac4415aa093b0df9f969716e4e9d7b99e7028 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 11 Jan 2022 22:24:13 +0100 Subject: [PATCH 032/454] Trac #32921: add random example with imhomogeneities only --- src/sage/combinat/k_regular_sequence.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index c8ce7627fdc..d18a01422cd 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1025,6 +1025,18 @@ def from_recurrence(self, *args, **kwds): ....: f(2*n) == 0, f(2*n + 1) == 0, ....: f(0) == 1, f(1) == 1, f(2) == 2, f(3) == 3], f, n, offset=2) 2-regular sequence 1, 1, 2, 3, 0, 0, 0, 0, 0, 0, ... + + :: + + sage: S = Seq2((random_matrix(ZZ, 3, 3), random_matrix(ZZ, 3, 3)), + ....: left=vector([randint(-2, 2) for i in srange(3)]), + ....: right=vector([randint(-2, 2) for i in srange(3)])) + sage: T = Seq2.from_recurrence(M=3, m=2, + ....: coeffs={}, + ....: initial_values={0: S[0]}, + ....: inhomogeneities={i: S.subsequence(2**3, i) for i in srange(2**3)}) + sage: all(S[i] == T[i] for i in srange(100)) + True """ RP = RecurrenceParser(self.k, self.coefficient_ring()) mu, left, right = RP(*args, **kwds) From 59479e5a75184ada8c463c728e8416e633a34304 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 14:50:56 +0100 Subject: [PATCH 033/454] Trac #32921: add some tests --- src/sage/combinat/k_regular_sequence.py | 26 +++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index d18a01422cd..71e2b2229be 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -849,9 +849,10 @@ def from_recurrence(self, *args, **kwds): n sage: function('f') f - sage: Seq2.from_recurrence([ + sage: SB = Seq2.from_recurrence([ ....: f(2*n) == f(n), f(2*n + 1) == f(n) + f(n + 1), ....: f(0) == 0, f(1) == 1], f, n) + sage: SB 2-regular sequence 0, 1, 1, 2, 1, 3, 2, 3, 1, 4, ... Number of Odd Entries in Pascal's Triangle:: @@ -919,12 +920,13 @@ def from_recurrence(self, *args, **kwds): Number of Non-Zero Elements in the Generalized Pascal's Triangle (see [LRS2017]_):: sage: Seq2 = kRegularSequenceSpace(2, QQ) - sage: Seq2.from_recurrence([ + sage: P = Seq2.from_recurrence([ ....: f(4*n) == 5/3*f(2*n) - 1/3*f(2*n + 1), ....: f(4*n + 1) == 4/3*f(2*n) + 1/3*f(2*n + 1), ....: f(4*n + 2) == 1/3*f(2*n) + 4/3*f(2*n + 1), ....: f(4*n + 3) == -1/3*f(2*n) + 5/3*f(2*n + 1), ....: f(0) == 1, f(1) == 2], f, n) + sage: P 2-regular sequence 1, 2, 3, 3, 4, 5, 5, 4, 5, 7, ... Finally, the same sequence can also be obtained via direct parameters @@ -1037,6 +1039,26 @@ def from_recurrence(self, *args, **kwds): ....: inhomogeneities={i: S.subsequence(2**3, i) for i in srange(2**3)}) sage: all(S[i] == T[i] for i in srange(100)) True + + Connection between the Stern--Brocot sequence and the number + of non-zero elements in the generalized Pascal's triangle (see + [LRS2017]_):: + + sage: U = Seq2.from_recurrence(M=1, m=0, + ....: coeffs={(0, 0): 1}, + ....: initial_values={0: 0, 1: 1}, + ....: inhomogeneities={1: P}) + sage: (U - Seq2(SB)).is_trivial_zero() + True + + :: + + sage: U = Seq2.from_recurrence(M=1, m=0, + ....: coeffs={}, + ....: initial_values={0: 0, 1: 1}, + ....: inhomogeneities={0: SB, 1: P}) + sage: (U - Seq2(SB)).is_trivial_zero() + True """ RP = RecurrenceParser(self.k, self.coefficient_ring()) mu, left, right = RP(*args, **kwds) From 6bc9c110a540f54bfc791fcca8cd95cc1b4bd77b Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 15:15:24 +0100 Subject: [PATCH 034/454] Trac #32921: minor modifications in tests --- src/sage/combinat/k_regular_sequence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 71e2b2229be..034fa00c77b 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -894,7 +894,7 @@ def from_recurrence(self, *args, **kwds): 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... In order to check if this sequence is indeed the binary sum of digits, - we construct directly via its linear representation and compare it + we construct it directly via its linear representation and compare it with ``S``:: sage: S2 = Seq2( @@ -1030,6 +1030,7 @@ def from_recurrence(self, *args, **kwds): :: + sage: set_random_seed() sage: S = Seq2((random_matrix(ZZ, 3, 3), random_matrix(ZZ, 3, 3)), ....: left=vector([randint(-2, 2) for i in srange(3)]), ....: right=vector([randint(-2, 2) for i in srange(3)])) From 60f76025f9b90da18da7c59a1f72f4b998c0be39 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 15:16:20 +0100 Subject: [PATCH 035/454] Trac #32921: fix issue for left vector --- src/sage/combinat/k_regular_sequence.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 034fa00c77b..2b7bdf7cf3e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2517,6 +2517,7 @@ def left(self, recurrence_rules): :meth:`kRegularSequenceSpace.from_recurrence` """ + from sage.arith.srange import srange from sage.functions.other import floor from sage.modules.free_module_element import vector @@ -2529,8 +2530,11 @@ def left(self, recurrence_rules): m = recurrence_rules.m ll = recurrence_rules.ll uu = recurrence_rules.uu - dim = dim + (floor((k**(M-1) - k**m + uu)/k**M) - floor(ll/k**M) + 1) * \ - sum(S.mu[0].ncols() for S in inhomogeneities.values()) + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + dim = dim + sum(inhomogeneities[i].subsequence(1, b, minimize=False).mu[0].ncols() + for i in inhomogeneities.keys() + for b in srange(lower, upper + 1)) return vector([1] + (dim - 1)*[0]) From 49363c791a40883dc4fb3ab8d3aa50d21dbf61bd Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 15:17:22 +0100 Subject: [PATCH 036/454] Trac #32921: deactivate minimization for now --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 2b7bdf7cf3e..595b2f15c25 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2468,7 +2468,7 @@ def entry(i, kk): current_row = 0 for i in inhomogeneities.keys(): for b in srange(lower, upper + 1): - S_b = inhomogeneities[i].subsequence(1, b) + S_b = inhomogeneities[i].subsequence(1, b, minimize=False) shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) current_row += S_b.mu[0].ncols() @@ -2612,7 +2612,7 @@ def right(self, recurrence_rules): uu = recurrence_rules.uu lower = floor(ll/k**M) upper = floor((k**(M-1) - k**m + uu)/k**M) - shifted_inhomogeneities = [S.subsequence(1, b) + shifted_inhomogeneities = [S.subsequence(1, b, minimize=False) for S in inhomogeneities.values() for b in srange(lower, upper + 1)] right = vector(chain(right, *[S.right for S in shifted_inhomogeneities])) From a65412ee40bcea65ad212ee3d15eda0dea0bb511 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 16:07:54 +0100 Subject: [PATCH 037/454] Trac #32921: shift parts from right to v_eval_n --- src/sage/combinat/k_regular_sequence.py | 32 ++++++++++--------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 595b2f15c25..455a01f4270 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2246,10 +2246,20 @@ def v_eval_n(self, recurrence_rules, n): uu = recurrence_rules.uu dim = recurrence_rules.dim - recurrence_rules.n1 initial_values = recurrence_rules.initial_values + inhomogeneities = recurrence_rules.inhomogeneities ind = self.ind(M, m, ll, uu) - return vector( - [initial_values[k**ind[i][0]*n + ind[i][1]] for i in srange(dim)]) + v = vector([initial_values[k**ind[i][0]*n + ind[i][1]] for i in srange(dim)]) + + if not all(S.is_trivial_zero() for S in inhomogeneities.values()): + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + shifted_inhomogeneities = [S.subsequence(1, b, minimize=False) + for S in inhomogeneities.values() + for b in srange(lower, upper + 1)] + v = vector(chain(v, *[S.right for S in shifted_inhomogeneities])) + + return v def matrix(self, recurrence_rules, rem, correct_offset=True): r""" @@ -2591,32 +2601,14 @@ def right(self, recurrence_rules): sage: RP.right(UB_rules) (1, 1, 2, 1, 2, 2, 4, 2, 4, 6, 0, 4, 4, 1, 0, 0) """ - from itertools import chain - - from sage.arith.srange import srange - from sage.functions.other import floor from sage.modules.free_module_element import vector n1 = recurrence_rules.n1 - inhomogeneities = recurrence_rules.inhomogeneities right = self.v_eval_n(recurrence_rules, 0) if n1 >= 1: right = vector(list(right) + [1] + (n1 - 1)*[0]) - if not all(S.is_trivial_zero() for S in inhomogeneities.values()): - k = self.k - M = recurrence_rules.M - m = recurrence_rules.m - ll = recurrence_rules.ll - uu = recurrence_rules.uu - lower = floor(ll/k**M) - upper = floor((k**(M-1) - k**m + uu)/k**M) - shifted_inhomogeneities = [S.subsequence(1, b, minimize=False) - for S in inhomogeneities.values() - for b in srange(lower, upper + 1)] - right = vector(chain(right, *[S.right for S in shifted_inhomogeneities])) - return right def __call__(self, *args, **kwds): From e10abb3160fdc6a1fc11850cd8afcdb106a8de89 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 16:08:41 +0100 Subject: [PATCH 038/454] Trac #32921: fix offset correction --- src/sage/combinat/k_regular_sequence.py | 42 ++++++++++++++----------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 455a01f4270..58df4bfe328 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2236,7 +2236,10 @@ def v_eval_n(self, recurrence_rules, n): :meth:`kRegularSequenceSpace.from_recurrence` """ + from itertools import chain + from sage.arith.srange import srange + from sage.functions.other import floor from sage.modules.free_module_element import vector k = self.k @@ -2456,21 +2459,6 @@ def entry(i, kk): mat = Matrix(coefficient_ring, dim_without_corr, dim_without_corr, entry) - if n1 > 0 and correct_offset: - W = Matrix(coefficient_ring, dim_without_corr, 0) - for i in srange(n1): - W = W.augment( - self.v_eval_n(recurrence_rules, k*i + rem) - - mat*self.v_eval_n(recurrence_rules, i)) - - J = Matrix(coefficient_ring, 0, n1) - for i in srange(n1): - J = J.stack(vector([int(j*k == i - rem) for j in srange(n1)])) - - Mat = MatrixSpace(coefficient_ring, dim, dim) - mat = Mat(block_matrix([[mat, W], - [zero_matrix(n1, dim_without_corr), J]])) - if not all(S.is_trivial_zero() for S in inhomogeneities.values()): lower = floor(ll/k**M) upper = floor((k**(M-1) - k**m + uu)/k**M) @@ -2485,17 +2473,35 @@ def entry(i, kk): mat = block_diagonal_matrix(mat, *[S[0].mu[rem] for S in shifted_inhomogeneities.values()]) - for i in srange(dim): + for i in srange(dim_without_corr): j, d = ind[i] if j == M - 1: rem_d = k**(M-1)*rem + (d%k**M) dd = d // k**M if rem_d < k**M and rem_d in inhomogeneities.keys(): mat[ind[(j, d)], - dim + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 + dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 elif rem_d >= k**M and rem_d - k in inhomogeneities.keys(): mat[ind[(j, d)], - dim + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 + dim_without_corr + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 + + dim += current_row + dim_without_corr += current_row + + if n1 > 0 and correct_offset: + W = Matrix(coefficient_ring, dim_without_corr, 0) + for i in srange(n1): + W = W.augment( + self.v_eval_n(recurrence_rules, k*i + rem) - + mat*self.v_eval_n(recurrence_rules, i)) + + J = Matrix(coefficient_ring, 0, n1) + for i in srange(n1): + J = J.stack(vector([int(j*k == i - rem) for j in srange(n1)])) + + Mat = MatrixSpace(coefficient_ring, dim, dim) + mat = Mat(block_matrix([[mat, W], + [zero_matrix(n1, dim_without_corr), J]])) return mat From 268fe7c625a54c94566bb5fcf6c98daf0a06bff3 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 16:09:06 +0100 Subject: [PATCH 039/454] Trac #32921: add further example --- src/sage/combinat/k_regular_sequence.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 58df4bfe328..421ee90154a 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -864,7 +864,7 @@ def from_recurrence(self, *args, **kwds): Number of Unbordered Factors in the Thue--Morse Sequence:: - sage: Seq2.from_recurrence([ + sage: UB = Seq2.from_recurrence([ ....: f(8*n) == 2*f(4*n), ....: f(8*n + 1) == f(4*n + 1), ....: f(8*n + 2) == f(4*n + 1) + f(4*n + 3), @@ -878,6 +878,7 @@ def from_recurrence(self, *args, **kwds): ....: f(10) == 4, f(11) == 4, f(12) == 12, f(13) == 0, f(14) == 4, ....: f(15) == 4, f(16) == 8, f(17) == 4, f(18) == 8, f(19) == 0, ....: f(20) == 8, f(21) == 4, f(22) == 4, f(23) == 8], f, n, offset=3) + sage: UB 2-regular sequence 1, 2, 2, 4, 2, 4, 6, 0, 4, 4, ... Binary sum of digits `S(n)`, characterized by the recurrence relations @@ -1060,6 +1061,28 @@ def from_recurrence(self, *args, **kwds): ....: inhomogeneities={0: SB, 1: P}) sage: (U - Seq2(SB)).is_trivial_zero() True + + Number of Unbordered Factors in the Thue--Morse Sequence, but partly + encoded with inhomogeneities:: + + sage: UB2 = Seq2.from_recurrence([ + ....: f(8*n) == 2*f(4*n), + ....: f(8*n + 1) == f(4*n + 1), + ....: f(8*n + 2) == f(4*n + 1), + ....: f(8*n + 3) == -f(4*n + 1) + f(4*n + 2), + ....: f(8*n + 4) == 2*f(4*n + 2), + ....: f(8*n + 5) == f(4*n + 3), + ....: f(8*n + 6) == -f(4*n + 1), + ....: f(8*n + 7) == 2*f(4*n + 1) + f(4*n + 3), + ....: f(0) == 1, f(1) == 2, f(2) == 2, f(3) == 4, f(4) == 2, + ....: f(5) == 4, f(6) == 6, f(7) == 0, f(8) == 4, f(9) == 4, + ....: f(10) == 4, f(11) == 4, f(12) == 12, f(13) == 0, f(14) == 4, + ....: f(15) == 4, f(16) == 8, f(17) == 4, f(18) == 8, f(19) == 0, + ....: f(20) == 8, f(21) == 4, f(22) == 4, f(23) == 8], f, n, offset=3, + ....: inhomogeneities={2: UB.subsequence(4, 3), 3: -UB.subsequence(4, 1), + ....: 6: UB.subsequence(4, 2) + UB.subsequence(4, 3)}) + sage: (UB2 - Seq2(UB)).is_trivial_zero() + True """ RP = RecurrenceParser(self.k, self.coefficient_ring()) mu, left, right = RP(*args, **kwds) From 54bc5db1008b6b186d5ffbeefc4a82b83aeef988 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 16:21:58 +0100 Subject: [PATCH 040/454] Trac #32921: simplify construction of matrix --- src/sage/combinat/k_regular_sequence.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 421ee90154a..4785558241c 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2496,17 +2496,16 @@ def entry(i, kk): mat = block_diagonal_matrix(mat, *[S[0].mu[rem] for S in shifted_inhomogeneities.values()]) - for i in srange(dim_without_corr): - j, d = ind[i] - if j == M - 1: - rem_d = k**(M-1)*rem + (d%k**M) - dd = d // k**M - if rem_d < k**M and rem_d in inhomogeneities.keys(): - mat[ind[(j, d)], - dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 - elif rem_d >= k**M and rem_d - k in inhomogeneities.keys(): - mat[ind[(j, d)], - dim_without_corr + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 + for i in srange(dim_without_corr - k**(M-1) + k**m - uu + ll - 1, dim_without_corr): + j, d = ind[i] # j = M - 1 + rem_d = k**(M-1)*rem + (d%k**M) + dd = d // k**M + if rem_d < k**M and rem_d in inhomogeneities.keys(): + mat[ind[(j, d)], + dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 + elif rem_d >= k**M and rem_d - k in inhomogeneities.keys(): + mat[ind[(j, d)], + dim_without_corr + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 dim += current_row dim_without_corr += current_row From 25d53987b491f848d23038ff2b885cf768f86d6e Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 12 Jan 2022 16:22:37 +0100 Subject: [PATCH 041/454] Trac #32921: add inhomogeneities to docstring --- src/sage/combinat/k_regular_sequence.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 4785558241c..1994a7a3ad9 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1874,6 +1874,11 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) - ``initial_values`` -- a dictionary mapping integers ``n`` to the ``n``-th value of the sequence + - ``inhomogeneities`` -- (default: ``{}``) a dictionary + mapping integers ``r`` to the inhomogeneity `g_r` as given + in [HKL2021]_, Corollary D. All inhomogeneities have to be + regular sequences from ``self``. + EXAMPLES:: sage: from sage.combinat.k_regular_sequence import RecurrenceParser From 997c5ccc4ad2901716a241cadd14e0130954d149 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 12:20:50 +0100 Subject: [PATCH 042/454] Trac #32921, review item 2: mention default value --- src/sage/combinat/k_regular_sequence.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index eebecf7bff3..febb65ca8d0 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -831,12 +831,13 @@ def from_recurrence(self, *args, **kwds): Optional keyword-only argument: - - ``offset`` -- an integer (default: ``0``). See explanation of + - ``offset`` -- (default: ``0``) an integer. See explanation of ``equations`` above. - - ``inhomogeneities`` -- a dictionary mapping integers ``r`` to the - inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. All - inhomogeneities have to be regular sequences from ``self``. + - ``inhomogeneities`` -- (default: ``{}``) a dictionary + mapping integers ``r`` to the inhomogeneity `g_r` as given + in [HKL2021]_, Corollary D. All inhomogeneities have to be + regular sequences from ``self``. OUTPUT: a :class:`kRegularSequence` From dd4f2f6b19e051ac1a781d4efa76814fec3805b5 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 12:24:37 +0100 Subject: [PATCH 043/454] Trac #32921, review item 3: adapt descrition of output --- src/sage/combinat/k_regular_sequence.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index febb65ca8d0..6ef9c4b1856 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1910,10 +1910,8 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) - ``initial_values`` -- a dictionary mapping integers ``n`` to the ``n``-th value of the sequence - - ``inhomogeneities`` -- (default: ``{}``) a dictionary - mapping integers ``r`` to the inhomogeneity `g_r` as given - in [HKL2021]_, Corollary D. All inhomogeneities have to be - regular sequences from ``self``. + - ``inhomogeneities`` -- a dictionary mapping integers ``r`` + to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. EXAMPLES:: From 87a3cfb225259a0ab25b3e297d3e505f32c2d66b Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 12:27:01 +0100 Subject: [PATCH 044/454] Trac #32921, review item 4: adapt description of input --- src/sage/combinat/k_regular_sequence.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 6ef9c4b1856..7ba462d678e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2053,9 +2053,8 @@ def values(self, *, M, m, l, u, ll, coeffs, - ``last_value_needed`` -- last initial value which is needed to determine the linear representation - - ``inhomogeneities`` -- a dictionary mapping integers ``r`` to the - inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. All - inhomogeneities have to be regular sequences from ``self``. + - ``inhomogeneities`` -- a dictionary mapping integers ``r`` + to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. OUTPUT: From f4356b2b0cce709290d6a8ee999ed55df9315f7c Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 12:34:24 +0100 Subject: [PATCH 045/454] Trac #32921, review item 9: fix index --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 7ba462d678e..611ecd5fc99 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2547,9 +2547,9 @@ def entry(i, kk): if rem_d < k**M and rem_d in inhomogeneities.keys(): mat[ind[(j, d)], dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 - elif rem_d >= k**M and rem_d - k in inhomogeneities.keys(): + elif rem_d >= k**M and rem_d - k**M in inhomogeneities.keys(): mat[ind[(j, d)], - dim_without_corr + shifted_inhomogeneities[(rem_d - k, dd+1)][1]] = 1 + dim_without_corr + shifted_inhomogeneities[(rem_d - k**M, dd+1)][1]] = 1 dim += current_row dim_without_corr += current_row From 03cc1b4caad89f039af2baa15617408239f36c5e Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 12:40:01 +0100 Subject: [PATCH 046/454] Trac #32921, review item 14: use one_hadamard --- src/sage/combinat/k_regular_sequence.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 611ecd5fc99..93ff8711f08 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -908,8 +908,7 @@ def from_recurrence(self, *args, **kwds): Alternatively, we can also use the simpler but inhomogeneous recurrence relations `S(2n) = S(n)` and `S(2n+1) = S(n) + 1` via direct parameters:: - sage: one = Seq2((Matrix([[1]]), Matrix([[1]])), - ....: left=vector([1]), right=vector([1])) + sage: one = Seq2.one_hadamard() sage: S = Seq2.from_recurrence(M=1, m=0, ....: coeffs={(0, 0): 1, (1, 0): 1}, ....: initial_values={0: 0, 1: 1}, From 97fbbddce43f807744c524057d74d47009d5c459 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 16:55:40 +0100 Subject: [PATCH 047/454] Trac #32921, review item 5: fix missing n --- src/sage/combinat/k_regular_sequence.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 93ff8711f08..c84aedb6507 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2307,6 +2307,7 @@ def v_eval_n(self, recurrence_rules, n): from sage.arith.srange import srange from sage.functions.other import floor from sage.modules.free_module_element import vector + from sage.rings.integer_ring import ZZ k = self.k M = recurrence_rules.M @@ -2321,12 +2322,11 @@ def v_eval_n(self, recurrence_rules, n): v = vector([initial_values[k**ind[i][0]*n + ind[i][1]] for i in srange(dim)]) if not all(S.is_trivial_zero() for S in inhomogeneities.values()): - lower = floor(ll/k**M) - upper = floor((k**(M-1) - k**m + uu)/k**M) - shifted_inhomogeneities = [S.subsequence(1, b, minimize=False) - for S in inhomogeneities.values() - for b in srange(lower, upper + 1)] - v = vector(chain(v, *[S.right for S in shifted_inhomogeneities])) + Seq = list(inhomogeneities.values())[0].parent() + W = Seq.indices() + shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) + vv = [(S._mu_of_word_(W(ZZ(n).digits(k))) * S.right) for S in shifted_inhomogeneities] + v = vector(chain(v, *vv)) return v From f4807380ee9414931cc9c006f3df48c01b1fe3f1 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 17:26:32 +0100 Subject: [PATCH 048/454] Trac #32921, review item 12: write new method --- src/sage/combinat/k_regular_sequence.py | 74 +++++++++++++++++++++---- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index c84aedb6507..1df6975a627 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2273,6 +2273,66 @@ def ind(self, M, m, ll, uu): return ind + def shifted_inhomogeneities(self, recurrence_rules): + r""" + Return a list of all needed shifted inhomogeineities as described + in the proof of Coroallary D in [HKL2021]_. + + INPUT: + + - ``recurrence_rules`` -- a namedtuple generated by + :meth:`parameters` + + OUTPUT: a list + + EXAMPLES:: + + sage: from collections import namedtuple + sage: from sage.combinat.k_regular_sequence import RecurrenceParser + sage: RP = RecurrenceParser(2, ZZ) + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: S = Seq2((Matrix([[1, 0], [0, 1]]), Matrix([[1, 0], [1, 1]])), + ....: left=vector([0, 1]), right=vector([1, 0])) + sage: S + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... + sage: RR = namedtuple('recurrence_rules', + ....: ['M', 'm', 'll', 'uu', 'inhomogeneities']) + sage: recurrence_rules = RR(M=3, m=0, ll=-8, uu=14, + ....: inhomogeneities={0: S}) + sage: RP.shifted_inhomogeneities(recurrence_rules) + [2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., + 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., + 2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., + 2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ...] + + .. SEEALSO:: + + :meth:`kRegularSequenceSpace.from_recurrence` + """ + from sage.arith.srange import srange + from sage.functions.other import floor + + k = self.k + M = recurrence_rules.M + m = recurrence_rules.m + ll = recurrence_rules.ll + uu = recurrence_rules.uu + inhomogeneities = recurrence_rules.inhomogeneities + + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + + shifted_inhomogeneities = [S.subsequence(1, b) + for S in inhomogeneities.values() + for b in srange(lower, upper + 1)] + + CR = self.coefficient_ring + assert(all([S.left[0] == CR(1) for S in shifted_inhomogeneities]) and + all([all(a == CR(0) for a in S.left[1:]) + for S in shifted_inhomogeneities])) + + return shifted_inhomogeneities + def v_eval_n(self, recurrence_rules, n): r""" Return the vector `v(n)` as given in [HKL2021]_, Theorem A. @@ -2532,7 +2592,7 @@ def entry(i, kk): current_row = 0 for i in inhomogeneities.keys(): for b in srange(lower, upper + 1): - S_b = inhomogeneities[i].subsequence(1, b, minimize=False) + S_b = inhomogeneities[i].subsequence(1, b) shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) current_row += S_b.mu[0].ncols() @@ -2606,16 +2666,8 @@ def left(self, recurrence_rules): inhomogeneities = recurrence_rules.inhomogeneities if not all(S.is_trivial_zero() for S in inhomogeneities.values()): - k = self.k - M = recurrence_rules.M - m = recurrence_rules.m - ll = recurrence_rules.ll - uu = recurrence_rules.uu - lower = floor(ll/k**M) - upper = floor((k**(M-1) - k**m + uu)/k**M) - dim = dim + sum(inhomogeneities[i].subsequence(1, b, minimize=False).mu[0].ncols() - for i in inhomogeneities.keys() - for b in srange(lower, upper + 1)) + shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) + dim = dim + sum(S.mu[0].ncols() for S in shifted_inhomogeneities) return vector([1] + (dim - 1)*[0]) From 48ebcc4ea03461fec7be47ba7c0a2c39ca380338 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 18 Jan 2022 17:30:15 +0100 Subject: [PATCH 049/454] Trac #32921, review item 12: modify method for matrix-construction (could be reverted) --- src/sage/combinat/k_regular_sequence.py | 47 +++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 1df6975a627..bb4b8a250c0 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2300,10 +2300,10 @@ def shifted_inhomogeneities(self, recurrence_rules): sage: recurrence_rules = RR(M=3, m=0, ll=-8, uu=14, ....: inhomogeneities={0: S}) sage: RP.shifted_inhomogeneities(recurrence_rules) - [2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., - 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., - 2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., - 2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ...] + {(0, -1): (2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., 0), + (0, 0): (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., 4), + (0, 1): (2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., 6), + (0, 2): (2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ..., 9)} .. SEEALSO:: @@ -2322,14 +2322,18 @@ def shifted_inhomogeneities(self, recurrence_rules): lower = floor(ll/k**M) upper = floor((k**(M-1) - k**m + uu)/k**M) - shifted_inhomogeneities = [S.subsequence(1, b) - for S in inhomogeneities.values() - for b in srange(lower, upper + 1)] + shifted_inhomogeneities = {} + current_row = 0 + for i in inhomogeneities.keys(): + for b in srange(lower, upper + 1): + S_b = inhomogeneities[i].subsequence(1, b) + shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) + current_row += S_b.mu[0].ncols() CR = self.coefficient_ring - assert(all([S.left[0] == CR(1) for S in shifted_inhomogeneities]) and - all([all(a == CR(0) for a in S.left[1:]) - for S in shifted_inhomogeneities])) + assert(all([S[0].left[0] == CR(1) for S in shifted_inhomogeneities.values()]) and + all([all(a == CR(0) for a in S[0].left[1:]) + for S in shifted_inhomogeneities.values()])) return shifted_inhomogeneities @@ -2384,7 +2388,7 @@ def v_eval_n(self, recurrence_rules, n): if not all(S.is_trivial_zero() for S in inhomogeneities.values()): Seq = list(inhomogeneities.values())[0].parent() W = Seq.indices() - shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) + shifted_inhomogeneities = [S[0] for S in self.shifted_inhomogeneities(recurrence_rules).values()] vv = [(S._mu_of_word_(W(ZZ(n).digits(k))) * S.right) for S in shifted_inhomogeneities] v = vector(chain(v, *vv)) @@ -2586,15 +2590,7 @@ def entry(i, kk): mat = Matrix(coefficient_ring, dim_without_corr, dim_without_corr, entry) if not all(S.is_trivial_zero() for S in inhomogeneities.values()): - lower = floor(ll/k**M) - upper = floor((k**(M-1) - k**m + uu)/k**M) - shifted_inhomogeneities = {} - current_row = 0 - for i in inhomogeneities.keys(): - for b in srange(lower, upper + 1): - S_b = inhomogeneities[i].subsequence(1, b) - shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) - current_row += S_b.mu[0].ncols() + shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) mat = block_diagonal_matrix(mat, *[S[0].mu[rem] for S in shifted_inhomogeneities.values()]) @@ -2610,8 +2606,10 @@ def entry(i, kk): mat[ind[(j, d)], dim_without_corr + shifted_inhomogeneities[(rem_d - k**M, dd+1)][1]] = 1 - dim += current_row - dim_without_corr += current_row + dim_of_inhom = shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][1] + \ + shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][0].mu[0].ncols() + dim += dim_of_inhom + dim_without_corr += dim_of_inhom if n1 > 0 and correct_offset: W = Matrix(coefficient_ring, dim_without_corr, 0) @@ -2667,7 +2665,10 @@ def left(self, recurrence_rules): if not all(S.is_trivial_zero() for S in inhomogeneities.values()): shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) - dim = dim + sum(S.mu[0].ncols() for S in shifted_inhomogeneities) + dim += shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][1] + \ + shifted_inhomogeneities[ + list(shifted_inhomogeneities)[-1] + ][0].mu[0].ncols() return vector([1] + (dim - 1)*[0]) From b6740008ea69b15e7f7172fda758a9e3f10db8f8 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 10:13:28 +0100 Subject: [PATCH 050/454] Trac #32921, review item 10: add minimize=True --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index bb4b8a250c0..bef2dc82e15 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2326,7 +2326,7 @@ def shifted_inhomogeneities(self, recurrence_rules): current_row = 0 for i in inhomogeneities.keys(): for b in srange(lower, upper + 1): - S_b = inhomogeneities[i].subsequence(1, b) + S_b = inhomogeneities[i].subsequence(1, b, minimize=True) shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) current_row += S_b.mu[0].ncols() From df04b444b3eaf5de44317d9ea7b3594afa81b784 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 11:00:25 +0100 Subject: [PATCH 051/454] Trac #32921: use assert really as statement... --- src/sage/combinat/k_regular_sequence.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index bef2dc82e15..78617de9b64 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2331,9 +2331,9 @@ def shifted_inhomogeneities(self, recurrence_rules): current_row += S_b.mu[0].ncols() CR = self.coefficient_ring - assert(all([S[0].left[0] == CR(1) for S in shifted_inhomogeneities.values()]) and - all([all(a == CR(0) for a in S[0].left[1:]) - for S in shifted_inhomogeneities.values()])) + assert (all([S[0].left[0] == CR(1) for S in shifted_inhomogeneities.values()]) and + all([all(a == CR(0) for a in S[0].left[1:]) + for S in shifted_inhomogeneities.values()])) return shifted_inhomogeneities From 8d1721dcb7fb3151aaed203df1c12c29bec1e3c2 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 13:13:04 +0100 Subject: [PATCH 052/454] Trac #32921, review item 10: simplify assert --- src/sage/combinat/k_regular_sequence.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 78617de9b64..071f63bb99a 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2331,9 +2331,8 @@ def shifted_inhomogeneities(self, recurrence_rules): current_row += S_b.mu[0].ncols() CR = self.coefficient_ring - assert (all([S[0].left[0] == CR(1) for S in shifted_inhomogeneities.values()]) and - all([all(a == CR(0) for a in S[0].left[1:]) - for S in shifted_inhomogeneities.values()])) + assert all([S[0].left[0] == CR(1) and S[0].left[1:] == vector(len(S[0].left[1:])*[CR(0)]) + for S in shifted_inhomogeneities.values()]) return shifted_inhomogeneities From 1f73dfe2651b411f0ba1bb083e9a5e8a42d08ba2 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 13:41:31 +0100 Subject: [PATCH 053/454] Trac #32921, review item 10: mention standard unit vector in documentation --- src/sage/combinat/k_regular_sequence.py | 1 + src/sage/combinat/recognizable_series.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 071f63bb99a..7a284946778 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2311,6 +2311,7 @@ def shifted_inhomogeneities(self, recurrence_rules): """ from sage.arith.srange import srange from sage.functions.other import floor + from sage.modules.free_module_element import vector k = self.k M = recurrence_rules.M diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 8c65e0356ff..f0be8b5af61 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -931,6 +931,12 @@ def minimized(self): This method implements the minimization algorithm presented in Chapter 2 of [BR2010a]_. + .. NOTE:: + + Due to the algorithm, the vector :meth:`RecognizableSeries.left` + is always `(1, 0, \ldots, 0)`, i.e., the first vector of the + standard basis. + EXAMPLES:: sage: from itertools import islice @@ -948,6 +954,8 @@ def minimized(self): [3 0] [ 0 1] [6 1], [-6 5], (1, 0), (0, 1) ) + sage: M.left == vector([1, 0]) + True sage: all(c == d and v == w ....: for (c, v), (d, w) in islice(zip(iter(S), iter(M)), 20)) True From e505a43b4c68c8b9d6f3fd61557afbc490391c34 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 14:13:34 +0100 Subject: [PATCH 054/454] Trac #32921, review item 6: fix upper bound --- src/sage/combinat/k_regular_sequence.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 7a284946778..9f3dbd72f38 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2297,13 +2297,15 @@ def shifted_inhomogeneities(self, recurrence_rules): 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... sage: RR = namedtuple('recurrence_rules', ....: ['M', 'm', 'll', 'uu', 'inhomogeneities']) - sage: recurrence_rules = RR(M=3, m=0, ll=-8, uu=14, + sage: recurrence_rules = RR(M=3, m=0, ll=-14, uu=14, ....: inhomogeneities={0: S}) sage: RP.shifted_inhomogeneities(recurrence_rules) - {(0, -1): (2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., 0), - (0, 0): (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., 4), - (0, 1): (2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., 6), - (0, 2): (2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ..., 9)} + {(0, -2): (2-regular sequence 0, 0, 0, 1, 1, 2, 1, 2, 2, 3, ..., 0), + (0, -1): (2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., 5), + (0, 0): (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., 9), + (0, 1): (2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., 11), + (0, 2): (2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ..., 14), + (0, 3): (2-regular sequence 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, ..., 18)} .. SEEALSO:: @@ -2321,7 +2323,7 @@ def shifted_inhomogeneities(self, recurrence_rules): inhomogeneities = recurrence_rules.inhomogeneities lower = floor(ll/k**M) - upper = floor((k**(M-1) - k**m + uu)/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + 1 shifted_inhomogeneities = {} current_row = 0 From 0f2f415753c7f0d9922903cf692245af24f1ee70 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 16:44:22 +0100 Subject: [PATCH 055/454] Trac #32921: fix copy/paste error in test... --- src/sage/combinat/k_regular_sequence.py | 39 ++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 9f3dbd72f38..994ab374c98 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1069,7 +1069,7 @@ def from_recurrence(self, *args, **kwds): ....: f(8*n) == 2*f(4*n), ....: f(8*n + 1) == f(4*n + 1), ....: f(8*n + 2) == f(4*n + 1), - ....: f(8*n + 3) == -f(4*n + 1) + f(4*n + 2), + ....: f(8*n + 3) == f(4*n + 2), ....: f(8*n + 4) == 2*f(4*n + 2), ....: f(8*n + 5) == f(4*n + 3), ....: f(8*n + 6) == -f(4*n + 1), @@ -2307,6 +2307,43 @@ def shifted_inhomogeneities(self, recurrence_rules): (0, 2): (2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ..., 14), (0, 3): (2-regular sequence 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, ..., 18)} + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: var('n') + n + sage: function('f') + f + sage: UB = Seq2.from_recurrence([ + ....: f(8*n) == 2*f(4*n), + ....: f(8*n + 1) == f(4*n + 1), + ....: f(8*n + 2) == f(4*n + 1) + f(4*n + 3), + ....: f(8*n + 3) == -f(4*n + 1) + f(4*n + 2), + ....: f(8*n + 4) == 2*f(4*n + 2), + ....: f(8*n + 5) == f(4*n + 3), + ....: f(8*n + 6) == -f(4*n + 1) + f(4*n + 2) + f(4*n + 3), + ....: f(8*n + 7) == 2*f(4*n + 1) + f(4*n + 3), + ....: f(0) == 1, f(1) == 2, f(2) == 2, f(3) == 4, f(4) == 2, + ....: f(5) == 4, f(6) == 6, f(7) == 0, f(8) == 4, f(9) == 4, + ....: f(10) == 4, f(11) == 4, f(12) == 12, f(13) == 0, f(14) == 4, + ....: f(15) == 4, f(16) == 8, f(17) == 4, f(18) == 8, f(19) == 0, + ....: f(20) == 8, f(21) == 4, f(22) == 4, f(23) == 8], f, n, offset=3) + sage: recurrence_rules_UB = RR(M=3, m=2, ll=0, uu=9, + ....: inhomogeneities={2: UB.subsequence(4, 3), 3: -UB.subsequence(4, 1), + ....: 6: UB.subsequence(4, 2) + UB.subsequence(4, 3)}) + sage: RP.shifted_inhomogeneities(recurrence_rules_UB) + {(2, 0): (2-regular sequence 4, 0, 4, 4, 0, 8, 4, 4, 4, 8, ..., 0), + (2, 1): (2-regular sequence 0, 4, 4, 0, 8, 4, 4, 4, 8, 0, ..., 6), + (2, 2): (2-regular sequence 4, 4, 0, 8, 4, 4, 4, 8, 0, 16, ..., 14), + (3, 0): (2-regular sequence -2, -4, -4, 0, -4, -4, 0, -4, -4, 0, ..., 23), + (3, 1): (2-regular sequence -4, -4, 0, -4, -4, 0, -4, -4, 0, -4, ..., 29), + (3, 2): (2-regular sequence -4, 0, -4, -4, 0, -4, -4, 0, -4, -8, ..., 35), + (6, 0): (2-regular sequence 6, 6, 8, 8, 8, 12, 8, 12, 8, 12, ..., 43), + (6, 1): (2-regular sequence 6, 8, 8, 8, 12, 8, 12, 8, 12, 12, ..., 49), + (6, 2): (2-regular sequence 8, 8, 8, 12, 8, 12, 8, 12, 12, 24, ..., 56)} + sage: RP.shifted_inhomogeneities(recurrence_rules_UB)[(6, 2)][0].mu[0].ncols() + 9 + .. SEEALSO:: :meth:`kRegularSequenceSpace.from_recurrence` From f5b81af717f080c65bc827b0e2a5b4b479741de1 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 16:47:10 +0100 Subject: [PATCH 056/454] Trac #32921: simplify construction of matrix and right vector --- src/sage/combinat/k_regular_sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 994ab374c98..3c784bafac2 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2428,7 +2428,8 @@ def v_eval_n(self, recurrence_rules, n): Seq = list(inhomogeneities.values())[0].parent() W = Seq.indices() shifted_inhomogeneities = [S[0] for S in self.shifted_inhomogeneities(recurrence_rules).values()] - vv = [(S._mu_of_word_(W(ZZ(n).digits(k))) * S.right) for S in shifted_inhomogeneities] + vv = [(S.coefficient_of_word(W(ZZ(n).digits(k)), multiply_left=False)) + for S in shifted_inhomogeneities] v = vector(chain(v, *vv)) return v @@ -2639,11 +2640,10 @@ def entry(i, kk): rem_d = k**(M-1)*rem + (d%k**M) dd = d // k**M if rem_d < k**M and rem_d in inhomogeneities.keys(): - mat[ind[(j, d)], - dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 + mat[i, dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 elif rem_d >= k**M and rem_d - k**M in inhomogeneities.keys(): - mat[ind[(j, d)], - dim_without_corr + shifted_inhomogeneities[(rem_d - k**M, dd+1)][1]] = 1 + mat[i, dim_without_corr + + shifted_inhomogeneities[(rem_d - k**M, dd + 1)][1]] = 1 dim_of_inhom = shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][1] + \ shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][0].mu[0].ncols() From 6294eb114ffee5e44c76a2092b5a4476055bbf16 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 19 Jan 2022 19:26:45 +0100 Subject: [PATCH 057/454] Trac #32921: allow constants as inhomogeneities, add tests for input --- src/sage/combinat/k_regular_sequence.py | 60 ++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 3c784bafac2..a0c1c9a3bc1 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -837,7 +837,7 @@ def from_recurrence(self, *args, **kwds): - ``inhomogeneities`` -- (default: ``{}``) a dictionary mapping integers ``r`` to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. All inhomogeneities have to be - regular sequences from ``self``. + regular sequences from ``self`` or elements of ``coefficient_ring``. OUTPUT: a :class:`kRegularSequence` @@ -908,14 +908,13 @@ def from_recurrence(self, *args, **kwds): Alternatively, we can also use the simpler but inhomogeneous recurrence relations `S(2n) = S(n)` and `S(2n+1) = S(n) + 1` via direct parameters:: - sage: one = Seq2.one_hadamard() - sage: S = Seq2.from_recurrence(M=1, m=0, + sage: S3 = Seq2.from_recurrence(M=1, m=0, ....: coeffs={(0, 0): 1, (1, 0): 1}, ....: initial_values={0: 0, 1: 1}, - ....: inhomogeneities={1: one}) - sage: S + ....: inhomogeneities={1: 1}) + sage: S3 2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ... - sage: (S - S2).is_trivial_zero() + sage: (S3 - S2).is_trivial_zero() True Number of Non-Zero Elements in the Generalized Pascal's Triangle (see [LRS2017]_):: @@ -1934,6 +1933,37 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) TESTS:: + sage: var('n') + n + sage: RP.parameters(1, 0, {(0, 0): 1}, {}, 0, + ....: {-1: 0, 1: 0, 10: 0, I: 0, n: 0}) + Traceback (most recent call last): + ... + ValueError: Indices [-1, 10, I, n] for inhomogeneities are + no integers between 0 and 1. + + :: + + sage: RP.parameters(1, 0, {(0, 0): 1}, {}, 0, + ....: {0: n}) + Traceback (most recent call last): + ... + ValueError: Inhomogeneities {0: n} are neither 2-regular sequences + nor elements of Integer Ring. + + :: + + sage: Seq3 = kRegularSequenceSpace(3, ZZ) + sage: RP.parameters(1, 0, {(0, 0): 1}, {}, 0, + ....: {0: Seq3.some_elements()[0]}) + Traceback (most recent call last): + ... + ValueError: Inhomogeneities {0: 3-regular sequence 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, ...} are neither 2-regular sequences nor elements of + Integer Ring. + + :: + sage: RP.parameters(1, 0, {(0, 0): 1}, {}, 0) Traceback (most recent call last): ... @@ -1971,6 +2001,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) """ from collections import namedtuple + from sage.arith.srange import srange from sage.functions.other import ceil, floor coefficient_ring = self.coefficient_ring @@ -1993,6 +2024,23 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) n1 = offset - floor(ll/k**M) dim = (k**M - 1)/(k - 1) + (M - m)*(uu - ll - k**m + 1) + n1 + if inhomogeneities: + invalid_indices = [i for i in inhomogeneities + if i not in srange(k**M)] + if invalid_indices: + raise ValueError(f"Indices {invalid_indices} for inhomogeneities are no " + f"integers between 0 and {k**M - 1}.") + Seq = kRegularSequenceSpace(k, coefficient_ring) + inhomogeneities.update({i: inhomogeneities[i] * Seq.one_hadamard() + for i in inhomogeneities + if inhomogeneities[i] in coefficient_ring}) + invalid = {i: inhomogeneities[i] for i in inhomogeneities + if not (isinstance(inhomogeneities[i].parent(), kRegularSequenceSpace) and + inhomogeneities[i].parent().k == k)} + if invalid: + raise ValueError(f"Inhomogeneities {invalid} are neither {k}-regular " + f"sequences nor elements of {coefficient_ring}.") + if not initial_values: raise ValueError("No initial values are given.") keys_initial = initial_values.keys() From 94b183a4c1fc7988843ee6927ea43f9fe55321f3 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Fri, 21 Jan 2022 14:58:19 +0100 Subject: [PATCH 058/454] Trac #32921: rewrite construction of matrix, left, right --- src/sage/combinat/k_regular_sequence.py | 69 ++++++++++++++----------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a0c1c9a3bc1..3a5fa06a690 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2410,19 +2410,9 @@ def shifted_inhomogeneities(self, recurrence_rules): lower = floor(ll/k**M) upper = floor((k**(M-1) - k**m + uu)/k**M) + 1 - shifted_inhomogeneities = {} - current_row = 0 - for i in inhomogeneities.keys(): - for b in srange(lower, upper + 1): - S_b = inhomogeneities[i].subsequence(1, b, minimize=True) - shifted_inhomogeneities.update({(i, b): (S_b, current_row)}) - current_row += S_b.mu[0].ncols() - - CR = self.coefficient_ring - assert all([S[0].left[0] == CR(1) and S[0].left[1:] == vector(len(S[0].left[1:])*[CR(0)]) - for S in shifted_inhomogeneities.values()]) - - return shifted_inhomogeneities + return {i: inhomogeneities[i].subsequence(1, {b: 1 for b in srange(lower, upper + 1)}, + minimize=False) + for i in inhomogeneities} def v_eval_n(self, recurrence_rules, n): r""" @@ -2475,9 +2465,9 @@ def v_eval_n(self, recurrence_rules, n): if not all(S.is_trivial_zero() for S in inhomogeneities.values()): Seq = list(inhomogeneities.values())[0].parent() W = Seq.indices() - shifted_inhomogeneities = [S[0] for S in self.shifted_inhomogeneities(recurrence_rules).values()] + shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) vv = [(S.coefficient_of_word(W(ZZ(n).digits(k)), multiply_left=False)) - for S in shifted_inhomogeneities] + for S in shifted_inhomogeneities.values()] v = vector(chain(v, *vv)) return v @@ -2633,6 +2623,8 @@ def matrix(self, recurrence_rules, rem, correct_offset=True): :meth:`kRegularSequenceSpace.from_recurrence` """ + from itertools import chain + from sage.arith.srange import srange from sage.functions.other import floor from sage.matrix.constructor import Matrix @@ -2679,24 +2671,41 @@ def entry(i, kk): if not all(S.is_trivial_zero() for S in inhomogeneities.values()): shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) + lower = floor(ll/k**M) + upper = floor((k**(M-1) - k**m + uu)/k**M) + 1 - mat = block_diagonal_matrix(mat, *[S[0].mu[rem] - for S in shifted_inhomogeneities.values()]) - - for i in srange(dim_without_corr - k**(M-1) + k**m - uu + ll - 1, dim_without_corr): - j, d = ind[i] # j = M - 1 + def wanted_inhomogeneity(row): + j, d = ind[row] + if j != M - 1: + return (None, None) rem_d = k**(M-1)*rem + (d%k**M) dd = d // k**M if rem_d < k**M and rem_d in inhomogeneities.keys(): - mat[i, dim_without_corr + shifted_inhomogeneities[(rem_d, dd)][1]] = 1 + return (rem_d, dd) elif rem_d >= k**M and rem_d - k**M in inhomogeneities.keys(): - mat[i, dim_without_corr + - shifted_inhomogeneities[(rem_d - k**M, dd + 1)][1]] = 1 + return (rem_d - k**M, dd + 1) + else: + return (None, None) + + def left_for_inhomogeneity(wanted): + return list(chain(*[(wanted[1] == i and wanted[0] == r)*inhomogeneities[r].left + for r in inhomogeneities + for i in srange(lower, upper + 1)])) + + def matrix_row(row): + wanted = wanted_inhomogeneity(row) + return list(mat[row]) + left_for_inhomogeneity(wanted) + + mat = Matrix([matrix_row(row) for row in srange(dim_without_corr)]) + mat_inhomog = block_diagonal_matrix([S.mu[rem] + for S in shifted_inhomogeneities.values()]) + + mat = block_matrix([[mat], + [block_matrix([[zero_matrix(mat_inhomog.nrows(), dim_without_corr), + mat_inhomog]])]]) - dim_of_inhom = shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][1] + \ - shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][0].mu[0].ncols() - dim += dim_of_inhom - dim_without_corr += dim_of_inhom + dim_without_corr = mat.ncols() + dim = dim_without_corr + n1 if n1 > 0 and correct_offset: W = Matrix(coefficient_ring, dim_without_corr, 0) @@ -2752,10 +2761,8 @@ def left(self, recurrence_rules): if not all(S.is_trivial_zero() for S in inhomogeneities.values()): shifted_inhomogeneities = self.shifted_inhomogeneities(recurrence_rules) - dim += shifted_inhomogeneities[list(shifted_inhomogeneities)[-1]][1] + \ - shifted_inhomogeneities[ - list(shifted_inhomogeneities)[-1] - ][0].mu[0].ncols() + dim += sum(shifted_inhomogeneities[i].mu[0].ncols() + for i in shifted_inhomogeneities) return vector([1] + (dim - 1)*[0]) From 27a8f5ddac30badfc9a19249361efdb0ca97f68f Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Fri, 21 Jan 2022 14:59:15 +0100 Subject: [PATCH 059/454] Trac #32921: adapt tests --- src/sage/combinat/k_regular_sequence.py | 34 ++++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 3a5fa06a690..745cd835be1 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2030,6 +2030,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) if invalid_indices: raise ValueError(f"Indices {invalid_indices} for inhomogeneities are no " f"integers between 0 and {k**M - 1}.") + Seq = kRegularSequenceSpace(k, coefficient_ring) inhomogeneities.update({i: inhomogeneities[i] * Seq.one_hadamard() for i in inhomogeneities @@ -2346,14 +2347,9 @@ def shifted_inhomogeneities(self, recurrence_rules): sage: RR = namedtuple('recurrence_rules', ....: ['M', 'm', 'll', 'uu', 'inhomogeneities']) sage: recurrence_rules = RR(M=3, m=0, ll=-14, uu=14, - ....: inhomogeneities={0: S}) + ....: inhomogeneities={0: S, 1: S}) sage: RP.shifted_inhomogeneities(recurrence_rules) - {(0, -2): (2-regular sequence 0, 0, 0, 1, 1, 2, 1, 2, 2, 3, ..., 0), - (0, -1): (2-regular sequence 0, 0, 1, 1, 2, 1, 2, 2, 3, 1, ..., 5), - (0, 0): (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., 9), - (0, 1): (2-regular sequence 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., 11), - (0, 2): (2-regular sequence 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, ..., 14), - (0, 3): (2-regular sequence 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, ..., 18)} + {0: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ...} TESTS:: @@ -2376,21 +2372,17 @@ def shifted_inhomogeneities(self, recurrence_rules): ....: f(10) == 4, f(11) == 4, f(12) == 12, f(13) == 0, f(14) == 4, ....: f(15) == 4, f(16) == 8, f(17) == 4, f(18) == 8, f(19) == 0, ....: f(20) == 8, f(21) == 4, f(22) == 4, f(23) == 8], f, n, offset=3) + sage: inhomogeneities={2: UB.subsequence(4, 3), 3: -UB.subsequence(4, 1), + ....: 6: UB.subsequence(4, 2) + UB.subsequence(4, 3)} sage: recurrence_rules_UB = RR(M=3, m=2, ll=0, uu=9, - ....: inhomogeneities={2: UB.subsequence(4, 3), 3: -UB.subsequence(4, 1), - ....: 6: UB.subsequence(4, 2) + UB.subsequence(4, 3)}) - sage: RP.shifted_inhomogeneities(recurrence_rules_UB) - {(2, 0): (2-regular sequence 4, 0, 4, 4, 0, 8, 4, 4, 4, 8, ..., 0), - (2, 1): (2-regular sequence 0, 4, 4, 0, 8, 4, 4, 4, 8, 0, ..., 6), - (2, 2): (2-regular sequence 4, 4, 0, 8, 4, 4, 4, 8, 0, 16, ..., 14), - (3, 0): (2-regular sequence -2, -4, -4, 0, -4, -4, 0, -4, -4, 0, ..., 23), - (3, 1): (2-regular sequence -4, -4, 0, -4, -4, 0, -4, -4, 0, -4, ..., 29), - (3, 2): (2-regular sequence -4, 0, -4, -4, 0, -4, -4, 0, -4, -8, ..., 35), - (6, 0): (2-regular sequence 6, 6, 8, 8, 8, 12, 8, 12, 8, 12, ..., 43), - (6, 1): (2-regular sequence 6, 8, 8, 8, 12, 8, 12, 8, 12, 12, ..., 49), - (6, 2): (2-regular sequence 8, 8, 8, 12, 8, 12, 8, 12, 12, 24, ..., 56)} - sage: RP.shifted_inhomogeneities(recurrence_rules_UB)[(6, 2)][0].mu[0].ncols() - 9 + ....: inhomogeneities=inhomogeneities) + sage: shifted_inhomog = RP.shifted_inhomogeneities(recurrence_rules_UB) + sage: shifted_inhomog + {2: 2-regular sequence 8, 8, 8, 12, 12, 16, 12, 16, 12, 24, ..., + 3: 2-regular sequence -10, -8, -8, -8, -8, -8, -8, -8, -8, -12, ..., + 6: 2-regular sequence 20, 22, 24, 28, 28, 32, 28, 32, 32, 48, ...} + sage: shifted_inhomog[2].mu[0].ncols() == 3*inhomogeneities[2].mu[0].ncols() + True .. SEEALSO:: From 56c548f480aa47798f4576813532e2a1610b2477 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Fri, 21 Jan 2022 15:43:32 +0100 Subject: [PATCH 060/454] Trac #32921: add some further doctests --- src/sage/combinat/k_regular_sequence.py | 36 ++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index b8756cc8649..a6687caa12e 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1194,6 +1194,14 @@ def from_recurrence(self, *args, **kwds): ....: f(0) == 1, f(1) == 1, f(2) == 2, f(3) == 3], f, n, offset=2) 2-regular sequence 1, 1, 2, 3, 0, 0, 0, 0, 0, 0, ... + Check if inhomogeneities `0` do not change the sequence:: + + sage: Seq2.from_recurrence([ + ....: f(2*n) == 0, f(2*n + 1) == 0, + ....: f(0) == 1, f(1) == 1, f(2) == 2, f(3) == 3], f, n, offset=2, + ....: inhomogeneities={0: 0, 1: Seq2.zero()}) + 2-regular sequence 1, 1, 2, 3, 0, 0, 0, 0, 0, 0, ... + :: sage: set_random_seed() @@ -2084,14 +2092,15 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) sage: RP.parameters(2, 1, ....: {(0, -2): 3, (0, 0): 1, (0, 1): 2, (1, -2): 6, (1, 0): 4, ....: (1, 1): 5, (2, -2): 9, (2, 0): 7, (2, 1): 8, (3, -2): 12, - ....: (3, 0): 10, (3, 1): 11}, {0: 1, 1: 2, 2: 1, 3: 4}, 0) + ....: (3, 0): 10, (3, 1): 11}, {0: 1, 1: 2, 2: 1, 3: 4}, 0, {0: 1}) recurrence_rules(M=2, m=1, l=-2, u=1, ll=-6, uu=3, dim=14, coeffs={(0, -2): 3, (0, 0): 1, (0, 1): 2, (1, -2): 6, (1, 0): 4, (1, 1): 5, (2, -2): 9, (2, 0): 7, (2, 1): 8, (3, -2): 12, (3, 0): 10, (3, 1): 11}, initial_values={0: 1, 1: 2, 2: 1, 3: 4, - 4: 12, 5: 30, 6: 48, 7: 66, 8: 75, 9: 204, 10: 333, 11: 462, - 12: 216, 13: 594, -6: 0, -5: 0, -4: 0, -3: 0, -2: 0, -1: 0}, - offset=1, n1=3, inhomogeneities={}) + 4: 13, 5: 30, 6: 48, 7: 66, 8: 77, 9: 208, 10: 340, 11: 472, + 12: 220, 13: 600, -6: 0, -5: 0, -4: 0, -3: 0, -2: 0, -1: 0}, + offset=1, n1=3, inhomogeneities={0: 2-regular sequence 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, ...}) .. SEEALSO:: @@ -2368,6 +2377,15 @@ def values(self, *, M, m, l, u, ll, coeffs, ....: coeffs={(0, 0): 0, (1, 1): 0}, initial_values={}, ....: last_value_needed=10, offset=0, inhomogeneities={}) {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0} + + :: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: RP.values(M=1, m=0, l=0, u=0, ll=0, + ....: coeffs={(0, 0): 0, (1, 1): 0}, initial_values={}, + ....: last_value_needed=10, offset=0, + ....: inhomogeneities={0: Seq2.one_hadamard()}) + {0: 1, 1: 0, 2: 1, 3: 0, 4: 1, 5: 0, 6: 1, 7: 0, 8: 1, 9: 0, 10: 1} """ from sage.arith.srange import srange from sage.rings.integer_ring import ZZ @@ -2906,6 +2924,16 @@ def left(self, recurrence_rules): sage: RP.left(recurrence_rules) (1, 0, 0, 0, 0) + :: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: RRD = namedtuple('recurrence_rules_dim', + ....: ['M', 'm', 'll', 'uu', 'dim', 'inhomogeneities']) + sage: recurrence_rules = RRD(M=3, m=2, ll=0, uu=9, dim=5, + ....: inhomogeneities={0: Seq2.one_hadamard()}) + sage: RP.left(recurrence_rules) + (1, 0, 0, 0, 0, 0, 0, 0) + .. SEEALSO:: :meth:`kRegularSequenceSpace.from_recurrence` From aa2fb415e3a6b38665728e4b8a9b1f9007cec013 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:14:56 +0100 Subject: [PATCH 061/454] Trac #32921, review item 15: list --> dictionary --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index a6687caa12e..ff1c21c2886 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2508,7 +2508,7 @@ def ind(self, M, m, ll, uu): def shifted_inhomogeneities(self, recurrence_rules): r""" - Return a list of all needed shifted inhomogeineities as described + Return a dictionary of all needed shifted inhomogeineities as described in the proof of Coroallary D in [HKL2021]_. INPUT: @@ -2516,7 +2516,7 @@ def shifted_inhomogeneities(self, recurrence_rules): - ``recurrence_rules`` -- a namedtuple generated by :meth:`parameters` - OUTPUT: a list + OUTPUT: a dictionary EXAMPLES:: From 26c86880ced304d452651e1714baaf2adc2c3afd Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:16:10 +0100 Subject: [PATCH 062/454] Trac #32921, review item 16: fix typo --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ff1c21c2886..510d462bf45 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2508,7 +2508,7 @@ def ind(self, M, m, ll, uu): def shifted_inhomogeneities(self, recurrence_rules): r""" - Return a dictionary of all needed shifted inhomogeineities as described + Return a dictionary of all needed shifted inhomogeneities as described in the proof of Coroallary D in [HKL2021]_. INPUT: From 7983b5ef45366a3d4332afd3f2679da4ddcc96b6 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:20:34 +0100 Subject: [PATCH 063/454] Trac #32921, review item 18: rephrase docstring --- src/sage/combinat/recognizable_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index f0be8b5af61..9351a06e3ee 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -933,7 +933,7 @@ def minimized(self): .. NOTE:: - Due to the algorithm, the vector :meth:`RecognizableSeries.left` + Due to the algorithm, the left vector of the result is always `(1, 0, \ldots, 0)`, i.e., the first vector of the standard basis. From 7b0e213ffe866b137f907ce54dfdd4f9f4237c4e Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:23:21 +0100 Subject: [PATCH 064/454] Trac #32921, review item 19: simplify condition in left_for_inhomogeneity --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 510d462bf45..0340cefd7a9 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2864,7 +2864,7 @@ def wanted_inhomogeneity(row): return (None, None) def left_for_inhomogeneity(wanted): - return list(chain(*[(wanted[1] == i and wanted[0] == r)*inhomogeneities[r].left + return list(chain(*[(wanted == (r, i))*inhomogeneities[r].left for r in inhomogeneities for i in srange(lower, upper + 1)])) From 613976a29dbcaae177245b3a8e6fd769ef7a760e Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:26:41 +0100 Subject: [PATCH 065/454] Trac #32921, review item 20 --- src/sage/combinat/k_regular_sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 0340cefd7a9..8f5756b83cb 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2870,15 +2870,15 @@ def left_for_inhomogeneity(wanted): def matrix_row(row): wanted = wanted_inhomogeneity(row) - return list(mat[row]) + left_for_inhomogeneity(wanted) + return left_for_inhomogeneity(wanted) - mat = Matrix([matrix_row(row) for row in srange(dim_without_corr)]) + mat_upper_right = Matrix([matrix_row(row) for row in srange(dim_without_corr)]) mat_inhomog = block_diagonal_matrix([S.mu[rem] for S in shifted_inhomogeneities.values()]) - mat = block_matrix([[mat], - [block_matrix([[zero_matrix(mat_inhomog.nrows(), dim_without_corr), - mat_inhomog]])]]) + mat = block_matrix([[mat, mat_upper_right], + [zero_matrix(mat_inhomog.nrows(), dim_without_corr), + mat_inhomog]]) dim_without_corr = mat.ncols() dim = dim_without_corr + n1 From afa97f1a7bcf0b8fa0821ec975427cda84f5f3ea Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:28:41 +0100 Subject: [PATCH 066/454] Trac #32921, review item 21: remove unnecessary condition --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 8f5756b83cb..ce0ca66bc37 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2856,9 +2856,9 @@ def wanted_inhomogeneity(row): return (None, None) rem_d = k**(M-1)*rem + (d%k**M) dd = d // k**M - if rem_d < k**M and rem_d in inhomogeneities.keys(): + if rem_d < k**M: return (rem_d, dd) - elif rem_d >= k**M and rem_d - k**M in inhomogeneities.keys(): + elif rem_d >= k**M: return (rem_d - k**M, dd + 1) else: return (None, None) From bcb5c255881ea527efb2401759f54afe240e57fb Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Sat, 22 Jan 2022 13:31:23 +0100 Subject: [PATCH 067/454] Trac #32921, review item 22: simplify left_for_inhomogeneity --- src/sage/combinat/k_regular_sequence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index ce0ca66bc37..247f152a32b 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2864,8 +2864,8 @@ def wanted_inhomogeneity(row): return (None, None) def left_for_inhomogeneity(wanted): - return list(chain(*[(wanted == (r, i))*inhomogeneities[r].left - for r in inhomogeneities + return list(chain(*[(wanted == (r, i))*inhomogeneity.left + for r, inhomogeneity in inhomogeneities.items() for i in srange(lower, upper + 1)])) def matrix_row(row): From fdb70b75c19cd0a220e4665bfaa84c60ee6ec868 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Mon, 24 Jan 2022 13:32:40 +0100 Subject: [PATCH 068/454] Trac #32921, review item 1: use sequence with well-chosen linear representation --- src/sage/combinat/k_regular_sequence.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 247f152a32b..dfe4479478d 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -1204,15 +1204,15 @@ def from_recurrence(self, *args, **kwds): :: - sage: set_random_seed() - sage: S = Seq2((random_matrix(ZZ, 3, 3), random_matrix(ZZ, 3, 3)), - ....: left=vector([randint(-2, 2) for i in srange(3)]), - ....: right=vector([randint(-2, 2) for i in srange(3)])) + sage: S = Seq2([matrix([[3/2, -1, 1], [0, 1/2, 1/2], [0, -1, 2]]), + ....: matrix([[-1, 0, 1], [1, 5, -5], [-4, 0, 0]])], + ....: left=vector([1, 2, 3]), + ....: right=vector([0, 1, 1])) sage: T = Seq2.from_recurrence(M=3, m=2, ....: coeffs={}, ....: initial_values={0: S[0]}, ....: inhomogeneities={i: S.subsequence(2**3, i) for i in srange(2**3)}) - sage: all(S[i] == T[i] for i in srange(100)) + sage: (S - T).is_trivial_zero() True Connection between the Stern--Brocot sequence and the number From 34850f6b3811528862b0c4dc496c644198d2719a Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 1 Feb 2022 20:06:43 +0100 Subject: [PATCH 069/454] Trac #32921, review item 17: use @cached_method --- src/sage/combinat/k_regular_sequence.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index dfe4479478d..92c0eb4b03d 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2506,6 +2506,12 @@ def ind(self, M, m, ll, uu): return ind + @cached_method(key=lambda self, recurrence_rules: + (recurrence_rules.M, + recurrence_rules.m, + recurrence_rules.ll, + recurrence_rules.uu, + tuple(recurrence_rules.inhomogeneities.items()))) def shifted_inhomogeneities(self, recurrence_rules): r""" Return a dictionary of all needed shifted inhomogeneities as described From af9f496fe3d3d863bc36f48a4d1db71b8210a2b0 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Tue, 1 Feb 2022 20:27:18 +0100 Subject: [PATCH 070/454] Trac #32921, review item 15: add description for output --- src/sage/combinat/k_regular_sequence.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 92c0eb4b03d..699ddfc2b22 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2522,7 +2522,17 @@ def shifted_inhomogeneities(self, recurrence_rules): - ``recurrence_rules`` -- a namedtuple generated by :meth:`parameters` - OUTPUT: a dictionary + OUTPUT: + + A dictionary mapping `r` to the regular sequence + `\sum_i g_r(n + i)` for `g_r` as given in [HKL2021]_, Corollary D, + and `i` between `\lfloor\ell'/k^{M}\rfloor` and + `\lfloor (k^{M-1} - k^{m} + u')/k^{M}\rfloor + 1`; see [HKL2021]_, + proof of Corollary D. The first blocks of the corresponding + vector-valued sequence (obtained from its linear + representation) correspond to the sequences `g_r(n + i)` where + `i` is as in the sum above; the remaining blocks consist of + other shifts which are required for the regular sequence. EXAMPLES:: From 0866d861ef3e112f048328d7b5c8d884d9ff5324 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 2 Mar 2022 11:41:54 +0000 Subject: [PATCH 071/454] Typo fix --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index bbb09e2def6..44422d073ba 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3542,7 +3542,7 @@ def divisor_to_divisor_list(self, divisor): eps = self._RR(2)**(-self._prec+3) dl = [] - PZ = PolynomialRing(S._R.base(), 'z').fraction_field() + PZ = PolynomialRing(self._R.base(), 'z').fraction_field() RF = PolynomialRing(PZ, 'w') for d in divisor.support(): From d8ccc5a55fd7afa9c3fe4ad8604e6522453bd07d Mon Sep 17 00:00:00 2001 From: Dennis Jahn Date: Thu, 14 Apr 2022 09:30:47 +0200 Subject: [PATCH 072/454] added flag to bruhat_lower_covers_reflections() similar to bruhat_lower_covers and bruhat_upper_covers, this fixed a bug only appearing while using the permutation implementation - see doctest --- src/sage/categories/coxeter_groups.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 58f80fa120e..0025d1bb255 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -2176,12 +2176,25 @@ def bruhat_lower_covers_reflections(self): sage: w.bruhat_lower_covers_reflections() [(s1*s2*s1, s1*s2*s3*s2*s1), (s3*s2*s1, s2), (s3*s1*s2, s1)] + TESTS: + + Check bug discovered in :trac:`32669` is fixed:: + + sage: W = CoxeterGroup(['A',3], implementation='permutation') + sage: W.w0.bruhat_lower_covers_reflections() + [((1,3,7,9)(2,11,6,10)(4,8,5,12), (2,5)(3,9)(4,6)(8,11)(10,12)), + ((1,11)(3,10)(4,9)(5,7)(6,12), (1,4)(2,8)(3,5)(7,10)(9,11)), + ((1,9,7,3)(2,10,6,11)(4,12,5,8), (1,7)(2,4)(5,6)(8,10)(11,12))] """ - i = self.first_descent() + i = self.first_descent(side='right') if i is None: return [] - wi = self.apply_simple_reflection(i) - return [(u.apply_simple_reflection(i), r.apply_conjugation_by_simple_reflection(i)) for u, r in wi.bruhat_lower_covers_reflections() if not u.has_descent(i)] + [(wi, self.parent().simple_reflection(i))] + wi = self.apply_simple_reflection(i, side='right') + return [(u.apply_simple_reflection(i, side='right'), + r.apply_conjugation_by_simple_reflection(i)) + for u,r in wi.bruhat_lower_covers_reflections() + if not u.has_descent(i, side='right')] + [ + (wi, self.parent().simple_reflection(i))] def lower_cover_reflections(self, side='right'): r""" From 84b5726017c226182cf917441afc347f22dc1b94 Mon Sep 17 00:00:00 2001 From: Dennis Jahn Date: Thu, 14 Apr 2022 09:31:25 +0200 Subject: [PATCH 073/454] added the method bruhat_cone including references --- .../root_system/reflection_group_real.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py index 310e9f5305b..38bfee60855 100644 --- a/src/sage/combinat/root_system/reflection_group_real.py +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -27,6 +27,11 @@ - Christian Stump (initial version 2011--2015) +REFERENCES: + +.. [Dye] Dyer. *Bruhat intervals, polyhedral cones and Kazhdan-Lusztig-Stanley polynomials*. Math.Z., 215(2):223-236, 1994. +.. [JahStu] Jahn and Stump. *Bruhat intervals, subword complexes and brick polyhedra for finite Coxeter groups*. Preprint, available at :arxiv:`2103.03715`, 2021. + .. WARNING:: Uses the GAP3 package *Chevie* which is available as an @@ -706,6 +711,90 @@ def simple_root_index(self, i): [0, 1, 2] """ return self._index_set_inverse[i] + + def bruhat_cone(self, x, y, side='upper', backend='cdd'): + r""" + Return the (upper or lower) Bruhat cone associated to the interval ``[x,y]``. + + To a cover relation `v \prec w` in strong Bruhat order you can assign a positive + root `\beta` given by the unique reflection `s_\beta` such that `s_\beta v = w`. + + The upper Bruhat cone of the interval `[x,y]` is the non-empty, polyhedral cone generated + by the roots corresponding to `x \prec a` for all atoms `a` in the interval. + The lower Bruhat cone of the interval `[x,y]` is the non-empty, polyhedral cone generated + by the roots corresponding to `c \prec y` for all coatoms `c` in the interval. + + INPUT: + + - ``x`` - an element in the group `W` + + - ``y`` - an element in the group `W` + + - ``side`` (default: ``'upper'``) -- must be one of the following: + + * ``'upper'`` - return the upper Bruhat cone of the interval [``x``, ``y``] + * ``'lower'`` - return the lower Bruhat cone of the interval [``x``, ``y``] + + - ``backend`` -- string (default: ``'cdd'``); the backend to use to create the polyhedron + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: x = W.from_reduced_word([1]) # optional - gap3 + sage: y = W.w0 # optional - gap3 + sage: W.bruhat_cone(x, y) # optional - gap3 + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays + + sage: W = ReflectionGroup(['E',6]) # optional - gap3 + sage: x = W.one() # optional - gap3 + sage: y = W.w0 # optional - gap3 + sage: W.bruhat_cone(x, y, side='lower') # optional - gap3 + A 6-dimensional polyhedron in QQ^6 defined as the convex hull of 1 vertex and 6 rays + + TESTS:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: x = W.one() # optional - gap3 + sage: y = W.w0 # optional - gap3 + sage: W.bruhat_cone(x, y, side='nonsense') # optional - gap3 + Traceback (most recent call last): + ... + ValueError: side must be either 'upper' or 'lower' + + REFERENCES: + + - [Dye]_ + - [JahStu]_ + """ + if side == 'upper': + roots = [self.reflection_to_positive_root(x * r * x.inverse()) + for z, r in x.bruhat_upper_covers_reflections() + if z.bruhat_le(y)] + elif side == 'lower': + roots = [self.reflection_to_positive_root(y * r * y.inverse()) + for z, r in y.bruhat_lower_covers_reflections() + if x.bruhat_le(z)] + else: + raise ValueError("side must be either 'upper' or 'lower'") + + from sage.geometry.polyhedron.constructor import Polyhedron + if self.is_crystallographic(): + return Polyhedron(vertices=[[0] * self.rank()], + rays=roots, + ambient_dim=self.rank(), + backend=backend) + if backend == 'cdd': + from warnings import warn + warn("Using floating point numbers for roots of unity. This might cause numerical errors!") + from sage.rings.real_double import RDF as base_ring + else: + from sage.rings.qqbar import AA as base_ring + + return Polyhedron(vertices=[[0] * self.rank()], + rays=roots, + ambient_dim=self.rank(), + base_ring=base_ring, + backend=backend) class Element(RealReflectionGroupElement, ComplexReflectionGroup.Element): From 8180f9639e4bd555a25e4d7202bba020472f9376 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 14 Apr 2022 10:38:39 +0100 Subject: [PATCH 074/454] Removed old comments and fixed a pycodestyle-minimal error --- .../schemes/riemann_surfaces/riemann_surface.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 2e0ff063901..e90943bcb8b 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3552,9 +3552,6 @@ def divisor_to_divisor_list(self, divisor): g0 = self._R(gs[0]) gis = [sum([PZ(gi.list()[i])*RF.gen()**i for i in range(len(gi.list()))]) for gi in gs[1:]] - #gs = [self._R(gi) for gi in gs] - #g0 = gs[0] - #gis = gs[1:] rs = self._CCz(g0).roots() rys = [] @@ -3562,15 +3559,14 @@ def divisor_to_divisor_list(self, divisor): for r, m in rs: ys = [] for gi in gis: - ers = [gi(y, r).abs() for y in ys] - #ers = [gi(r,y).abs() for y in ys] - try: - ers = min(ers) - except: + # This test is a bit clunky, it surely can be made more efficient. + if len(ys): + ers = min([gi(y, r).abs() for y in ys]) + else: ers = 1 + if not ers<=eps: poly = self._CCw(gi(self._CCw.gen(0), r)) - #poly = self._CCw(gi(r, self._CCw.gen(0))) if poly==0: nys = [] else: From 54acc94c441b29a94c03d77fbc3f409db09d526c Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 14 Apr 2022 10:42:53 +0100 Subject: [PATCH 075/454] Fixed logicals to remove pycodestyle-minimal error --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index e90943bcb8b..cca389f4b8c 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -1661,7 +1661,7 @@ def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): z_start, z_end = downstairs_edge z_start = self._CC(z_start) z_end = self._CC(z_end) - if initial_continuation==None: + if initial_continuation is None: initial_continuation = self.homotopy_continuation(downstairs_edge) currL = initial_continuation windex = upstairs_edge[0][1] @@ -2864,14 +2864,14 @@ def initialise(z, i): fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) for mp in mp_list] - if prec==None: + if prec is None: prec = self._prec # tau here is playing the role of the desired error. tau = self._RR(2)**(-prec+3) ONE = self._RR(1) LAMBDA = self._RR.pi()/2 - if cutoff_individually==None: + if cutoff_individually is None: cutoffs = [0] cutoff_individually = False else: From 2569965fd33576eafede32a4081b93dca2e186ab Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 14 Apr 2022 12:14:33 +0100 Subject: [PATCH 076/454] Fixed errors highlighted by chapoton --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index cca389f4b8c..02128458ba7 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3207,7 +3207,7 @@ def abel_jacobi(self, divisor, verbose=False): r""" Return the Abel-Jacobi map of ``divisor``. - Returns a representative of the Abel-Jacobi map of a divisor with basepoint + Return a representative of the Abel-Jacobi map of a divisor with basepoint ``self._basepoint``. INPUT: @@ -3324,7 +3324,7 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal PM = self.period_matrix() if normalised: - AM = PM[:,0:g] + AM = PM[:, 0:self.genus] AInv = numerical_inverse(AM) PM = AInv*PM @@ -3372,7 +3372,7 @@ def places_at_branch_locus(self): r""" Return the places above the branch locus. - Returns a list of the of places above the branch locus. This must be + Return a list of the of places above the branch locus. This must be done over the base ring, and so the places are given in terms of the factors of the discriminant. Currently, this method only works when ``self._R.base_ring()==QQ`` as for other rings, the function field @@ -3461,8 +3461,6 @@ def strong_approximation(self, divisor, S): KC = C.function_field() g0, g1 = self._R.gens() Kb = FunctionField(K, str(g0)) - Pz = PolynomialRing(K, g0) - Pw = PolynomialRing(K, g1) MO = Kb.maximal_order() D_base = -sum(S) From 268865ae60170d1b4677d9a77e9bdbf7841d7fa1 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 27 Apr 2022 12:18:50 +0100 Subject: [PATCH 077/454] Replaced slow doctest --- .../riemann_surfaces/riemann_surface.py | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 02128458ba7..e783b0b1296 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -59,32 +59,31 @@ True We can look at an extended example of the Abel-Jacobi functionality. We will -show that the sum of the intersections of a bitangent to a quadratic is a -half-canonical divisor. We will use the Edge quartic as the example, which has -bitangent `x=1/2`:: +demonstrate a particular half-canonical divisor on Klein's Curve, known in +the literature.:: - sage: f = 25*(x^4+y^4+1) - 34*(x^2*y^2+x^2+y^2) - sage: S = RiemannSurface(f) + sage: f = x^3*y + y^3 + x + sage: S = RiemannSurface(f, integration_method='rigorous') sage: BL = S.places_at_branch_locus(); BL - [Place (x - 2, (x - 2)*y, y^2 - 17/5, y^3 - 17/5*y), - Place (x + 2, (x + 2)*y, y^2 - 17/5, y^3 - 17/5*y), - Place (x - 1/2, (x - 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), - Place (x + 1/2, (x + 1/2)*y, y^2 - 17/20, y^3 - 17/20*y), - Place (x^4 - 34/25*x^2 + 1, y, y^2, y^3), - Place (x^4 - 34/25*x^2 + 1, (x^4 - 34/25*x^2 + 1)*y, y^2 - 34/25*x^2 - 34/25, y^3 + (-34/25*x^2 - 34/25)*y)] + [Place (x, y, y^2), + Place (x^7 + 27/4, y + 4/9*x^5, y^2 + 4/3*x^3), + Place (x^7 + 27/4, y - 2/9*x^5, y^2 + 1/3*x^3)] We can read off out the output of ``places_at_branch_locus`` to choose our divisor, and we can calculate the canonical divisor using curve functionality:: - sage: D = 1*BL[2] + sage: P0 = 1*BL[0] sage: from sage.schemes.curves.constructor import Curve sage: C = Curve(f) sage: F = C.function_field() - sage: K = (F(x).differential()).divisor() + sage: K = (F(x).differential()).divisor() - F(f.derivative(y)).divisor() + sage: Pinf, Pinf_prime = C.places_at_infinity() + sage: if K-3*Pinf-1*Pinf_prime: Pinf, Pinf_prime = (Pinf_prime, Pinf); + sage: D = P0 + 2*Pinf - Pinf_prime Note we could check using exact techniques that `2D=K`:: - sage: Z = K-2*D + sage: Z = K - 2*D sage: (Z.degree()==0, len(Z.basis_differential_space())==S.genus, len(Z.basis_function_space())==1) (True, True, True) @@ -93,7 +92,7 @@ sage: avoid = C.places_at_infinity() sage: Zeq, _ = S.strong_approximation(Z, avoid) sage: Zlist = S.divisor_to_divisor_list(Zeq) - sage: AJ = S.abel_jacobi(Zlist) # long time (50 seconds) + sage: AJ = S.abel_jacobi(Zlist) # long time (1 second) sage: S.reduce_over_period_lattice(AJ).norm() < 1e-10 # long time True @@ -3236,7 +3235,7 @@ def abel_jacobi(self, divisor, verbose=False): sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) # long time (15 seconds) sage: AJxp = [p*z for z in AJ] # long time - sage: bool(S.reduce_over_period_lattice(AJx2).norm()<1e-7) # long time + sage: bool(S.reduce_over_period_lattice(AJxp).norm()<1e-7) # long time True """ ans = 0 From b5402d06f7a69f2580d7120cb760d199648046d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 24 May 2022 09:54:02 +0200 Subject: [PATCH 078/454] fix typos in errors --- src/sage/monoids/free_monoid_element.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index c6b88e5c9f7..23a6cc709c7 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -48,7 +48,7 @@ class FreeMonoidElement(MonoidElement): sage: x**(-1) Traceback (most recent call last): ... - NotimplementedError + NotImplementedError """ def __init__(self, F, x, check=True): """ @@ -274,7 +274,7 @@ def __invert__(self): sage: x**(-1) Traceback (most recent call last): ... - NotimplementedError + NotImplementedError """ raise NotImplementedError From 9e8b89913fac5b6fde62cc3233be88559ed9d88d Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 25 May 2022 10:23:55 +0200 Subject: [PATCH 079/454] pyflakes: remove unused imports --- src/sage/combinat/k_regular_sequence.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 8b304c7c5b4..4fdf9300a73 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2596,7 +2596,6 @@ def shifted_inhomogeneities(self, recurrence_rules): """ from sage.arith.srange import srange from sage.functions.other import floor - from sage.modules.free_module_element import vector k = self.k M = recurrence_rules.M @@ -2644,7 +2643,6 @@ def v_eval_n(self, recurrence_rules, n): from itertools import chain from sage.arith.srange import srange - from sage.functions.other import floor from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ @@ -2956,8 +2954,6 @@ def left(self, recurrence_rules): :meth:`kRegularSequenceSpace.from_recurrence` """ - from sage.arith.srange import srange - from sage.functions.other import floor from sage.modules.free_module_element import vector dim = recurrence_rules.dim From 3bcf07215a259dadc74eaa5c6949dc0e12c8400a Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Wed, 25 May 2022 10:37:44 +0200 Subject: [PATCH 080/454] fix some error from resolving the merge conflict --- src/sage/combinat/k_regular_sequence.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 4fdf9300a73..71c071db68f 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2914,7 +2914,9 @@ def matrix_row(row): J = J.stack(vector([int(j*k == i - rem) for j in srange(n1)])) Z = zero_matrix(coefficient_ring, n1, dim_without_corr) - return block_matrix([[mat, W], [Z, J]], subdivide=False) + mat = block_matrix([[mat, W], [Z, J]], subdivide=False) + + return mat def left(self, recurrence_rules): r""" From bc77bfd8f3d6cf4fcbf65beb5493e85944f3d7f0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 14 May 2022 11:28:51 -0700 Subject: [PATCH 081/454] bootstrap-conda: Write an additional comment --- bootstrap-conda | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap-conda b/bootstrap-conda index 92aea50f939..7dd6b701128 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -59,6 +59,7 @@ for pkg in $BOOTSTRAP_PACKAGES; do echo " - $pkg" >> environment.yml done sed 's/name: sage-build/name: sage/' environment.yml > src/environment.yml +echo " # Additional packages providing all dependencies for the Sage library" >> src/environment.yml for pkg in $SAGELIB_SYSTEM_PACKAGES; do echo " - $pkg" >> src/environment.yml done From 4c2adf7e74f3783564bd88a78d786bc35c346932 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 14 May 2022 11:59:08 -0700 Subject: [PATCH 082/454] bootstrap-conda: Rewrite using fewer redirects --- bootstrap-conda | 76 +++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/bootstrap-conda b/bootstrap-conda index 7dd6b701128..3cb23bfbac9 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -46,36 +46,50 @@ for PKG_BASE in $(sage-package list --has-file distros/conda.txt); do fi done echo >&2 $0:$LINENO: generate conda enviroment files -echo "name: sage-build" > environment.yml -echo "channels:" >> environment.yml -echo " - conda-forge" >> environment.yml -echo " - nodefaults" >> environment.yml -echo "dependencies:" >> environment.yml -for pkg in $SYSTEM_PACKAGES; do - echo " - $pkg" >> environment.yml -done -echo " # Packages needed for ./bootstrap" >> environment.yml -for pkg in $BOOTSTRAP_PACKAGES; do - echo " - $pkg" >> environment.yml -done -sed 's/name: sage-build/name: sage/' environment.yml > src/environment.yml -echo " # Additional packages providing all dependencies for the Sage library" >> src/environment.yml -for pkg in $SAGELIB_SYSTEM_PACKAGES; do - echo " - $pkg" >> src/environment.yml -done -sed 's/name: sage/name: sage-dev/' src/environment.yml > src/environment-dev.yml -echo " # Additional dev tools" >> src/environment-dev.yml -echo " - openssh" >> src/environment-dev.yml -echo " - pycodestyle" >> src/environment-dev.yml -echo " - pytest" >> src/environment-dev.yml +( + echo "name: sage-build" + echo "channels:" + echo " - conda-forge" + echo " - nodefaults" + echo "dependencies:" + for pkg in $SYSTEM_PACKAGES; do + echo " - $pkg" + done + echo " # Packages needed for ./bootstrap" + for pkg in $BOOTSTRAP_PACKAGES; do + echo " - $pkg" + done +) > environment.yml -cp environment.yml environment-optional.yml - echo " # optional packages" >> environment-optional.yml -for pkg in $OPTIONAL_SYSTEM_PACKAGES; do - echo " - $pkg" >> environment-optional.yml +( + sed 's/name: sage-build/name: sage/' environment.yml + echo " # Additional packages providing all dependencies for the Sage library" + for pkg in $SAGELIB_SYSTEM_PACKAGES; do + echo " - $pkg" done -cp src/environment.yml src/environment-optional.yml - echo " # optional packages" >> src/environment-optional.yml -for pkg in $OPTIONAL_SYSTEM_PACKAGES $SAGELIB_OPTIONAL_SYSTEM_PACKAGES; do - echo " - $pkg" >> src/environment-optional.yml -done +) > src/environment.yml + +DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" +( + sed 's/name: sage/name: sage-dev/' src/environment.yml + echo " # Additional dev tools" + for pkg in $DEVELOP_SYSTEM_PACKAGES; do + echo " - $pkg" + done +) > src/environment-dev.yml + +( + cat environment.yml + echo " # optional packages" + for pkg in $OPTIONAL_SYSTEM_PACKAGES; do + echo " - $pkg" + done +) > environment-optional.yml + +( + cat src/environment.yml + echo " # optional packages" + for pkg in $OPTIONAL_SYSTEM_PACKAGES $SAGELIB_OPTIONAL_SYSTEM_PACKAGES; do + echo " - $pkg" + done +) > src/environment-optional.yml From ac2c48db84c1a59e333552e5abfcdc3229fb9084 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 16 May 2022 15:10:34 -0700 Subject: [PATCH 083/454] build/pkgs/git_trac_command: New --- build/pkgs/git_trac_command/SPKG.rst | 14 ++++++++++++++ build/pkgs/git_trac_command/requirements.txt | 1 + build/pkgs/git_trac_command/trees.txt | 1 + build/pkgs/git_trac_command/type | 1 + 4 files changed, 17 insertions(+) create mode 100644 build/pkgs/git_trac_command/SPKG.rst create mode 100644 build/pkgs/git_trac_command/requirements.txt create mode 100644 build/pkgs/git_trac_command/trees.txt create mode 100644 build/pkgs/git_trac_command/type diff --git a/build/pkgs/git_trac_command/SPKG.rst b/build/pkgs/git_trac_command/SPKG.rst new file mode 100644 index 00000000000..d8da01bd818 --- /dev/null +++ b/build/pkgs/git_trac_command/SPKG.rst @@ -0,0 +1,14 @@ +git_trac_command: Provides the subcommand "git trac" +==================================================== + +Description +----------- + +This module implements a subcommand ``git trac``. +See https://doc.sagemath.org/html/en/developer/git_trac.html + + +Upstream Contact +---------------- + +https://github.com/sagemath/git-trac-command diff --git a/build/pkgs/git_trac_command/requirements.txt b/build/pkgs/git_trac_command/requirements.txt new file mode 100644 index 00000000000..4f36b5eae53 --- /dev/null +++ b/build/pkgs/git_trac_command/requirements.txt @@ -0,0 +1 @@ +git+https://github.com/sagemath/git-trac-command diff --git a/build/pkgs/git_trac_command/trees.txt b/build/pkgs/git_trac_command/trees.txt new file mode 100644 index 00000000000..b268580307d --- /dev/null +++ b/build/pkgs/git_trac_command/trees.txt @@ -0,0 +1 @@ +# Users should install this manually in their environment. It should not be installed in SAGE_VENV diff --git a/build/pkgs/git_trac_command/type b/build/pkgs/git_trac_command/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/git_trac_command/type @@ -0,0 +1 @@ +optional From 4a0b6f47a798a00643f51827da229e913cd8680d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 16 May 2022 16:44:16 -0700 Subject: [PATCH 084/454] bootstrap-conda: Also generate pip: lines --- bootstrap-conda | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bootstrap-conda b/bootstrap-conda index 3cb23bfbac9..260c8751312 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -93,3 +93,21 @@ DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" echo " - $pkg" done ) > src/environment-optional.yml +( + echo >&4 " - pip:" + echo >&5 " - pip:" + for PKG_BASE in $(sage-package list --has-file requirements.txt --no-file distros/conda.txt); do + PKG_SCRIPTS=build/pkgs/$PKG_BASE + SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/requirements.txt + PKG_TYPE=$(cat $PKG_SCRIPTS/type) + if [ -n "PKG_SYSTEM_PACKAGES" ]; then + case "$PKG_BASE:$PKG_TYPE" in + $DEVELOP_SPKG_PATTERN:*) FD=4;; + *) FD=5;; + esac + ${STRIP_COMMENTS} $SYSTEM_PACKAGES_FILE | while read -r line; do + [ -n "$line" ] && echo >&$FD " - $line" + done + fi + done +) 4>> src/environment-dev.yml 5>> src/environment-optional.yml From ef01ba46603937842a3a6d984c3bf5baa8e8997d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 16 May 2022 17:08:24 -0700 Subject: [PATCH 085/454] bootstrap-conda: Do not include packages that declare SAGERUNTIME as a dependency --- bootstrap-conda | 4 +++- build/pkgs/sage_flatsurf/dependencies | 2 +- build/pkgs/slabbe/dependencies | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bootstrap-conda b/bootstrap-conda index 260c8751312..3337bca58d3 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -100,7 +100,9 @@ DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" PKG_SCRIPTS=build/pkgs/$PKG_BASE SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/requirements.txt PKG_TYPE=$(cat $PKG_SCRIPTS/type) - if [ -n "PKG_SYSTEM_PACKAGES" ]; then + if grep -q SAGERUNTIME $PKG_SCRIPTS/dependencies 2>/dev/null; then + : # cannot install packages that depend on the Sage library + else case "$PKG_BASE:$PKG_TYPE" in $DEVELOP_SPKG_PATTERN:*) FD=4;; *) FD=5;; diff --git a/build/pkgs/sage_flatsurf/dependencies b/build/pkgs/sage_flatsurf/dependencies index 951fd368f40..4c62fdd4fef 100644 --- a/build/pkgs/sage_flatsurf/dependencies +++ b/build/pkgs/sage_flatsurf/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) surface_dynamics +$(PYTHON) | $(PYTHON_TOOLCHAIN) surface_dynamics $(SAGERUNTIME) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/slabbe/dependencies b/build/pkgs/slabbe/dependencies index 0738c2d7777..05ba0d8954b 100644 --- a/build/pkgs/slabbe/dependencies +++ b/build/pkgs/slabbe/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) | $(PYTHON_TOOLCHAIN) $(SAGERUNTIME) ---------- All lines of this file are ignored except the first. From 8e511f232585f98f18befe40945be71964b21d41 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 29 May 2022 15:43:21 -0700 Subject: [PATCH 086/454] bootstrap-conda: Hardcode git_trac_command as a devel package --- bootstrap-conda | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap-conda b/bootstrap-conda index 3337bca58d3..d8c76e3f1ee 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -8,6 +8,7 @@ export PATH="$(pwd)/build/bin:$PATH" STRIP_COMMENTS="sed s/#.*//;" RECOMMENDED_SPKG_PATTERN="@(_recommended$(for a in $(head -n 1 build/pkgs/_recommended/dependencies); do echo -n "|"$a; done))" +DEVELOP_SPKG_PATTERN="git_trac_command" BOOTSTRAP_PACKAGES=$(echo $(${STRIP_COMMENTS} build/pkgs/_bootstrap/distros/conda.txt)) SYSTEM_PACKAGES= From 1954bab489b2d3a7c0615174514642263b6ef8b0 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Mon, 30 May 2022 15:43:54 +0200 Subject: [PATCH 087/454] use zero sequence instead of Seq3.some_elements()[0] in some test --- src/sage/combinat/k_regular_sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 71c071db68f..8cb5ad1393b 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2136,7 +2136,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) sage: Seq3 = kRegularSequenceSpace(3, ZZ) sage: RP.parameters(1, 0, {(0, 0): 1}, {}, 0, - ....: {0: Seq3.some_elements()[0]}) + ....: {0: Seq3.zero()}) Traceback (most recent call last): ... ValueError: Inhomogeneities {0: 3-regular sequence 0, 0, 0, 0, 0, 0, From 5695619b23e5e635824460a61bc65fb8d3e08799 Mon Sep 17 00:00:00 2001 From: Gabriel Lipnik Date: Mon, 30 May 2022 15:51:05 +0200 Subject: [PATCH 088/454] add subdivide=False two times --- src/sage/combinat/k_regular_sequence.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 8cb5ad1393b..5e58f3d64bd 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2893,11 +2893,12 @@ def matrix_row(row): mat_upper_right = Matrix([matrix_row(row) for row in srange(dim_without_corr)]) mat_inhomog = block_diagonal_matrix([S.mu[rem] - for S in shifted_inhomogeneities.values()]) + for S in shifted_inhomogeneities.values()], + subdivide=False) mat = block_matrix([[mat, mat_upper_right], [zero_matrix(mat_inhomog.nrows(), dim_without_corr), - mat_inhomog]]) + mat_inhomog]], subdivide=False) dim_without_corr = mat.ncols() dim = dim_without_corr + n1 From 80b2e688c5a6cad698fc50ea02355bda77e7c852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 20 Jun 2022 16:26:50 +0200 Subject: [PATCH 089/454] factorisation of symbolic polynomials --- src/sage/symbolic/ginac/normal.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/symbolic/ginac/normal.cpp b/src/sage/symbolic/ginac/normal.cpp index 2d34d8f16f2..b7e17b81186 100644 --- a/src/sage/symbolic/ginac/normal.cpp +++ b/src/sage/symbolic/ginac/normal.cpp @@ -1221,8 +1221,6 @@ bool factor(const ex& the_ex, ex& res_ex) den = normalized.op(1); ex res_den; bool dres = factorpoly(den, res_den); - if (not nres and not dres) - return false; if (not nres) res_ex = num; if (not dres) From b41c93f6e7d42c69684bcbaad3cbc0e822f76e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 22 Jun 2022 10:40:13 +0200 Subject: [PATCH 090/454] adding doctest for 33640 --- src/sage/symbolic/expression.pyx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index eecf2951e0b..5ba5d76430b 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -11885,6 +11885,11 @@ cdef class Expression(Expression_abc): x + sqrt(x) sage: factor((x + sqrt(x))/(x - sqrt(x))) (x + sqrt(x))/(x - sqrt(x)) + + Check that :trac:`33640` is fixed:: + + sage: ((x + 1)^2 - 2*x - 1).factor() + x^2 """ from sage.calculus.calculus import symbolic_expression_from_maxima_string cdef GEx x From 2019f4791f0bc6edf6c9f35a1ae618206d02623b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 29 Jun 2022 13:51:30 -0700 Subject: [PATCH 091/454] bootstrap-conda: Also check dependencies_order_only --- bootstrap-conda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap-conda b/bootstrap-conda index d8c76e3f1ee..2631dc8723f 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -101,7 +101,7 @@ DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" PKG_SCRIPTS=build/pkgs/$PKG_BASE SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/requirements.txt PKG_TYPE=$(cat $PKG_SCRIPTS/type) - if grep -q SAGERUNTIME $PKG_SCRIPTS/dependencies 2>/dev/null; then + if grep -q SAGERUNTIME $PKG_SCRIPTS/dependencies $PKG_SCRIPTS/dependencies_order_only 2>/dev/null; then : # cannot install packages that depend on the Sage library else case "$PKG_BASE:$PKG_TYPE" in From 119baaf65cf80ebb90f31b533339e30f828015a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 30 Jun 2022 11:40:53 +0200 Subject: [PATCH 092/454] 33002: tikz method of polyhedron can now output a TikzPicture object --- src/sage/geometry/polyhedron/base6.py | 53 +++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index b569f40613d..4a455bbffa4 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -475,7 +475,8 @@ def show(self, **kwds): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', - opacity=0.8, vertex_color='green', axis=False): + opacity=0.8, vertex_color='green', axis=False, + output_type='LatexExpr'): r""" Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` according to a projection ``view`` and an angle ``angle`` @@ -496,10 +497,14 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of the front facets. - ``axis`` - Boolean (default: False) draw the axes at the origin or not. + - ``output_type`` - string (default: ``None``), valid values are + ``None``, ``'LatexExpr'`` and ``'TikzPicture'``, whether to + return a LatexExpr object (which inherits from Python str) or a + ``TikzPicture`` object from module :mod:`sage.misc.latex_standalone` OUTPUT: - - LatexExpr -- containing the TikZ picture. + - LatexExpr object or TikzPicture object .. NOTE:: @@ -563,10 +568,52 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, %% \coordinate (-1.00000, -1.00000, 0.00000) at (-1.00000, -1.00000, 0.00000); \coordinate (-1.00000, 0.00000, -1.00000) at (-1.00000, 0.00000, -1.00000); + + When output type is a :class:`sage.misc.latex_standalone.TikzPicture`:: + + sage: co = polytopes.cuboctahedron() + sage: t = co.tikz([674,108,-731], 112, output_type='TikzPicture') + sage: t + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [x={(0.249656cm, -0.577639cm)}, + y={(0.777700cm, -0.358578cm)}, + z={(-0.576936cm, -0.733318cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 0.00000, 1.00000) {}; + \node[vertex] at (1.00000, 1.00000, 0.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: path_to_file = t.pdf() # not tested + """ - return self.projection().tikz(view, angle, scale, + tikz_string = self.projection().tikz(view, angle, scale, edge_color, facet_color, opacity, vertex_color, axis) + # set default value + if output_type is None: + # we may want to raise a deprecation warning here + # to announce that the default will later change + # to 'TikzPicture' + output_type = 'LatexExpr' + + # return + if output_type == 'LatexExpr': + return tikz_string + elif output_type == 'TikzPicture': + from sage.misc.latex_standalone import TikzPicture + return TikzPicture(tikz_string, standalone_config=None, + usepackage=None, usetikzlibrary=None, macros=None, + use_sage_preamble=False) + else: + raise ValueError("output_type (='{}') must be 'LatexExpr' or" + " 'TikzPicture'".format(output_type)) def _rich_repr_(self, display_manager, **kwds): r""" From 33afbd3bfabb2198836daf99e01e72ef2a73883a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Wed, 6 Jul 2022 16:22:52 +0200 Subject: [PATCH 093/454] 33002: moving the LatexExpr vs TikzPicture output type choice inside method projection().tikz() instead of tikz() --- src/sage/geometry/polyhedron/base6.py | 37 ++++---------- src/sage/geometry/polyhedron/plot.py | 74 ++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index 4a455bbffa4..0d6202722c7 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -476,9 +476,10 @@ def show(self, **kwds): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', opacity=0.8, vertex_color='green', axis=False, - output_type='LatexExpr'): + output_type=None): r""" - Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` + Return a tikz picture of ``self`` as a string or as a + :class:`~sage.misc.latex_standalone.TikzPicture` according to a projection ``view`` and an angle ``angle`` obtained via the threejs viewer. ``self`` must be bounded. @@ -497,10 +498,11 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of the front facets. - ``axis`` - Boolean (default: False) draw the axes at the origin or not. - - ``output_type`` - string (default: ``None``), valid values are - ``None``, ``'LatexExpr'`` and ``'TikzPicture'``, whether to - return a LatexExpr object (which inherits from Python str) or a - ``TikzPicture`` object from module :mod:`sage.misc.latex_standalone` + - ``output_type`` - string (default: ``None``), valid values + are ``None`` (deprecated), ``'LatexExpr'`` and ``'TikzPicture'``, + whether to return a LatexExpr object (which inherits from Python + str) or a ``TikzPicture`` object from module + :mod:`sage.misc.latex_standalone` OUTPUT: @@ -593,27 +595,10 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: path_to_file = t.pdf() # not tested """ - tikz_string = self.projection().tikz(view, angle, scale, + return self.projection().tikz(view, angle, scale, edge_color, facet_color, - opacity, vertex_color, axis) - # set default value - if output_type is None: - # we may want to raise a deprecation warning here - # to announce that the default will later change - # to 'TikzPicture' - output_type = 'LatexExpr' - - # return - if output_type == 'LatexExpr': - return tikz_string - elif output_type == 'TikzPicture': - from sage.misc.latex_standalone import TikzPicture - return TikzPicture(tikz_string, standalone_config=None, - usepackage=None, usetikzlibrary=None, macros=None, - use_sage_preamble=False) - else: - raise ValueError("output_type (='{}') must be 'LatexExpr' or" - " 'TikzPicture'".format(output_type)) + opacity, vertex_color, axis, + output_type=output_type) def _rich_repr_(self, display_manager, **kwds): r""" diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index df157066c43..d85c5524e83 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1228,11 +1228,13 @@ def render_3d(self, point_opts=None, line_opts=None, polygon_opts=None): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', - opacity=0.8, vertex_color='green', axis=False): + opacity=0.8, vertex_color='green', axis=False, + output_type=None): r""" - Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` + Return a tikz picture of ``self`` as a string or as a + :class:`~sage.misc.latex_standalone.TikzPicture` according to a projection ``view`` and an angle ``angle`` - obtained via Jmol through the current state property. + obtained via the threejs viewer. INPUT: @@ -1249,10 +1251,15 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of the front facets. - ``axis`` - Boolean (default: False) draw the axes at the origin or not. + - ``output_type`` - string (default: ``None``), valid values + are ``None`` (deprecated), ``'LatexExpr'`` and ``'TikzPicture'``, + whether to return a LatexExpr object (which inherits from Python + str) or a ``TikzPicture`` object from module + :mod:`sage.misc.latex_standalone` OUTPUT: - - LatexExpr -- containing the TikZ picture. + - LatexExpr object or TikzPicture object .. NOTE:: @@ -1328,6 +1335,29 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. + Using :class:`~sage.misc.latex_standalone.TikzPicture` as output type:: + + sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]]) + sage: t = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True, output_type='TikzPicture') + sage: t + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [x={(0.658184cm, -0.242192cm)}, + y={(-0.096240cm, 0.912008cm)}, + z={(-0.746680cm, -0.331036cm)}, + scale=3.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-1.00000, 2.00000, -1.00000) {}; + \node[vertex] at (2.00000, -1.00000, -1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: t.pdf(view=False) # not tested + .. TODO:: Make it possible to draw Schlegel diagram for 4-polytopes. :: @@ -1347,15 +1377,45 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, elif self.polyhedron_dim < 2 or self.polyhedron_dim > 3: raise NotImplementedError("The polytope has to be 2 or 3-dimensional.") elif self.polyhedron_ambient_dim == 2: # self is a polygon in 2-space - return self._tikz_2d(scale, edge_color, facet_color, opacity, + tikz_string = self._tikz_2d(scale, edge_color, facet_color, opacity, vertex_color, axis) elif self.polyhedron_dim == 2: # self is a polygon in 3-space - return self._tikz_2d_in_3d(view, angle, scale, edge_color, + tikz_string = self._tikz_2d_in_3d(view, angle, scale, edge_color, facet_color, opacity, vertex_color, axis) else: # self is a 3-polytope in 3-space - return self._tikz_3d_in_3d(view, angle, scale, edge_color, + tikz_string = self._tikz_3d_in_3d(view, angle, scale, edge_color, facet_color, opacity, vertex_color, axis) + # set default value + if output_type is None: + from sage.misc.superseded import deprecation + msg = ("Since SageMath 5.13 (ticket #12083), the method .tikz() " + "of a polyhedron returns an object of type ``LatexExpr`` " + "which is a Python str. Since SageMath 9.7, this " + "default behavior of returning an object of type " + "LatexExpr is deprecated as the default output will soon " + "change to an object of type ``TikzPicture`` from the " + "module sage.misc.latex_standalone (newly introduced in " + "SageMath 9.6). Please update your code to specify the " + "desired output type as ``.tikz(output_type='LatexExpr')`` " + "to keep the old behavior or " + "``.tikz(output_type='TikzPicture')`` to use " + "the future default behavior.") + deprecation(33002, msg) + output_type = 'LatexExpr' + + # return + if output_type == 'LatexExpr': + return tikz_string + elif output_type == 'TikzPicture': + from sage.misc.latex_standalone import TikzPicture + return TikzPicture(tikz_string, standalone_config=None, + usepackage=None, usetikzlibrary=None, macros=None, + use_sage_preamble=False) + else: + raise ValueError("output_type (='{}') must be 'LatexExpr' or" + " 'TikzPicture'".format(output_type)) + def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): r""" Return a string ``tikz_pic`` consisting of a tikz picture of From a1d598991a123c9ad1f7e812a1ef1daa6889f0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Wed, 6 Jul 2022 17:07:09 +0200 Subject: [PATCH 094/454] 33002: adapting the doctests with the new behavior thus also avoiding deprecation warnings --- .../geometry/polytope_tikz.rst | 37 ++++-- .../geometry/visualization.rst | 23 +++- src/sage/geometry/polyhedron/base6.py | 37 ++++-- src/sage/geometry/polyhedron/plot.py | 112 ++++++++++++------ 4 files changed, 139 insertions(+), 70 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index 9fb6be2700e..4d4d5bdc593 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -15,8 +15,9 @@ paper. TikZ is a very versatile tool to draw in scientific documents and Sage can deal easily with 3-dimensional polytopes. Finally sagetex makes everything work together nicely between Sage, TikZ and LaTeX. Since version 6.3 of Sage, there is a function for (projection -of) polytopes to output a TikZ picture of the polytope. This short -tutorial shows how it all works. +of) polytopes to output a TikZ picture of the polytope. Since Version 9.7 of SageMath, +the tikz output can be a ``TikzPicture`` object from the sage module +``sage.misc.latex_standalone``. This short tutorial shows how it all works. Instructions """""""""""" @@ -30,21 +31,23 @@ To put an image of a 3D-polytope in LaTeX using TikZ and Sage, simply follow the - Visualize the polytope P using the command ``P.show(aspect_ratio=1)`` - This will open an interactive view in your default browser, where you can rotate the polytope. - Once the desired view angle is found, click on the information icon in the lower right-hand corner and select *Get Viewpoint*. This will copy a string of the form '[x,y,z],angle' to your local clipboard. -- Go back to Sage and type ``Img = P.tikz([x,y,z],angle)``. You can paste the string here to save some typing. +- Go back to Sage and type ``Img = P.tikz([x,y,z],angle,output_type='LatexExpr')``. You can paste the string here to save some typing. - *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope -Then, you can either copy-paste it to your article by typing ``Img`` in Sage or save it to a file, by doing +Alternatively, you can save the tikz image to a file, by doing .. CODE-BLOCK:: python - f = open('Img_poly.tex','w') - f.write(Img) - f.close() + Img = P.tikz([x,y,z], angle, output_type='TikzPicture') + Img.tex('Img_poly.tex') + Img.tex('Img_poly.tex', content_only=True) + Img.pdf('Img_poly.pdf') .. end of output Then in the pwd (present working directory of sage, the one of your article) -you will have a file named ``Img_poly.tex`` containing the tikzpicture of your polytope. +you will have a file named ``Img_poly.tex`` and ``Img_poly.pdf`` containing the +tikzpicture of the polytope ``P``. Customization """"""""""""" @@ -57,6 +60,9 @@ You can customize the polytope using the following options in the command ``P.ti - ``vertex_color`` : string (default: ``green``) representing colors which tikz recognize, - ``opacity`` : real number (default: ``0.8``) between 0 and 1 giving the opacity of the front facets, - ``axis`` : Boolean (default: ``False``) draw the axes at the origin or not. +- ``output_type`` : string (default: ``None``) ``None``, ``'LatexExpr'`` or + ``'TikzPicture'``, the type of the output. Since SageMath 9.7, the value ``None`` is deprecated + as the default value will soon be changed from ``'LatexExpr'`` to ``'TikzPicture'``. Examples """""""" @@ -80,15 +86,20 @@ When you found a good angle, follow the above procedure to obtain the values :: - Img = P.tikz([674,108,-731],112) + Img = P.tikz([674,108,-731], 112, output_type='TikzPicture') .. end of output +Note: the ``output_type='TikzPicture'`` is necessary since SagMath 9.7 to avoid +a deprecation warning message since the default output type will soon change +from a ``LatexExpr`` (Python str) to a ``TikzPicture`` object (allowing more +versatility, like being able to view it directly in the Jupyter notebook). + Or you may want to customize using the command :: - Img = P.tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4) + Img = P.tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4, output_type='TikzPicture') .. end of output @@ -134,7 +145,7 @@ some possibilities. .. CODE-BLOCK:: latex - \sagestr{(polytopes.permutahedron(4)).tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3)} + \sagestr{(polytopes.permutahedron(4)).tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3, output_type='LatexExpr')} .. end of output @@ -142,8 +153,8 @@ some possibilities. .. CODE-BLOCK:: latex - \newcommand{\polytopeimg}[4]{\sagestr{(#1).tikz(#2,#3,#4)}} - \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).tikz(#2,#3,#4,#5,#6,#7,#8,#9)}} + \newcommand{\polytopeimg}[4]{\sagestr{(#1).tikz(#2,#3,#4,output_type='LatexExpr')}} + \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).tikz(#2,#3,#4,#5,#6,#7,#8,#9,output_type='LatexExpr')}} .. end of output diff --git a/src/doc/en/thematic_tutorials/geometry/visualization.rst b/src/doc/en/thematic_tutorials/geometry/visualization.rst index ac7412e665c..438b6cff4c8 100644 --- a/src/doc/en/thematic_tutorials/geometry/visualization.rst +++ b/src/doc/en/thematic_tutorials/geometry/visualization.rst @@ -113,11 +113,22 @@ This method returns a tikz picture of the polytope (must be 2 or :: sage: c = polytopes.cube() - sage: c.tikz().splitlines()[:5] - ['\\begin{tikzpicture}%', - '\t[x={(1.000000cm, 0.000000cm)},', - '\ty={(-0.000000cm, 1.000000cm)},', - '\tz={(0.000000cm, -0.000000cm)},', - '\tscale=1.000000,'] + sage: c.tikz(output_type='TikzPicture') + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [x={(1.000000cm, 0.000000cm)}, + y={(-0.000000cm, 1.000000cm)}, + z={(0.000000cm, -0.000000cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-1.00000, -1.00000, 1.00000) {}; + \node[vertex] at (-1.00000, 1.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} .. end of output diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index 0d6202722c7..94e8aa668fa 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -50,7 +50,10 @@ class Polyhedron_base6(Polyhedron_base5): sage: P = polytopes.cube() sage: Polyhedron_base6.plot(P) Graphics3d Object - sage: Polyhedron_base6.tikz(P) + sage: print(Polyhedron_base6.tikz(P, output_type='TikzPicture')) + \RequirePackage{luatex85} + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% [x={(1.000000cm, 0.000000cm)}, y={(-0.000000cm, 1.000000cm)}, @@ -127,6 +130,7 @@ class Polyhedron_base6(Polyhedron_base5): %% %% \end{tikzpicture} + \end{document} sage: Q = polytopes.hypercube(4) sage: Polyhedron_base6.show(Q) @@ -544,18 +548,25 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, EXAMPLES:: sage: co = polytopes.cuboctahedron() - sage: Img = co.tikz([0,0,1], 0) - sage: print('\n'.join(Img.splitlines()[:9])) + sage: Img = co.tikz([0,0,1], 0, output_type='TikzPicture') + sage: Img + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% - [x={(1.000000cm, 0.000000cm)}, - y={(0.000000cm, 1.000000cm)}, - z={(0.000000cm, 0.000000cm)}, - scale=1.000000, - back/.style={loosely dotted, thin}, - edge/.style={color=blue!95!black, thick}, - facet/.style={fill=blue!95!black,fill opacity=0.800000}, - vertex/.style={inner sep=1pt,circle,draw=green!25!black,fill=green!75!black,thick}] - sage: print('\n'.join(Img.splitlines()[12:21])) + [x={(1.000000cm, 0.000000cm)}, + y={(0.000000cm, 1.000000cm)}, + z={(0.000000cm, 0.000000cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 0.00000, 1.00000) {}; + \node[vertex] at (1.00000, 1.00000, 0.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: print('\n'.join(Img.content().splitlines()[12:21])) %% with the command: ._tikz_3d_in_3d and parameters: %% view = [0, 0, 1] %% angle = 0 @@ -565,7 +576,7 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, %% opacity = 0.8 %% vertex_color = green %% axis = False - sage: print('\n'.join(Img.splitlines()[22:26])) + sage: print('\n'.join(Img.content().splitlines()[22:26])) %% Coordinate of the vertices: %% \coordinate (-1.00000, -1.00000, 0.00000) at (-1.00000, -1.00000, 0.00000); diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index d85c5524e83..6bd567c68fc 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1292,19 +1292,56 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, EXAMPLES:: sage: P1 = polytopes.small_rhombicuboctahedron() - sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4) + sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4, output_type='TikzPicture') sage: type(Image1) - - sage: print('\n'.join(Image1.splitlines()[:4])) + + sage: Image1 + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% - [x={(-0.939161cm, 0.244762cm)}, - y={(0.097442cm, -0.482887cm)}, - z={(0.329367cm, 0.840780cm)}, - sage: with open('polytope-tikz1.tex', 'w') as f: # not tested - ....: _ = f.write(Image1) + [x={(-0.939161cm, 0.244762cm)}, + y={(0.097442cm, -0.482887cm)}, + z={(0.329367cm, 0.840780cm)}, + scale=4.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-2.41421, 1.00000, -1.00000) {}; + \node[vertex] at (-2.41421, -1.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: _ = Image1.tex('polytope-tikz1.tex') # not tested + sage: _ = Image1.png('polytope-tikz1.png') # not tested + sage: _ = Image1.pdf('polytope-tikz1.pdf') # not tested + sage: _ = Image1.svg('polytope-tikz1.svg') # not tested + + A second example:: sage: P2 = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]]) - sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True) + sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True, output_type='TikzPicture') + sage: Image2 + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [scale=3.000000, + back/.style={loosely dotted, thin}, + edge/.style={color=blue!95!black, thick}, + facet/.style={fill=orange!95!black,fill opacity=0.400000}, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 2.00000) {}; + \node[vertex] at (2.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + + The second example using a LatexExpr as output type:: + + sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True, output_type='LatexExpr') sage: type(Image2) sage: print('\n'.join(Image2.splitlines()[:4])) @@ -1315,31 +1352,13 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: with open('polytope-tikz2.tex', 'w') as f: # not tested ....: _ = f.write(Image2) + A third example:: + sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]]) sage: P3 A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: Image3 = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True) - sage: print('\n'.join(Image3.splitlines()[:4])) - \begin{tikzpicture}% - [x={(0.658184cm, -0.242192cm)}, - y={(-0.096240cm, 0.912008cm)}, - z={(-0.746680cm, -0.331036cm)}, - sage: with open('polytope-tikz3.tex', 'w') as f: # not tested - ....: _ = f.write(Image3) - - sage: P = Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) - sage: P - A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: P.projection().tikz() - Traceback (most recent call last): - ... - NotImplementedError: The polytope has to live in 2 or 3 dimensions. - - Using :class:`~sage.misc.latex_standalone.TikzPicture` as output type:: - - sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]]) - sage: t = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True, output_type='TikzPicture') - sage: t + sage: Image3 = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True, output_type='TikzPicture') + sage: Image3 \documentclass[tikz]{standalone} \begin{document} \begin{tikzpicture}% @@ -1356,7 +1375,20 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, %% \end{tikzpicture} \end{document} - sage: t.pdf(view=False) # not tested + sage: _ = Image3.tex('polytope-tikz3.tex') # not tested + sage: _ = Image3.png('polytope-tikz3.png') # not tested + sage: _ = Image3.pdf('polytope-tikz3.pdf') # not tested + sage: _ = Image3.svg('polytope-tikz3.svg') # not tested + + A fourth example:: + + sage: P = Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) + sage: P + A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices + sage: P.projection().tikz(output_type='TikzPicture') + Traceback (most recent call last): + ... + NotImplementedError: The polytope has to live in 2 or 3 dimensions. .. TODO:: @@ -1365,7 +1397,7 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) sage: P A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: P.projection().tikz() + sage: P.projection().tikz(output_type='TikzPicture') Traceback (most recent call last): ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. @@ -1454,9 +1486,9 @@ def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): Scientific notation is not used in the output (:trac:`16519`):: - sage: P=Polyhedron([[2*10^-10,0],[0,1],[1,0]],base_ring=QQ) - sage: tikzstr=P.projection().tikz() - sage: 'e-10' in tikzstr + sage: P = Polyhedron([[2*10^-10,0],[0,1],[1,0]],base_ring=QQ) + sage: tikz = P.projection().tikz(output_type='TikzPicture') + sage: 'e-10' in tikz.content() False .. NOTE:: @@ -1582,9 +1614,11 @@ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color, sage: with open('polytope-tikz3.tex', 'w') as f: # not tested ....: _ = f.write(Image) + :: + sage: p = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]) sage: proj = p.projection() - sage: Img = proj.tikz([1,1,1],130,axis=True) + sage: Img = proj.tikz([1,1,1],130,axis=True, output_type='LatexExpr') sage: print('\n'.join(Img.splitlines()[12:21])) %% with the command: ._tikz_2d_in_3d and parameters: %% view = [1, 1, 1] @@ -1730,8 +1764,10 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, sage: with open('polytope-tikz1.tex', 'w') as f: # not tested ....: _ = f.write(Image) + :: + sage: Associahedron = Polyhedron(vertices=[[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]]).polar() - sage: ImageAsso = Associahedron.projection().tikz([-15,-755,-655], 116, scale=1) + sage: ImageAsso = Associahedron.projection().tikz([-15,-755,-655], 116, scale=1, output_type='LatexExpr') sage: print('\n'.join(ImageAsso.splitlines()[12:30])) %% with the command: ._tikz_3d_in_3d and parameters: %% view = [-15, -755, -655] From 4ef07a9544b35713cb4fd182211c40965de086fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 7 Jul 2022 10:03:17 +0200 Subject: [PATCH 095/454] 33002: shorter deprecation message --- .../geometry/polytope_tikz.rst | 4 ++-- src/sage/geometry/polyhedron/plot.py | 17 ++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index 4d4d5bdc593..4dbd6839ca0 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -15,8 +15,8 @@ paper. TikZ is a very versatile tool to draw in scientific documents and Sage can deal easily with 3-dimensional polytopes. Finally sagetex makes everything work together nicely between Sage, TikZ and LaTeX. Since version 6.3 of Sage, there is a function for (projection -of) polytopes to output a TikZ picture of the polytope. Since Version 9.7 of SageMath, -the tikz output can be a ``TikzPicture`` object from the sage module +of) polytopes to output a TikZ picture of the polytope. Since version 9.7 of +SageMath, the tikz output can be a ``TikzPicture`` object from the sage module ``sage.misc.latex_standalone``. This short tutorial shows how it all works. Instructions diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 6bd567c68fc..d0ae10da640 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1421,17 +1421,12 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, # set default value if output_type is None: from sage.misc.superseded import deprecation - msg = ("Since SageMath 5.13 (ticket #12083), the method .tikz() " - "of a polyhedron returns an object of type ``LatexExpr`` " - "which is a Python str. Since SageMath 9.7, this " - "default behavior of returning an object of type " - "LatexExpr is deprecated as the default output will soon " - "change to an object of type ``TikzPicture`` from the " - "module sage.misc.latex_standalone (newly introduced in " - "SageMath 9.6). Please update your code to specify the " - "desired output type as ``.tikz(output_type='LatexExpr')`` " - "to keep the old behavior or " - "``.tikz(output_type='TikzPicture')`` to use " + msg = ("The default type of the returned object will soon be " + "changed from `sage.misc.latex.LatexExpr` to " + "`sage.misc.latex_standalone.TikzPicture`. Please " + "update your code to specify the desired output type as " + "`.tikz(output_type='LatexExpr')` to keep the old " + "behavior or `.tikz(output_type='TikzPicture')` to use " "the future default behavior.") deprecation(33002, msg) output_type = 'LatexExpr' From 57aff708ab601cf3d320ff029191721cd7ff4594 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 7 Jul 2022 16:06:23 -0700 Subject: [PATCH 096/454] build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch: New --- ...7f07d5c9e1f3afe793f782d191d4465088ae.patch | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch diff --git a/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch b/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch new file mode 100644 index 00000000000..308456304d7 --- /dev/null +++ b/build/pkgs/latte_int/patches/6dbf7f07d5c9e1f3afe793f782d191d4465088ae.patch @@ -0,0 +1,79 @@ +From 6dbf7f07d5c9e1f3afe793f782d191d4465088ae Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Thu, 7 Jul 2022 15:42:35 -0700 +Subject: [PATCH] Remove dynamic exception specifications to conform to ISO + C++17 + +--- + code/latte/ExponentialSubst.cpp | 2 -- + code/latte/ExponentialSubst.h | 6 ++---- + code/latte/sqlite/IntegrationDB.cpp | 2 +- + code/latte/sqlite/IntegrationDB.h | 2 +- + 4 files changed, 4 insertions(+), 8 deletions(-) + +diff --git a/code/latte/ExponentialSubst.cpp b/code/latte/ExponentialSubst.cpp +index a839b820..bcbfa934 100644 +--- a/code/latte/ExponentialSubst.cpp ++++ b/code/latte/ExponentialSubst.cpp +@@ -57,7 +57,6 @@ mpq_vector + computeExponentialResidueWeights(const vec_ZZ &generic_vector, + mpz_class &prod_ray_scalar_products, + const listCone *cone, int numOfVars) +- throw(NotGenericException) + { + // Compute dimension; can be smaller than numOfVars + int dimension = 0; +@@ -95,7 +94,6 @@ computeExponentialResidueWeights(const vec_ZZ &generic_vector, + mpq_vector + computeExponentialResidueWeights(const vec_ZZ &generic_vector, + const listCone *cone, int numOfVars) +- throw(NotGenericException) + { + mpz_class prod_ray_scalar_products; + return computeExponentialResidueWeights(generic_vector, +diff --git a/code/latte/ExponentialSubst.h b/code/latte/ExponentialSubst.h +index c9fa4ace..43a4ab63 100644 +--- a/code/latte/ExponentialSubst.h ++++ b/code/latte/ExponentialSubst.h +@@ -58,13 +58,11 @@ class Exponential_Single_Cone_Parameters + mpq_vector /* FIXME: This version can probably go away */ + computeExponentialResidueWeights(const vec_ZZ &generic_vector, + mpz_class &prod_ray_scalar_products, +- const listCone *cone, int numOfVars) +- throw(NotGenericException); ++ const listCone *cone, int numOfVars); + + mpq_vector + computeExponentialResidueWeights(const vec_ZZ &generic_vector, +- const listCone *cone, int numOfVars) +- throw(NotGenericException); ++ const listCone *cone, int numOfVars); + + ZZ + scalar_power(const vec_ZZ &generic_vector, +diff --git a/code/latte/sqlite/IntegrationDB.cpp b/code/latte/sqlite/IntegrationDB.cpp +index ab8df535..c1dde830 100644 +--- a/code/latte/sqlite/IntegrationDB.cpp ++++ b/code/latte/sqlite/IntegrationDB.cpp +@@ -1277,7 +1277,7 @@ void IntegrationDB::insertSpecficPolytopeIntegrationTest(string polymakeFile, i + * @parm filePath: to the latte-style polynomial. + * @return rowid of the inserted row. + */ +-int IntegrationDB::insertPolynomial(int dim, int degree, const char*filePath) throw(SqliteDBexception) ++int IntegrationDB::insertPolynomial(int dim, int degree, const char*filePath) + { + if ( doesPolynomialExist(filePath)) + throw SqliteDBexception(string("insertPolynomial::Polynomial ")+filePath+" already exist"); +diff --git a/code/latte/sqlite/IntegrationDB.h b/code/latte/sqlite/IntegrationDB.h +index d690a832..ce8cfac6 100644 +--- a/code/latte/sqlite/IntegrationDB.h ++++ b/code/latte/sqlite/IntegrationDB.h +@@ -67,7 +67,7 @@ class IntegrationDB: public SqliteDB + int insertIntegrationTest(int polynomialID, int polytopeID); + void insertIntegrationTest(int dim, int degree, int vertexCount, int count); + void insertSpecficPolytopeIntegrationTest(string polymakeFile, int degree, int count); +- int insertPolynomial(int dim, int degree, const char*filePath) throw(SqliteDBexception); ++ int insertPolynomial(int dim, int degree, const char*filePath); + + int insertPolytope(int dim, int vertexCount, int simple, int dualRowID, const char* latteFilePath, const char* polymakeFilePath); + From 7a71bbe3d90d3da65814da6c6c53f0686c51f9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 21 Jul 2022 16:45:37 +0200 Subject: [PATCH 097/454] 33002: fixing small remarks in polytope_tikz.rst --- src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index 4dbd6839ca0..a0c98ea3836 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -32,7 +32,7 @@ To put an image of a 3D-polytope in LaTeX using TikZ and Sage, simply follow the - This will open an interactive view in your default browser, where you can rotate the polytope. - Once the desired view angle is found, click on the information icon in the lower right-hand corner and select *Get Viewpoint*. This will copy a string of the form '[x,y,z],angle' to your local clipboard. - Go back to Sage and type ``Img = P.tikz([x,y,z],angle,output_type='LatexExpr')``. You can paste the string here to save some typing. -- *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope +- *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope. Alternatively, you can save the tikz image to a file, by doing @@ -46,7 +46,7 @@ Alternatively, you can save the tikz image to a file, by doing .. end of output Then in the pwd (present working directory of sage, the one of your article) -you will have a file named ``Img_poly.tex`` and ``Img_poly.pdf`` containing the +you will have two files named ``Img_poly.tex`` and ``Img_poly.pdf`` containing the tikzpicture of the polytope ``P``. Customization From 62f69ae4f3b5bef46d4e657d7507c84f65f7a429 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 16:54:47 -0700 Subject: [PATCH 098/454] build/bin/sage-spkg-installcheck, build/sage_bootstrap/installcheck.py: New --- build/bin/sage-spkg-installcheck | 24 ++++ build/sage_bootstrap/installcheck.py | 158 +++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100755 build/bin/sage-spkg-installcheck create mode 100644 build/sage_bootstrap/installcheck.py diff --git a/build/bin/sage-spkg-installcheck b/build/bin/sage-spkg-installcheck new file mode 100755 index 00000000000..a8ef89ba2aa --- /dev/null +++ b/build/bin/sage-spkg-installcheck @@ -0,0 +1,24 @@ +#!/usr/bin/env sage-bootstrap-python + +# usage: sage-spkg-installcheck [-h] PKG [SAGE_LOCAL] +# +# Check shared libraries that are part of an installed package. +# +# positional arguments: +# PKG the name of the package to uninstall +# SAGE_LOCAL the path to SAGE_LOCAL (default: value of the $SAGE_LOCAL +# environment variable if set; exits otherwise) +# +# optional arguments: +# -h, --help show this help message and exit + + +try: + import sage_bootstrap +except ImportError: + import os, sys + sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + import sage_bootstrap + +from sage_bootstrap.installcheck import run +run() diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py new file mode 100644 index 00000000000..a6f350e424b --- /dev/null +++ b/build/sage_bootstrap/installcheck.py @@ -0,0 +1,158 @@ +""" +Command-line script for checking an installed SPKG in $SAGE_LOCAL. +""" + +# **************************************************************************** +# Copyright (C) 2017 Erik M. Bray +# 2022 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 __future__ import print_function + +import glob +import json +import os +import shutil +import subprocess +import sys +import argparse + +from .env import SAGE_ROOT + +pth = os.path +PKGS = pth.join(SAGE_ROOT, 'build', 'pkgs') +"""Directory where all spkg sources are found.""" + + +def installcheck(spkg_name, sage_local, verbose=False): + """ + Given a package name and path to SAGE_LOCAL, check the installation of the package + in SAGE_LOCAL. + """ + + # The default path to this directory; however its value should be read + # from the environment if possible + spkg_inst = pth.join(sage_local, 'var', 'lib', 'sage', 'installed') + + # Find all stamp files for the package; there should be only one, but if + # there is somehow more than one we'll work with the most recent and delete + # the rest + pattern = pth.join(spkg_inst, '{0}-*'.format(spkg_name)) + stamp_files = sorted(glob.glob(pattern), key=pth.getmtime) + + if stamp_files: + stamp_file = stamp_files[-1] + else: + stamp_file = None + + spkg_meta = {} + if stamp_file: + try: + with open(stamp_file) as f: + spkg_meta = json.load(f) + except (OSError, ValueError): + pass + + if 'files' not in spkg_meta: + if stamp_file: + print("Old-style or corrupt stamp file '{0}'" + .format(stamp_file), file=sys.stderr) + else: + print("Package '{0}' is currently not installed in '{1}'" + .format(spkg_name, sage_local), file=sys.stderr) + else: + files = spkg_meta['files'] + if not files: + print("Warning: No files to check for " + "'{0}'".format(spkg_name), file=sys.stderr) + + for f in files: + if f.endswith(('.so', '.dylib')): + if verbose: + print("Checking shared library file '{0}'" + .format(f), file=sys.stderr) + elif f.endswith('.whl'): + if verbose: + print("Checking wheel file '{0}'" + .format(f), file=sys.stderr) + + +def dir_type(path): + """ + A custom argument 'type' for directory paths. + """ + + if path and not pth.isdir(path): + raise argparse.ArgumentTypeError( + "'{0}' is not a directory".format(path)) + + return path + + +def spkg_type(pkg): + """ + A custom argument 'type' for spkgs--checks whether the given package name + is a known spkg. + """ + pkgbase = pth.join(PKGS, pkg) + + if not pth.isdir(pkgbase): + raise argparse.ArgumentTypeError( + "'{0}' is not a known spkg".format(pkg)) + + return pkg + + +def make_parser(): + """Returns the command-line argument parser for sage-spkg-installcheck.""" + + doc_lines = __doc__.strip().splitlines() + + parser = argparse.ArgumentParser( + description=doc_lines[0], + epilog='\n'.join(doc_lines[1:]).strip(), + formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument('spkg', type=spkg_type, help='the spkg to check') + parser.add_argument('sage_local', type=dir_type, nargs='?', + default=os.environ.get('SAGE_LOCAL'), + help='the SAGE_LOCAL path (default: the $SAGE_LOCAL ' + 'environment variable if set)') + parser.add_argument('-v', '--verbose', action='store_true', + help='verbose output showing all files removed') + parser.add_argument('--debug', action='store_true', help=argparse.SUPPRESS) + + return parser + + +def run(argv=None): + parser = make_parser() + + args = parser.parse_args(argv if argv is not None else sys.argv[1:]) + + if args.sage_local is None: + print('Error: SAGE_LOCAL must be specified either at the command ' + 'line or in the $SAGE_LOCAL environment variable', + file=sys.stderr) + sys.exit(1) + + try: + installcheck(args.spkg, args.sage_local, + verbose=args.verbose) + except Exception as exc: + print("Error during installcheck of '{0}': {1}".format( + args.spkg, exc), file=sys.stderr) + + if args.debug: + raise + + sys.exit(1) + + +if __name__ == '__main__': + run() From 243f985d99a83fabcf003d6fd8eb908123e47f47 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 17:21:31 -0700 Subject: [PATCH 099/454] build/pkgs/auditwheel_or_delocate: New --- build/pkgs/auditwheel_or_delocate/SPKG.rst | 22 +++++++++++++++++++ .../pkgs/auditwheel_or_delocate/dependencies | 4 ++++ .../auditwheel_or_delocate/requirements.txt | 2 ++ build/pkgs/auditwheel_or_delocate/type | 1 + 4 files changed, 29 insertions(+) create mode 100644 build/pkgs/auditwheel_or_delocate/SPKG.rst create mode 100644 build/pkgs/auditwheel_or_delocate/dependencies create mode 100644 build/pkgs/auditwheel_or_delocate/requirements.txt create mode 100644 build/pkgs/auditwheel_or_delocate/type diff --git a/build/pkgs/auditwheel_or_delocate/SPKG.rst b/build/pkgs/auditwheel_or_delocate/SPKG.rst new file mode 100644 index 00000000000..845a8da1c24 --- /dev/null +++ b/build/pkgs/auditwheel_or_delocate/SPKG.rst @@ -0,0 +1,22 @@ +auditwheel_or_delocate: Repair wheels on Linux or macOS +======================================================= + +Description +----------- + +This package represents auditwheel on Linux +and delocate on macOS. + +License +------- + +MIT + +BSD 2-clause + +Upstream Contact +---------------- + +https://pypi.org/project/auditwheel/ + +https://pypi.org/project/delocate/ diff --git a/build/pkgs/auditwheel_or_delocate/dependencies b/build/pkgs/auditwheel_or_delocate/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/auditwheel_or_delocate/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/auditwheel_or_delocate/requirements.txt b/build/pkgs/auditwheel_or_delocate/requirements.txt new file mode 100644 index 00000000000..365b7415e16 --- /dev/null +++ b/build/pkgs/auditwheel_or_delocate/requirements.txt @@ -0,0 +1,2 @@ +delocate; sys_platform == 'darwin' +auditwheel; sys_platform != 'darwin' diff --git a/build/pkgs/auditwheel_or_delocate/type b/build/pkgs/auditwheel_or_delocate/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/auditwheel_or_delocate/type @@ -0,0 +1 @@ +optional From 00ec17bfe4dab2e710f942c00e8a706de7f57d57 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 17:46:24 -0700 Subject: [PATCH 100/454] build/sage_bootstrap/installcheck.py: Implement for macOS --- build/sage_bootstrap/installcheck.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index a6f350e424b..48d069a7205 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -72,14 +72,21 @@ def installcheck(spkg_name, sage_local, verbose=False): "'{0}'".format(spkg_name), file=sys.stderr) for f in files: + f = os.path.join(sage_local, f) if f.endswith(('.so', '.dylib')): if verbose: print("Checking shared library file '{0}'" .format(f), file=sys.stderr) + from delocate.libsana import _tree_libs_from_libraries, _filter_system_libs + _tree_libs_from_libraries([f], + lib_filt_func=_filter_system_libs, + copy_filt_func=lambda path: True) elif f.endswith('.whl'): if verbose: print("Checking wheel file '{0}'" .format(f), file=sys.stderr) + from delocate import wheel_libs + wheel_libs(f) def dir_type(path): From b2033b96d9d92258c74add5acb75f1007a319feb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 18:36:07 -0700 Subject: [PATCH 101/454] build/sage_bootstrap/installcheck.py: Do not warn if there are no files to check --- build/sage_bootstrap/installcheck.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index 48d069a7205..d81f91f4601 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -67,9 +67,6 @@ def installcheck(spkg_name, sage_local, verbose=False): .format(spkg_name, sage_local), file=sys.stderr) else: files = spkg_meta['files'] - if not files: - print("Warning: No files to check for " - "'{0}'".format(spkg_name), file=sys.stderr) for f in files: f = os.path.join(sage_local, f) From eb4778041ca6529078a0455a1c575a2bf4cdb7b1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 18:36:30 -0700 Subject: [PATCH 102/454] build/sage_bootstrap/installcheck.py: Skip pure Python wheels --- build/sage_bootstrap/installcheck.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index d81f91f4601..c452548f570 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -78,6 +78,9 @@ def installcheck(spkg_name, sage_local, verbose=False): _tree_libs_from_libraries([f], lib_filt_func=_filter_system_libs, copy_filt_func=lambda path: True) + elif f.endswith('-any.whl'): + # pure Python wheel, nothing to check + pass elif f.endswith('.whl'): if verbose: print("Checking wheel file '{0}'" From 30d9b5ff2523d19340db166e86faf3b48a6863a9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 18:36:47 -0700 Subject: [PATCH 103/454] build/make/Makefile.in (list-broken-packages): New --- build/make/Makefile.in | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index c32b8d98554..42b041b69b5 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -391,6 +391,27 @@ _clean-broken-gcc: package=$${package%%-*}; \ $(MAKE) $$package-SAGE_LOCAL-uninstall $$package-SAGE_VENV-uninstall +list-broken-packages: auditwheel_or_delocate + @broken_packages=; \ + for tree in "$(SAGE_LOCAL)" "$(SAGE_VENV)"; do \ + for stampfile in $$tree/$(SPKG_INST_RELDIR)/*; do \ + if [ -s $$stampfile ]; then \ + echo 2>&1 "# Checking $$stampfile"; \ + package_with_version=$${stampfile##*/}; \ + package=$${package_with_version%-*}; \ + if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package $$tree; then \ + broken_packages="$$broken_packages $$package"; \ + echo $$package; \ + fi; \ + fi; \ + done; \ + done; \ + if [ -n "$$broken_packages" ]; then \ + echo 2>&1 "Uninstall broken packages by typing:"; \ + echo 2>&1 " make $$(for package in $$broken_packages; do echo $$package-clean; done)"; \ + fi + + #============================================================================== # Setting SAGE_CHECK... variables #============================================================================== From e6679a7ccd91afec50ba25feb29c41b8a673d3ec Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 18:58:16 -0700 Subject: [PATCH 104/454] build/sage_bootstrap/installcheck.py: Only warn when delocate is not available --- build/sage_bootstrap/installcheck.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index c452548f570..d2c6821da3f 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -21,6 +21,7 @@ import subprocess import sys import argparse +from warnings import warn from .env import SAGE_ROOT @@ -74,10 +75,14 @@ def installcheck(spkg_name, sage_local, verbose=False): if verbose: print("Checking shared library file '{0}'" .format(f), file=sys.stderr) - from delocate.libsana import _tree_libs_from_libraries, _filter_system_libs - _tree_libs_from_libraries([f], - lib_filt_func=_filter_system_libs, - copy_filt_func=lambda path: True) + try: + from delocate.libsana import _tree_libs_from_libraries, _filter_system_libs + except ImportError: + warnings.warn('delocate is not available, so nothing is actually checked') + else: + _tree_libs_from_libraries([f], + lib_filt_func=_filter_system_libs, + copy_filt_func=lambda path: True) elif f.endswith('-any.whl'): # pure Python wheel, nothing to check pass @@ -85,8 +90,12 @@ def installcheck(spkg_name, sage_local, verbose=False): if verbose: print("Checking wheel file '{0}'" .format(f), file=sys.stderr) - from delocate import wheel_libs - wheel_libs(f) + try: + from delocate import wheel_libs + except ImportError: + warnings.warn('delocate is not available, so nothing is actually checked') + else: + wheel_libs(f) def dir_type(path): From 3d9448863b0cdfc5847d0aeb8ae675ccebc00b95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 18:58:46 -0700 Subject: [PATCH 105/454] build/make/Makefile.in (list-broken-packages): Parallelize it --- build/make/Makefile.in | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 42b041b69b5..105ae4052af 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -391,24 +391,24 @@ _clean-broken-gcc: package=$${package%%-*}; \ $(MAKE) $$package-SAGE_LOCAL-uninstall $$package-SAGE_VENV-uninstall +%-installcheck: + @stampfile=$@; \ + stampfile=$${stampfile%-installcheck}; \ + if [ -s $$stampfile ]; then \ + echo >&2 "# Checking $$stampfile"; \ + package_with_version=$${stampfile##*/}; \ + package=$${package_with_version%%-*}; \ + if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package $$tree; then \ + broken_packages="$$broken_packages $$package"; \ + echo $$package; \ + fi; \ + fi; + list-broken-packages: auditwheel_or_delocate - @broken_packages=; \ - for tree in "$(SAGE_LOCAL)" "$(SAGE_VENV)"; do \ - for stampfile in $$tree/$(SPKG_INST_RELDIR)/*; do \ - if [ -s $$stampfile ]; then \ - echo 2>&1 "# Checking $$stampfile"; \ - package_with_version=$${stampfile##*/}; \ - package=$${package_with_version%-*}; \ - if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package $$tree; then \ - broken_packages="$$broken_packages $$package"; \ - echo $$package; \ - fi; \ - fi; \ - done; \ - done; \ + @broken_packages=$$($(MAKE) -s $(patsubst %,%-installcheck,$(wildcard $(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/* $(SAGE_VENV)/$(SPKG_INST_RELDIR)/*))); \ if [ -n "$$broken_packages" ]; then \ - echo 2>&1 "Uninstall broken packages by typing:"; \ - echo 2>&1 " make $$(for package in $$broken_packages; do echo $$package-clean; done)"; \ + echo >&2 "Uninstall broken packages by typing:"; \ + echo >&2 " make -k $$(echo $$(for package in $$broken_packages; do echo $$package-clean; done))"; \ fi From 6881409206960ce37a2e1e29450a9c29fe61c1ec Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 20:03:24 -0700 Subject: [PATCH 106/454] build/make/Makefile.in (list-broken-packages): Show uninstallation commands that actually work --- build/make/Makefile.in | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 105ae4052af..e998518bc05 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -396,19 +396,26 @@ _clean-broken-gcc: stampfile=$${stampfile%-installcheck}; \ if [ -s $$stampfile ]; then \ echo >&2 "# Checking $$stampfile"; \ + tree=$${stampfile%%/$(SPKG_INST_RELDIR)/*}; \ package_with_version=$${stampfile##*/}; \ package=$${package_with_version%%-*}; \ - if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package $$tree; then \ - broken_packages="$$broken_packages $$package"; \ - echo $$package; \ + if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package; then \ + case "$$tree" in \ + "$(SAGE_LOCAL)") tree="";; \ + "$(SAGE_VENV)") tree="\\\$$SAGE_VENV";; \ + *) tree="$$tree";; \ + esac; \ + echo " ./sage --buildsh -c \"sage-spkg-uninstall $$package $$tree\";"; \ fi; \ fi; list-broken-packages: auditwheel_or_delocate - @broken_packages=$$($(MAKE) -s $(patsubst %,%-installcheck,$(wildcard $(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/* $(SAGE_VENV)/$(SPKG_INST_RELDIR)/*))); \ - if [ -n "$$broken_packages" ]; then \ + @fix_broken_packages=$$($(MAKE) -s $(patsubst %,%-installcheck,$(wildcard $(SAGE_LOCAL)/$(SPKG_INST_RELDIR)/* $(SAGE_VENV)/$(SPKG_INST_RELDIR)/*))); \ + if [ -n "$$fix_broken_packages" ]; then \ + echo >&2 ; \ echo >&2 "Uninstall broken packages by typing:"; \ - echo >&2 " make -k $$(echo $$(for package in $$broken_packages; do echo $$package-clean; done))"; \ + echo >&2 ; \ + echo >&2 "$$fix_broken_packages"; \ fi From b5a7d05fd49da94e179b5b9c7e8ed00ff7c903a1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 20:33:05 -0700 Subject: [PATCH 107/454] build/make/Makefile.in (list-broken-packages): Use targets %-SAGE_LOCAL-uninstall, %-SAGE_VENV-uninstall when possible --- build/make/Makefile.in | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index e998518bc05..1b0eea338d3 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -401,11 +401,10 @@ _clean-broken-gcc: package=$${package_with_version%%-*}; \ if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package; then \ case "$$tree" in \ - "$(SAGE_LOCAL)") tree="";; \ - "$(SAGE_VENV)") tree="\\\$$SAGE_VENV";; \ - *) tree="$$tree";; \ + "$(SAGE_LOCAL)") echo " make $$package-SAGE_LOCAL-uninstall;";; \ + "$(SAGE_VENV)") echo " make $$package-SAGE_VENV-uninstall;";; \ + *) echo " ./sage --buildsh -c \"sage-spkg-uninstall $$package $$tree\";";; \ esac; \ - echo " ./sage --buildsh -c \"sage-spkg-uninstall $$package $$tree\";"; \ fi; \ fi; From 6a73497b7a8d5148627643589921ca2549b4ea9b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 20 Jul 2022 20:44:53 -0700 Subject: [PATCH 108/454] build/make/Makefile.in (list-broken-packages): Fix up --- build/make/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 1b0eea338d3..b00ec8bc1cb 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -399,7 +399,7 @@ _clean-broken-gcc: tree=$${stampfile%%/$(SPKG_INST_RELDIR)/*}; \ package_with_version=$${stampfile##*/}; \ package=$${package_with_version%%-*}; \ - if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package; then \ + if ! $(SAGE_VENV)/bin/python3 $(SAGE_ROOT)/build/bin/sage-spkg-installcheck --verbose $$package $$tree; then \ case "$$tree" in \ "$(SAGE_LOCAL)") echo " make $$package-SAGE_LOCAL-uninstall;";; \ "$(SAGE_VENV)") echo " make $$package-SAGE_VENV-uninstall;";; \ From 400eaee762e33cdc28c46ef7587d6f889c5cda22 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 21 Jul 2022 16:01:08 -0700 Subject: [PATCH 109/454] build/sage_bootstrap/installclean.py: Clarify that the sage_local argument is SAGE_LOCAL or SAGE_VENV --- build/sage_bootstrap/installcheck.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index d2c6821da3f..3a3238f5819 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -1,5 +1,5 @@ """ -Command-line script for checking an installed SPKG in $SAGE_LOCAL. +Command-line script for checking an installed SPKG in an installation tree ($SAGE_LOCAL, $SAGE_VENV). """ # **************************************************************************** @@ -32,8 +32,8 @@ def installcheck(spkg_name, sage_local, verbose=False): """ - Given a package name and path to SAGE_LOCAL, check the installation of the package - in SAGE_LOCAL. + Given a package name and path to an installation tree (SAGE_LOCAL or SAGE_VENV), + check the installation of the package in that tree. """ # The default path to this directory; however its value should be read @@ -137,7 +137,7 @@ def make_parser(): parser.add_argument('spkg', type=spkg_type, help='the spkg to check') parser.add_argument('sage_local', type=dir_type, nargs='?', default=os.environ.get('SAGE_LOCAL'), - help='the SAGE_LOCAL path (default: the $SAGE_LOCAL ' + help='the path of the installation tree (default: the $SAGE_LOCAL ' 'environment variable if set)') parser.add_argument('-v', '--verbose', action='store_true', help='verbose output showing all files removed') @@ -152,7 +152,7 @@ def run(argv=None): args = parser.parse_args(argv if argv is not None else sys.argv[1:]) if args.sage_local is None: - print('Error: SAGE_LOCAL must be specified either at the command ' + print('Error: An installation tree must be specified either at the command ' 'line or in the $SAGE_LOCAL environment variable', file=sys.stderr) sys.exit(1) From ef9226bc1717bc579bdeddfb84f77c158a6ce516 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 23 Jul 2022 14:53:06 -0700 Subject: [PATCH 110/454] build/sage_bootstrap/installcheck.py: Implement for Linux --- .../auditwheel_or_delocate/requirements.txt | 2 +- build/sage_bootstrap/installcheck.py | 60 +++++++++++++++---- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/build/pkgs/auditwheel_or_delocate/requirements.txt b/build/pkgs/auditwheel_or_delocate/requirements.txt index 365b7415e16..5d409e519cd 100644 --- a/build/pkgs/auditwheel_or_delocate/requirements.txt +++ b/build/pkgs/auditwheel_or_delocate/requirements.txt @@ -1,2 +1,2 @@ -delocate; sys_platform == 'darwin' +delocate auditwheel; sys_platform != 'darwin' diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index 3a3238f5819..83f28c07c6d 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -21,7 +21,7 @@ import subprocess import sys import argparse -from warnings import warn +import warnings from .env import SAGE_ROOT @@ -30,6 +30,16 @@ """Directory where all spkg sources are found.""" +def check_lib_auditwheel(f, verbose=False): + from auditwheel.lddtree import lddtree + for lib, info in lddtree(f)["libs"].items(): + if verbose: + print('- {0}: {1}'.format(lib, info["realpath"]), file=sys.stderr) + if info["realpath"] is None: + raise RuntimeError('Shared library {0} needed by {1} is not found' + .format(lib, f)) + + def installcheck(spkg_name, sage_local, verbose=False): """ Given a package name and path to an installation tree (SAGE_LOCAL or SAGE_VENV), @@ -75,14 +85,20 @@ def installcheck(spkg_name, sage_local, verbose=False): if verbose: print("Checking shared library file '{0}'" .format(f), file=sys.stderr) - try: - from delocate.libsana import _tree_libs_from_libraries, _filter_system_libs - except ImportError: - warnings.warn('delocate is not available, so nothing is actually checked') + if sys.platform == 'darwin': + try: + from delocate.libsana import _tree_libs_from_libraries, _filter_system_libs + except ImportError: + warnings.warn('delocate is not available, so nothing is actually checked') + else: + _tree_libs_from_libraries([f], + lib_filt_func=_filter_system_libs, + copy_filt_func=lambda path: True) else: - _tree_libs_from_libraries([f], - lib_filt_func=_filter_system_libs, - copy_filt_func=lambda path: True) + try: + check_lib_auditwheel(f, verbose=False) + except ImportError: + warnings.warn('auditwheel is not available, so nothing is actually checked') elif f.endswith('-any.whl'): # pure Python wheel, nothing to check pass @@ -90,12 +106,30 @@ def installcheck(spkg_name, sage_local, verbose=False): if verbose: print("Checking wheel file '{0}'" .format(f), file=sys.stderr) - try: - from delocate import wheel_libs - except ImportError: - warnings.warn('delocate is not available, so nothing is actually checked') + if sys.platform == 'darwin': + try: + from delocate import wheel_libs + except ImportError: + warnings.warn('delocate is not available, so nothing is actually checked') + else: + wheel_libs(f) else: - wheel_libs(f) + try: + from delocate.tmpdirs import TemporaryDirectory + from delocate.tools import zip2dir + except ImportError: + warnings.warn('delocate is not available, so nothing is actually checked') + else: + try: + with TemporaryDirectory() as tmpdir: + zip2dir(f, tmpdir) + for dirpath, dirnames, basenames in os.walk(tmpdir): + for base in basenames: + if base.endswith('.so'): + depending_path = os.path.realpath(os.path.join(dirpath, base)) + check_lib_auditwheel(depending_path, verbose=False) + except ImportError: + warnings.warn('auditwheel is not available, so nothing is actually checked') def dir_type(path): From a9eaae57c9cf70c8bc0ed2968beb584906134927 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 15:32:29 -0700 Subject: [PATCH 111/454] tox.ini, build/bin/write-dockerfile.sh: Add 'tox -e docker-...-incremental' --- build/bin/write-dockerfile.sh | 22 +++++++++++++++++----- tox.ini | 7 ++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index bf3afa3947f..5de4a1987fe 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -34,6 +34,13 @@ echo "# to simplify writing scripts that customize this file" ADD="ADD $__CHOWN" RUN=RUN case $SYSTEM in + none) + # No system packages to install + cat < Date: Tue, 26 Jul 2022 16:40:40 -0700 Subject: [PATCH 112/454] tox.ini: Add comment --- tox.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tox.ini b/tox.ini index 9e4b3e2338d..31c9441b0b6 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,12 @@ # # This will do a complete build of the Sage distribution in a Docker container, which will take a while. # +# To do an incremental build based on the latest build of a beta version on GitHub Actions: +# +# $ tox -e docker-fedora-31-standard-incremental +# +# This will download a large (multi-gigabyte) Docker image from GitHub Packages (ghcr.io). +# # Specific 'make' targets can be given as additional arguments after "--". # For example, to only run the configuration phase: # From 4c0d7f50b18f78ce561eccfc3a6c1196887dc226 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 27 Jul 2022 00:53:11 -0700 Subject: [PATCH 113/454] tox.ini: Use FROM_DOCKER_REPOSITORY --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 31c9441b0b6..1de5af78246 100644 --- a/tox.ini +++ b/tox.ini @@ -148,7 +148,7 @@ passenv = docker: DOCKER_PUSH_REPOSITORY # If set, we symlink this file into {envdir}/.docker/; this can be used for providing credentials for pushing docker: DOCKER_CONFIG_FILE - docker-incremental: FROM_DOCKER_REPO + docker-incremental: FROM_DOCKER_REPOSITORY docker-incremental: FROM_DOCKER_TARGET docker-incremental: FROM_DOCKER_TAG local: MAKE @@ -457,7 +457,7 @@ setenv = # Resulting full image:tag name # docker: FULL_BASE_IMAGE_AND_TAG={env:ARCH_IMAGE_PREFIX:}{env:BASE_IMAGE}{env:ARCH_IMAGE_SUFFIX:}:{env:ARCH_TAG_PREFIX:}{env:BASE_TAG}{env:ARCH_TAG_SUFFIX:} - docker-incremental: FULL_BASE_IMAGE_AND_TAG={env:FROM_DOCKER_REPO:ghcr.io/sagemath/sage/sage-}$(echo {envname} | sed 's/-incremental//')-{env:FROM_DOCKER_TARGET:with-targets}:{env:FROM_DOCKER_TAG:dev} + docker-incremental: FULL_BASE_IMAGE_AND_TAG={env:FROM_DOCKER_REPOSITORY:ghcr.io/sagemath/sage/}sage-$(echo {envname} | sed 's/-incremental//')-{env:FROM_DOCKER_TARGET:with-targets}:{env:FROM_DOCKER_TAG:dev} docker-incremental: SYSTEM=none # docker-nobootstrap: BOOTSTRAP=./bootstrap -D From a07874d2f2443785cc0a295d2d8989ab380d79d2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 27 Jul 2022 09:56:20 -0700 Subject: [PATCH 114/454] build/bin/write-dockerfile.sh: In incremental build, keep logs --- build/bin/write-dockerfile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 5de4a1987fe..0d207c68e52 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -220,7 +220,7 @@ cat < Date: Thu, 28 Jul 2022 15:09:16 -0700 Subject: [PATCH 115/454] tox.ini (docker-incremental): Do not include '-incremental' in the Docker image name --- tox.ini | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 1de5af78246..b3188982a5f 100644 --- a/tox.ini +++ b/tox.ini @@ -636,9 +636,10 @@ commands = docker-{arm64,armhf}: docker run --rm --privileged multiarch/qemu-user-static:register --reset docker: bash -c 'if [ x"{env:DOCKER_CONFIG_FILE:}" != x ]; then mkdir -p {envdir}/.docker && ln -sf $(realpath "{env:DOCKER_CONFIG_FILE:}") {envdir}/.docker/; fi' docker: bash -c 'for docker_target in {env:DOCKER_TARGETS:with-targets}; do \ - docker: BUILD_IMAGE={env:DOCKER_PUSH_REPOSITORY:}sage-{envname}-$docker_target; \ - docker: BUILD_TAG=$BUILD_IMAGE:$(git describe --dirty --always); \ - docker: TAG_ARGS=$(echo --tag $BUILD_TAG; for tag in {env:EXTRA_DOCKER_TAGS:}; do echo --tag $BUILD_IMAGE:$tag; done); \ + docker: BUILD_IMAGE_STEM=sage-$(echo {envname} | sed s/-incremental//); \ + docker: BUILD_IMAGE=$DOCKER_PUSH_REPOSITORY$BUILD_IMAGE_STEM-$docker_target; \ + docker: BUILD_TAG=$(git describe --dirty --always); \ + docker: TAG_ARGS=$(for tag in $BUILD_TAG {env:EXTRA_DOCKER_TAGS:}; do echo --tag $BUILD_IMAGE:$tag; done); \ docker: DOCKER_BUILDKIT={env:DOCKER_BUILDKIT:0} \ docker: docker build . -f {envdir}/Dockerfile \ docker: --target $docker_target \ @@ -652,11 +653,11 @@ commands = docker: --build-arg TARGETS_OPTIONAL="{env:TARGETS_OPTIONAL:ptest}" \ docker: {env:EXTRA_DOCKER_BUILD_ARGS:}; status=$?; \ docker: if [ $status != 0 ]; then \ - docker: BUILD_TAG="$BUILD_TAG-failed"; docker commit $(docker ps -l -q) $BUILD_TAG; PUSH_TAGS=$BUILD_TAG; \ + docker: BUILD_TAG="$BUILD_TAG-failed"; docker commit $(docker ps -l -q) $BUILD_IMAGE:$BUILD_TAG; PUSH_TAGS=$BUILD_IMAGE:$BUILD_TAG; \ docker: else \ - docker: PUSH_TAGS=$(echo $BUILD_TAG; for tag in {env:EXTRA_DOCKER_TAGS:}; do echo "$BUILD_IMAGE:$tag"; done); \ + docker: PUSH_TAGS=$(echo $BUILD_IMAGE:$BUILD_TAG; for tag in {env:EXTRA_DOCKER_TAGS:}; do echo "$BUILD_IMAGE:$tag"; done); \ docker: fi; \ - docker: echo $BUILD_TAG >> {envdir}/Dockertags; \ + docker: echo $BUILD_IMAGE:$BUILD_TAG >> {envdir}/Dockertags; \ docker: if [ x"{env:DOCKER_PUSH_REPOSITORY:}" != x ]; then \ docker: echo Pushing $PUSH_TAGS; \ docker: for tag in $PUSH_TAGS; do \ From a627ce685f4ac30ba9660f66b24fae246209c5d1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 28 Jul 2022 19:04:22 -0700 Subject: [PATCH 116/454] src/sage/rings/qqbar.py (AlgebraicNumber_base._maxima_init_): New --- src/sage/rings/qqbar.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 7db32272b26..3c0830c23c7 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4710,6 +4710,35 @@ def radical_expression(self): roots = candidates interval_field = interval_field.to_prec(interval_field.prec() * 2) + def _maxima_init_(self, I=None): + r""" + EXAMPLES:: + + sage: maxima(QQbar(sqrt(5/2))) + sqrt(5)/sqrt(2) + sage: maxima(AA(-sqrt(5))) + -sqrt(5) + sage: QQbar(sqrt(-2))._maxima_init_() + Traceback (most recent call last): + ... + NotImplementedError: conversion implemented only for square roots of nonnegative rationals + """ + try: + return self._rational_()._maxima_init_(I) + except ValueError: + pass + try: + square_QQ = (self ** 2)._rational_() + except ValueError: + raise NotImplementedError('conversion implemented only for square roots of nonnegative rationals') + else: + if square_QQ < 0: + raise NotImplementedError('conversion implemented only for square roots of nonnegative rationals') + if self >= 0: + return 'sqrt(' + square_QQ._maxima_init_() + ')' + else: + return '-sqrt(' + square_QQ._maxima_init_() + ')' + class AlgebraicNumber(AlgebraicNumber_base): r""" From 6204a76d0ec2cbc3cd350cd82f77b2b3ec8088c0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 28 Jul 2022 21:22:39 -0700 Subject: [PATCH 117/454] src/sage/rings/qqbar.py (AlgebraicNumber_base._maxima_init_): Fixup --- 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 3c0830c23c7..eadc0689385 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4724,7 +4724,7 @@ def _maxima_init_(self, I=None): NotImplementedError: conversion implemented only for square roots of nonnegative rationals """ try: - return self._rational_()._maxima_init_(I) + return self._rational_()._maxima_init_() except ValueError: pass try: From 3e560cf5b30377da9d3122427f81f9b0ef93c5c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 28 Jul 2022 21:25:00 -0700 Subject: [PATCH 118/454] src/sage/rings/qqbar.py: Add doctest --- src/sage/rings/qqbar.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index eadc0689385..7da3e3fa415 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4714,6 +4714,8 @@ def _maxima_init_(self, I=None): r""" EXAMPLES:: + sage: maxima(AA(7)) + 7 sage: maxima(QQbar(sqrt(5/2))) sqrt(5)/sqrt(2) sage: maxima(AA(-sqrt(5))) From 58f4cd1c63b7d7a8aee04de5d8c53a7fac27d061 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 28 Jul 2022 22:55:42 -0700 Subject: [PATCH 119/454] src/sage/rings/qqbar.py (AlgebraicNumber_base._maxima_init_): Generalize using radical_expression --- src/sage/rings/qqbar.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 7da3e3fa415..9e865493bf9 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4717,29 +4717,26 @@ def _maxima_init_(self, I=None): sage: maxima(AA(7)) 7 sage: maxima(QQbar(sqrt(5/2))) - sqrt(5)/sqrt(2) + sqrt(10)/2 sage: maxima(AA(-sqrt(5))) -sqrt(5) - sage: QQbar(sqrt(-2))._maxima_init_() + sage: maxima(QQbar(sqrt(-2))) + sqrt(2)*%i + sage: maxima(AA(2+sqrt(5))) + sqrt(5)+2 + sage: maxima(QQ[x](x^7 - x - 1).roots(AA, False)[0]) Traceback (most recent call last): ... - NotImplementedError: conversion implemented only for square roots of nonnegative rationals + NotImplementedError: cannot find radical expression """ try: return self._rational_()._maxima_init_() except ValueError: pass - try: - square_QQ = (self ** 2)._rational_() - except ValueError: - raise NotImplementedError('conversion implemented only for square roots of nonnegative rationals') - else: - if square_QQ < 0: - raise NotImplementedError('conversion implemented only for square roots of nonnegative rationals') - if self >= 0: - return 'sqrt(' + square_QQ._maxima_init_() + ')' - else: - return '-sqrt(' + square_QQ._maxima_init_() + ')' + rad = self.radical_expression() + if isinstance(rad.parent(), sage.rings.abc.SymbolicRing): + return rad._maxima_init_() + raise NotImplementedError('cannot find radical expression') class AlgebraicNumber(AlgebraicNumber_base): From b354a3873b4c6fac29dee7bbd5fbf3ef6a35d8f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 31 Jul 2022 22:22:22 -0700 Subject: [PATCH 120/454] tox.ini, .github/workflows/docker.yml: Reimplement -incremental via SKIP_SYSTEM_PACKAGES --- build/bin/write-dockerfile.sh | 105 +++++++++++++++------------------- tox.ini | 2 +- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 0d207c68e52..98d422cbe98 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -33,25 +33,12 @@ echo "# the :comments: separate the generated file into sections" echo "# to simplify writing scripts that customize this file" ADD="ADD $__CHOWN" RUN=RUN -case $SYSTEM in - none) - # No system packages to install - cat < /bin/dash; # but some of the scripts in /opt/conda/etc/conda/activate.d # from conda-forge (as of 2020-01-27) contain bash-isms: @@ -173,11 +160,13 @@ EOF exit 1 ;; esac -case $SYSTEM in - none) - ;; - *) - cat < Date: Wed, 27 Jul 2022 17:46:14 -0700 Subject: [PATCH 121/454] build/bin/write-dockerfile.sh: Do not use persistent env var PACKAGES (except on nix) --- build/bin/write-dockerfile.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 98d422cbe98..85412cfb474 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -114,6 +114,7 @@ EOF cat < Date: Wed, 27 Jul 2022 18:28:52 -0700 Subject: [PATCH 122/454] tox.ini (ubuntu-jammy, debian-bookworm, fedora-35, fedora-36): Do not IGNORE_MISSING_SYSTEM_PACKAGES --- tox.ini | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 61e4f19b09c..75e982a741c 100644 --- a/tox.ini +++ b/tox.ini @@ -211,7 +211,6 @@ setenv = ubuntu-bionic: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-focal: BASE_TAG=focal ubuntu-jammy: BASE_TAG=jammy - ubuntu-jammy: IGNORE_MISSING_SYSTEM_PACKAGES=yes ubuntu-kinetic: BASE_TAG=kinetic ubuntu-kinetic: IGNORE_MISSING_SYSTEM_PACKAGES=yes # @@ -228,7 +227,6 @@ setenv = debian-bullseye: BASE_TAG=bullseye debian-bullseye: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-bookworm: BASE_TAG=bookworm - debian-bookworm: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-sid: BASE_TAG=sid # # https://hub.docker.com/u/linuxmintd @@ -268,9 +266,9 @@ setenv = fedora-34: BASE_TAG=34 fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-35: BASE_TAG=35 - fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=yes + fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-36: BASE_TAG=36 - fedora-36: IGNORE_MISSING_SYSTEM_PACKAGES=yes + fedora-36: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-37: BASE_TAG=37 fedora-37: IGNORE_MISSING_SYSTEM_PACKAGES=yes # From 326f61dc6ff96d15019954b55f4de87298f5e96c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 28 Jul 2022 16:40:37 -0700 Subject: [PATCH 123/454] Replace tox-optional, tox-experimental.yml by use of reusable workflow in ci-linux.yml --- .github/workflows/ci-linux.yml | 98 +++++++++++++++++++- .github/workflows/ci-macos.yml | 2 +- .github/workflows/docker.yml | 27 +++++- .github/workflows/tox-experimental.yml | 123 ------------------------- .github/workflows/tox-optional.yml | 123 ------------------------- build/bin/write-dockerfile.sh | 1 + tox.ini | 1 + 7 files changed, 125 insertions(+), 250 deletions(-) delete mode 100644 .github/workflows/tox-experimental.yml delete mode 100644 .github/workflows/tox-optional.yml diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index b405d335ab5..44471c39ca5 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -33,15 +33,111 @@ env: jobs: - docker: + docker-pre: uses: ./.github/workflows/docker.yml with: + # Build from scratch + docker_targets: "with-system-packages configured with-targets-pre" # FIXME: duplicated from env.TARGETS targets_pre: all-sage-local + tox_packages_factors: >- + ["minimal", + "standard", + "maximal", + ] + docker_push_repository: ghcr.io/${{ github.repository }}/ + + docker: + needs: [docker-pre] + uses: ./.github/workflows/docker.yml + with: + # Build incrementally from previous stage (docker-pre) + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + docker_targets: "with-targets with-targets-optional" + # FIXME: duplicated from env.TARGETS targets: build doc-html targets_optional: ptest docker_push_repository: ghcr.io/${{ github.repository }}/ + docker-optional-0-o: + needs: [docker-pre] + uses: ./.github/workflows/docker.yml + with: + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + tox_packages_factors: >- + ["maximal"] + docker_targets: "with-targets-optional" + targets_optional: >- + $( echo $(export PATH=build/bin:$PATH + && (for a in spkg-install.in spkg-install requirements.txt; do + sage-package list :optional: + --has-file $a + --no-file huge + --no-file has_nonfree_dependencies; + done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) + + docker-optional-p-z: + needs: [docker-pre] + uses: ./.github/workflows/docker.yml + with: + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + tox_packages_factors: >- + ["maximal"] + docker_targets: "with-targets-optional" + targets_optional: >- + $( echo $(export PATH=build/bin:$PATH + && (for a in spkg-install.in spkg-install requirements.txt; do + sage-package list :optional: + --has-file $a + --no-file huge + --no-file has_nonfree_dependencies; + done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) + + docker-experimental-0-o: + needs: [docker-pre] + uses: ./.github/workflows/docker.yml + with: + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + tox_packages_factors: >- + ["maximal"] + docker_targets: "with-targets-optional" + targets_optional: >- + $( echo $(export PATH=build/bin:$PATH + && (for a in spkg-install.in spkg-install requirements.txt; do + sage-package list :experimental: + --has-file $a + --no-file huge + --no-file has_nonfree_dependencies; + done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) + + docker-experimental-p-z: + needs: [docker-pre] + uses: ./.github/workflows/docker.yml + with: + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + tox_packages_factors: >- + ["maximal"] + docker_targets: "with-targets-optional" + targets_optional: >- + $( echo $(export PATH=build/bin:$PATH + && (for a in spkg-install.in spkg-install requirements.txt; do + sage-package list :experimental: + --has-file $a + --no-file huge + --no-file has_nonfree_dependencies; + done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) + + local-ubuntu: runs-on: ubuntu-latest diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 49b97aa6e08..54edd8fdb5f 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -76,7 +76,7 @@ jobs: # For doctesting, we use a lower parallelization to avoid timeouts. run: | case "${{ matrix.stage }}" in - 1) export TARGETS_PRE="all-sage-local" TARGETS="all-sage-local" TARGETS_OPTIONAL="" + 1) export TARGETS_PRE="all-sage-local" TARGETS="all-sage-local" TARGETS_OPTIONAL="build/make/Makefile" ;; 2) export TARGETS_PRE="all-sage-local" TARGETS="build doc-html" TARGETS_OPTIONAL="ptest" ;; diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 23cab40539d..b9d96a87890 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,6 +76,25 @@ on: required: false type: string # + # Incremental builds + # + docker_targets: + default: "with-system-packages configured with-targets-pre with-targets with-targets-optional" + type: string + incremental: + default: false + type: boolean + from_docker_repository: + required: false + type: string + from_docker_target: + required: false + type: string + from_docker_tag: + required: false + default: "$BUILD_TAG" + type: string + # # For use in upstream CIs # upstream_artifact: @@ -104,12 +123,16 @@ jobs: tox_system_factor: ${{ fromJson(inputs.tox_system_factors) }} tox_packages_factor: ${{ fromJson(inputs.tox_packages_factors) }} env: - TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} + TOX_ENV: "docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}${{ inputs.incremental && '-incremental' || '' }}" LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} - DOCKER_TARGETS: with-system-packages configured with-targets-pre with-targets with-targets-optional + DOCKER_TARGETS: ${{ inputs.docker_targets }} TARGETS_PRE: ${{ inputs.targets_pre }} TARGETS: ${{ inputs.targets }} TARGETS_OPTIONAL: ${{ inputs.targets_optional }} + FROM_DOCKER_REPOSITORY: ${{ inputs.from_docker_repository }} + FROM_DOCKER_TARGET: ${{ inputs.from_docker_target }} + FROM_DOCKER_TAG: ${{ inputs.from_docker_tag }} + steps: - name: Check out SageMath uses: actions/checkout@v2 diff --git a/.github/workflows/tox-experimental.yml b/.github/workflows/tox-experimental.yml deleted file mode 100644 index 3abeec4a775..00000000000 --- a/.github/workflows/tox-experimental.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Test experimental packages with tox - -## This GitHub Actions workflow runs SAGE_ROOT/tox.ini with select environments, -## whenever a GitHub pull request is opened or synchronized in a repository -## where GitHub Actions are enabled. -## -## It builds and checks some sage spkgs as defined in TARGETS. -## -## A job succeeds if there is no error. -## -## The build is run with "make V=0", so the build logs of individual packages are suppressed. -## -## At the end, all package build logs that contain an error are printed out. -## -## After all jobs have finished (or are canceled) and a short delay, -## tar files of all logs are made available as "build artifacts". - -#on: [push, pull_request] - -on: - pull_request: - types: [opened, synchronize] - push: - tags: - - '*' - workflow_dispatch: - # Allow to run manually - -env: - TARGETS_PRE: build/make/Makefile - TARGETS: build/make/Makefile - # TARGETS_OPTIONAL see below - -jobs: - docker: - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 6 - matrix: - tox_system_factor: [ubuntu-trusty-toolchain-gcc_9, ubuntu-xenial-toolchain-gcc_9, ubuntu-bionic, ubuntu-focal, ubuntu-jammy, ubuntu-kinetic, debian-stretch, debian-buster, debian-bullseye, debian-bookworm, debian-sid, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, linuxmint-20.3, linuxmint-21, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, fedora-36, fedora-37, centos-7-devtoolset-gcc_11, centos-stream-8, gentoo-python3.9, gentoo-python3.10, archlinux-latest, opensuse-15.3, opensuse-tumbleweed, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386-devtoolset-gcc_11] - tox_packages_factor: [maximal] - targets_pattern: [0-g, h-o, p, q-z] - env: - TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} - LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} - DOCKER_TARGETS: configured with-targets with-targets-optional - # Test all non-dummy experimental packages, but do not test huge packages - # and do not test packages that require external software - TARGETS_OPTIONAL: "$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :experimental: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep '^[${{ matrix.targets_pattern }}]' ) )" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 500 - - name: fetch tags - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - name: free disk space - run: | - df -h - sudo swapoff -a - sudo rm -f /swapfile - sudo apt-get clean - docker rmi $(docker image ls -aq) - echo "Largest packages:" - dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 50 - sudo apt-get --fix-broken --yes remove $(dpkg-query -f '${Package}\n' -W | grep -E '^(ghc-|google-cloud-sdk|google-chrome|firefox|mysql-server|dotnet-sdk|hhvm|mono)') || echo "(error ignored)" - df -h - - name: Install test prerequisites - run: | - sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - sudo apt-get clean - df -h - - name: Try to login to ghcr.io - # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable - run: | - TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" - if [ -z "$TOKEN" ]; then - TOKEN="${{ secrets.GITHUB_TOKEN }}" - fi - if echo "$TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin; then - echo "DOCKER_PUSH_REPOSITORY=ghcr.io/${{ github.repository }}/" >> $GITHUB_ENV - echo "DOCKER_CONFIG_FILE=$HOME/.docker/config.json" >> $GITHUB_ENV - fi - # From the docker documentation via .ci/update-env.sh: - # "A tag name must be valid ASCII and may - # contain lowercase and uppercase letters, digits, underscores, periods and - # dashes. A tag name may not start with a period or a dash and may contain a - # maximum of 128 characters." - EXTRA_DOCKER_TAGS=`echo $GITHUB_REF_NAME | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` - shopt -s extglob - case "$GITHUB_REF_NAME" in - +([0-9]).+([0-9])?(.+([0-9])) ) - EXTRA_DOCKER_TAGS="latest dev $EXTRA_DOCKER_TAGS";; - +([0-9]).+([0-9])?(.+([0-9])).@(beta|rc)+([0-9]) ) - EXTRA_DOCKER_TAGS="dev $EXTRA_DOCKER_TAGS";; - esac - echo "EXTRA_DOCKER_TAGS=$EXTRA_DOCKER_TAGS" >> $GITHUB_ENV - - run: | - set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=3\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;" - - name: Copy logs from the docker image or build container - run: | - mkdir -p "artifacts/$LOGS_ARTIFACT_NAME" - cp -r .tox/$TOX_ENV/Dockerfile .tox/$TOX_ENV/log "artifacts/$LOGS_ARTIFACT_NAME" - if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi - if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi - if: always() - - uses: actions/upload-artifact@v1 - with: - path: artifacts - name: ${{ env.LOGS_ARTIFACT_NAME }} - if: always() - - name: Print out logs for immediate inspection - # and markup the output with GitHub Actions logging commands - run: | - .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME" - if: always() - - name: List docker images - run: | - if [ -f .tox/$TOX_ENV/Dockertags ]; then - cat .tox/$TOX_ENV/Dockertags - fi - if: always() diff --git a/.github/workflows/tox-optional.yml b/.github/workflows/tox-optional.yml deleted file mode 100644 index 72b63ef61c3..00000000000 --- a/.github/workflows/tox-optional.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Test optional packages with tox - -## This GitHub Actions workflow runs SAGE_ROOT/tox.ini with select environments, -## whenever a GitHub pull request is opened or synchronized in a repository -## where GitHub Actions are enabled. -## -## It builds and checks some sage spkgs as defined in TARGETS. -## -## A job succeeds if there is no error. -## -## The build is run with "make V=0", so the build logs of individual packages are suppressed. -## -## At the end, all package build logs that contain an error are printed out. -## -## After all jobs have finished (or are canceled) and a short delay, -## tar files of all logs are made available as "build artifacts". - -#on: [push, pull_request] - -on: - pull_request: - types: [opened, synchronize] - push: - tags: - - '*' - workflow_dispatch: - # Allow to run manually - -env: - TARGETS_PRE: build/make/Makefile - TARGETS: build/make/Makefile - # TARGETS_OPTIONAL see below - -jobs: - docker: - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 6 - matrix: - tox_system_factor: [ubuntu-trusty-toolchain-gcc_9, ubuntu-xenial-toolchain-gcc_9, ubuntu-bionic, ubuntu-focal, ubuntu-jammy, ubuntu-kinetic, debian-stretch, debian-buster, debian-bullseye, debian-bookworm, debian-sid, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, linuxmint-20.3, linuxmint-21, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, fedora-36, fedora-37, centos-7-devtoolset-gcc_11, centos-stream-8, gentoo-python3.9, gentoo-python3.10, archlinux-latest, opensuse-15.3, opensuse-tumbleweed, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386-devtoolset-gcc_11] - tox_packages_factor: [maximal] - targets_pattern: [0-g, h-o, p, q-z] - env: - TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} - LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} - DOCKER_TARGETS: configured with-targets with-targets-optional - # Test all non-dummy optional packages, but do not test huge packages - # and do not test packages that require external software - TARGETS_OPTIONAL: "$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :optional: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep '^[${{ matrix.targets_pattern }}]' ) )" - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 500 - - name: fetch tags - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - - name: free disk space - run: | - df -h - sudo swapoff -a - sudo rm -f /swapfile - sudo apt-get clean - docker rmi $(docker image ls -aq) - echo "Largest packages:" - dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 50 - sudo apt-get --fix-broken --yes remove $(dpkg-query -f '${Package}\n' -W | grep -E '^(ghc-|google-cloud-sdk|google-chrome|firefox|mysql-server|dotnet-sdk|hhvm|mono)') || echo "(error ignored)" - df -h - - name: Install test prerequisites - run: | - sudo DEBIAN_FRONTEND=noninteractive apt-get update - sudo DEBIAN_FRONTEND=noninteractive apt-get install tox - sudo apt-get clean - df -h - - name: Try to login to ghcr.io - # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable - run: | - TOKEN="${{ secrets.DOCKER_PKG_GITHUB_TOKEN }}" - if [ -z "$TOKEN" ]; then - TOKEN="${{ secrets.GITHUB_TOKEN }}" - fi - if echo "$TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin; then - echo "DOCKER_PUSH_REPOSITORY=ghcr.io/${{ github.repository }}/" >> $GITHUB_ENV - echo "DOCKER_CONFIG_FILE=$HOME/.docker/config.json" >> $GITHUB_ENV - fi - # From the docker documentation via .ci/update-env.sh: - # "A tag name must be valid ASCII and may - # contain lowercase and uppercase letters, digits, underscores, periods and - # dashes. A tag name may not start with a period or a dash and may contain a - # maximum of 128 characters." - EXTRA_DOCKER_TAGS=`echo $GITHUB_REF_NAME | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` - shopt -s extglob - case "$GITHUB_REF_NAME" in - +([0-9]).+([0-9])?(.+([0-9])) ) - EXTRA_DOCKER_TAGS="latest dev $EXTRA_DOCKER_TAGS";; - +([0-9]).+([0-9])?(.+([0-9])).@(beta|rc)+([0-9]) ) - EXTRA_DOCKER_TAGS="dev $EXTRA_DOCKER_TAGS";; - esac - echo "EXTRA_DOCKER_TAGS=$EXTRA_DOCKER_TAGS" >> $GITHUB_ENV - - run: | - set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=3\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;" - - name: Copy logs from the docker image or build container - run: | - mkdir -p "artifacts/$LOGS_ARTIFACT_NAME" - cp -r .tox/$TOX_ENV/Dockerfile .tox/$TOX_ENV/log "artifacts/$LOGS_ARTIFACT_NAME" - if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi - if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi - if: always() - - uses: actions/upload-artifact@v1 - with: - path: artifacts - name: ${{ env.LOGS_ARTIFACT_NAME }} - if: always() - - name: Print out logs for immediate inspection - # and markup the output with GitHub Actions logging commands - run: | - .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME" - if: always() - - name: List docker images - run: | - if [ -f .tox/$TOX_ENV/Dockertags ]; then - cat .tox/$TOX_ENV/Dockertags - fi - if: always() diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 85412cfb474..0653c164a06 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -129,6 +129,7 @@ EOF ;; opensuse*) UPDATE="zypper refresh &&" + EXISTS="zypper --quiet install --no-confirm --auto-agree-with-licenses --no-recommends --download-only > /dev/null" INSTALL="zypper --ignore-unknown install --no-confirm --auto-agree-with-licenses --no-recommends --details" ;; conda*) diff --git a/tox.ini b/tox.ini index 75e982a741c..917b9254d09 100644 --- a/tox.ini +++ b/tox.ini @@ -141,6 +141,7 @@ passenv = docker: EXTRA_DOCKER_TAGS # Use DOCKER_BUILDKIT=1 for new version - for which unfortunately we cannot save failed builds as an image docker: DOCKER_BUILDKIT + docker: BUILDKIT_INLINE_CACHE # Set for example to "with-system-packages configured with-targets-pre with-targets" # to tag intermediate images. docker: DOCKER_TARGETS From 31c4722cb7f540f517d8dab60d67c5f173db42a4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 29 Jul 2022 03:26:59 -0700 Subject: [PATCH 124/454] .github/workflows/ci-linux.yml: Run following stages even when a job in the previous stage fails --- .github/workflows/ci-linux.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 44471c39ca5..70585815747 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -48,6 +48,7 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ docker: + if: ${{ always() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -62,6 +63,7 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ docker-optional-0-o: + if: ${{ always() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -81,6 +83,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) docker-optional-p-z: + if: ${{ always() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -100,6 +103,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) docker-experimental-0-o: + if: ${{ always() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -119,6 +123,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) docker-experimental-p-z: + if: ${{ always() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: From a6ee7a59e10f0be55c84bac109c0739f163e3968 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 29 Jul 2022 18:21:17 -0700 Subject: [PATCH 125/454] .github/workflows/ci-linux.yml: Use success() || failure() --- .github/workflows/ci-linux.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 70585815747..4f740dc1f36 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -48,7 +48,7 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ docker: - if: ${{ always() }} + if: ${{ success() || failure() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -63,7 +63,7 @@ jobs: docker_push_repository: ghcr.io/${{ github.repository }}/ docker-optional-0-o: - if: ${{ always() }} + if: ${{ success() || failure() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -83,7 +83,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) docker-optional-p-z: - if: ${{ always() }} + if: ${{ success() || failure() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -103,7 +103,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) docker-experimental-0-o: - if: ${{ always() }} + if: ${{ success() || failure() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: @@ -123,7 +123,7 @@ jobs: done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) docker-experimental-p-z: - if: ${{ always() }} + if: ${{ success() || failure() }} needs: [docker-pre] uses: ./.github/workflows/docker.yml with: From f2b52ef90dfd97128c2f458d5fe5c2dd6cf188e3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 30 Jul 2022 09:28:30 -0700 Subject: [PATCH 126/454] .github/workflows/ci-linux.yml: First run all 'standard' jobs, then 'minimal', ... --- .github/workflows/ci-linux.yml | 65 ++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 4f740dc1f36..ce19a0c4e86 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -33,7 +33,7 @@ env: jobs: - docker-pre: + docker-pre-standard: uses: ./.github/workflows/docker.yml with: # Build from scratch @@ -41,15 +41,12 @@ jobs: # FIXME: duplicated from env.TARGETS targets_pre: all-sage-local tox_packages_factors: >- - ["minimal", - "standard", - "maximal", - ] + ["standard"] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker: + docker-standard: if: ${{ success() || failure() }} - needs: [docker-pre] + needs: [docker-pre-standard] uses: ./.github/workflows/docker.yml with: # Build incrementally from previous stage (docker-pre) @@ -60,11 +57,57 @@ jobs: # FIXME: duplicated from env.TARGETS targets: build doc-html targets_optional: ptest + tox_packages_factors: >- + ["standard"] + docker_push_repository: ghcr.io/${{ github.repository }}/ + + docker-pre-minimal: + if: ${{ success() || failure() }} + # It does not really "need" it. + needs: [docker-standard] + uses: ./.github/workflows/docker.yml + with: + # Build from scratch + docker_targets: "with-system-packages configured with-targets-pre" + # FIXME: duplicated from env.TARGETS + targets_pre: all-sage-local + tox_packages_factors: >- + ["minimal"] + docker_push_repository: ghcr.io/${{ github.repository }}/ + + docker-minimal: + if: ${{ success() || failure() }} + needs: [docker-pre-minimal] + uses: ./.github/workflows/docker.yml + with: + # Build incrementally from previous stage (docker-pre) + incremental: true + from_docker_repository: ghcr.io/${{ github.repository }}/ + from_docker_target: "with-targets-pre" + docker_targets: "with-targets with-targets-optional" + # FIXME: duplicated from env.TARGETS + targets: build doc-html + targets_optional: ptest + tox_packages_factors: >- + ["minimal] + docker_push_repository: ghcr.io/${{ github.repository }}/ + + docker-pre-maximal: + if: ${{ success() || failure() }} + needs: [docker-minimal] + uses: ./.github/workflows/docker.yml + with: + # Build from scratch + docker_targets: "with-system-packages configured with-targets-pre" + # FIXME: duplicated from env.TARGETS + targets_pre: all-sage-local + tox_packages_factors: >- + ["maximal"] docker_push_repository: ghcr.io/${{ github.repository }}/ docker-optional-0-o: if: ${{ success() || failure() }} - needs: [docker-pre] + needs: [docker-pre-maximal] uses: ./.github/workflows/docker.yml with: incremental: true @@ -84,7 +127,7 @@ jobs: docker-optional-p-z: if: ${{ success() || failure() }} - needs: [docker-pre] + needs: [docker-optional-0-o] uses: ./.github/workflows/docker.yml with: incremental: true @@ -104,7 +147,7 @@ jobs: docker-experimental-0-o: if: ${{ success() || failure() }} - needs: [docker-pre] + needs: [docker-optional-p-z] uses: ./.github/workflows/docker.yml with: incremental: true @@ -124,7 +167,7 @@ jobs: docker-experimental-p-z: if: ${{ success() || failure() }} - needs: [docker-pre] + needs: [docker-experimental-0-o] uses: ./.github/workflows/docker.yml with: incremental: true From ca6d16398420c242228656c04373b7e920684100 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 30 Jul 2022 09:45:21 -0700 Subject: [PATCH 127/454] .github/workflows/ci-linux.yml: Rename stages --- .github/workflows/ci-linux.yml | 38 +++++++++++++++++----------------- .github/workflows/docker.yml | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index ce19a0c4e86..c0df79de3a5 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -33,7 +33,7 @@ env: jobs: - docker-pre-standard: + standard-pre: uses: ./.github/workflows/docker.yml with: # Build from scratch @@ -44,12 +44,12 @@ jobs: ["standard"] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker-standard: + standard: if: ${{ success() || failure() }} - needs: [docker-pre-standard] + needs: [standard-pre] uses: ./.github/workflows/docker.yml with: - # Build incrementally from previous stage (docker-pre) + # Build incrementally from previous stage (pre) incremental: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" @@ -61,10 +61,10 @@ jobs: ["standard"] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker-pre-minimal: + minimal-pre: if: ${{ success() || failure() }} # It does not really "need" it. - needs: [docker-standard] + needs: [standard] uses: ./.github/workflows/docker.yml with: # Build from scratch @@ -75,12 +75,12 @@ jobs: ["minimal"] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker-minimal: + minimal: if: ${{ success() || failure() }} - needs: [docker-pre-minimal] + needs: [minimal-pre] uses: ./.github/workflows/docker.yml with: - # Build incrementally from previous stage (docker-pre) + # Build incrementally from previous stage (pre) incremental: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" @@ -92,9 +92,9 @@ jobs: ["minimal] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker-pre-maximal: + maximal-pre: if: ${{ success() || failure() }} - needs: [docker-minimal] + needs: [minimal] uses: ./.github/workflows/docker.yml with: # Build from scratch @@ -105,9 +105,9 @@ jobs: ["maximal"] docker_push_repository: ghcr.io/${{ github.repository }}/ - docker-optional-0-o: + optional-0-o: if: ${{ success() || failure() }} - needs: [docker-pre-maximal] + needs: [maximal-pre] uses: ./.github/workflows/docker.yml with: incremental: true @@ -125,9 +125,9 @@ jobs: --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) - docker-optional-p-z: + optional-p-z: if: ${{ success() || failure() }} - needs: [docker-optional-0-o] + needs: [optional-0-o] uses: ./.github/workflows/docker.yml with: incremental: true @@ -145,9 +145,9 @@ jobs: --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) - docker-experimental-0-o: + experimental-0-o: if: ${{ success() || failure() }} - needs: [docker-optional-p-z] + needs: [optional-p-z] uses: ./.github/workflows/docker.yml with: incremental: true @@ -165,9 +165,9 @@ jobs: --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) - docker-experimental-p-z: + experimental-p-z: if: ${{ success() || failure() }} - needs: [docker-experimental-0-o] + needs: [experimental-0-o] uses: ./.github/workflows/docker.yml with: incremental: true diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b9d96a87890..79fd279963c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -68,7 +68,7 @@ on: ] max_parallel: type: number - default: 20 + default: 24 # # Publishing to GitHub Packages # From 5713937de7ed72810013e0856b7ac0cca0d9b3e8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 2 Aug 2022 21:54:51 -0700 Subject: [PATCH 128/454] sage -package list --has-file, --no-file: Handle disjunctions --- .github/workflows/ci-linux.yml | 38 ++++------------------------ .github/workflows/ci-macos.yml | 4 +-- build/sage_bootstrap/cmdline.py | 4 +-- build/sage_bootstrap/expand_class.py | 8 ++++-- 4 files changed, 15 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index c0df79de3a5..d5935789219 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -116,14 +116,8 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - targets_optional: >- - $( echo $(export PATH=build/bin:$PATH - && (for a in spkg-install.in spkg-install requirements.txt; do - sage-package list :optional: - --has-file $a - --no-file huge - --no-file has_nonfree_dependencies; - done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[0-o]))' + optional-p-z: if: ${{ success() || failure() }} @@ -136,14 +130,7 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - targets_optional: >- - $( echo $(export PATH=build/bin:$PATH - && (for a in spkg-install.in spkg-install requirements.txt; do - sage-package list :optional: - --has-file $a - --no-file huge - --no-file has_nonfree_dependencies; - done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[p-z]))' experimental-0-o: if: ${{ success() || failure() }} @@ -156,14 +143,7 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - targets_optional: >- - $( echo $(export PATH=build/bin:$PATH - && (for a in spkg-install.in spkg-install requirements.txt; do - sage-package list :experimental: - --has-file $a - --no-file huge - --no-file has_nonfree_dependencies; - done) | grep -v ^_ | grep -v sagemath_doc | grep ^[0-o] ) ) + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[0-o]))' experimental-p-z: if: ${{ success() || failure() }} @@ -176,15 +156,7 @@ jobs: tox_packages_factors: >- ["maximal"] docker_targets: "with-targets-optional" - targets_optional: >- - $( echo $(export PATH=build/bin:$PATH - && (for a in spkg-install.in spkg-install requirements.txt; do - sage-package list :experimental: - --has-file $a - --no-file huge - --no-file has_nonfree_dependencies; - done) | grep -v ^_ | grep -v sagemath_doc | grep ^[p-z] ) ) - + targets_optional: '$(echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file "spkg-install.in|spkg-install|requirements.txt" --no-file "huge|has_nonfree_dependencies" | grep -v sagemath_doc | grep ^[p-z]))' local-ubuntu: diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 54edd8fdb5f..d2df2268199 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -83,12 +83,12 @@ jobs: 2-optional*) export TARGETS_PRE="build/make/Makefile" TARGETS="build/make/Makefile" targets_pattern="${{ matrix.stage }}" targets_pattern="${targets_pattern#2-optional-}" - export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :optional: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) + export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && sage-package list :optional: --has-file 'spkg-install.in|spkg-install|requirements.txt' --no-file huge|has_nonfree_dependencies | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) ;; 2-experimental*) export TARGETS_PRE="build/make/Makefile" TARGETS="build/make/Makefile" targets_pattern="${{ matrix.stage }}" targets_pattern="${targets_pattern#2-experimental-}" - export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :experimental: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) + export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && sage-package list :experimental: --has-file 'spkg-install.in|spkg-install|requirements.txt' --no-file huge|has_nonfree_dependencies | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) ;; esac MAKE="make -j12" tox -e $TOX_ENV -- SAGE_NUM_THREADS=4 $TARGETS diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 8c0515c0ede..4a39ecba7cc 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -206,11 +206,11 @@ def make_parser(): parser_list.add_argument( '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files', help=('only include packages that have this file in their metadata directory ' - '(examples: SPKG.rst, spkg-configure.m4, distros/debian.txt)')) + '(examples: SPKG.rst, spkg-configure.m4, distros/debian.txt, spkg-install|spkg-install.in)')) parser_list.add_argument( '--no-file', action='append', default=[], metavar='FILENAME', dest='no_files', help=('only include packages that do not have this file in their metadata directory ' - '(examples: huge, patches)')) + '(examples: huge, patches, huge|has_nonfree_dependencies)')) parser_list.add_argument( '--exclude', action='append', default=[], metavar='PACKAGE_NAME', help='exclude package from list') diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py index 555513905a4..10ec907d0fa 100644 --- a/build/sage_bootstrap/expand_class.py +++ b/build/sage_bootstrap/expand_class.py @@ -32,9 +32,13 @@ def __init__(self, *package_names_or_classes, **filters): def included_in_filter(pkg): if pkg.name in excluded: return False - if not all(pkg.has_file(filename) for filename in filenames): + if not all(any(pkg.has_file(filename) + for filename in filename_disjunction.split('|')) + for filename_disjunction in filenames): return False - return not any(pkg.has_file(filename) for filename in no_filenames) + return not any(any(pkg.has_file(filename) + for filename in no_filename_disjunction.split('|')) + for no_filename_disjunction in no_filenames) for package_name_or_class in package_names_or_classes: if package_name_or_class == ':all:': From c599e491b1e994bd631371d12da74a9b9166e6ba Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Fri, 5 Aug 2022 08:44:39 +0200 Subject: [PATCH 129/454] 34282: initial --- src/sage/features/latex.py | 42 ++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index b8915502582..733a75df9d7 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -13,6 +13,9 @@ # **************************************************************************** from . import StaticFile, Executable, FeatureTestResult, FeatureNotPresentError +from sage.features.join_feature import JoinFeature + +latex_url = 'https://www.latex-project.org/' class latex(Executable): r""" @@ -32,8 +35,7 @@ def __init__(self): sage: isinstance(latex(), latex) True """ - Executable.__init__(self, "latex", executable="latex", - url="https://www.latex-project.org/") + Executable.__init__(self, "latex", executable="latex", url=latex_url) def is_functional(self): r""" @@ -92,8 +94,7 @@ def __init__(self): sage: isinstance(pdflatex(), pdflatex) True """ - Executable.__init__(self, "pdflatex", executable="pdflatex", - url="https://www.latex-project.org/") + Executable.__init__(self, "pdflatex", executable="pdflatex", url=latex_url) class xelatex(Executable): r""" @@ -113,8 +114,8 @@ def __init__(self): sage: isinstance(xelatex(), xelatex) True """ - Executable.__init__(self, "xelatex", executable="xelatex", - url="https://www.latex-project.org/") + Executable.__init__(self, "xelatex", executable="xelatex", url=latex_url) + class lualatex(Executable): r""" @@ -134,11 +135,10 @@ def __init__(self): sage: isinstance(lualatex(), lualatex) True """ - Executable.__init__(self, "lualatex", executable="lualatex", - url="https://www.latex-project.org/") + Executable.__init__(self, "lualatex", executable="lualatex", url=latex_url) -class TeXFile(StaticFile): +class TeXFile(StaticFile, JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of a TeX file @@ -151,6 +151,14 @@ class TeXFile(StaticFile): FeatureTestResult('nonexisting', False) """ def __init__(self, name, filename, **kwds): + r""" + EXAMPLES:: + + sage: from sage.features.latex import LaTeXPackage, pdflatex + sage: LaTeXPackage("tkz-graph")._features + [Feature('pdflatex')] + """ + JoinFeature.__init__(self, name, [pdflatex()], url=latex_url) # see :trac:`34282` StaticFile.__init__(self, name, filename, search_path=[], **kwds) def absolute_filename(self) -> str: @@ -174,6 +182,22 @@ def absolute_filename(self) -> str: raise FeatureNotPresentError(self, reason="{filename!r} not found by kpsewhich".format(filename=self.filename)) + def _is_present(self): + r""" + Test for the presence of the TeX-file. + + EXAMPLES:: + + sage: from sage.features.latex import LaTeXPackage, pdflatex + sage: f = LaTeXPackage("tkz-graph") + sage: g = pdflatex() + sage: bool(f.is_present()) == bool(g.is_present()) # indirect doctest + True + """ + test = JoinFeature._is_present(self) + if not test: + return test + return super(TeXFile, self)._is_present() class LaTeXPackage(TeXFile): r""" From a004e002d8353e08e5d1f7d6601ac2d434c50f30 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 20:24:59 -0700 Subject: [PATCH 130/454] build/pkgs/python_build/distros/conda.txt: New --- build/pkgs/python_build/distros/conda.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/python_build/distros/conda.txt diff --git a/build/pkgs/python_build/distros/conda.txt b/build/pkgs/python_build/distros/conda.txt new file mode 100644 index 00000000000..378eac25d31 --- /dev/null +++ b/build/pkgs/python_build/distros/conda.txt @@ -0,0 +1 @@ +build From d508d7bff7e4d2fce3e097b58ba4726aa973b65d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 20:58:57 -0700 Subject: [PATCH 131/454] build/pkgs: Add many more distros/conda.txt files --- build/pkgs/appnope/distros/conda.txt | 1 + build/pkgs/beniget/conda.txt | 1 + build/pkgs/distlib/distros/conda.txt | 1 + build/pkgs/fastjsonschema/distros/conda.txt | 1 + build/pkgs/hatchling/distros/conda.txt | 1 + build/pkgs/matplotlib_inline/distros/conda.txt | 1 + build/pkgs/nest_asyncio/distros/conda.txt | 1 + build/pkgs/ply/distros/conda.txt | 1 + build/pkgs/pure_eval/distros/conda.txt | 1 + build/pkgs/py/distros/conda.txt | 1 + build/pkgs/soupsieve/distros/conda.txt | 1 + build/pkgs/stack_data/distros/conda.txt | 1 + build/pkgs/toml/distros/conda.txt | 1 + 13 files changed, 13 insertions(+) create mode 100644 build/pkgs/appnope/distros/conda.txt create mode 100644 build/pkgs/beniget/conda.txt create mode 100644 build/pkgs/distlib/distros/conda.txt create mode 100644 build/pkgs/fastjsonschema/distros/conda.txt create mode 100644 build/pkgs/hatchling/distros/conda.txt create mode 100644 build/pkgs/matplotlib_inline/distros/conda.txt create mode 100644 build/pkgs/nest_asyncio/distros/conda.txt create mode 100644 build/pkgs/ply/distros/conda.txt create mode 100644 build/pkgs/pure_eval/distros/conda.txt create mode 100644 build/pkgs/py/distros/conda.txt create mode 100644 build/pkgs/soupsieve/distros/conda.txt create mode 100644 build/pkgs/stack_data/distros/conda.txt create mode 100644 build/pkgs/toml/distros/conda.txt diff --git a/build/pkgs/appnope/distros/conda.txt b/build/pkgs/appnope/distros/conda.txt new file mode 100644 index 00000000000..010137fae0e --- /dev/null +++ b/build/pkgs/appnope/distros/conda.txt @@ -0,0 +1 @@ +appnope diff --git a/build/pkgs/beniget/conda.txt b/build/pkgs/beniget/conda.txt new file mode 100644 index 00000000000..8b5faaea7c2 --- /dev/null +++ b/build/pkgs/beniget/conda.txt @@ -0,0 +1 @@ +beniget diff --git a/build/pkgs/distlib/distros/conda.txt b/build/pkgs/distlib/distros/conda.txt new file mode 100644 index 00000000000..f68bb07272d --- /dev/null +++ b/build/pkgs/distlib/distros/conda.txt @@ -0,0 +1 @@ +distlib diff --git a/build/pkgs/fastjsonschema/distros/conda.txt b/build/pkgs/fastjsonschema/distros/conda.txt new file mode 100644 index 00000000000..7a8bdf5369b --- /dev/null +++ b/build/pkgs/fastjsonschema/distros/conda.txt @@ -0,0 +1 @@ +python-fastjsonschema diff --git a/build/pkgs/hatchling/distros/conda.txt b/build/pkgs/hatchling/distros/conda.txt new file mode 100644 index 00000000000..1685d03c212 --- /dev/null +++ b/build/pkgs/hatchling/distros/conda.txt @@ -0,0 +1 @@ +hatchling diff --git a/build/pkgs/matplotlib_inline/distros/conda.txt b/build/pkgs/matplotlib_inline/distros/conda.txt new file mode 100644 index 00000000000..7b78209dd96 --- /dev/null +++ b/build/pkgs/matplotlib_inline/distros/conda.txt @@ -0,0 +1 @@ +matplotlib-inline diff --git a/build/pkgs/nest_asyncio/distros/conda.txt b/build/pkgs/nest_asyncio/distros/conda.txt new file mode 100644 index 00000000000..875c8427e6c --- /dev/null +++ b/build/pkgs/nest_asyncio/distros/conda.txt @@ -0,0 +1 @@ +nest-asyncio diff --git a/build/pkgs/ply/distros/conda.txt b/build/pkgs/ply/distros/conda.txt new file mode 100644 index 00000000000..90412f06833 --- /dev/null +++ b/build/pkgs/ply/distros/conda.txt @@ -0,0 +1 @@ +ply diff --git a/build/pkgs/pure_eval/distros/conda.txt b/build/pkgs/pure_eval/distros/conda.txt new file mode 100644 index 00000000000..e50c81f634f --- /dev/null +++ b/build/pkgs/pure_eval/distros/conda.txt @@ -0,0 +1 @@ +pure_eval diff --git a/build/pkgs/py/distros/conda.txt b/build/pkgs/py/distros/conda.txt new file mode 100644 index 00000000000..edfce786a4d --- /dev/null +++ b/build/pkgs/py/distros/conda.txt @@ -0,0 +1 @@ +py diff --git a/build/pkgs/soupsieve/distros/conda.txt b/build/pkgs/soupsieve/distros/conda.txt new file mode 100644 index 00000000000..9eb997f7a10 --- /dev/null +++ b/build/pkgs/soupsieve/distros/conda.txt @@ -0,0 +1 @@ +soupsieve diff --git a/build/pkgs/stack_data/distros/conda.txt b/build/pkgs/stack_data/distros/conda.txt new file mode 100644 index 00000000000..09e7428c13d --- /dev/null +++ b/build/pkgs/stack_data/distros/conda.txt @@ -0,0 +1 @@ +stack_data diff --git a/build/pkgs/toml/distros/conda.txt b/build/pkgs/toml/distros/conda.txt new file mode 100644 index 00000000000..bd79a658fe7 --- /dev/null +++ b/build/pkgs/toml/distros/conda.txt @@ -0,0 +1 @@ +toml From 098a8e7a9a473a437a8dc12902fb1571b4450da0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 21:02:42 -0700 Subject: [PATCH 132/454] build/pkgs: Add some more distros/conda.txt files --- build/pkgs/executing/distros/conda.txt | 1 + build/pkgs/tzdata/distros/conda.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 build/pkgs/executing/distros/conda.txt create mode 100644 build/pkgs/tzdata/distros/conda.txt diff --git a/build/pkgs/executing/distros/conda.txt b/build/pkgs/executing/distros/conda.txt new file mode 100644 index 00000000000..a920f2c56c3 --- /dev/null +++ b/build/pkgs/executing/distros/conda.txt @@ -0,0 +1 @@ +executing diff --git a/build/pkgs/tzdata/distros/conda.txt b/build/pkgs/tzdata/distros/conda.txt new file mode 100644 index 00000000000..0883ff0705b --- /dev/null +++ b/build/pkgs/tzdata/distros/conda.txt @@ -0,0 +1 @@ +tzdata From c56aa71a7c48b63754554f9449152197123f6c1c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 21:03:08 -0700 Subject: [PATCH 133/454] bootstrap-conda: Also add pip lines when install-requires.txt is present --- bootstrap-conda | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bootstrap-conda b/bootstrap-conda index 9ad39468e5d..cc9f02d0ec6 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -97,9 +97,12 @@ DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" ( echo >&4 " - pip:" echo >&5 " - pip:" - for PKG_BASE in $(sage-package list --has-file requirements.txt --no-file distros/conda.txt); do + for PKG_BASE in $(sage-package list :standard: :optional: --has-file requirements.txt --no-file distros/conda.txt) $(sage-package list :standard: :optional: --has-file install-requires.txt --no-file requirements.txt --no-file distros/conda.txt); do PKG_SCRIPTS=build/pkgs/$PKG_BASE SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/requirements.txt + if [ ! -f $SYSTEM_PACKAGES_FILE ]; then + SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/install-requires.txt + fi PKG_TYPE=$(cat $PKG_SCRIPTS/type) if grep -q SAGERUNTIME $PKG_SCRIPTS/dependencies $PKG_SCRIPTS/dependencies_order_only 2>/dev/null; then : # cannot install packages that depend on the Sage library From c13eaf1176c9067b220927a4cc57c7bf901c0ef7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 22:17:24 -0700 Subject: [PATCH 134/454] build/pkgs: Add even more distros/conda.txt files --- build/pkgs/appdirs/distros/conda.txt | 1 + build/pkgs/argon2_cffi/distros/conda.txt | 1 + build/pkgs/asttokens/distros/conda.txt | 1 + build/pkgs/backports_zoneinfo/distros/conda.txt | 1 + build/pkgs/charset_normalizer/distros/conda.txt | 1 + build/pkgs/cppy/distros/conda.txt | 1 + build/pkgs/debugpy/distros/conda.txt | 1 + build/pkgs/deprecation/distros/conda.txt | 1 + build/pkgs/dot2tex/distros/conda.txt | 1 + build/pkgs/editables/distros/conda.txt | 1 + build/pkgs/filelock/distros/conda.txt | 1 + build/pkgs/flit_core/distros/conda.txt | 1 + build/pkgs/fonttools/distros/conda.txt | 1 + build/pkgs/gast/distros/conda.txt | 1 + build/pkgs/idna/distros/conda.txt | 1 + build/pkgs/importlib_resources/distros/conda.txt | 1 + build/pkgs/jupyter_jsmol/distros/conda.txt | 1 + build/pkgs/jupyterlab_pygments/distros/conda.txt | 1 + build/pkgs/mathics/distros/conda.txt | 1 + build/pkgs/nbclient/distros/conda.txt | 1 + build/pkgs/palettable/distros/conda.txt | 1 + build/pkgs/pathspec/distros/conda.txt | 1 + build/pkgs/pint/distros/conda.txt | 1 + build/pkgs/platformdirs/distros/conda.txt | 1 + build/pkgs/pluggy/distros/conda.txt | 1 + build/pkgs/poetry_core/distros/conda.txt | 1 + build/pkgs/pythran/distros/conda.txt | 1 + build/pkgs/pytz_deprecation_shim/distros/conda.txt | 1 + build/pkgs/setuptools_scm_git_archive/distros/conda.txt | 1 + build/pkgs/texttable/distros/conda.txt | 1 + build/pkgs/tinycss2/distros/conda.txt | 1 + build/pkgs/tomli/distros/conda.txt | 1 + build/pkgs/tomlkit/distros/conda.txt | 1 + build/pkgs/typing_extensions/distros/conda.txt | 1 + build/pkgs/urllib3/distros/conda.txt | 1 + build/pkgs/virtualenv/distros/conda.txt | 1 + 36 files changed, 36 insertions(+) create mode 100644 build/pkgs/appdirs/distros/conda.txt create mode 100644 build/pkgs/argon2_cffi/distros/conda.txt create mode 100644 build/pkgs/asttokens/distros/conda.txt create mode 100644 build/pkgs/backports_zoneinfo/distros/conda.txt create mode 100644 build/pkgs/charset_normalizer/distros/conda.txt create mode 100644 build/pkgs/cppy/distros/conda.txt create mode 100644 build/pkgs/debugpy/distros/conda.txt create mode 100644 build/pkgs/deprecation/distros/conda.txt create mode 100644 build/pkgs/dot2tex/distros/conda.txt create mode 100644 build/pkgs/editables/distros/conda.txt create mode 100644 build/pkgs/filelock/distros/conda.txt create mode 100644 build/pkgs/flit_core/distros/conda.txt create mode 100644 build/pkgs/fonttools/distros/conda.txt create mode 100644 build/pkgs/gast/distros/conda.txt create mode 100644 build/pkgs/idna/distros/conda.txt create mode 100644 build/pkgs/importlib_resources/distros/conda.txt create mode 100644 build/pkgs/jupyter_jsmol/distros/conda.txt create mode 100644 build/pkgs/jupyterlab_pygments/distros/conda.txt create mode 100644 build/pkgs/mathics/distros/conda.txt create mode 100644 build/pkgs/nbclient/distros/conda.txt create mode 100644 build/pkgs/palettable/distros/conda.txt create mode 100644 build/pkgs/pathspec/distros/conda.txt create mode 100644 build/pkgs/pint/distros/conda.txt create mode 100644 build/pkgs/platformdirs/distros/conda.txt create mode 100644 build/pkgs/pluggy/distros/conda.txt create mode 100644 build/pkgs/poetry_core/distros/conda.txt create mode 100644 build/pkgs/pythran/distros/conda.txt create mode 100644 build/pkgs/pytz_deprecation_shim/distros/conda.txt create mode 100644 build/pkgs/setuptools_scm_git_archive/distros/conda.txt create mode 100644 build/pkgs/texttable/distros/conda.txt create mode 100644 build/pkgs/tinycss2/distros/conda.txt create mode 100644 build/pkgs/tomli/distros/conda.txt create mode 100644 build/pkgs/tomlkit/distros/conda.txt create mode 100644 build/pkgs/typing_extensions/distros/conda.txt create mode 100644 build/pkgs/urllib3/distros/conda.txt create mode 100644 build/pkgs/virtualenv/distros/conda.txt diff --git a/build/pkgs/appdirs/distros/conda.txt b/build/pkgs/appdirs/distros/conda.txt new file mode 100644 index 00000000000..d64bc321a11 --- /dev/null +++ b/build/pkgs/appdirs/distros/conda.txt @@ -0,0 +1 @@ +appdirs diff --git a/build/pkgs/argon2_cffi/distros/conda.txt b/build/pkgs/argon2_cffi/distros/conda.txt new file mode 100644 index 00000000000..d05a5eb79fb --- /dev/null +++ b/build/pkgs/argon2_cffi/distros/conda.txt @@ -0,0 +1 @@ +argon2-cffi diff --git a/build/pkgs/asttokens/distros/conda.txt b/build/pkgs/asttokens/distros/conda.txt new file mode 100644 index 00000000000..7adf4c51fd2 --- /dev/null +++ b/build/pkgs/asttokens/distros/conda.txt @@ -0,0 +1 @@ +asttokens diff --git a/build/pkgs/backports_zoneinfo/distros/conda.txt b/build/pkgs/backports_zoneinfo/distros/conda.txt new file mode 100644 index 00000000000..5a8be642f33 --- /dev/null +++ b/build/pkgs/backports_zoneinfo/distros/conda.txt @@ -0,0 +1 @@ +backports.zoneinfo diff --git a/build/pkgs/charset_normalizer/distros/conda.txt b/build/pkgs/charset_normalizer/distros/conda.txt new file mode 100644 index 00000000000..5f964199cd0 --- /dev/null +++ b/build/pkgs/charset_normalizer/distros/conda.txt @@ -0,0 +1 @@ +charset-normalizer diff --git a/build/pkgs/cppy/distros/conda.txt b/build/pkgs/cppy/distros/conda.txt new file mode 100644 index 00000000000..9d2b4aaeee0 --- /dev/null +++ b/build/pkgs/cppy/distros/conda.txt @@ -0,0 +1 @@ +cppy diff --git a/build/pkgs/debugpy/distros/conda.txt b/build/pkgs/debugpy/distros/conda.txt new file mode 100644 index 00000000000..2802a6b1b1c --- /dev/null +++ b/build/pkgs/debugpy/distros/conda.txt @@ -0,0 +1 @@ +debugpy diff --git a/build/pkgs/deprecation/distros/conda.txt b/build/pkgs/deprecation/distros/conda.txt new file mode 100644 index 00000000000..4ba9b7530ed --- /dev/null +++ b/build/pkgs/deprecation/distros/conda.txt @@ -0,0 +1 @@ +deprecation diff --git a/build/pkgs/dot2tex/distros/conda.txt b/build/pkgs/dot2tex/distros/conda.txt new file mode 100644 index 00000000000..4d0a832a550 --- /dev/null +++ b/build/pkgs/dot2tex/distros/conda.txt @@ -0,0 +1 @@ +dot2tex diff --git a/build/pkgs/editables/distros/conda.txt b/build/pkgs/editables/distros/conda.txt new file mode 100644 index 00000000000..35c51715e64 --- /dev/null +++ b/build/pkgs/editables/distros/conda.txt @@ -0,0 +1 @@ +editables diff --git a/build/pkgs/filelock/distros/conda.txt b/build/pkgs/filelock/distros/conda.txt new file mode 100644 index 00000000000..83c2e35706e --- /dev/null +++ b/build/pkgs/filelock/distros/conda.txt @@ -0,0 +1 @@ +filelock diff --git a/build/pkgs/flit_core/distros/conda.txt b/build/pkgs/flit_core/distros/conda.txt new file mode 100644 index 00000000000..14ccfd92035 --- /dev/null +++ b/build/pkgs/flit_core/distros/conda.txt @@ -0,0 +1 @@ +flit-core diff --git a/build/pkgs/fonttools/distros/conda.txt b/build/pkgs/fonttools/distros/conda.txt new file mode 100644 index 00000000000..d32bfca1a29 --- /dev/null +++ b/build/pkgs/fonttools/distros/conda.txt @@ -0,0 +1 @@ +fonttools diff --git a/build/pkgs/gast/distros/conda.txt b/build/pkgs/gast/distros/conda.txt new file mode 100644 index 00000000000..beb259c8453 --- /dev/null +++ b/build/pkgs/gast/distros/conda.txt @@ -0,0 +1 @@ +gast diff --git a/build/pkgs/idna/distros/conda.txt b/build/pkgs/idna/distros/conda.txt new file mode 100644 index 00000000000..c40472e6fc2 --- /dev/null +++ b/build/pkgs/idna/distros/conda.txt @@ -0,0 +1 @@ +idna diff --git a/build/pkgs/importlib_resources/distros/conda.txt b/build/pkgs/importlib_resources/distros/conda.txt new file mode 100644 index 00000000000..2b0146fc669 --- /dev/null +++ b/build/pkgs/importlib_resources/distros/conda.txt @@ -0,0 +1 @@ +importlib-resources diff --git a/build/pkgs/jupyter_jsmol/distros/conda.txt b/build/pkgs/jupyter_jsmol/distros/conda.txt new file mode 100644 index 00000000000..9465bfb8e0c --- /dev/null +++ b/build/pkgs/jupyter_jsmol/distros/conda.txt @@ -0,0 +1 @@ +jupyter-jsmol diff --git a/build/pkgs/jupyterlab_pygments/distros/conda.txt b/build/pkgs/jupyterlab_pygments/distros/conda.txt new file mode 100644 index 00000000000..23254749a23 --- /dev/null +++ b/build/pkgs/jupyterlab_pygments/distros/conda.txt @@ -0,0 +1 @@ +jupyterlab_pygments diff --git a/build/pkgs/mathics/distros/conda.txt b/build/pkgs/mathics/distros/conda.txt new file mode 100644 index 00000000000..800ac5e8aa4 --- /dev/null +++ b/build/pkgs/mathics/distros/conda.txt @@ -0,0 +1 @@ +mathics3 diff --git a/build/pkgs/nbclient/distros/conda.txt b/build/pkgs/nbclient/distros/conda.txt new file mode 100644 index 00000000000..66ffbb0ca10 --- /dev/null +++ b/build/pkgs/nbclient/distros/conda.txt @@ -0,0 +1 @@ +nbclient diff --git a/build/pkgs/palettable/distros/conda.txt b/build/pkgs/palettable/distros/conda.txt new file mode 100644 index 00000000000..646dd7426bb --- /dev/null +++ b/build/pkgs/palettable/distros/conda.txt @@ -0,0 +1 @@ +palettable diff --git a/build/pkgs/pathspec/distros/conda.txt b/build/pkgs/pathspec/distros/conda.txt new file mode 100644 index 00000000000..6486958df08 --- /dev/null +++ b/build/pkgs/pathspec/distros/conda.txt @@ -0,0 +1 @@ +pathspec diff --git a/build/pkgs/pint/distros/conda.txt b/build/pkgs/pint/distros/conda.txt new file mode 100644 index 00000000000..45f523a5a6e --- /dev/null +++ b/build/pkgs/pint/distros/conda.txt @@ -0,0 +1 @@ +pint diff --git a/build/pkgs/platformdirs/distros/conda.txt b/build/pkgs/platformdirs/distros/conda.txt new file mode 100644 index 00000000000..67fd014bbdd --- /dev/null +++ b/build/pkgs/platformdirs/distros/conda.txt @@ -0,0 +1 @@ +platformdirs diff --git a/build/pkgs/pluggy/distros/conda.txt b/build/pkgs/pluggy/distros/conda.txt new file mode 100644 index 00000000000..11bdb5c1f5f --- /dev/null +++ b/build/pkgs/pluggy/distros/conda.txt @@ -0,0 +1 @@ +pluggy diff --git a/build/pkgs/poetry_core/distros/conda.txt b/build/pkgs/poetry_core/distros/conda.txt new file mode 100644 index 00000000000..9b1f9e3fa7d --- /dev/null +++ b/build/pkgs/poetry_core/distros/conda.txt @@ -0,0 +1 @@ +poetry-core diff --git a/build/pkgs/pythran/distros/conda.txt b/build/pkgs/pythran/distros/conda.txt new file mode 100644 index 00000000000..86d056b339f --- /dev/null +++ b/build/pkgs/pythran/distros/conda.txt @@ -0,0 +1 @@ +pythran diff --git a/build/pkgs/pytz_deprecation_shim/distros/conda.txt b/build/pkgs/pytz_deprecation_shim/distros/conda.txt new file mode 100644 index 00000000000..2fd546ce62e --- /dev/null +++ b/build/pkgs/pytz_deprecation_shim/distros/conda.txt @@ -0,0 +1 @@ +pytz-deprecation-shim diff --git a/build/pkgs/setuptools_scm_git_archive/distros/conda.txt b/build/pkgs/setuptools_scm_git_archive/distros/conda.txt new file mode 100644 index 00000000000..538474ff946 --- /dev/null +++ b/build/pkgs/setuptools_scm_git_archive/distros/conda.txt @@ -0,0 +1 @@ +setuptools-scm-git-archive diff --git a/build/pkgs/texttable/distros/conda.txt b/build/pkgs/texttable/distros/conda.txt new file mode 100644 index 00000000000..5d54c8a784d --- /dev/null +++ b/build/pkgs/texttable/distros/conda.txt @@ -0,0 +1 @@ +texttable diff --git a/build/pkgs/tinycss2/distros/conda.txt b/build/pkgs/tinycss2/distros/conda.txt new file mode 100644 index 00000000000..29992f20d2c --- /dev/null +++ b/build/pkgs/tinycss2/distros/conda.txt @@ -0,0 +1 @@ +tinycss2 diff --git a/build/pkgs/tomli/distros/conda.txt b/build/pkgs/tomli/distros/conda.txt new file mode 100644 index 00000000000..aab392a3ac2 --- /dev/null +++ b/build/pkgs/tomli/distros/conda.txt @@ -0,0 +1 @@ +tomli diff --git a/build/pkgs/tomlkit/distros/conda.txt b/build/pkgs/tomlkit/distros/conda.txt new file mode 100644 index 00000000000..8141b831035 --- /dev/null +++ b/build/pkgs/tomlkit/distros/conda.txt @@ -0,0 +1 @@ +tomlkit diff --git a/build/pkgs/typing_extensions/distros/conda.txt b/build/pkgs/typing_extensions/distros/conda.txt new file mode 100644 index 00000000000..5fd4f05f341 --- /dev/null +++ b/build/pkgs/typing_extensions/distros/conda.txt @@ -0,0 +1 @@ +typing_extensions diff --git a/build/pkgs/urllib3/distros/conda.txt b/build/pkgs/urllib3/distros/conda.txt new file mode 100644 index 00000000000..a42590bebea --- /dev/null +++ b/build/pkgs/urllib3/distros/conda.txt @@ -0,0 +1 @@ +urllib3 diff --git a/build/pkgs/virtualenv/distros/conda.txt b/build/pkgs/virtualenv/distros/conda.txt new file mode 100644 index 00000000000..66072c76450 --- /dev/null +++ b/build/pkgs/virtualenv/distros/conda.txt @@ -0,0 +1 @@ +virtualenv From 1c47261840fcee978b7471ff648a2b39eecde5fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 22:20:04 -0700 Subject: [PATCH 135/454] bootstrap-conda: Do not include pip lines for packages for which we have source directories --- bootstrap-conda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap-conda b/bootstrap-conda index cc9f02d0ec6..06c81e21521 100755 --- a/bootstrap-conda +++ b/bootstrap-conda @@ -97,7 +97,7 @@ DEVELOP_SYSTEM_PACKAGES="openssh pycodestyle pytest" ( echo >&4 " - pip:" echo >&5 " - pip:" - for PKG_BASE in $(sage-package list :standard: :optional: --has-file requirements.txt --no-file distros/conda.txt) $(sage-package list :standard: :optional: --has-file install-requires.txt --no-file requirements.txt --no-file distros/conda.txt); do + for PKG_BASE in $((sage-package list :standard: :optional: --has-file requirements.txt --no-file distros/conda.txt --no-file src; sage-package list :standard: :optional: --has-file install-requires.txt --no-file requirements.txt --no-file distros/conda.txt --no-file src) | sort); do PKG_SCRIPTS=build/pkgs/$PKG_BASE SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/requirements.txt if [ ! -f $SYSTEM_PACKAGES_FILE ]; then From 6d067200c54e48597c262695b866cc3c321ff766 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 5 Aug 2022 22:23:24 -0700 Subject: [PATCH 136/454] build/pkgs: Add even more distros/conda.txt files (fixup) --- build/pkgs/beniget/{ => distros}/conda.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename build/pkgs/beniget/{ => distros}/conda.txt (100%) diff --git a/build/pkgs/beniget/conda.txt b/build/pkgs/beniget/distros/conda.txt similarity index 100% rename from build/pkgs/beniget/conda.txt rename to build/pkgs/beniget/distros/conda.txt From 6a51349cb1bf1ef2b2b00a9815efe17e559c21db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 6 Aug 2022 13:49:06 +0200 Subject: [PATCH 137/454] modernize super in asymptotics and crystals --- src/sage/combinat/crystals/affine.py | 6 +- .../combinat/crystals/affine_factorization.py | 2 +- src/sage/combinat/crystals/alcove_path.py | 12 ++- src/sage/combinat/crystals/bkk_crystals.py | 4 +- src/sage/combinat/crystals/direct_sum.py | 3 +- .../combinat/crystals/elementary_crystals.py | 8 +- src/sage/combinat/crystals/fast_crystals.py | 10 +-- .../fully_commutative_stable_grothendieck.py | 5 +- .../crystals/generalized_young_walls.py | 6 +- .../combinat/crystals/induced_structure.py | 3 +- .../combinat/crystals/infinity_crystals.py | 9 +- src/sage/combinat/crystals/kac_modules.py | 5 +- .../combinat/crystals/kyoto_path_model.py | 2 +- src/sage/combinat/crystals/letters.pyx | 9 +- src/sage/combinat/crystals/littelmann_path.py | 5 +- .../combinat/crystals/monomial_crystals.py | 8 +- src/sage/combinat/crystals/pbw_crystal.py | 2 +- .../crystals/polyhedral_realization.py | 3 +- src/sage/combinat/crystals/subcrystal.py | 12 +-- src/sage/combinat/crystals/tensor_product.py | 5 +- .../crystals/tensor_product_element.pyx | 6 +- src/sage/combinat/crystals/virtual_crystal.py | 10 +-- src/sage/rings/asymptotic/asymptotic_ring.py | 83 +++---------------- ...otics_multivariate_generating_functions.py | 12 +-- .../asymptotic/growth_group_cartesian.py | 10 +-- src/sage/rings/asymptotic/misc.py | 22 ++--- src/sage/rings/asymptotic/term_monoid.py | 6 +- 27 files changed, 105 insertions(+), 163 deletions(-) diff --git a/src/sage/combinat/crystals/affine.py b/src/sage/combinat/crystals/affine.py index 65df64542b0..813492aad80 100644 --- a/src/sage/combinat/crystals/affine.py +++ b/src/sage/combinat/crystals/affine.py @@ -78,7 +78,7 @@ def __classcall__(cls, cartan_type, *args, **options): True """ ct = CartanType(cartan_type) - return super(AffineCrystalFromClassical, cls).__classcall__(cls, ct, *args, **options) + return super().__classcall__(cls, ct, *args, **options) def __init__(self, cartan_type, classical_crystal, category=None): """ @@ -397,7 +397,7 @@ def epsilon0(self): sage: [x.epsilon0() for x in A.list()] [1, 0, 0] """ - return super(AffineCrystalFromClassicalElement, self).epsilon(0) + return super().epsilon(0) def epsilon(self, i): """ @@ -436,7 +436,7 @@ def phi0(self): sage: [x.phi0() for x in A.list()] [0, 0, 1] """ - return super(AffineCrystalFromClassicalElement, self).phi(0) + return super().phi(0) def phi(self, i): r""" diff --git a/src/sage/combinat/crystals/affine_factorization.py b/src/sage/combinat/crystals/affine_factorization.py index 1bc568aeb71..2499a05a5f9 100644 --- a/src/sage/combinat/crystals/affine_factorization.py +++ b/src/sage/combinat/crystals/affine_factorization.py @@ -117,7 +117,7 @@ def __classcall_private__(cls, w, n, x = None, k = None): w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k)) w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k)) w = w0*(w1.inverse()) - return super(AffineFactorizationCrystal, cls).__classcall__(cls, w, n, x) + return super().__classcall__(cls, w, n, x) def __init__(self, w, n, x = None): r""" diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index e72e6a394e8..912e4797d02 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -276,10 +276,8 @@ def __classcall_private__(cls, starting_weight, cartan_type=None, if not starting_weight.is_dominant(): raise ValueError("{0} is not a dominant weight".format(starting_weight)) - - return super(CrystalOfAlcovePaths, cls).__classcall__(cls, - starting_weight, highest_weight_crystal) - + return super().__classcall__(cls, starting_weight, + highest_weight_crystal) def __init__(self, starting_weight, highest_weight_crystal): r""" @@ -1153,7 +1151,7 @@ def __classcall_private__(cls, cartan_type): True """ cartan_type = CartanType(cartan_type) - return super(InfinityCrystalOfAlcovePaths, cls).__classcall__(cls, cartan_type) + return super().__classcall__(cls, cartan_type) def __init__(self, cartan_type): """ @@ -1394,6 +1392,7 @@ def projection(self, k=None): A = CrystalOfAlcovePaths(self.parent()._cartan_type, [k]*n) return A(tuple([A._R(rt, h + k*s(rt)) for rt,h in self.value])) + class RootsWithHeight(UniqueRepresentation, Parent): r""" Data structure of the ordered pairs `(\beta,k)`, @@ -1462,8 +1461,7 @@ def __classcall_private__(cls, starting_weight, cartan_type = None): offset = R.index_set()[Integer(0)] starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set()) - return super(RootsWithHeight, cls).__classcall__(cls, starting_weight) - + return super().__classcall__(cls, starting_weight) def __init__(self, weight): r""" diff --git a/src/sage/combinat/crystals/bkk_crystals.py b/src/sage/combinat/crystals/bkk_crystals.py index 622fbb9e350..15e795ca484 100644 --- a/src/sage/combinat/crystals/bkk_crystals.py +++ b/src/sage/combinat/crystals/bkk_crystals.py @@ -61,7 +61,7 @@ def __classcall_private__(cls, ct, shape): shape = _Partitions(shape) if len(shape) > ct.m + 1 and shape[ct.m+1] > ct.n + 1: raise ValueError("invalid hook shape") - return super(CrystalOfBKKTableaux, cls).__classcall__(cls, ct, shape) + return super().__classcall__(cls, ct, shape) def __init__(self, ct, shape): r""" @@ -135,7 +135,7 @@ def genuine_highest_weight_vectors(self, index_set=None): """ if index_set is None or index_set == self.index_set(): return self.module_generators - return super(CrystalOfBKKTableaux, self).genuine_highest_weight_vectors(index_set) + return super().genuine_highest_weight_vectors(index_set) class Element(CrystalOfBKKTableauxElement): pass diff --git a/src/sage/combinat/crystals/direct_sum.py b/src/sage/combinat/crystals/direct_sum.py index 84676264822..da2ce05bee2 100644 --- a/src/sage/combinat/crystals/direct_sum.py +++ b/src/sage/combinat/crystals/direct_sum.py @@ -22,6 +22,7 @@ from sage.structure.element_wrapper import ElementWrapper from sage.structure.element import get_coercion_model + class DirectSumOfCrystals(DisjointUnionEnumeratedSets): r""" Direct sum of crystals. @@ -114,7 +115,7 @@ def __classcall_private__(cls, crystals, facade=True, keepkey=False, category=No else: ret.append(x) category = Category.meet([Category.join(c.categories()) for c in ret]) - return super(DirectSumOfCrystals, cls).__classcall__(cls, + return super().__classcall__(cls, Family(ret), facade=facade, keepkey=keepkey, category=category) def __init__(self, crystals, facade, keepkey, category, **options): diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index b3d01c09249..607206c52f6 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -258,7 +258,7 @@ def __classcall_private__(cls, cartan_type, weight=None): weight = cartan_type cartan_type = weight.parent().cartan_type() cartan_type = CartanType(cartan_type) - return super(TCrystal, cls).__classcall__(cls, cartan_type, weight) + return super().__classcall__(cls, cartan_type, weight) def __init__(self, cartan_type, weight): r""" @@ -514,7 +514,7 @@ def __classcall_private__(cls, cartan_type, weight=None, dual=False): weight = cartan_type cartan_type = weight.parent().cartan_type() cartan_type = CartanType(cartan_type) - return super(RCrystal, cls).__classcall__(cls, cartan_type, weight, dual) + return super().__classcall__(cls, cartan_type, weight, dual) def __init__(self, cartan_type, weight, dual): r""" @@ -789,7 +789,7 @@ def __classcall_private__(cls, cartan_type, i): cartan_type = CartanType(cartan_type) if i not in cartan_type.index_set(): raise ValueError('i must an element of the index set') - return super(ElementaryCrystal, cls).__classcall__(cls, cartan_type, i) + return super().__classcall__(cls, cartan_type, i) def __init__(self, cartan_type, i): r""" @@ -1091,7 +1091,7 @@ def __classcall_private__(cls, cartan_type, P=None): P = cartan_type.root_system().ambient_space() if P is None: P = cartan_type.root_system().weight_lattice() - return super(ComponentCrystal, cls).__classcall__(cls, cartan_type, P) + return super().__classcall__(cls, cartan_type, P) def __init__(self, cartan_type, P): r""" diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py index 2f964bb60de..9899f87122b 100644 --- a/src/sage/combinat/crystals/fast_crystals.py +++ b/src/sage/combinat/crystals/fast_crystals.py @@ -1,4 +1,4 @@ -r""" +sr""" Fast Rank Two Crystals """ # **************************************************************************** @@ -105,7 +105,7 @@ def __classcall__(cls, cartan_type, shape, format = "string"): if len(shape) > 2: raise ValueError("The shape must have length <=2") shape = shape + (0,)*(2-len(shape)) - return super(FastCrystal, cls).__classcall__(cls, cartan_type, shape, format) + return super().__classcall__(cls, cartan_type, shape, format) def __init__(self, ct, shape, format): """ @@ -116,7 +116,7 @@ def __init__(self, ct, shape, format): sage: TestSuite(C).run() """ Parent.__init__(self, category = ClassicalCrystals()) -# super(FastCrystal, self).__init__(category = FiniteEnumeratedSets()) +# super().__init__(category = FiniteEnumeratedSets()) self._cartan_type = ct if ct[1] != 2: raise NotImplementedError @@ -167,9 +167,9 @@ def __init__(self, ct, shape, format): self.rename("The fast crystal for %s2 with shape [%s,%s]"%(ct[0],l1_str,l2_str)) self.module_generators = [self(0)] # self._list = ClassicalCrystal.list(self) - self._list = super(FastCrystal, self).list() + self._list = super().list() # self._digraph = ClassicalCrystal.digraph(self) - self._digraph = super(FastCrystal, self).digraph() + self._digraph = super().digraph() self._digraph_closure = self.digraph().transitive_closure() def _type_a_init(self, l1, l2): diff --git a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py index e2f9d09bd45..df9805e07b3 100644 --- a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py +++ b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py @@ -309,6 +309,7 @@ def to_increasing_hecke_biword(self): L[0] += [j+1]*len(self.value[-j-1]) return L + class DecreasingHeckeFactorizations(UniqueRepresentation, Parent): """ Set of decreasing factorizations in the 0-Hecke monoid. @@ -358,7 +359,7 @@ def __classcall_private__(cls, w, factors, excess): w = H.from_reduced_word(w.reduced_word()) if (not w.reduced_word()) and excess!=0: raise ValueError("excess must be 0 for the empty word") - return super(DecreasingHeckeFactorizations, cls).__classcall__(cls, w, factors, excess) + return super().__classcall__(cls, w, factors, excess) def __init__(self, w, factors, excess): """ @@ -520,7 +521,7 @@ def __classcall_private__(cls, w, factors, excess, shape=False): w = H.from_reduced_word(w.reduced_word()) if (not w.reduced_word()) and excess!=0: raise ValueError("excess must be 0 for the empty word") - return super(FullyCommutativeStableGrothendieckCrystal, cls).__classcall__(cls, w, factors, excess) + return super().__classcall__(cls, w, factors, excess) def __init__(self, w, factors, excess): """ diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index 60c47154c8e..0d30991e00e 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -834,7 +834,7 @@ def __classcall_private__(cls, n, category=None): sage: Yinf is Yinf2 True """ - return super(InfinityCrystalOfGeneralizedYoungWalls,cls).__classcall__(cls,n,category) + return super().__classcall__(cls,n,category) def __init__(self, n, category): r""" @@ -1028,7 +1028,7 @@ def __classcall_private__(cls, n, La): True """ La = RootSystem(['A',n,1]).weight_lattice(extended=True)(La) - return super(CrystalOfGeneralizedYoungWalls, cls).__classcall__(cls, n, La) + return super().__classcall__(cls, n, La) def __init__(self, n, La): r""" @@ -1067,6 +1067,6 @@ def __iter__(self): sage: next(x) [0] """ - for c in super(CrystalOfGeneralizedYoungWalls, self).__iter__(): + for c in super().__iter__(): if c.in_highest_weight_crystal(self.hw): yield c diff --git a/src/sage/combinat/crystals/induced_structure.py b/src/sage/combinat/crystals/induced_structure.py index 1e89220f3ea..a2265e8df71 100644 --- a/src/sage/combinat/crystals/induced_structure.py +++ b/src/sage/combinat/crystals/induced_structure.py @@ -27,6 +27,7 @@ from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper + class InducedCrystal(UniqueRepresentation, Parent): r""" A crystal induced from an injection. @@ -120,7 +121,7 @@ def __classcall_private__(cls, X, phi, inverse=None, from_crystal=False): if from_crystal: return InducedFromCrystal(X, phi, inverse) - return super(InducedCrystal, cls).__classcall__(cls, X, phi, inverse) + return super().__classcall__(cls, X, phi, inverse) def __init__(self, X, phi, inverse): """ diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index 0edf826c73e..7fa7e5f7a2d 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -206,7 +206,7 @@ def __classcall_private__(cls, cartan_type): return InfinityCrystalOfTableauxTypeD(cartan_type) if cartan_type.type() == 'Q': return DualInfinityQueerCrystalOfTableaux(cartan_type) - return super(InfinityCrystalOfTableaux, cls).__classcall__(cls, cartan_type) + return super().__classcall__(cls, cartan_type) def __init__(self, cartan_type): """ @@ -288,7 +288,7 @@ def _coerce_map_from_(self, P): or isinstance(P, InfinityCrystalOfNonSimplyLacedRC))): from sage.combinat.rigged_configurations.bij_infinity import FromRCIsomorphism return FromRCIsomorphism(Hom(P, self)) - return super(InfinityCrystalOfTableaux, self)._coerce_map_from_(P) + return super()._coerce_map_from_(P) class Element(InfinityCrystalOfTableauxElement): r""" @@ -605,7 +605,7 @@ def __classcall_private__(cls, cartan_type): sage: B is B2 True """ - return super(InfinityCrystalOfTableauxTypeD, cls).__classcall__(cls, CartanType(cartan_type)) + return super().__classcall__(cls, CartanType(cartan_type)) @cached_method def module_generator(self): @@ -633,6 +633,7 @@ class Element(InfinityCrystalOfTableauxElementTypeD, InfinityCrystalOfTableaux.E """ pass + ######################################################### ## Queer superalgebra @@ -650,7 +651,7 @@ def __classcall_private__(cls, cartan_type): True """ cartan_type = CartanType(cartan_type) - return super(DualInfinityQueerCrystalOfTableaux, cls).__classcall__(cls, cartan_type) + return super().__classcall__(cls, cartan_type) def __init__(self, cartan_type): """ diff --git a/src/sage/combinat/crystals/kac_modules.py b/src/sage/combinat/crystals/kac_modules.py index f6244cd4158..90edd27d93d 100644 --- a/src/sage/combinat/crystals/kac_modules.py +++ b/src/sage/combinat/crystals/kac_modules.py @@ -62,7 +62,7 @@ def __classcall_private__(cls, cartan_type): sage: S1 is S2 True """ - return super(CrystalOfOddNegativeRoots, cls).__classcall__(cls, CartanType(cartan_type)) + return super().__classcall__(cls, CartanType(cartan_type)) def __init__(self, cartan_type): """ @@ -430,6 +430,7 @@ def weight(self): e = WLR.basis() return WLR.sum(-e[i]+e[j] for (i,j) in self.value) + class CrystalOfKacModule(UniqueRepresentation, Parent): r""" Crystal of a Kac module. @@ -530,7 +531,7 @@ def __classcall_private__(cls, cartan_type, la, mu): cartan_type = CartanType(cartan_type) la = _Partitions(la) mu = _Partitions(mu) - return super(CrystalOfKacModule, cls).__classcall__(cls, cartan_type, la, mu) + return super().__classcall__(cls, cartan_type, la, mu) def __init__(self, cartan_type, la, mu): """ diff --git a/src/sage/combinat/crystals/kyoto_path_model.py b/src/sage/combinat/crystals/kyoto_path_model.py index 12d3af264a6..6ee4d0e58a4 100644 --- a/src/sage/combinat/crystals/kyoto_path_model.py +++ b/src/sage/combinat/crystals/kyoto_path_model.py @@ -227,7 +227,7 @@ def __classcall_private__(cls, crystals, weight, P=None): enumerate(P.simple_coroots()) ) != level: raise ValueError( "{} is not a level {} weight".format(weight, level) ) - return super(KyotoPathModel, cls).__classcall__(cls, crystals, weight, P) + return super().__classcall__(cls, crystals, weight, P) def __init__(self, crystals, weight, P): """ diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index 8a06939ce07..ccd57d461bf 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -116,6 +116,7 @@ def CrystalOfLetters(cartan_type, element_print_style=None, dual=None): else: raise NotImplementedError + class ClassicalCrystalOfLetters(UniqueRepresentation, Parent): r""" A generic class for classical crystals of letters. @@ -166,7 +167,7 @@ class ClassicalCrystalOfLetters(UniqueRepresentation, Parent): C = CrystalOfNakajimaMonomials(cartan_type, la) hw = C.highest_weight_vector() self.module_generators = (self._element_constructor_(hw),) - self._list = [x for x in super(ClassicalCrystalOfLetters, self).__iter__()] + self._list = [x for x in super().__iter__()] elif cartan_type.type() == 'F': from sage.combinat.crystals.monomial_crystals import CrystalOfNakajimaMonomials from sage.combinat.root_system.root_system import RootSystem @@ -174,7 +175,7 @@ class ClassicalCrystalOfLetters(UniqueRepresentation, Parent): C = CrystalOfNakajimaMonomials(cartan_type, la) hw = C.highest_weight_vector() self.module_generators = (self._element_constructor_(hw),) - self._list = [x for x in super(ClassicalCrystalOfLetters, self).__iter__()] + self._list = [x for x in super().__iter__()] else: self.module_generators = (self._element_constructor_(1),) if cartan_type.type() == 'G': @@ -2520,7 +2521,7 @@ class CrystalOfBKKLetters(ClassicalCrystalOfLetters): if dual is None: dual = False ct = CartanType(ct) - return super(CrystalOfBKKLetters, cls).__classcall__(cls, ct, dual) + return super().__classcall__(cls, ct, dual) def __init__(self, ct, dual): """ @@ -2613,7 +2614,7 @@ class CrystalOfQueerLetters(ClassicalCrystalOfLetters): The queer crystal of letters for q(3) """ ct = CartanType(ct) - return super(CrystalOfQueerLetters, cls).__classcall__(cls, ct) + return super().__classcall__(cls, ct) def __init__(self, ct): """ diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 9eb8ecd6dcd..2ddd6f3c461 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -702,7 +702,8 @@ def __classcall_private__(cls, weight): raise ValueError("The weight should be in the non-extended weight lattice!") La = weight.parent().basis() weight = weight - weight.level() * La[0] / La[0].level() - return super(CrystalOfLSPaths, cls).__classcall__(cls, weight, starting_weight_parent = weight.parent()) + return super().__classcall__(cls, weight, + starting_weight_parent=weight.parent()) @cached_method def maximal_vector(self): @@ -1208,7 +1209,7 @@ def __classcall_private__(cls, cartan_type): True """ cartan_type = CartanType(cartan_type) - return super(InfinityCrystalOfLSPaths, cls).__classcall__(cls, cartan_type) + return super().__classcall__(cls, cartan_type) def __init__(self, cartan_type): """ diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 43d71f16747..9accb0195e5 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -836,7 +836,7 @@ def __classcall_private__(cls, ct, c=None): cartan_type = CartanType(ct) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) - M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) + M = super().__classcall__(cls, cartan_type, c) M.set_variables('Y') return M @@ -1074,7 +1074,7 @@ def f(self, i): """ if self.phi(i) == 0: return None - return super(CrystalOfNakajimaMonomialsElement, self).f(i) + return super().f(i) def weight(self): r""" @@ -1197,7 +1197,7 @@ def __classcall_private__(cls, cartan_type, La=None, c=None): La = RootSystem(cartan_type).weight_lattice()(La) n = len(cartan_type.index_set()) c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) - return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La, c) + return super().__classcall__(cls, cartan_type, La, c) def __init__(self, ct, La, c): r""" @@ -1252,6 +1252,6 @@ def cardinality(self): """ if not self.cartan_type().is_finite(): return Infinity - return super(InfinityCrystalOfNakajimaMonomials, self).cardinality() + return super().cardinality() Element = CrystalOfNakajimaMonomialsElement diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py index 1803b501f9e..4a313c8fa78 100644 --- a/src/sage/combinat/crystals/pbw_crystal.py +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -401,7 +401,7 @@ def __classcall__(cls, cartan_type): cartan_type = CartanType(cartan_type) if not cartan_type.is_finite(): raise NotImplementedError("only implemented for finite types") - return super(PBWCrystal, cls).__classcall__(cls, cartan_type) + return super().__classcall__(cls, cartan_type) def __init__(self, cartan_type): """ diff --git a/src/sage/combinat/crystals/polyhedral_realization.py b/src/sage/combinat/crystals/polyhedral_realization.py index 2860eec417c..a709284149f 100644 --- a/src/sage/combinat/crystals/polyhedral_realization.py +++ b/src/sage/combinat/crystals/polyhedral_realization.py @@ -25,6 +25,7 @@ from sage.combinat.crystals.elementary_crystals import ElementaryCrystal from sage.combinat.root_system.cartan_type import CartanType + class InfinityCrystalAsPolyhedralRealization(TensorProductOfCrystals): r""" The polyhedral realization of `B(\infty)`. @@ -156,7 +157,7 @@ def __classcall_private__(cls, cartan_type, seq=None): seq = tuple(seq) if set(seq) != set(cartan_type.index_set()): raise ValueError("the support of seq is not the index set") - return super(InfinityCrystalAsPolyhedralRealization, cls).__classcall__(cls, cartan_type, seq) + return super().__classcall__(cls, cartan_type, seq) def __init__(self, cartan_type, seq): """ diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index 9881a021e27..22e22453488 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -159,11 +159,11 @@ def __classcall_private__(cls, ambient, contained=None, generators=None, generators, cartan_type, index_set, category) # We need to give these as optional arguments so it unpickles correctly - return super(Subcrystal, cls).__classcall__(cls, ambient, contained, - tuple(generators), - cartan_type=cartan_type, - index_set=tuple(index_set), - category=category) + return super().__classcall__(cls, ambient, contained, + tuple(generators), + cartan_type=cartan_type, + index_set=tuple(index_set), + category=category) def __init__(self, ambient, contained, generators, cartan_type, index_set, category): """ @@ -291,7 +291,7 @@ def cardinality(self): if self in FiniteCrystals(): return Integer(len(self.list())) try: - card = super(Subcrystal, self).cardinality() + card = super().cardinality() except AttributeError: raise NotImplementedError("unknown cardinality") if card == infinity: diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index c15eac03795..18d80008ccb 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -689,6 +689,7 @@ class FullTensorProductOfQueerSuperCrystals(FullTensorProductOfCrystals, QueerSu class Element(TensorProductOfQueerSuperCrystalsElement): pass + ######################################################### ## Crystal of tableaux @@ -931,7 +932,7 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): raise ValueError("shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: shapes = tuple(_Partitions(shape) if shape[n1-1] in NN else shape for shape in shapes) - return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) + return super().__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux # Caveat: this currently only supports all shapes being half @@ -979,7 +980,7 @@ def __init__(self, cartan_type, shapes): sage: T = crystals.Tableaux(['A',3], shape = [2,2]) sage: TestSuite(T).run() """ -# super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets()) +# super().__init__(category = FiniteEnumeratedSets()) Parent.__init__(self, category = ClassicalCrystals()) self.letters = CrystalOfLetters(cartan_type) self.shapes = shapes diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx index b7f60016ecc..fad1ced578d 100644 --- a/src/sage/combinat/crystals/tensor_product_element.pyx +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -478,6 +478,7 @@ cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): return self._set_index(-k, crystal) return None + cdef class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): """ Element class for a tensor product of regular crystals. @@ -1651,6 +1652,7 @@ cdef class TensorProductOfQueerSuperCrystalsElement(TensorProductOfRegularCrysta x = x.f(i) return string_length + cdef class InfinityQueerCrystalOfTableauxElement(TensorProductOfQueerSuperCrystalsElement): def __init__(self, parent, list, row_lengths=[]): """ @@ -1673,7 +1675,7 @@ cdef class InfinityQueerCrystalOfTableauxElement(TensorProductOfQueerSuperCrysta row_lengths.append(len(row)) list = ret self._row_lengths = row_lengths - super(InfinityQueerCrystalOfTableauxElement, self).__init__(parent, list) + super().__init__(parent, list) def _repr_(self): r""" @@ -1768,7 +1770,7 @@ cdef class InfinityQueerCrystalOfTableauxElement(TensorProductOfQueerSuperCrysta [[4, 4, 4, 4, 4, 3, 2, 1], [3, 3, 3, 3], [2, 2, 1], [1]] sage: t.e(-1) """ - ret = super(InfinityQueerCrystalOfTableauxElement, self).e(i) + ret = super().e(i) if ret is None: return None ( ret)._row_lengths = self._row_lengths diff --git a/src/sage/combinat/crystals/virtual_crystal.py b/src/sage/combinat/crystals/virtual_crystal.py index a537a64bfb8..0283b4a9543 100644 --- a/src/sage/combinat/crystals/virtual_crystal.py +++ b/src/sage/combinat/crystals/virtual_crystal.py @@ -23,14 +23,13 @@ # # http://www.gnu.org/licenses/ #**************************************************************************** - - from sage.categories.crystals import Crystals from sage.categories.finite_crystals import FiniteCrystals from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.subcrystal import Subcrystal from sage.sets.family import Family + class VirtualCrystal(Subcrystal): r""" A virtual crystal `V` of an ambient crystal `\widehat{B}` is a crystal @@ -195,9 +194,10 @@ def __classcall_private__(cls, ambient, virtualization, scaling_factors, if ambient in FiniteCrystals() or isinstance(contained, frozenset): category = category.Finite() - return super(Subcrystal, cls).__classcall__(cls, ambient, virtualization, scaling_factors, - contained, tuple(generators), cartan_type, - tuple(index_set), category) + return super().__classcall__(cls, ambient, virtualization, + scaling_factors, contained, + tuple(generators), cartan_type, + tuple(index_set), category) def __init__(self, ambient, virtualization, scaling_factors, contained, generators, cartan_type, index_set, category): diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index ed8504ebc95..4c5deb5ce21 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -428,7 +428,6 @@ class NoConvergenceError(RuntimeError): A special :python:`RuntimeError` which is raised when an algorithm does not converge/stop. """ - pass class AsymptoticExpansion(CommutativeAlgebraElement): @@ -666,7 +665,7 @@ def __init__(self, parent, summands, simplify=True, convert=True): sage: 1 + (-1)^x + 2^x + (-2)^x 2^x + 2^x*(-1)^x + (-1)^x + 1 """ - super(AsymptoticExpansion, self).__init__(parent=parent) + super().__init__(parent=parent) from sage.data_structures.mutable_poset import MutablePoset if not isinstance(summands, MutablePoset): @@ -716,7 +715,6 @@ def summands(self): """ return self._summands_ - def __hash__(self): r""" A hash value for this element. @@ -736,7 +734,6 @@ def __hash__(self): """ return hash(str(self)) - def __bool__(self): r""" Return whether this asymptotic expansion is not identically zero. @@ -761,8 +758,6 @@ def __bool__(self): """ return bool(self._summands_) - - def __eq__(self, other): r""" Return whether this asymptotic expansion is equal to ``other``. @@ -805,7 +800,6 @@ def __eq__(self, other): except (TypeError, ValueError): return False - def __ne__(self, other): r""" Return whether this asymptotic expansion is not equal to ``other``. @@ -838,7 +832,6 @@ def __ne__(self, other): """ return not self == other - def has_same_summands(self, other): r""" Return whether this asymptotic expansion and ``other`` have the @@ -887,7 +880,6 @@ def has_same_summands(self, other): lambda self, other: self._has_same_summands_(other)) - def _has_same_summands_(self, other): r""" Return whether this :class:`AsymptoticExpansion` has the same @@ -961,7 +953,6 @@ def _simplify_(self): """ self._summands_.merge(reverse=True) - def _repr_(self, latex=False): r""" A representation string for this asymptotic expansion. @@ -996,7 +987,6 @@ def _repr_(self, latex=False): return '0' return s - def _latex_(self): r""" A LaTeX-representation string for this asymptotic expansion. @@ -1015,7 +1005,6 @@ def _latex_(self): """ return self._repr_(latex=True) - def show(self): r""" Pretty-print this asymptotic expansion. @@ -1162,7 +1151,6 @@ def _add_(self, other): return self.parent()(self.summands.union(other.summands), simplify=True, convert=False) - def _sub_(self, other): r""" Subtract ``other`` from this asymptotic expansion. @@ -1191,7 +1179,6 @@ def _sub_(self, other): """ return self + self.parent().coefficient_ring(-1)*other - def _mul_term_(self, term): r""" Helper method: multiply this asymptotic expansion by the @@ -1408,10 +1395,8 @@ def __invert__(self, precision=None): return result._mul_term_(imax_elem) - invert = __invert__ - def truncate(self, precision=None): r""" Truncate this asymptotic expansion. @@ -1704,7 +1689,7 @@ def __pow__(self, exponent, precision=None): except (TypeError, ValueError): pass else: - return super(AsymptoticExpansion, self).__pow__(exponent) + return super().__pow__(exponent) from sage.rings.rational_field import QQ try: @@ -1926,7 +1911,6 @@ def sqrt(self, precision=None): from sage.rings.rational_field import QQ return self.pow(QQ(1)/QQ(2), precision=precision) - def O(self): r""" Convert all terms in this asymptotic expansion to `O`-terms. @@ -1972,7 +1956,6 @@ def O(self): return sum(self.parent().create_summand('O', growth=element) for element in self.summands.maximal_elements()) - def log(self, base=None, precision=None, locals=None): r""" The logarithm of this asymptotic expansion. @@ -2106,7 +2089,6 @@ def log(self, base=None, precision=None, locals=None): return result - def is_exact(self): r""" Return whether all terms of this expansion are exact. @@ -2132,7 +2114,6 @@ def is_exact(self): """ return all(T.is_exact() for T in self.summands) - def is_little_o_of_one(self): r""" Return whether this expansion is of order `o(1)`. @@ -2173,7 +2154,6 @@ def is_little_o_of_one(self): """ return all(term.is_little_o_of_one() for term in self.summands.maximal_elements()) - def rpow(self, base, precision=None, locals=None): r""" Return the power of ``base`` to this asymptotic expansion. @@ -2290,7 +2270,6 @@ def inverted_factorials(): return result * large_result - def _main_term_relative_error_(self, return_inverse_main_term=False): r""" Split this asymptotic expansion into `m(1+x)` with `x=o(1)`. @@ -2363,7 +2342,6 @@ def _main_term_relative_error_(self, return_inverse_main_term=False): else: return (max_elem, x) - @staticmethod def _power_series_(coefficients, start, ratio, ratio_start, precision): r""" @@ -2438,7 +2416,6 @@ def _power_series_(coefficients, start, ratio, ratio_start, precision): result = new_result return result - def exp(self, precision=None): r""" Return the exponential of (i.e., the power of `e` to) this asymptotic expansion. @@ -2508,7 +2485,6 @@ def exp(self, precision=None): """ return self.rpow('e', precision=precision) - def substitute(self, rules=None, domain=None, **kwds): r""" Substitute the given ``rules`` in this asymptotic expansion. @@ -2669,7 +2645,7 @@ def substitute(self, rules=None, domain=None, **kwds): # init and process keyword arguments gens = self.parent().gens() - locals = kwds or dict() + locals = kwds or {} # update with rules if isinstance(rules, dict): @@ -2784,7 +2760,6 @@ def _substitute_(self, rules): from .misc import substitute_raise_exception substitute_raise_exception(self, e) - def compare_with_values(self, variable, function, values, rescaled=True, ring=RIF): """ @@ -2944,7 +2919,6 @@ def function(arg): return points - def plot_comparison(self, variable, function, values, rescaled=True, ring=RIF, relative_tolerance=0.025, **kwargs): r""" @@ -3041,7 +3015,6 @@ def plot_comparison(self, variable, function, values, rescaled=True, return list_plot(points, **kwargs) - def symbolic_expression(self, R=None): r""" Return this asymptotic expansion as a symbolic expression. @@ -3096,10 +3069,8 @@ def symbolic_expression(self, R=None): for g in self.parent().gens()), domain=R) - _symbolic_ = symbolic_expression # will be used by SR._element_constructor_ - def map_coefficients(self, f, new_coefficient_ring=None): r""" Return the asymptotic expansion obtained by applying ``f`` to @@ -3154,7 +3125,6 @@ def mapping(term): S.map(mapping) return P(S, simplify=False, convert=False) - def factorial(self): r""" Return the factorial of this asymptotic expansion. @@ -3256,7 +3226,6 @@ def factorial(self): 'Cannot build the factorial of {} since it is not ' 'univariate.'.format(self)) - def variable_names(self): r""" Return the names of the variables of this asymptotic expansion. @@ -3291,7 +3260,6 @@ def variable_names(self): from itertools import groupby return tuple(v for v, _ in groupby(vars)) - def _singularity_analysis_(self, var, zeta, precision=None): r""" Return the asymptotic growth of the coefficients of some @@ -3597,7 +3565,6 @@ class AsymptoticRing(Algebra, UniqueRepresentation, WithLocals): # enable the category framework for elements Element = AsymptoticExpansion - @staticmethod def __classcall__(cls, growth_group=None, coefficient_ring=None, names=None, category=None, default_prec=None, @@ -3736,12 +3703,11 @@ def format_names(N): if locals is not None: locals = cls._convert_locals_(locals) - return super(AsymptoticRing, - cls).__classcall__(cls, growth_group, coefficient_ring, - category=category, - default_prec=default_prec, - term_monoid_factory=term_monoid_factory, - locals=locals) + return super().__classcall__(cls, growth_group, coefficient_ring, + category=category, + default_prec=default_prec, + term_monoid_factory=term_monoid_factory, + locals=locals) def __init__(self, growth_group, coefficient_ring, category, default_prec, @@ -3770,9 +3736,8 @@ def __init__(self, growth_group, coefficient_ring, self._default_prec_ = default_prec self._term_monoid_factory_ = term_monoid_factory self._locals_ = locals - super(AsymptoticRing, self).__init__(base_ring=coefficient_ring, - category=category) - + super().__init__(base_ring=coefficient_ring, + category=category) @property def growth_group(self): @@ -3791,7 +3756,6 @@ def growth_group(self): """ return self._growth_group_ - @property def coefficient_ring(self): r""" @@ -3805,7 +3769,6 @@ def coefficient_ring(self): """ return self._coefficient_ring_ - @property def default_prec(self): r""" @@ -3826,7 +3789,6 @@ def default_prec(self): """ return self._default_prec_ - @property def term_monoid_factory(self): r""" @@ -3844,7 +3806,6 @@ def term_monoid_factory(self): """ return self._term_monoid_factory_ - def term_monoid(self, type): r""" Return the term monoid of this asymptotic ring of specified ``type``. @@ -3874,7 +3835,6 @@ def term_monoid(self, type): TermMonoid = self.term_monoid_factory return TermMonoid(type, asymptotic_ring=self) - def change_parameter(self, **kwds): r""" Return an asymptotic ring with a change in one or more of the given parameters. @@ -3908,7 +3868,7 @@ def change_parameter(self, **kwds): """ parameters = ('growth_group', 'coefficient_ring', 'default_prec', 'term_monoid_factory') - values = dict() + values = {} category = self.category() values['category'] = category locals = self._locals_ @@ -3998,7 +3958,6 @@ def _create_element_in_extension_(self, term, old_term_parent=None): coefficient_ring=term.parent().coefficient_ring) return parent(term, simplify=False, convert=False) - def _element_constructor_(self, data, simplify=True, convert=True): r""" Convert a given object to this asymptotic ring. @@ -4223,7 +4182,6 @@ def _element_constructor_(self, data, simplify=True, convert=True): return self.create_summand('exact', data) - def _coerce_map_from_(self, R): r""" Return whether ``R`` coerces into this asymptotic ring. @@ -4277,7 +4235,6 @@ def _coerce_map_from_(self, R): self.coefficient_ring.has_coerce_map_from(R.coefficient_ring): return True - def _repr_(self): r""" A representation string of this asymptotic ring. @@ -4303,7 +4260,6 @@ def _repr_(self): G = repr(self.growth_group) return 'Asymptotic Ring %s over %s' % (G, self.coefficient_ring) - def _an_element_(self): r""" Return an element of this asymptotic ring. @@ -4330,7 +4286,6 @@ def _an_element_(self): return self(E.an_element(), simplify=False, convert=False)**3 + \ self(O.an_element(), simplify=False, convert=False) - def some_elements(self): r""" Return some elements of this term monoid. @@ -4369,7 +4324,6 @@ def some_elements(self): for e, o in cantor_product( E.some_elements(), O.some_elements())) - def gens(self): r""" Return a tuple with generators of this asymptotic ring. @@ -4404,7 +4358,6 @@ def gens(self): coefficient=self.coefficient_ring(1)) for g in self.growth_group.gens_monomial()) - def gen(self, n=0): r""" Return the ``n``-th generator of this asymptotic ring. @@ -4425,7 +4378,6 @@ def gen(self, n=0): """ return self.gens()[n] - def ngens(self): r""" Return the number of generators of this asymptotic ring. @@ -4446,7 +4398,6 @@ def ngens(self): """ return len(self.growth_group.gens_monomial()) - def coefficients_of_generating_function(self, function, singularities, precision=None, return_singular_expansions=False, error_term=None): @@ -4592,7 +4543,6 @@ def coefficients_of_generating_function(self, function, singularities, precision else: return result - def create_summand(self, type, data=None, **kwds): r""" Create a simple asymptotic expansion consisting of a single @@ -4686,7 +4636,6 @@ def create_summand(self, type, data=None, **kwds): except ZeroCoefficientError: return self.zero() - def variable_names(self): r""" Return the names of the variables. @@ -4703,7 +4652,6 @@ def variable_names(self): """ return self.growth_group.variable_names() - def construction(self): r""" Return the construction of this asymptotic ring. @@ -4822,7 +4770,6 @@ class AsymptoticRingFunctor(ConstructionFunctor): rank = 13 - def __init__(self, growth_group, default_prec=None, category=None, term_monoid_factory=None, locals=None, @@ -4848,9 +4795,7 @@ def __init__(self, growth_group, self._locals_ = locals from sage.categories.rings import Rings - super(ConstructionFunctor, self).__init__( - Rings(), Rings()) - + super().__init__(Rings(), Rings()) def _repr_(self): r""" @@ -4877,7 +4822,6 @@ def _repr_(self): return '{}<{}>'.format(self.cls.__name__, self.growth_group._repr_(condense=True)) - def _apply_functor(self, coefficient_ring): r""" Apply this functor to the given ``coefficient_ring``. @@ -4930,7 +4874,6 @@ def _apply_functor(self, coefficient_ring): kwds[parameter] = value return self.cls(**kwds) - def merge(self, other): r""" Merge this functor with ``other`` if possible. @@ -5027,7 +4970,6 @@ def merge(self, other): category=category, cls=self.cls) - def __eq__(self, other): r""" Return whether this functor is equal to ``other``. @@ -5057,7 +4999,6 @@ def __eq__(self, other): and self._category_ == other._category_ and self.cls == other.cls) - def __ne__(self, other): r""" Return whether this functor is not equal to ``other``. diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index e80235a7d9d..ec4c5ce878f 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -336,7 +336,7 @@ def __init__(self, parent, numerator, denominator_factored, reduce=True): sage: f = FFPD(x, df) sage: TestSuite(f).run() """ - super(FractionWithFactoredDenominator, self).__init__(parent) + super().__init__(parent) from sage.rings.semirings.non_negative_integer_semiring import NN self._numerator = parent._numerator_ring(numerator) @@ -1940,7 +1940,7 @@ def asymptotics_smooth(self, p, alpha, N, asy_var, coordinate=None, sub_final=[Tstar, atP], rekey=AA) Phitu_derivs = diff_all(Phitu, T, N - 1 + v, sub=hderivs1, sub_final=[Tstar, atP], - zero_order=v + 1 , rekey=BB) + zero_order=v + 1, rekey=BB) AABB_derivs = At_derivs AABB_derivs.update(Phitu_derivs) AABB_derivs[AA] = At.subs(Tstar).subs(atP) @@ -2007,7 +2007,7 @@ def asymptotics_smooth(self, p, alpha, N, asy_var, coordinate=None, AABB_derivs[BB] = Phitu.subs(Tstar).subs(atP) if verbose: print("Computing second order differential operator actions...") - DD = diff_op(AA, BB, AABB_derivs, T, a_inv, 1 , N) + DD = diff_op(AA, BB, AABB_derivs, T, a_inv, 1, N) # Plug above into asymptotic formula. L = [] @@ -3066,8 +3066,8 @@ def __classcall_private__(cls, denominator_ring, numerator_ring=None, category=N 'denominator ring {}'.format( numerator_ring, denominator_ring)) category = Rings().or_subcategory(category) - return super(FractionWithFactoredDenominatorRing, cls).__classcall__(cls, - denominator_ring, numerator_ring, category) + return super().__classcall__(cls, denominator_ring, + numerator_ring, category) def __init__(self, denominator_ring, numerator_ring=None, category=None): r""" @@ -3792,7 +3792,7 @@ def subs_all(f, sub, simplify=False): sage: var('x, y') (x, y) sage: a = {'foo': x**2 + y**2, 'bar': x - y} - sage: b = {x: 1 , y: 2} + sage: b = {x: 1, y: 2} sage: subs_all(a, b) {'bar': -1, 'foo': 5} """ diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index 3f2e6d7e692..645d9723e68 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -512,7 +512,7 @@ def convert_factors(data, raw_data): # room for other parents (e.g. polynomial ring et al.) try: - return super(GenericProduct, self)._element_constructor_(data) + return super()._element_constructor_(data) except (TypeError, ValueError): pass if isinstance(data, (tuple, list, CartesianProduct.Element)): @@ -1441,9 +1441,7 @@ def __init__(self, sets, category, **kwargs): sage: type(GrowthGroup('x^ZZ * log(x)^ZZ')) # indirect doctest """ - super(UnivariateProduct, self).__init__( - sets, category, order='lex', **kwargs) - + super().__init__(sets, category, order='lex', **kwargs) CartesianProduct = CartesianProductGrowthGroups @@ -1474,8 +1472,6 @@ def __init__(self, sets, category, **kwargs): sage: type(GrowthGroup('x^ZZ * y^ZZ')) # indirect doctest """ - super(MultivariateProduct, self).__init__( - sets, category, order='product', **kwargs) - + super().__init__(sets, category, order='product', **kwargs) CartesianProduct = CartesianProductGrowthGroups diff --git a/src/sage/rings/asymptotic/misc.py b/src/sage/rings/asymptotic/misc.py index 33388f9f183..8117d8ede30 100644 --- a/src/sage/rings/asymptotic/misc.py +++ b/src/sage/rings/asymptotic/misc.py @@ -17,7 +17,7 @@ ============================== """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Daniel Krenn # # This program is free software: you can redistribute it and/or modify @@ -25,8 +25,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** - +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject @@ -269,7 +268,7 @@ def is_balanced(s): return False return bool(open == 0) - factors = list() + factors = [] balanced = True if string and op is not None and string.startswith(op): raise ValueError("'%s' is invalid since it starts with a '%s'." % @@ -357,10 +356,8 @@ def add_parentheses(s, op): if any(sig in s for sig in signals) or latex and s.startswith(r'\frac'): if latex: return r'\left({}\right)'.format(s) - else: - return '({})'.format(s) - else: - return s + return '({})'.format(s) + return s return add_parentheses(left, op) + op + add_parentheses(right, op) @@ -843,7 +840,7 @@ def __init__(self, asymptotic_ring=None, var=None, exact_part=0): exact_part = asymptotic_ring.zero() self.exact_part = exact_part - super(NotImplementedOZero, self).__init__(message) + super().__init__(message) class NotImplementedBZero(NotImplementedError): @@ -913,7 +910,7 @@ def __init__(self, asymptotic_ring=None, var=None, exact_part=0): exact_part = asymptotic_ring.zero() self.exact_part = exact_part - super(NotImplementedBZero, self).__init__(message) + super().__init__(message) def transform_category(category, @@ -1081,7 +1078,7 @@ def __getitem__(self, key): """ try: - return super(Locals, self).__getitem__(key) + return super().__getitem__(key) except KeyError as ke: try: return self.default_locals()[key] @@ -1188,8 +1185,7 @@ def _convert_locals_(locals): """ if locals is None: return Locals() - else: - return Locals(locals) + return Locals(locals) def locals(self, locals=None): r""" diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 36b3d2e4f33..b5b39091371 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -1864,7 +1864,7 @@ def _element_constructor_(self, data, *args, **kwds): f'takes one positional argument, ' f'another positional argument is deprecated, ' f'but {len(args)+1} were given') - elif len(args) == 1: + if len(args) == 1: from sage.misc.superseded import deprecation deprecation(32215, "Passing 'coefficient' as a positional argument is deprecated; " @@ -4877,7 +4877,7 @@ def _absorb_(self, other): if not (self.growth >= other.growth): raise ArithmeticError(f'{self} cannot absorb {other}') - valid_from_new = dict() + valid_from_new = {} for variable_name in set().union(self.valid_from.keys(), other.valid_from.keys()): if variable_name in self.valid_from and other.valid_from: valid_from_new[variable_name] = (max(self.valid_from[variable_name], other.valid_from[variable_name])) @@ -5339,7 +5339,7 @@ def __init__(self, name, sage: type(MyTermMonoid('B', G, QQ)) """ - super(TermMonoidFactory, self).__init__(name) + super().__init__(name) if exact_term_monoid_class is None: exact_term_monoid_class = ExactTermMonoid From cc504c598b2358603025df90f8c8ebce315c327f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 6 Aug 2022 18:10:56 +0200 Subject: [PATCH 138/454] oops --- src/sage/combinat/crystals/fast_crystals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py index 9899f87122b..86e3d39cfc3 100644 --- a/src/sage/combinat/crystals/fast_crystals.py +++ b/src/sage/combinat/crystals/fast_crystals.py @@ -1,4 +1,4 @@ -sr""" +r""" Fast Rank Two Crystals """ # **************************************************************************** From f6bf70bca1123ec6f9be720da698558caf3e4ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 6 Aug 2022 19:03:32 +0200 Subject: [PATCH 139/454] fixing some W391 (removing empty final lines) --- src/sage/algebras/lie_algebras/subalgebra.py | 3 --- src/sage/all_cmdline.py | 9 +++------ src/sage/calculus/predefined.py | 1 - src/sage/coding/grs_code.py | 1 - src/sage/coding/self_dual_codes.py | 8 +++----- src/sage/crypto/classical_cipher.py | 13 ++++--------- src/sage/crypto/mq/mpolynomialsystemgenerator.py | 9 ++++----- src/sage/ext_data/nbconvert/postprocess.py | 4 +--- src/sage/finance/markov_multifractal.py | 1 - src/sage/functions/orthogonal_polys.py | 2 +- src/sage/game_theory/parser.py | 3 +-- src/sage/games/hexad.py | 1 - src/sage/homology/algebraic_topological_model.py | 4 +--- src/sage/homology/chain_complex.py | 1 - src/sage/homology/chain_homotopy.py | 4 ++-- src/sage/homology/homology_group.py | 7 ++----- .../homology/homology_vector_space_with_basis.py | 4 +--- src/sage/homology/koszul_complex.py | 5 ++--- src/sage/homology/matrix_utils.py | 4 +--- src/sage/homology/simplicial_complex_homset.py | 1 - src/sage/interfaces/axiom.py | 1 - src/sage/interfaces/gap.py | 1 - src/sage/interfaces/gnuplot.py | 4 ---- src/sage/interfaces/macaulay2.py | 2 -- src/sage/interfaces/mupad.py | 1 - src/sage/interfaces/mwrank.py | 1 - src/sage/interfaces/polymake.py | 1 - src/sage/interfaces/primecount.py | 1 - src/sage/interfaces/qepcad.py | 2 -- src/sage/interfaces/scilab.py | 1 - src/sage/interfaces/sympy.py | 3 +-- src/sage/libs/gap/all.py | 4 ---- src/sage/libs/gap/test_long.py | 12 ++++-------- src/sage/libs/lrcalc/lrcalc.py | 2 +- src/sage/libs/mpmath/all.py | 9 ++++----- src/sage/logic/logicparser.py | 4 +--- 36 files changed, 37 insertions(+), 97 deletions(-) diff --git a/src/sage/algebras/lie_algebras/subalgebra.py b/src/sage/algebras/lie_algebras/subalgebra.py index baa633c1cb7..90779eb91fc 100644 --- a/src/sage/algebras/lie_algebras/subalgebra.py +++ b/src/sage/algebras/lie_algebras/subalgebra.py @@ -5,7 +5,6 @@ - Eero Hakavuori (2018-08-29): initial version """ - # **************************************************************************** # Copyright (C) 2018 Eero Hakavuori # @@ -15,7 +14,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.algebras.lie_algebras.lie_algebra_element import LieSubalgebraElementWrapper from sage.categories.lie_algebras import LieAlgebras from sage.categories.homset import Hom @@ -975,4 +973,3 @@ def adjoint_matrix(self, sparse=False): return matrix(self.base_ring(), [M.coordinate_vector(P.bracket(self, b).to_vector(sparse=sparse)) for b in basis], sparse=sparse).transpose() - diff --git a/src/sage/all_cmdline.py b/src/sage/all_cmdline.py index 644d3f2c863..81c56b018ab 100644 --- a/src/sage/all_cmdline.py +++ b/src/sage/all_cmdline.py @@ -3,21 +3,18 @@ This is all.py (load all sage functions) plus set-up for the Sage commandline. """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein # # 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/ +# **************************************************************************** sage_mode = 'cmdline' from sage.all import * from sage.calculus.predefined import x sage.misc.session.init() - diff --git a/src/sage/calculus/predefined.py b/src/sage/calculus/predefined.py index ea677fe15da..8e7f499b1ae 100644 --- a/src/sage/calculus/predefined.py +++ b/src/sage/calculus/predefined.py @@ -48,4 +48,3 @@ X = _var('X') Y = _var('Y') Z = _var('Z') - diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 840152f75f9..230a4a1f7ee 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -2389,4 +2389,3 @@ def decoding_radius(self): GRSErrorErasureDecoder._decoder_type = {"error-erasure", "always-succeed"} GeneralizedReedSolomonCode._registered_decoders["KeyEquationSyndrome"] = GRSKeyEquationSyndromeDecoder GRSKeyEquationSyndromeDecoder._decoder_type = {"hard-decision", "always-succeed"} - diff --git a/src/sage/coding/self_dual_codes.py b/src/sage/coding/self_dual_codes.py index 199db9257a7..a0974add584 100644 --- a/src/sage/coding/self_dual_codes.py +++ b/src/sage/coding/self_dual_codes.py @@ -81,7 +81,7 @@ - [HP2003] \W. C. Huffman, V. Pless, Fundamentals of Error-Correcting Codes, Cambridge Univ. Press, 2003. -- [P] \V. Pless, "A classification of self-orthogonal codes over GF(2)", +- [P] \V. Pless, *A classification of self-orthogonal codes over GF(2)*, Discrete Math 3 (1972) 209-246. """ @@ -97,6 +97,7 @@ _F = GF(2) + def _MS(n): r""" For internal use; returns the floor(n/2) x n matrix space over GF(2). @@ -162,6 +163,7 @@ def _matId(n): Id.append(MSn.identity_matrix()) return Id + def _MS2(n): r""" For internal use; returns the floor(n/2) x floor(n/2) matrix space over GF(2). @@ -933,7 +935,3 @@ def self_dual_binary_codes(n): "3":self_dual_codes_22_3,"4":self_dual_codes_22_4,"5":self_dual_codes_22_5,\ "6":self_dual_codes_22_6} return self_dual_codes - - - - diff --git a/src/sage/crypto/classical_cipher.py b/src/sage/crypto/classical_cipher.py index 0fe8c6b9434..72ea18a3117 100644 --- a/src/sage/crypto/classical_cipher.py +++ b/src/sage/crypto/classical_cipher.py @@ -1,19 +1,18 @@ """ Classical Ciphers """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 David Kohel # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from .cipher import SymmetricKeyCipher from sage.monoids.string_monoid_element import StringMonoidElement from sage.modules.free_module import FreeModule + class AffineCipher(SymmetricKeyCipher): r""" Affine cipher class. This is the class that does the actual work of @@ -574,7 +573,3 @@ def inverse(self): E = self.parent() K = E.inverse_key(self.key()) return E(K) - - - - diff --git a/src/sage/crypto/mq/mpolynomialsystemgenerator.py b/src/sage/crypto/mq/mpolynomialsystemgenerator.py index 9dd861ed469..3c0bb6b349c 100644 --- a/src/sage/crypto/mq/mpolynomialsystemgenerator.py +++ b/src/sage/crypto/mq/mpolynomialsystemgenerator.py @@ -2,11 +2,12 @@ Abstract base class for generators of polynomial systems AUTHOR: - Martin Albrecht -""" +Martin Albrecht +""" from sage.structure.sage_object import SageObject + class MPolynomialSystemGenerator(SageObject): """ Abstract base class for generators of polynomial systems. @@ -26,8 +27,7 @@ def __getattr__(self, attr): if attr == "R": self.R = self.ring() return self.R - else: - raise AttributeError("'%s' object has no attribute '%s'"%(self.__class__,attr)) + raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__,attr)) def varformatstr(self, name): """ @@ -196,4 +196,3 @@ def random_element(self): NotImplementedError """ raise NotImplementedError - diff --git a/src/sage/ext_data/nbconvert/postprocess.py b/src/sage/ext_data/nbconvert/postprocess.py index f36497fb73f..524c5b213a6 100755 --- a/src/sage/ext_data/nbconvert/postprocess.py +++ b/src/sage/ext_data/nbconvert/postprocess.py @@ -11,7 +11,6 @@ - Thierry Monteil (2018): initial version. """ - import sys import re @@ -27,7 +26,7 @@ # processing new_file = '' -for i,line in enumerate(lines): +for i, line in enumerate(lines): if line.startswith(' # ') and not wrong_title_fixed: new_file += re.sub('^ # ', '', line) new_file += '=' * (len(line) - 4) + '\n' @@ -44,4 +43,3 @@ # write new file with open(file_name, 'w') as f: f.write(new_file) - diff --git a/src/sage/finance/markov_multifractal.py b/src/sage/finance/markov_multifractal.py index 99b5968f051..6206f56b304 100644 --- a/src/sage/finance/markov_multifractal.py +++ b/src/sage/finance/markov_multifractal.py @@ -276,4 +276,3 @@ def simulations(self, n, k=1): ## OUTPUT: ## m0, sigma, gamma_kbar, b ## """ - diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index 28f23f35ddf..012b05fa028 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -3028,5 +3028,5 @@ def eval_recursive(self, k, x, a, b, n, *args, **kwds): Hm2 = C * hahn.eval_recursive(k-2, x, a, b, n) return (Hm1 - Hm2) / A -hahn = Func_hahn() +hahn = Func_hahn() diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index 87b0676fecc..80d90d9a5cf 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -1,7 +1,6 @@ """ Parser For gambit And lrs Nash Equilibria """ - # **************************************************************************** # Copyright (C) 2014 James Campbell james.campbell@tanti.org.uk # 2015 Vincent Knight @@ -13,6 +12,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** + class Parser(): r""" A class for parsing the outputs of different algorithms called in other @@ -298,4 +298,3 @@ def format_gambit(self, gambit_game): nice_stuff.append(profile) return nice_stuff - diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index dff95e8e6b8..710513207a7 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -711,4 +711,3 @@ def blackjack_move(self, L0): return str(x) + ' --> ' + str(y) + ". The total went from " + str(total) + " to " + str(total - x + y) + "." print("This is a hexad. \n There is no winning move, so make a random legal move.") return L0 - diff --git a/src/sage/homology/algebraic_topological_model.py b/src/sage/homology/algebraic_topological_model.py index b14de9bdfa3..f89a1529dd9 100644 --- a/src/sage/homology/algebraic_topological_model.py +++ b/src/sage/homology/algebraic_topological_model.py @@ -12,7 +12,6 @@ - John H. Palmieri (2015-09) """ - ######################################################################## # Copyright (C) 2015 John H. Palmieri # @@ -20,7 +19,7 @@ # 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/ ######################################################################## # TODO: cythonize this. @@ -590,4 +589,3 @@ def conditionally_sparse(m): iota = ChainComplexMorphism(iota_data, M, C) phi = ChainContraction(phi_data, pi, iota) return phi, M - diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 99fa87c9751..044379c0b28 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -2238,4 +2238,3 @@ def scalar(a): from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.homology.chain_complex', 'ChainComplex', ChainComplex_class) - diff --git a/src/sage/homology/chain_homotopy.py b/src/sage/homology/chain_homotopy.py index feac17c26b7..16a1c385edc 100644 --- a/src/sage/homology/chain_homotopy.py +++ b/src/sage/homology/chain_homotopy.py @@ -39,13 +39,14 @@ # 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.categories.morphism import Morphism from sage.categories.homset import Hom from sage.homology.chain_complex_morphism import ChainComplexMorphism + # In a perfect world, this would inherit from something like # "TwoMorphism" rather than "Morphism"... class ChainHomotopy(Morphism): @@ -581,4 +582,3 @@ def dual(self): deg = self.domain().degree_of_differential() matrices = {i-deg: matrix_dict[i].transpose() for i in matrix_dict} return ChainContraction(matrices, self.iota().dual(), self.pi().dual()) - diff --git a/src/sage/homology/homology_group.py b/src/sage/homology/homology_group.py index adfac7717a0..0b27087a362 100644 --- a/src/sage/homology/homology_group.py +++ b/src/sage/homology/homology_group.py @@ -5,7 +5,6 @@ group that prints itself in a way that is suitable for homology groups. """ - ######################################################################## # Copyright (C) 2013 John H. Palmieri # Volker Braun @@ -14,7 +13,7 @@ # 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.modules.free_module import VectorSpace @@ -182,6 +181,4 @@ def HomologyGroup(n, base_ring, invfac=None): invfac = [0] * (n - len(invfac)) + invfac elif len(invfac) > n: raise ValueError("invfac (={}) must have length n (={})".format(invfac, n)) - M = HomologyGroup_class(n, invfac) - return M - + return HomologyGroup_class(n, invfac) diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index 8861ee20ad9..63a80657917 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -14,7 +14,6 @@ - John H. Palmieri, Travis Scrimshaw (2015-09) """ - ######################################################################## # Copyright (C) 2015 John H. Palmieri # Travis Scrimshaw @@ -23,7 +22,7 @@ # 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.cachefunc import cached_method @@ -871,4 +870,3 @@ def sum_indices(k, i_k_plus_one, S_k_plus_one): return [[S_k]] return [[i_k] + l for i_k in range(S_k, i_k_plus_one) for l in sum_indices(k-1, i_k, S_k)] - diff --git a/src/sage/homology/koszul_complex.py b/src/sage/homology/koszul_complex.py index c83605db2f5..858d5a71283 100644 --- a/src/sage/homology/koszul_complex.py +++ b/src/sage/homology/koszul_complex.py @@ -1,7 +1,6 @@ """ Koszul Complexes """ - ######################################################################## # Copyright (C) 2014 Travis Scrimshaw # @@ -9,7 +8,7 @@ # 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.structure.unique_representation import UniqueRepresentation @@ -22,6 +21,7 @@ import itertools + class KoszulComplex(ChainComplex_class, UniqueRepresentation): r""" A Koszul complex. @@ -166,4 +166,3 @@ def _repr_(self): if not self._elements: return "Trivial Koszul complex over {}".format(self.base_ring()) return "Koszul complex defined by {} over {}".format(self._elements, self.base_ring()) - diff --git a/src/sage/homology/matrix_utils.py b/src/sage/homology/matrix_utils.py index 1f2b583fc54..b1edc656e58 100644 --- a/src/sage/homology/matrix_utils.py +++ b/src/sage/homology/matrix_utils.py @@ -5,7 +5,6 @@ with the differentials thought of as matrices. This module contains some utility functions for this purpose. """ - ######################################################################## # Copyright (C) 2013 John H. Palmieri # @@ -13,7 +12,7 @@ # 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/ ######################################################################## # TODO: this module is a clear candidate for cythonizing. Need to @@ -203,4 +202,3 @@ def dhsw_snf(mat, verbose=False): if len(ed) < rows: return ed + [0]*(rows - len(ed)) return ed[:rows] - diff --git a/src/sage/homology/simplicial_complex_homset.py b/src/sage/homology/simplicial_complex_homset.py index 6a9d78f69f1..f5b54a59dc2 100644 --- a/src/sage/homology/simplicial_complex_homset.py +++ b/src/sage/homology/simplicial_complex_homset.py @@ -10,4 +10,3 @@ sage.topology.simplicial_complex_homset.is_SimplicialComplexHomset) SimplicialComplexHomset = deprecated_function_alias(31925, sage.topology.simplicial_complex_homset.SimplicialComplexHomset) - diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index c6ef330ae63..fa0334cfd4f 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -993,4 +993,3 @@ def axiom_console(): if not get_display_manager().is_in_terminal(): raise RuntimeError('Can use the console only in the terminal. Try %%axiom magics instead.') os.system('axiom -nox') - diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index c34fe530c34..ba175d4e340 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1814,4 +1814,3 @@ def gap_console(): cmd, _ = gap_command(use_workspace_cache=False) cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','console.g') os.system(cmd) - diff --git a/src/sage/interfaces/gnuplot.py b/src/sage/interfaces/gnuplot.py index d93f6b33fb4..8ab14c75e1c 100644 --- a/src/sage/interfaces/gnuplot.py +++ b/src/sage/interfaces/gnuplot.py @@ -196,7 +196,3 @@ def gnuplot_console(): if not get_display_manager().is_in_terminal(): raise RuntimeError('Can use the console only in the terminal. Try %%gnuplot magics instead.') os.system('gnuplot') - - - - diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 2389aa7ab10..45f1a13e198 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -1866,7 +1866,6 @@ def macaulay2_console(): os.system('M2') - def reduce_load_macaulay2(): """ Used for reconstructing a copy of the Macaulay2 interpreter from a pickle. @@ -1878,4 +1877,3 @@ def reduce_load_macaulay2(): Macaulay2 """ return macaulay2 - diff --git a/src/sage/interfaces/mupad.py b/src/sage/interfaces/mupad.py index 077ef21e70d..a27b2d4d25f 100644 --- a/src/sage/interfaces/mupad.py +++ b/src/sage/interfaces/mupad.py @@ -693,4 +693,3 @@ def __doctest_cleanup(): """ import sage.interfaces.quit sage.interfaces.quit.expect_quitall() - diff --git a/src/sage/interfaces/mwrank.py b/src/sage/interfaces/mwrank.py index 636b7d6d961..384438d4a2a 100644 --- a/src/sage/interfaces/mwrank.py +++ b/src/sage/interfaces/mwrank.py @@ -362,4 +362,3 @@ def mwrank_console(): if not get_display_manager().is_in_terminal(): raise RuntimeError('Can use the console only in the terminal. Try %%mwrank magics instead.') os.system('mwrank') - diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index e335f33ae0a..82787d4eb02 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -2739,4 +2739,3 @@ def reduce_load_Polymake(): polymake = polymake_jupymake else: polymake = polymake_expect - diff --git a/src/sage/interfaces/primecount.py b/src/sage/interfaces/primecount.py index e037cb2794d..e647bf9b980 100644 --- a/src/sage/interfaces/primecount.py +++ b/src/sage/interfaces/primecount.py @@ -3,4 +3,3 @@ from sage.misc.lazy_import import lazy_import lazy_import("primecountpy.primecount", ['phi', 'nth_prime', 'prime_pi', 'prime_pi_128'], deprecation=(32894, "the module sage.interfaces.primecount is deprecated - use primecountpy.primecount instead")) - diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 549877b392d..2378f765c8c 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -2752,6 +2752,4 @@ def sample_point_dict(self): """ points = self.sample_point() vars = self._parent._varlist - return dict([(vars[i], points[i]) for i in range(len(points))]) - diff --git a/src/sage/interfaces/scilab.py b/src/sage/interfaces/scilab.py index c1144d40ab0..c9e0daa1027 100644 --- a/src/sage/interfaces/scilab.py +++ b/src/sage/interfaces/scilab.py @@ -561,4 +561,3 @@ def scilab_version(): 'scilab-...' """ return str(scilab('getversion()')).strip() - diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 74764b13716..2c847d56892 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -1181,7 +1181,7 @@ def sympy_set_to_list(set, vars): elif isinstance(set, (Union, Interval)): x = vars[0] if isinstance(set, Interval): - left,right,lclosed,rclosed = set._args + left, right, lclosed, rclosed = set._args if lclosed: rel1 = [x._sage_() > left._sage_()] else: @@ -1198,4 +1198,3 @@ def sympy_set_to_list(set, vars): if isinstance(set, Union): return [sympy_set_to_list(iv, vars) for iv in set._args] return set - diff --git a/src/sage/libs/gap/all.py b/src/sage/libs/gap/all.py index fd40910d9e7..e69de29bb2d 100644 --- a/src/sage/libs/gap/all.py +++ b/src/sage/libs/gap/all.py @@ -1,4 +0,0 @@ - - - - diff --git a/src/sage/libs/gap/test_long.py b/src/sage/libs/gap/test_long.py index 5a929ab4585..262db5ad287 100644 --- a/src/sage/libs/gap/test_long.py +++ b/src/sage/libs/gap/test_long.py @@ -3,7 +3,6 @@ These stress test the garbage collection inside GAP """ - from sage.libs.gap.libgap import libgap @@ -26,8 +25,8 @@ def test_loop_2(): sage: from sage.libs.gap.test_long import test_loop_2 sage: test_loop_2() # long time (10s on sage.math, 2013) """ - G =libgap.FreeGroup(2) - a,b = G.GeneratorsOfGroup() + G = libgap.FreeGroup(2) + a, b = G.GeneratorsOfGroup() for i in range(100): rel = libgap([a**2, b**2, a*b*a*b]) H = G / rel @@ -47,12 +46,9 @@ def test_loop_3(): sage: test_loop_3() # long time (31s on sage.math, 2013) """ G = libgap.FreeGroup(2) - (a,b) = G.GeneratorsOfGroup() + a, b = G.GeneratorsOfGroup() for i in range(300000): - lis=libgap([]) + lis = libgap([]) lis.Add(a ** 2) lis.Add(b ** 2) lis.Add(b * a) - - - diff --git a/src/sage/libs/lrcalc/lrcalc.py b/src/sage/libs/lrcalc/lrcalc.py index b541bfacd89..cf50d52a927 100644 --- a/src/sage/libs/lrcalc/lrcalc.py +++ b/src/sage/libs/lrcalc/lrcalc.py @@ -192,6 +192,7 @@ from sage.rings.integer import Integer import lrcalc + def _lrcalc_dict_to_sage(result): r""" Translate from lrcalc output format to Sage expected format. @@ -517,4 +518,3 @@ def lrskew(outer, inner, weight=None, maxrows=-1): w[j] += 1 if w == wt: yield ST.from_shape_and_word(shape, [i+1 for i in data]) - diff --git a/src/sage/libs/mpmath/all.py b/src/sage/libs/mpmath/all.py index c8d60c91142..cae40f79314 100644 --- a/src/sage/libs/mpmath/all.py +++ b/src/sage/libs/mpmath/all.py @@ -14,13 +14,12 @@ # Use mpmath internal functions for constants, to avoid unnecessary overhead _constants_funcs = { - 'glaisher' : glaisher_fixed, - 'khinchin' : khinchin_fixed, - 'twinprime' : twinprime_fixed, - 'mertens' : mertens_fixed + 'glaisher': glaisher_fixed, + 'khinchin': khinchin_fixed, + 'twinprime': twinprime_fixed, + 'mertens': mertens_fixed } def eval_constant(name, ring): prec = ring.precision() + 20 return ring(_constants_funcs[name](prec)) >> prec - diff --git a/src/sage/logic/logicparser.py b/src/sage/logic/logicparser.py index 08d9eb9c90d..b854f416127 100644 --- a/src/sage/logic/logicparser.py +++ b/src/sage/logic/logicparser.py @@ -82,7 +82,7 @@ # 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/ #***************************************************************************** import string @@ -703,5 +703,3 @@ def apply_func(tree, func): lval = tree[1] rval = tree[2] return func([tree[0], lval, rval]) - - From b19a6aa1e86fa17a4d4c1ee0934780126ead2fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 7 Aug 2022 10:43:28 +0200 Subject: [PATCH 140/454] fix in crystal of letters --- src/sage/combinat/crystals/letters.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index ccd57d461bf..8eec7d22067 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -167,7 +167,7 @@ class ClassicalCrystalOfLetters(UniqueRepresentation, Parent): C = CrystalOfNakajimaMonomials(cartan_type, la) hw = C.highest_weight_vector() self.module_generators = (self._element_constructor_(hw),) - self._list = [x for x in super().__iter__()] + self._list = list(super(ClassicalCrystalOfLetters, self).__iter__()) elif cartan_type.type() == 'F': from sage.combinat.crystals.monomial_crystals import CrystalOfNakajimaMonomials from sage.combinat.root_system.root_system import RootSystem @@ -175,7 +175,7 @@ class ClassicalCrystalOfLetters(UniqueRepresentation, Parent): C = CrystalOfNakajimaMonomials(cartan_type, la) hw = C.highest_weight_vector() self.module_generators = (self._element_constructor_(hw),) - self._list = [x for x in super().__iter__()] + self._list = list(super(ClassicalCrystalOfLetters, self).__iter__()) else: self.module_generators = (self._element_constructor_(1),) if cartan_type.type() == 'G': From d4bf07b52c849cbe8e3ec2782994beb7e156d121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 7 Aug 2022 19:04:49 +0200 Subject: [PATCH 141/454] fix wrong change --- src/sage/combinat/crystals/monomial_crystals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 9accb0195e5..5179ffd1a76 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -1252,6 +1252,6 @@ def cardinality(self): """ if not self.cartan_type().is_finite(): return Infinity - return super().cardinality() + return super(InfinityCrystalOfNakajimaMonomials, self).cardinality() Element = CrystalOfNakajimaMonomialsElement From 7358dc2e601061c28ad2d0a1cfbb16c6ce317f8c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 8 Aug 2022 19:34:34 +0200 Subject: [PATCH 142/454] 34282: dd spkg='texlive', refactoring, pycodestyle --- src/sage/features/latex.py | 53 ++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index 733a75df9d7..cf65aea6afc 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -16,8 +16,10 @@ from sage.features.join_feature import JoinFeature latex_url = 'https://www.latex-project.org/' +latex_spkg = 'texlive' -class latex(Executable): + +class LaTeX(Executable): r""" A :class:`~sage.features.Feature` describing the presence of ``latex`` @@ -27,7 +29,7 @@ class latex(Executable): sage: latex().is_present() # optional - latex FeatureTestResult('latex', True) """ - def __init__(self): + def __init__(self, name): r""" TESTS:: @@ -35,7 +37,7 @@ def __init__(self): sage: isinstance(latex(), latex) True """ - Executable.__init__(self, "latex", executable="latex", url=latex_url) + Executable.__init__(self, name, executable=name, spkg=latex_spkg, url=latex_url) def is_functional(self): r""" @@ -64,7 +66,7 @@ def is_functional(self): # running latex from subprocess import run - cmd = ['latex', '-interaction=nonstopmode', filename_tex] + cmd = [self.name, '-interaction=nonstopmode', filename_tex] cmd = ' '.join(cmd) result = run(cmd, shell=True, cwd=base, capture_output=True, text=True) @@ -73,10 +75,32 @@ def is_functional(self): return FeatureTestResult(self, True) else: return FeatureTestResult(self, False, reason="Running latex on " - "a sample file returned non-zero " - "exit status {}".format(result.returncode)) + "a sample file returned non-zero " + "exit status {}".format(result.returncode)) + + +class latex(LaTeX): + r""" + A :class:`~sage.features.Feature` describing the presence of ``latex`` + + EXAMPLES:: + + sage: from sage.features.latex import latex + sage: latex().is_present() # optional - latex + FeatureTestResult('latex', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.latex import latex + sage: isinstance(latex(), latex) + True + """ + LaTeX.__init__(self, "latex") -class pdflatex(Executable): + +class pdflatex(LaTeX): r""" A :class:`~sage.features.Feature` describing the presence of ``pdflatex`` @@ -94,9 +118,10 @@ def __init__(self): sage: isinstance(pdflatex(), pdflatex) True """ - Executable.__init__(self, "pdflatex", executable="pdflatex", url=latex_url) + LaTeX.__init__(self, "pdflatex") + -class xelatex(Executable): +class xelatex(LaTeX): r""" A :class:`~sage.features.Feature` describing the presence of ``xelatex`` @@ -114,10 +139,10 @@ def __init__(self): sage: isinstance(xelatex(), xelatex) True """ - Executable.__init__(self, "xelatex", executable="xelatex", url=latex_url) + LaTeX.__init__(self, "xelatex") -class lualatex(Executable): +class lualatex(LaTeX): r""" A :class:`~sage.features.Feature` describing the presence of ``lualatex`` @@ -135,7 +160,7 @@ def __init__(self): sage: isinstance(lualatex(), lualatex) True """ - Executable.__init__(self, "lualatex", executable="lualatex", url=latex_url) + LaTeX.__init__(self, "lualatex") class TeXFile(StaticFile, JoinFeature): @@ -158,7 +183,8 @@ def __init__(self, name, filename, **kwds): sage: LaTeXPackage("tkz-graph")._features [Feature('pdflatex')] """ - JoinFeature.__init__(self, name, [pdflatex()], url=latex_url) # see :trac:`34282` + JoinFeature.__init__(self, name, [pdflatex()], + spkg=latex_spkg, url=latex_url) # see :trac:`34282` StaticFile.__init__(self, name, filename, search_path=[], **kwds) def absolute_filename(self) -> str: @@ -199,6 +225,7 @@ def _is_present(self): return test return super(TeXFile, self)._is_present() + class LaTeXPackage(TeXFile): r""" A :class:`sage.features.Feature` describing the presence of a LaTeX package From 9bf5ba9ca610bcd9178c1a9574a8640d9fa5d444 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 9 Aug 2022 13:49:21 +0200 Subject: [PATCH 143/454] trac #34313: clean strongly_regular_db.pyx - part 3 --- src/sage/graphs/strongly_regular_db.pyx | 230 ++++++++++++------------ 1 file changed, 119 insertions(+), 111 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 027011ac677..53864cb24ee 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -532,6 +532,7 @@ def is_goethals_seidel(int v, int k, int l, int mu): from sage.graphs.generators.families import GoethalsSeidelGraph return [GoethalsSeidelGraph, k_bibd, r_bibd] + @cached_function def is_NOodd(int v, int k, int l, int mu): r""" @@ -587,20 +588,21 @@ def is_NOodd(int v, int k, int l, int mu): return r += 1 s += 1 - if abs(r)>abs(s): + if abs(r) > abs(s): (r, s) = (s, r) # r=-eq^(n-1) s= eq^(n-1)(q-2) q = 2 - s//r p, t = is_prime_power(q, get_data=True) pp, kk = is_prime_power(abs(r), get_data=True) - if p == pp and t != 0: - n = kk//t + 1 - e = 1 if v == (q**n)*(q**n+1)//2 else -1 - if (v == (q**n)*(q**n+e)//2 and - k == (q**n-e)*(q**(n-1)+e) and - l == 2*(q**(2*n-2)-1)+e*q**(n-1)*(q-1) and - mu == 2*q**(n-1)*(q**(n-1)+e)): + if p == pp and t: + n = kk//t + 1 + e = 1 if v == (q**n)*(q**n + 1)//2 else -1 + if (v == (q**n)*(q**n + e)//2 and + k == (q**n - e)*(q**(n - 1) + e) and + l == 2*(q**(2*n - 2) - 1) + e*q**(n - 1)*(q - 1) and + mu == 2*q**(n - 1)*(q**(n - 1) + e)): from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph - return (NonisotropicOrthogonalPolarGraph, 2*n+1, q, '+' if e==1 else '-') + return (NonisotropicOrthogonalPolarGraph, 2*n + 1, q, '+' if e == 1 else '-') + @cached_function def is_NOperp_F5(int v, int k, int l, int mu): @@ -644,18 +646,19 @@ def is_NOperp_F5(int v, int k, int l, int mu): r, s = eigenvalues(v, k, l, mu) # 2*e*5**(n-1), -e*5**(n-1); note exceptional case n=1 if r is None: return - if abs(r)0 else -1 + e = 1 if r > 0 else -1 p, n = is_prime_power(abs(r), get_data=True) - if (3 == p and n != 0): + if 3 == p and n: n += 1 - if (v == 3**(n-1)*(3**n-e)//2 and - k == 3**(n-1)*(3**(n-1)-e)//2 and - l == 3**(n-2)*(3**(n-1)+e)//2 and - mu == 3**(n-1)*(3**(n-2)-e)//2): + if (v == 3**(n - 1)*(3**n - e)//2 and + k == 3**(n - 1)*(3**(n - 1) - e)//2 and + l == 3**(n - 2)*(3**(n - 1) + e)//2 and + mu == 3**(n - 1)*(3**(n - 2) - e)//2): from sage.graphs.generators.classical_geometries import NonisotropicOrthogonalPolarGraph - return (NonisotropicOrthogonalPolarGraph, 2*n, 3, '+' if e==1 else '-') + return (NonisotropicOrthogonalPolarGraph, 2*n, 3, '+' if e == 1 else '-') + @cached_function def is_NU(int v, int k, int l, int mu): @@ -805,29 +810,30 @@ def is_NU(int v, int k, int l, int mu): return r += 1 s += 1 - if abs(r)>abs(s): + if abs(r) > abs(s): (r, s) = (s, r) p, t = is_prime_power(abs(r), get_data=True) - if p==2: # it can be that q=2, then we'd have r>s now + if p == 2: # it can be that q=2, then we'd have r>s now pp, kk = is_prime_power(abs(s), get_data=True) - if pp==2 and kk>0: + if pp == 2 and kk > 0: (r, s) = (s, r) p, t = is_prime_power(abs(r), get_data=True) - if r==1: + if r == 1: return - kr = k//(r-1) # eq^{n-1}+1 - e = 1 if kr>0 else -1 + kr = k//(r-1) # eq^{n-1}+1 + e = 1 if kr > 0 else -1 q = (kr-1)//r pp, kk = is_prime_power(q, get_data=True) - if p == pp and kk != 0: - n = t//kk + 2 - if (v == q**(n-1)*(q**n - e)//(q + 1) and - k == (q**(n-1) + e)*(q**(n-2) - e) and - l == q**(2*n-5)*(q+1) - e*q**(n-2)*(q-1) - 2 and - mu == q**(n-3)*(q + 1)*(q**(n-2) - e)): + if p == pp and kk: + n = t//kk + 2 + if (v == q**(n - 1)*(q**n - e)//(q + 1) and + k == (q**(n - 1) + e)*(q**(n - 2) - e) and + l == q**(2*n - 5)*(q + 1) - e*q**(n - 2)*(q - 1) - 2 and + mu == q**(n - 3)*(q + 1)*(q**(n - 2) - e)): from sage.graphs.generators.classical_geometries import NonisotropicUnitaryPolarGraph return (NonisotropicUnitaryPolarGraph, n, q) + @cached_function def is_haemers(int v, int k, int l, int mu): r""" @@ -862,13 +868,14 @@ def is_haemers(int v, int k, int l, int mu): cdef int q, n, p p, n = is_prime_power(mu, get_data=True) q = mu - if 2 == p and n != 0: - if (v == q**2*(q+2) and - k == q*(q+1)-1 and - l == q-2): + if 2 == p and n: + if (v == q**2*(q + 2) and + k == q*(q + 1) - 1 and + l == q - 2): from sage.graphs.generators.classical_geometries import HaemersGraph return (HaemersGraph, q) + @cached_function def is_cossidente_penttila(int v, int k, int l, int mu): r""" @@ -905,15 +912,16 @@ def is_cossidente_penttila(int v, int k, int l, int mu): sage: t = is_cossidente_penttila(5,5,5,5); t """ cdef int q, n, p - q = 2*l+3 + q = 2*l + 3 p, n = is_prime_power(q, get_data=True) - if 2 < p and n != 0: - if (v == (q**3+1)*(q+1)//2 and - k == (q**2+1)*(q-1)//2 and - mu == (q-1)**2//2): + if 2 < p and n: + if (v == (q**3 + 1)*(q + 1)//2 and + k == (q**2 + 1)*(q - 1)//2 and + mu == (q - 1)**2//2): from sage.graphs.generators.classical_geometries import CossidentePenttilaGraph return (CossidentePenttilaGraph, q) + @cached_function def is_complete_multipartite(int v, int k, int l, int mu): r""" @@ -952,9 +960,9 @@ def is_complete_multipartite(int v, int k, int l, int mu): sage: g.is_strongly_regular(parameters=True) (20, 16, 12, 16) """ - if v>k: - r = v//(v-k) # number of parts (of size v-k each) - if l==(v-k)*(r-2) and k==mu and v == r*(v-k): + if v > k: + r = v//(v - k) # number of parts (of size v-k each) + if l == (v - k)*(r - 2) and k == mu and v == r*(v - k): from sage.graphs.generators.basic import CompleteMultipartiteGraph def CompleteMultipartiteSRG(nparts, partsize): @@ -1005,10 +1013,10 @@ def is_polhill(int v, int k, int l, int mu): [. at ...>] """ if (v, k, l, mu) not in [(1024, 231, 38, 56), - (1024, 264, 56, 72), - (1024, 297, 76, 90), - (1024, 330, 98, 110), - (1024, 462, 206, 210)]: + (1024, 264, 56, 72), + (1024, 297, 76, 90), + (1024, 330, 98, 110), + (1024, 462, 206, 210)]: return from itertools import product @@ -1019,7 +1027,7 @@ def is_polhill(int v, int k, int l, int mu): def additive_cayley(vertices): g = Graph() g.add_vertices(vertices[0].parent()) - edges = [(x,x+vv) + edges = [(x, x + vv) for vv in set(vertices) for x in g] g.add_edges(edges) @@ -1027,21 +1035,19 @@ def is_polhill(int v, int k, int l, int mu): return g # D is a Partial Difference Set of (Z4)^2, see section 2. - G = cartesian_product([IntegerModRing(4),IntegerModRing(4)]) - D = [ - [(2,0),(0,1),(0,3),(1,1),(3,3)], - [(1,0),(3,0),(0,2),(1,3),(3,1)], - [(1,2),(3,2),(2,1),(2,3),(2,2)] - ] + G = cartesian_product([IntegerModRing(4), IntegerModRing(4)]) + D = [[(2, 0), (0, 1), (0, 3), (1, 1), (3, 3)], + [(1, 0), (3, 0), (0, 2), (1, 3), (3, 1)], + [(1, 2), (3, 2), (2, 1), (2, 3), (2, 2)]] D = [[G(e) for e in x] for x in D] # The K_i are hyperplanes partitionning the nonzero elements of # GF(2^s)^2. See section 6. s = 3 G1 = GF(2**s,'x') - Gp = cartesian_product([G1,G1]) - K = [Gp((x,1)) for x in G1]+[Gp((1,0))] - K = [[x for x in Gp if x[0]*uu+x[1]*vv == 0] for (uu,vv) in K] + Gp = cartesian_product([G1, G1]) + K = [Gp((x, 1)) for x in G1] + [Gp((1, 0))] + K = [[x for x in Gp if x[0]*uu + x[1]*vv == 0] for (uu, vv) in K] # We now define the P_{i,j}. see section 6. @@ -1066,15 +1072,15 @@ def is_polhill(int v, int k, int l, int mu): P[2,4] = list(xrange((-1) + 2**(s-2)+3 , 2**(s-1)+1)) + [2**(s-1)+2**(s-2)+1,1] P[3,4] = list(xrange((-1) + 2**(s-1)+3 , 2**(s-1)+2**(s-2)+1)) + [2**(s-2)+1,0] - R = {x:copy(P[x]) for x in P} + R = {x: copy(P[x]) for x in P} for x in P: P[x] = [K[i] for i in P[x]] - P[x] = set(sum(P[x],[])).difference([Gp((0,0))]) + P[x] = set(sum(P[x], [])).difference([Gp((0, 0))]) - P[1,4].add(Gp((0,0))) - P[2,4].add(Gp((0,0))) - P[3,4].add(Gp((0,0))) + P[1, 4].add(Gp((0, 0))) + P[2, 4].add(Gp((0, 0))) + P[3, 4].add(Gp((0, 0))) # We now define the R_{i,j}. see *end* of section 6. @@ -1085,39 +1091,40 @@ def is_polhill(int v, int k, int l, int mu): for x in R: R[x] = [K[i] for i in R[x]] - R[x] = set(sum(R[x],[])).difference([Gp((0,0))]) + R[x] = set(sum(R[x], [])).difference([Gp((0, 0))]) - R[1,3].add(Gp((0,0))) - R[2,4].add(Gp((0,0))) - R[3,4].add(Gp((0,0))) + R[1, 3].add(Gp((0, 0))) + R[2, 4].add(Gp((0, 0))) + R[3, 4].add(Gp((0, 0))) # Dabcd = Da, Db, Dc, Dd (cf. p273) # D1234 = D1, D2, D3, D4 (cf. p276) Dabcd = [] D1234 = [] - Gprod = cartesian_product([G,Gp]) - for DD,PQ in [(Dabcd,P), (D1234,R)]: - for i in range(1,5): - Dtmp = [product([G.zero()],PQ[0,i]), - product(D[0],PQ[1,i]), - product(D[1],PQ[2,i]), - product(D[2],PQ[3,i])] + Gprod = cartesian_product([G, Gp]) + for DD,PQ in [(Dabcd, P), (D1234, R)]: + for i in range(1, 5): + Dtmp = [product([G.zero()], PQ[0, i]), + product(D[0], PQ[1, i]), + product(D[1], PQ[2, i]), + product(D[2], PQ[3, i])] Dtmp = map(set, Dtmp) Dtmp = [Gprod(e) for e in sum(map(list, Dtmp), [])] DD.append(Dtmp) # Now that we have the data, we can return the graphs. if k == 231: - return [lambda :additive_cayley(Dabcd[0])] + return [lambda: additive_cayley(Dabcd[0])] if k == 264: - return [lambda :additive_cayley(D1234[2])] + return [lambda: additive_cayley(D1234[2])] if k == 297: - return [lambda :additive_cayley(D1234[0]+D1234[1]+D1234[2]).complement()] + return [lambda: additive_cayley(D1234[0] + D1234[1] + D1234[2]).complement()] if k == 330: - return [lambda :additive_cayley(Dabcd[0]+Dabcd[1]+Dabcd[2]).complement()] + return [lambda: additive_cayley(Dabcd[0] + Dabcd[1] + Dabcd[2]).complement()] if k == 462: - return [lambda :additive_cayley(Dabcd[0]+Dabcd[1])] + return [lambda: additive_cayley(Dabcd[0] + Dabcd[1])] + def is_RSHCD(int v, int k, int l, int mu): r""" @@ -1143,11 +1150,11 @@ def is_RSHCD(int v, int k, int l, int mu): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 27, 10, 12) - """ if SRG_from_RSHCD(v, k, l, mu, existence=True) is True: return [SRG_from_RSHCD, v, k, l, mu] + def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): r""" Return a `(v,k,l,mu)`-strongly regular graph from a RSHCD @@ -1195,29 +1202,29 @@ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): Traceback (most recent call last): ... ValueError: I do not know how to build a (784, 0, 14, 38)-SRG from a RSHCD - """ from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal - sgn = lambda x: 1 if x>=0 else -1 + sgn = lambda x: 1 if x >= 0 else -1 n = v a = (n-4*mu)//2 e = 2*k - n + 1 + a t = abs(a//2) - if (e**2 == 1 and - k == (n-1-a+e)/2 and - l == (n-2*a)/4 - (1-e) and - mu== (n-2*a)/4 and - regular_symmetric_hadamard_matrix_with_constant_diagonal(n,sgn(a)*e,existence=True) is True): + if (e**2 == 1 and + k == (n-1-a+e)/2 and + l == (n-2*a)/4 - (1-e) and + mu== (n-2*a)/4 and + regular_symmetric_hadamard_matrix_with_constant_diagonal(n, sgn(a)*e, existence=True) is True): if existence: return True from sage.matrix.constructor import identity_matrix as I - from sage.matrix.constructor import ones_matrix as J + from sage.matrix.constructor import ones_matrix as J - H = regular_symmetric_hadamard_matrix_with_constant_diagonal(n,sgn(a)*e) + H = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, sgn(a)*e) if list(H.column(0)[1:]).count(1) == k: H = -H - G = Graph((J(n)-I(n)-H+H[0,0]*I(n))/2,loops=False,multiedges=False,format="adjacency_matrix") + G = Graph((J(n) - I(n) - H + H[0, 0]*I(n)) / 2, + loops=False, multiedges=False, format="adjacency_matrix") if check: assert G.is_strongly_regular(parameters=True) == (v, k, l, mu) return G @@ -1226,6 +1233,7 @@ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): return False raise ValueError("I do not know how to build a {}-SRG from a RSHCD".format((v, k, l, mu))) + @cached_function def is_unitary_polar(int v, int k, int l, int mu): r""" @@ -1273,7 +1281,7 @@ def is_unitary_polar(int v, int k, int l, int mu): q = k//mu if q*mu != k or q < 2: return - p,t = is_prime_power(q, get_data=True) + p, t = is_prime_power(q, get_data=True) if p**t != q or t % 2: return # at this point we know that we should have U(n,q) for some n and q=p^t, t even @@ -1281,22 +1289,22 @@ def is_unitary_polar(int v, int k, int l, int mu): q_pow_d_minus_one = r+1 else: q_pow_d_minus_one = -s-1 - ppp,ttt = is_prime_power(q_pow_d_minus_one, get_data=True) + ppp, ttt = is_prime_power(q_pow_d_minus_one, get_data=True) d = ttt//t + 1 if ppp != p or (d-1)*t != ttt: return t //= 2 # U(2d+1,q); write q^(1/2) as p^t - if (v == (q**d - 1)*((q**d)*p**t + 1)//(q - 1) and - k == q*(q**(d-1) - 1)*((q**d)//(p**t) + 1)//(q - 1) and - l == q*q*(q**(d-2)-1)*((q**(d-1))//(p**t) + 1)//(q - 1) + q - 1): + if (v == (q**d - 1)*((q**d)*p**t + 1)//(q - 1) and + k == q*(q**(d-1) - 1)*((q**d)//(p**t) + 1)//(q - 1) and + l == q*q*(q**(d-2)-1)*((q**(d-1))//(p**t) + 1)//(q - 1) + q - 1): from sage.graphs.generators.classical_geometries import UnitaryPolarGraph return (UnitaryPolarGraph, 2*d+1, p**t) # U(2d,q); - if (v == (q**d - 1)*((q**d)//(p**t) + 1)//(q - 1) and - k == q*(q**(d-1) - 1)*((q**(d-1))//(p**t) + 1)//(q - 1) and - l == q*q*(q**(d-2)-1)*((q**(d-2))//(p**t) + 1)//(q - 1) + q - 1): + if (v == (q**d - 1)*((q**d)//(p**t) + 1)//(q - 1) and + k == q*(q**(d-1) - 1)*((q**(d-1))//(p**t) + 1)//(q - 1) and + l == q*q*(q**(d-2)-1)*((q**(d-2))//(p**t) + 1)//(q - 1) + q - 1): from sage.graphs.generators.classical_geometries import UnitaryPolarGraph return (UnitaryPolarGraph, 2*d, p**t) From f2323c01dd8ff975546f648d4e1058b995d08ac2 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 9 Aug 2022 14:29:21 +0200 Subject: [PATCH 144/454] trac #34313: extra care --- src/sage/graphs/strongly_regular_db.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 53864cb24ee..a4eff7ecff8 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1204,7 +1204,8 @@ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): ValueError: I do not know how to build a (784, 0, 14, 38)-SRG from a RSHCD """ from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal - sgn = lambda x: 1 if x >= 0 else -1 + def sgn(x): + return 1 if x >= 0 else -1 n = v a = (n-4*mu)//2 e = 2*k - n + 1 + a @@ -1649,7 +1650,7 @@ def is_switch_OA_srg(int v, int k, int l, int mu): cdef int n_2_p_1 = v cdef int n = floor(sqrt(n_2_p_1 - 1)) - if n*n != n_2_p_1-1: # is it a square? + if n*n != n_2_p_1 - 1: # is it a square? return None cdef int c = k//n From a0814891a6d55359c1a4de5bb6b6124bac429208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 9 Aug 2022 18:16:26 +0200 Subject: [PATCH 145/454] fix E251 in schemes --- src/sage/schemes/berkovich/berkovich_space.py | 4 +-- src/sage/schemes/curves/affine_curve.py | 10 +++--- src/sage/schemes/curves/projective_curve.py | 6 ++-- .../elliptic_curves/descent_two_isogeny.pyx | 13 ++++---- .../elliptic_curves/ell_rational_field.py | 14 ++++---- src/sage/schemes/elliptic_curves/heegner.py | 10 ++---- src/sage/schemes/elliptic_curves/height.py | 2 +- .../schemes/elliptic_curves/isogeny_class.py | 13 ++++---- .../schemes/elliptic_curves/mod_sym_num.pyx | 19 +++++------ .../schemes/elliptic_curves/padic_lseries.py | 8 ++--- src/sage/schemes/elliptic_curves/padics.py | 12 ++++--- .../schemes/elliptic_curves/period_lattice.py | 9 +++--- .../schemes/elliptic_curves/saturation.py | 10 +++--- src/sage/schemes/generic/scheme.py | 4 +-- .../hyperelliptic_curves/constructor.py | 7 ++-- .../hyperelliptic_generic.py | 4 +-- .../hyperelliptic_padic_field.py | 4 +-- src/sage/schemes/plane_conics/con_field.py | 32 +++++++++---------- src/sage/schemes/product_projective/space.py | 8 ++--- .../schemes/product_projective/subscheme.py | 2 +- .../projective/projective_subscheme.py | 11 ++++--- 21 files changed, 100 insertions(+), 102 deletions(-) diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index c19b8bca9f8..73369fda8d4 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -647,11 +647,11 @@ def __init__(self, base, ideal=None): ideal = None self._base_type = 'padic field' if base.dimension_relative() != 1: - raise ValueError("base of projective Berkovich space must be " + \ + raise ValueError("base of projective Berkovich space must be " "projective space of dimension 1 over Qp or a number field") self._p = prime self._ideal = ideal - Parent.__init__(self, base = base, category=TopologicalSpaces()) + Parent.__init__(self, base=base, category=TopologicalSpaces()) def base_ring(self): r""" diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index f09cde624eb..4c33b81ac35 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -395,7 +395,7 @@ def local_coordinates(self, pt, n): y0 = F(pt[1]) astr = ["a"+str(i) for i in range(1,2*n)] x,y = R.gens() - R0 = PolynomialRing(F,2*n+2,names = [str(x),str(y),"t"]+astr) + R0 = PolynomialRing(F, 2 * n + 2, names=[str(x), str(y), "t"] + astr) vars0 = R0.gens() t = vars0[2] yt = y0*t**0+add([vars0[i]*t**(i-2) for i in range(3,2*n+2)]) @@ -1930,15 +1930,13 @@ def rational_points(self, algorithm="enum"): return sorted(set(pnts)) elif algorithm == "all": - - S_enum = self.rational_points(algorithm = "enum") - S_bn = self.rational_points(algorithm = "bn") + S_enum = self.rational_points(algorithm="enum") + S_bn = self.rational_points(algorithm="bn") if S_enum != S_bn: raise RuntimeError("Bug in rational_points -- different algorithms give different answers for curve %s!" % self) return S_enum - else: - raise ValueError("No algorithm '%s' known"%algorithm) + raise ValueError("No algorithm '%s' known" % algorithm) class IntegralAffineCurve(AffineCurve_field): diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 529721d8958..84a4117749a 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -704,7 +704,7 @@ def local_coordinates(self, pt, n): y0 = F(pt[1]) astr = ["a"+str(i) for i in range(1,2*n)] x,y = R.gens() - R0 = PolynomialRing(F,2*n+2,names = [str(x),str(y),"t"]+astr) + R0 = PolynomialRing(F, 2 * n + 2, names=[str(x), str(y), "t"] + astr) vars0 = R0.gens() t = vars0[2] yt = y0*t**0 + add([vars0[i]*t**(i-2) for i in range(3,2*n+2)]) @@ -2148,8 +2148,8 @@ def rational_points(self, algorithm="enum", sort=True): if algorithm == "bn": return self._points_via_singular(sort=sort) elif algorithm == "all": - S_enum = self.rational_points(algorithm = "enum") - S_bn = self.rational_points(algorithm = "bn") + S_enum = self.rational_points(algorithm="enum") + S_bn = self.rational_points(algorithm="bn") if S_enum != S_bn: raise RuntimeError("Bug in rational_points -- different\ algorithms give different answers for\ diff --git a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx index af82dd3d91c..0c3a7a72385 100644 --- a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx +++ b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx @@ -1045,10 +1045,10 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len return 0 def two_descent_by_two_isogeny(E, - int global_limit_small = 10, - int global_limit_large = 10000, - int verbosity = 0, - bint selmer_only = 0, bint proof = 1): + int global_limit_small=10, + int global_limit_large=10000, + int verbosity=0, + bint selmer_only=0, bint proof=1): """ Given an elliptic curve E with a two-isogeny phi : E --> E' and dual isogeny phi', runs a two-isogeny descent on E, returning n1, n2, n1' and n2'. Here @@ -1148,9 +1148,10 @@ def two_descent_by_two_isogeny(E, return two_descent_by_two_isogeny_work(c, d, global_limit_small, global_limit_large, verbosity, selmer_only, proof) + def two_descent_by_two_isogeny_work(Integer c, Integer d, - int global_limit_small = 10, int global_limit_large = 10000, - int verbosity = 0, bint selmer_only = 0, bint proof = 1): + int global_limit_small=10, int global_limit_large=10000, + int verbosity=0, bint selmer_only=0, bint proof=1): """ Do all the work in doing a two-isogeny descent. diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 38088228122..fc54013b7ba 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1095,7 +1095,7 @@ def _modular_symbol_normalize(self, sign, normalize, implementation, nap): raise ValueError("Implementation should be one of 'sage', 'num' or 'eclib'") return (sign, normalize, implementation, nap) - @cached_method(key = _modular_symbol_normalize) + @cached_method(key=_modular_symbol_normalize) def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0): r""" Return the modular symbol map associated to this elliptic curve @@ -1964,7 +1964,7 @@ def three_selmer_rank(self, algorithm='UseSUnits'): """ from sage.interfaces.magma import magma E = magma(self) - return Integer(E.ThreeSelmerGroup(MethodForFinalStep = magma('"%s"'%algorithm)).Ngens()) + return Integer(E.ThreeSelmerGroup(MethodForFinalStep=magma('"%s"' % algorithm)).Ngens()) def rank(self, use_database=True, verbose=False, only_use_mwrank=True, @@ -2301,8 +2301,8 @@ def _compute_gens(self, proof, if not only_use_mwrank: try: verbose_verbose("Trying to compute rank.") - r = self.rank(only_use_mwrank = False) - verbose_verbose("Got r = %s."%r) + r = self.rank(only_use_mwrank=False) + verbose_verbose("Got r = %s." % r) if r == 0: verbose_verbose("Rank = 0, so done.") return [], True @@ -2438,7 +2438,7 @@ def ngens(self, proof=None): Generator 1 is [29604565304828237474403861024284371796799791624792913256602210:-256256267988926809388776834045513089648669153204356603464786949:490078023219787588959802933995928925096061616470779979261000]; height 95.98037... Regulator = 95.980... """ - return len(self.gens(proof = proof)) + return len(self.gens(proof=proof)) def regulator(self, proof=None, precision=53, **kwds): r""" @@ -2646,7 +2646,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): from sage.libs.eclib.all import mwrank_MordellWeil mw = mwrank_MordellWeil(c, verbose) mw.process(v) # by default, this does no saturation yet - ok, index, unsat = mw.saturate(max_prime=max_prime, min_prime = min_prime) + ok, index, unsat = mw.saturate(max_prime=max_prime, min_prime=min_prime) if not ok: print("Failed to saturate failed at the primes {}".format(unsat)) sat = [Emin(P) for P in mw.points()] @@ -6630,7 +6630,7 @@ def S_integral_x_coords_with_abs_bounded_by(abs_bound): M = U.transpose()*M*U # NB "lambda" is a reserved word in Python! - lamda = min(M.charpoly(algorithm="hessenberg").roots(multiplicities = False)) + lamda = min(M.charpoly(algorithm="hessenberg").roots(multiplicities=False)) max_S = max(S) len_S += 1 #Counting infinity (always "included" in S) if verbose: diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 4701c66f5de..2b699fd6cf8 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -3520,7 +3520,7 @@ def point_exact(self, prec=53, algorithm='lll', var='a', optimize=False): M = K.extension(gg, names='b') y = M.gen()/dd x = M(x) - L = M.absolute_field(names = var) + L = M.absolute_field(names=var) phi = L.structure()[1] x = phi(x) y = phi(y) @@ -4364,13 +4364,9 @@ def mod(self, p, prec=53): # do actual calculation if self.conductor() == 1: - - P = self._trace_exact_conductor_1(prec = prec) + P = self._trace_exact_conductor_1(prec=prec) return E.change_ring(GF(p))(P) - - else: - - raise NotImplementedError + raise NotImplementedError ## def congruent_rational_point(self, n, prec=53): ## r""" diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index e948474c8b9..06d83bc7759 100644 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -1216,7 +1216,7 @@ def S(self, xi1, xi2, v): ([0.0781194447253472, 0.0823423732016403] U [0.917657626798360, 0.921880555274653]) """ L = self.E.period_lattice(v) - w1, w2 = L.basis(prec = v.codomain().prec()) + w1, w2 = L.basis(prec=v.codomain().prec()) beta = L.elliptic_exponential(w1/2)[0] if xi2 < beta: return UnionOfIntervals([]) diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index a5eb209bc69..bb5ae25a56e 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -403,7 +403,7 @@ def graph(self): from sage.graphs.graph import Graph if not self.E.base_field() is QQ: - M = self.matrix(fill = False) + M = self.matrix(fill=False) n = len(self) G = Graph(M, format='weighted_adjacency_matrix') D = dict([(v,self.curves[v]) for v in G.vertices(sort=False)]) @@ -416,11 +416,10 @@ def graph(self): G.relabel(list(range(1, n + 1))) return G - - M = self.matrix(fill = False) + M = self.matrix(fill=False) n = M.nrows() # = M.ncols() G = Graph(M, format='weighted_adjacency_matrix') - N = self.matrix(fill = True) + N = self.matrix(fill=True) D = dict([(v,self.curves[v]) for v in G.vertices(sort=False)]) # The maximum degree classifies the shape of the isogeny # graph, though the number of vertices is often enough. @@ -535,7 +534,8 @@ def reorder(self, order): return self if isinstance(order, str): if order == "lmfdb": - reordered_curves = sorted(self.curves, key = lambda E: E.a_invariants()) + reordered_curves = sorted(self.curves, + key=lambda E: E.a_invariants()) else: reordered_curves = list(self.E.isogeny_class(algorithm=order)) elif isinstance(order, (list, tuple, IsogenyClass_EC)): @@ -1068,7 +1068,8 @@ def _compute(self): raise RuntimeError("unable to find %s in the database" % self.E) # All curves will have the same conductor and isogeny class, # and there are most 8 of them, so lexicographic sorting is okay. - self.curves = tuple(sorted(curves, key = lambda E: E.cremona_label())) + self.curves = tuple(sorted(curves, + key=lambda E: E.cremona_label())) self._mat = None elif algorithm == "sage": curves = [self.E.minimal_model()] diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index dd452869e2e..8f50eff1e27 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -970,7 +970,7 @@ cdef class ModularSymbolNumerical: ans = self._evaluate_approx(ra, eps) if prec > self._om1.parent().prec(): - L = self._E.period_lattice().basis(prec = prec) + L = self._E.period_lattice().basis(prec=prec) self._om1 = L[0] self._om2 = L[1].imag() cinf = self._E.real_components() @@ -2156,10 +2156,8 @@ cdef class ModularSymbolNumerical: ans = su return CC(ans) - - def _from_r_to_rr_approx(self, Rational r, Rational rr, double eps, - method = None, int use_partials=2): + method=None, int use_partials=2): r""" Given a cusp `r` this computes the integral `\lambda(r\to r')` from `r` to `r'` to the given precision ``eps``. @@ -2313,7 +2311,7 @@ cdef class ModularSymbolNumerical: if method == "indirect" or method == "both": verbose(" using the indirect integration from %s to %s " - "with %s terms to sum"%(r, rr, T1+T2), level =2) + "with %s terms to sum"%(r, rr, T1+T2), level=2) #self.nc_indirect += 1 ans2 = ( self._from_ioo_to_r_approx(r, eps/2, use_partials=use_partials) @@ -2474,7 +2472,7 @@ cdef class ModularSymbolNumerical: # (key=lambda r,sign,use_partials:(r,sign)) lead to a compiler crash @cached_method - def _value_ioo_to_r(self, Rational r, int sign = 0, + def _value_ioo_to_r(self, Rational r, int sign=0, int use_partials=2): r""" Return `[r]^+` or `[r]^-` for a rational `r`. @@ -2532,7 +2530,7 @@ cdef class ModularSymbolNumerical: return self._round(lap, sign, True) @cached_method - def _value_r_to_rr(self, Rational r, Rational rr, int sign = 0, + def _value_r_to_rr(self, Rational r, Rational rr, int sign=0, int use_partials=2): r""" Return the rational number `[r']^+ - [r]^+`. However the @@ -2603,7 +2601,7 @@ cdef class ModularSymbolNumerical: return self._round(lap, sign, True) @cached_method - def transportable_symbol(self, Rational r, Rational rr, int sign = 0): + def transportable_symbol(self, Rational r, Rational rr, int sign=0): r""" Return the symbol `[r']^+ - [r]^+` where `r'=\gamma(r)` for some `\gamma\in\Gamma_0(N)`. These symbols can be computed by transporting @@ -2860,8 +2858,7 @@ cdef class ModularSymbolNumerical: res -= self._value_ioo_to_r(rr,sign, use_partials=2) return res - - def manin_symbol(self, llong u, llong v, int sign = 0): + def manin_symbol(self, llong u, llong v, int sign=0): r""" Given a pair `(u,v)` presenting a point in `\mathbb{P}^1(\mathbb{Z}/N\mathbb{Z})` and hence a coset of @@ -3755,7 +3752,7 @@ def _test_against_table(range_of_conductors, other_implementation="sage", list_o Mr = M(r) M2r = M(r, sign=-1) if verb: - print("r={} : ({},{}),({}, {})".format(r,mr,m2r,Mr,M2r), end= " ", flush=True) + print("r={} : ({},{}),({}, {})".format(r,mr,m2r,Mr,M2r), end=" ", flush=True) if mr != Mr or m2r != M2r: print (("B u g : curve = {}, cusp = {}, sage's symbols" + "({},{}), our symbols ({}, {})").format(C.label(), r, diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 3910da83476..f8e16b3407a 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -143,7 +143,7 @@ class pAdicLseries(SageObject): sage: lp == loads(dumps(lp)) True """ - def __init__(self, E, p, implementation = 'eclib', normalize='L_ratio'): + def __init__(self, E, p, implementation='eclib', normalize='L_ratio'): r""" INPUT: @@ -337,7 +337,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist=+1): return -sum([kronecker_symbol(D, u) * m(r + ZZ(u) / D) for u in range(1, -D)]) - def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): + def measure(self, a, n, prec, quadratic_twist=+1, sign=+1): r""" Return the measure on `\ZZ_p^{\times}` defined by @@ -1550,9 +1550,9 @@ def __phi_bpr(self, prec=0): print("Warning: Very large value for the precision.") if prec == 0: prec = floor((log(10000)/log(p))) - verbose("prec set to %s"%prec) + verbose("prec set to %s" % prec) eh = E.formal() - om = eh.differential(prec = p**prec+3) + om = eh.differential(prec=p**prec+3) verbose("differential computed") xt = eh.x(prec=p**prec + 3) et = xt*om diff --git a/src/sage/schemes/elliptic_curves/padics.py b/src/sage/schemes/elliptic_curves/padics.py index 509d94e9e33..d4b38156669 100644 --- a/src/sage/schemes/elliptic_curves/padics.py +++ b/src/sage/schemes/elliptic_curves/padics.py @@ -98,8 +98,10 @@ def _normalize_padic_lseries(self, p, normalize, implementation, precision): raise ValueError("Implementation should be one of 'sage', 'eclib', 'num' or 'pollackstevens'") return (p, normalize, implementation, precision) + @cached_method(key=_normalize_padic_lseries) -def padic_lseries(self, p, normalize = None, implementation = 'eclib', precision = None): +def padic_lseries(self, p, normalize=None, implementation='eclib', + precision=None): r""" Return the `p`-adic `L`-series of self at `p`, which is an object whose approx method computes @@ -209,16 +211,16 @@ def padic_lseries(self, p, normalize = None, implementation = 'eclib', precision if implementation in ['sage', 'eclib', 'num']: if self.ap(p) % p != 0: Lp = plseries.pAdicLseriesOrdinary(self, p, - normalize = normalize, implementation = implementation) + normalize=normalize, implementation=implementation) else: Lp = plseries.pAdicLseriesSupersingular(self, p, - normalize = normalize, implementation = implementation) + normalize=normalize, implementation=implementation) else: phi = self.pollack_stevens_modular_symbol(sign=0) if phi.parent().level() % p == 0: - Phi = phi.lift(p, precision, eigensymbol = True) + Phi = phi.lift(p, precision, eigensymbol=True) else: - Phi = phi.p_stabilize_and_lift(p, precision, eigensymbol = True) + Phi = phi.p_stabilize_and_lift(p, precision, eigensymbol=True) Lp = Phi.padic_lseries() #mm TODO should this pass precision on too ? Lp._cinf = self.real_components() return Lp diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 704fe7e4d0b..205c2ff12c1 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -802,7 +802,7 @@ def is_rectangular(self): return self.real_flag == +1 raise RuntimeError("Not defined for non-real lattices.") - def real_period(self, prec = None, algorithm='sage'): + def real_period(self, prec=None, algorithm='sage'): """ Return the real period of this period lattice. @@ -840,8 +840,9 @@ def real_period(self, prec = None, algorithm='sage'): return self.basis(prec,algorithm)[0] raise RuntimeError("Not defined for non-real lattices.") - def omega(self, prec = None, bsd_normalise = False): - r"""Return the real or complex volume of this period lattice. + def omega(self, prec=None, bsd_normalise=False): + r""" + Return the real or complex volume of this period lattice. INPUT: @@ -1014,7 +1015,7 @@ def complex_area(self, prec=None): w1,w2 = self.basis(prec) return (w1*w2.conjugate()).imag().abs() - def sigma(self, z, prec = None, flag=0): + def sigma(self, z, prec=None, flag=0): r""" Return the value of the Weierstrass sigma function for this elliptic curve period lattice. diff --git a/src/sage/schemes/elliptic_curves/saturation.py b/src/sage/schemes/elliptic_curves/saturation.py index 07b4738c079..1d75c076e53 100644 --- a/src/sage/schemes/elliptic_curves/saturation.py +++ b/src/sage/schemes/elliptic_curves/saturation.py @@ -674,10 +674,10 @@ def p_projections(Eq, Plist, p, debug=False): if debug: print("Cyclic case, taking dlogs to base {} of order {}".format(g,pp)) # logs are well-defined mod pp, hence mod p - v = [dlog(pt, g, ord = pp, operation = '+') for pt in pts] + v = [dlog(pt, g, ord=pp, operation='+') for pt in pts] if debug: print("dlogs: {}".format(v)) - return [vector(Fp,v)] + return [vector(Fp, v)] # We make no assumption about which generator order divides the # other, since conventions differ! @@ -700,5 +700,7 @@ def p_projections(Eq, Plist, p, debug=False): # logs are well-defined mod p1, hence mod p - return [vector(Fp, [dlog(pt.weil_pairing(g1,p2), zeta, ord = p1, operation = '*') for pt in pts]), - vector(Fp, [dlog(pt.weil_pairing(g2,p2), zeta, ord = p1, operation = '*') for pt in pts])] + return [vector(Fp, [dlog(pt.weil_pairing(g1,p2), zeta, + ord=p1, operation='*') for pt in pts]), + vector(Fp, [dlog(pt.weil_pairing(g2,p2), zeta, + ord=p1, operation='*') for pt in pts])] diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index 2793babda4a..10305c2ee99 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -126,9 +126,9 @@ def __init__(self, X=None, category=None): category = default_category else: assert category.is_subcategory(default_category), \ - "%s is not a subcategory of %s"%(category, default_category) + "%s is not a subcategory of %s" % (category, default_category) - Parent.__init__(self, self.base_ring(), category = category) + Parent.__init__(self, self.base_ring(), category=category) def union(self, X): """ diff --git a/src/sage/schemes/hyperelliptic_curves/constructor.py b/src/sage/schemes/hyperelliptic_curves/constructor.py index f5478623bdf..5aa3ad0abd4 100644 --- a/src/sage/schemes/hyperelliptic_curves/constructor.py +++ b/src/sage/schemes/hyperelliptic_curves/constructor.py @@ -260,14 +260,15 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): if g in genus_classes: superclass.append(genus_classes[g]) - cls_name.append("g%s"%g) + cls_name.append("g%s" % g) - for name,test,cls in fields: + for name, test, cls in fields: if test(R): superclass.append(cls) cls_name.append(name) break class_name = "_".join(cls_name) - cls = dynamic_class(class_name, tuple(superclass), HyperellipticCurve_generic, doccls = HyperellipticCurve) + cls = dynamic_class(class_name, tuple(superclass), + HyperellipticCurve_generic, doccls=HyperellipticCurve) return cls(PP, f, h, names=names, genus=g) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index b9d32c897b0..ad3b7d59a53 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -514,7 +514,7 @@ def local_coordinates_at_weierstrass(self, P, prec=20, name='t'): c -= (pol(c) - t2)/pol_prime(c) return (c, t.add_bigoh(prec)) - def local_coordinates_at_infinity(self, prec = 20, name = 't'): + def local_coordinates_at_infinity(self, prec=20, name='t'): """ For the genus `g` hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is @@ -569,7 +569,7 @@ def local_coordinates_at_infinity(self, prec = 20, name = 't'): y = x**g/t return x+O(t**(prec+2)) , y+O(t**(prec+2)) - def local_coord(self, P, prec = 20, name = 't'): + def local_coord(self, P, prec=20, name='t'): """ Calls the appropriate local_coordinates function diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py index f69234cdc04..373394b20af 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py @@ -702,9 +702,9 @@ def coleman_integrals_on_basis(self, P, Q, algorithm=None): # MW = monsky_washnitzer.MonskyWashnitzerDifferentialRing(S) # return MW.invariant_differential() - def coleman_integral(self, w, P, Q, algorithm = 'None'): + def coleman_integral(self, w, P, Q, algorithm='None'): r""" - Returns the Coleman integral `\int_P^Q w` + Return the Coleman integral `\int_P^Q w`. INPUT: diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 3ed2b06e960..9587573c882 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -357,7 +357,7 @@ def diagonalization(self, names=None): names = self.defining_polynomial().parent().variable_names() from .constructor import Conic D, T = self.diagonal_matrix() - con = Conic(D, names = names) + con = Conic(D, names=names) return con, con.hom(T, self), self.hom(T.inverse(), con) def gens(self): @@ -386,8 +386,8 @@ def gens(self): """ return self.coordinate_ring().gens() - def has_rational_point(self, point = False, - algorithm = 'default', read_cache = True): + def has_rational_point(self, point=False, + algorithm='default', read_cache=True): r""" Returns True if and only if the conic ``self`` has a point over its base field `B`. @@ -523,21 +523,21 @@ def has_rational_point(self, point = False, if d == 0: return True, self.point([0,1,0]) return True, self.point([0, ((e**2-4*d*f).sqrt()-e)/(2*d), 1], - check = False) + check=False) return True if isinstance(B, sage.rings.abc.RealField): D, T = self.diagonal_matrix() [a, b, c] = [D[0,0], D[1,1], D[2,2]] if a == 0: - ret = True, self.point(T*vector([1,0,0]), check = False) + ret = True, self.point(T*vector([1,0,0]), check=False) elif a*c <= 0: ret = True, self.point(T*vector([(-c/a).sqrt(),0,1]), - check = False) + check=False) elif b == 0: - ret = True, self.point(T*vector([0,1,0]), check = False) + ret = True, self.point(T*vector([0,1,0]), check=False) elif b*c <= 0: ret = True, self.point(T*vector([0,(-c/b).sqrt(),0,1]), - check = False) + check=False) else: ret = False, None if point: @@ -546,7 +546,7 @@ def has_rational_point(self, point = False, raise NotImplementedError("has_rational_point not implemented for " \ "conics over base field %s" % B) - def has_singular_point(self, point = False): + def has_singular_point(self, point=False): r""" Return True if and only if the conic ``self`` has a rational singular point. @@ -701,7 +701,7 @@ def hom(self, x, Y=None): "map from self (= %s) to Y (= %s)" % \ (x, self, Y)) x = Sequence(x*vector(self.ambient_space().gens())) - return self.Hom(Y)(x, check = False) + return self.Hom(Y)(x, check=False) return ProjectivePlaneCurve.hom(self, x, Y) def is_diagonal(self): @@ -938,7 +938,7 @@ def parametrization(self, point=None, morphism=True): if not morphism: return par P1 = ProjectiveSpace(self.base_ring(), 1, 'x,y') - return P1.hom(par[0],self), self.Hom(P1)(par[1], check = False) + return P1.hom(par[0],self), self.Hom(P1)(par[1], check=False) def point(self, v, check=True): r""" @@ -968,7 +968,6 @@ def point(self, v, check=True): self._rational_point = p return p - def random_rational_point(self, *args1, **args2): r""" Return a random rational point of the conic ``self``. @@ -1017,8 +1016,7 @@ def random_rational_point(self, *args1, **args2): y = B.random_element(*args1, **args2) return par[0]([x,y]) - - def rational_point(self, algorithm = 'default', read_cache = True): + def rational_point(self, algorithm='default', read_cache=True): r""" Return a point on ``self`` defined over the base field. @@ -1132,8 +1130,8 @@ def rational_point(self, algorithm = 'default', read_cache = True): ... ValueError: Conic Projective Conic Curve over Real Field with 53 bits of precision defined by x^2 + y^2 + z^2 has no rational points over Real Field with 53 bits of precision! """ - bl,pt = self.has_rational_point(point = True, algorithm = algorithm, - read_cache = read_cache) + bl,pt = self.has_rational_point(point=True, algorithm=algorithm, + read_cache=read_cache) if bl: return pt raise ValueError("Conic %s has no rational points over %s!" % \ @@ -1160,7 +1158,7 @@ def singular_point(self): ... ValueError: The conic self (= Projective Conic Curve over Rational Field defined by x^2 + x*y + y^2 + x*z + y*z + z^2) has no rational singular point """ - b = self.has_singular_point(point = True) + b = self.has_singular_point(point=True) if not b[0]: raise ValueError("The conic self (= %s) has no rational " \ "singular point" % self) diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index a7eb86ce43f..a727bb5e285 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -190,7 +190,7 @@ class ProductProjectiveSpaces_ring(AmbientSpace): sage: f(Q) (4 : 1 , 1 : 2 : 1) """ - def __init__(self, N, R = QQ, names = None): + def __init__(self, N, R=QQ, names=None): r""" The Python constructor. @@ -292,9 +292,9 @@ def _latex_(self): {\mathbf P}_{\Bold{Z}}^1 \times {\mathbf P}_{\Bold{Z}}^2 \times {\mathbf P}_{\Bold{Z}}^3 """ - return '%s' % " \\times ".join(PS._latex_() for PS in self) + return " \\times ".join(PS._latex_() for PS in self) - def _latex_generic_point(self, v = None): + def _latex_generic_point(self, v=None): """ Return a LaTeX representation of the generic point on this product space. @@ -893,7 +893,7 @@ def change_ring(self, R): new_components = [P.change_ring(R) for P in self._components] return ProductProjectiveSpaces(new_components) - def affine_patch(self, I, return_embedding = False): + def affine_patch(self, I, return_embedding=False): r""" Return the `I^{th}` affine patch of this projective space product where ``I`` is a multi-index. diff --git a/src/sage/schemes/product_projective/subscheme.py b/src/sage/schemes/product_projective/subscheme.py index 1ac8018f8ab..963feea3d09 100644 --- a/src/sage/schemes/product_projective/subscheme.py +++ b/src/sage/schemes/product_projective/subscheme.py @@ -282,7 +282,7 @@ def is_smooth(self, point=None): """ raise NotImplementedError("Not Implemented") - def affine_patch(self, I, return_embedding = False): + def affine_patch(self, I, return_embedding=False): r""" Return the `I^{th}` affine patch of this projective scheme where 'I' is a multi-index. diff --git a/src/sage/schemes/projective/projective_subscheme.py b/src/sage/schemes/projective/projective_subscheme.py index fec6ea3558c..3c0faa498a6 100644 --- a/src/sage/schemes/projective/projective_subscheme.py +++ b/src/sage/schemes/projective/projective_subscheme.py @@ -201,9 +201,10 @@ def dimension(self): self.__dimension = self.defining_ideal().dimension() - 1 return self.__dimension - def affine_patch(self, i, AA = None): + def affine_patch(self, i, AA=None): r""" Return the `i^{th}` affine patch of this projective scheme. + This is the intersection with this `i^{th}` affine patch of its ambient space. @@ -598,7 +599,7 @@ def nth_iterate(self, f, n): raise TypeError("must be a forward orbit") return self.orbit(f,[n,n+1])[0] - def _forward_image(self, f, check = True): + def _forward_image(self, f, check=True): r""" Compute the forward image of this subscheme by the morphism ``f``. @@ -771,11 +772,11 @@ def _forward_image(self, f, check = True): m = CR_codom.ngens() #can't call eliminate if the base ring is polynomial so we do it ourselves #with a lex ordering - R = PolynomialRing(f.base_ring(), n+m, 'tempvar', order = 'lex') + R = PolynomialRing(f.base_ring(), n + m, 'tempvar', order='lex') Rvars = R.gens()[0 : n] - phi = CR_dom.hom(Rvars,R) + phi = CR_dom.hom(Rvars, R) zero = n*[0] - psi = R.hom(zero + list(CR_codom.gens()),CR_codom) + psi = R.hom(zero + list(CR_codom.gens()), CR_codom) #set up ideal L = R.ideal([phi(t) for t in self.defining_polynomials()] + [R.gen(n+i) - phi(f[i]) for i in range(m)]) G = L.groebner_basis() # eliminate From 9567e09a4ac8092f58d65b67076931ac4f4b434f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 9 Aug 2022 18:59:41 +0200 Subject: [PATCH 146/454] fix E251 in groups --- .../additive_abelian_group.py | 3 ++- .../additive_abelian_wrapper.py | 2 +- src/sage/groups/cubic_braid.py | 17 ++++++++-------- src/sage/groups/finitely_presented.py | 2 +- src/sage/groups/old.pyx | 2 +- src/sage/groups/perm_gps/cubegroup.py | 2 +- .../perm_gps/partn_ref/refinement_graphs.pyx | 10 +++++++--- .../perm_gps/partn_ref/refinement_sets.pyx | 4 +++- .../partn_ref2/refinement_generic.pyx | 2 +- src/sage/groups/perm_gps/permgroup.py | 20 +++++++++---------- .../groups/perm_gps/permgroup_element.pyx | 4 ++-- 11 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py index 0a326d42c60..3efc1d7621c 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_group.py +++ b/src/sage/groups/additive_abelian/additive_abelian_group.py @@ -11,7 +11,8 @@ from sage.modules.fg_pid.fgp_element import FGP_Element from sage.rings.integer_ring import ZZ -def AdditiveAbelianGroup(invs, remember_generators = True): + +def AdditiveAbelianGroup(invs, remember_generators=True): r""" Construct a finitely-generated additive abelian group. diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 87bbfcb938e..7c0aec05ff8 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -465,7 +465,7 @@ def _element_constructor_(self, x, check=False): (6, 2) """ if parent(x) is self.universe(): - return self.element_class(self, self.discrete_log(x), element = x) + return self.element_class(self, self.discrete_log(x), element=x) return addgp.AdditiveAbelianGroup_fixed_gens._element_constructor_(self, x, check) diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index 295d43aa1d2..77f95ef3758 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -194,7 +194,8 @@ def AssionGroupS(n=None, names='s'): sage: S3 == S3x True """ - return CubicBraidGroup(n = n, names = names, cbg_type=CubicBraidGroup.type.AssionS) + return CubicBraidGroup(n=n, names=names, + cbg_type=CubicBraidGroup.type.AssionS) def AssionGroupU(n=None, names='u'): @@ -222,9 +223,9 @@ def AssionGroupU(n=None, names='u'): Assion group on 3 strands of type U sage: U3 == U3x True - """ - return CubicBraidGroup(n = n, names = names, cbg_type=CubicBraidGroup.type.AssionU) + return CubicBraidGroup(n=n, names=names, + cbg_type=CubicBraidGroup.type.AssionU) @@ -367,9 +368,9 @@ class :class:`Braid`. braid_group = self.parent().braid_group() return braid_group(self) - @cached_method - def burau_matrix(self, root_bur = None, domain = None, characteristic = None, var='t', reduced=False): + def burau_matrix(self, root_bur=None, domain=None, characteristic=None, + var='t', reduced=False): r""" Return the Burau matrix of the cubic braid coset. @@ -2022,10 +2023,10 @@ def is_finite(self): from sage.rings.infinity import infinity return not self.order() is infinity - # ---------------------------------------------------------------------------------- + # ------------------------------------------------------------------ # creating a CubicBraidGroup as subgroup of self on less strands - # ---------------------------------------------------------------------------------- - def cubic_braid_subgroup(self, nstrands = None): + # ------------------------------------------------------------------ + def cubic_braid_subgroup(self, nstrands=None): r""" Creates a cubic braid group as subgroup of ``self`` on the first ``nstrands`` strands. diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 2a61bbf91dc..d953022d3da 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -1486,7 +1486,7 @@ def epimorphisms(self, H): res.append(fhom) return res - def alexander_matrix(self, im_gens = None): + def alexander_matrix(self, im_gens=None): """ Return the Alexander matrix of the group. diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx index 8d6740a454a..c17082f59cb 100644 --- a/src/sage/groups/old.pyx +++ b/src/sage/groups/old.pyx @@ -31,7 +31,7 @@ cdef class Group(sage.structure.parent.Parent): """ Generic group class """ - def __init__(self, category = None): + def __init__(self, category=None): """ TESTS:: diff --git a/src/sage/groups/perm_gps/cubegroup.py b/src/sage/groups/perm_gps/cubegroup.py index 3c704b0dc9b..921c6201c43 100644 --- a/src/sage/groups/perm_gps/cubegroup.py +++ b/src/sage/groups/perm_gps/cubegroup.py @@ -944,7 +944,7 @@ def repr2d(self, mv): line13 = " +--------------+\n" return line1+line2+line3+line4+line5+line6+line7+line8+line9+line10+line11+line12+line13 - def plot_cube(self, mv, title=True, colors = [lpurple, yellow, red, green, orange, blue]): + def plot_cube(self, mv, title=True, colors=[lpurple, yellow, red, green, orange, blue]): r""" Input the move mv, as a string in the Singmaster notation, and output the 2D plot of the cube in that state. diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index 34d453cba77..4ab08bd6ba7 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -1257,8 +1257,9 @@ cdef void free_dg_edge_gen(iterator *dg_edge_gen): sig_free(dg_edge_gen) -def generate_dense_graphs_edge_addition(int n, bint loops, G = None, depth = None, bint construct = False, - bint indicate_mem_err = True): +def generate_dense_graphs_edge_addition(int n, bint loops, G=None, depth=None, + bint construct=False, + bint indicate_mem_err=True): r""" EXAMPLES:: @@ -1534,7 +1535,10 @@ cdef void free_cgd_2(void *data): cdef canonical_generator_data *cgd = data deallocate_cgd(cgd) -def generate_dense_graphs_vert_addition(int n, base_G = None, bint construct = False, bint indicate_mem_err = True): + +def generate_dense_graphs_vert_addition(int n, base_G=None, + bint construct=False, + bint indicate_mem_err=True): r""" EXAMPLES:: diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx index 1ae52e88426..6e80294db3c 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx @@ -685,7 +685,9 @@ cdef iterator *setup_set_gen(iterator *subset_gen, int degree, int max_size): bitset_clear(&empty_set.bits) return subset_iterator -def sets_modulo_perm_group(list generators, int max_size, bint indicate_mem_err = 1): + +def sets_modulo_perm_group(list generators, int max_size, + bint indicate_mem_err=1): r""" Given generators of a permutation group, list subsets up to permutations in the group. diff --git a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx index 2fcb0363a8b..b423ef036b0 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx @@ -639,7 +639,7 @@ cdef class PartitionRefinement_generic: self._backtrack(True) self._finish_latex() - cdef void _backtrack(self, bint first_step = False): + cdef void _backtrack(self, bint first_step=False): r""" Backtracking with pruning. diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 49089346995..9d3dae84cb0 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -2781,7 +2781,7 @@ def semidirect_product(self, N, mapping, check=True): from sage.categories.finite_permutation_groups import FinitePermutationGroups if N not in FinitePermutationGroups(): raise TypeError("{0} is not a permutation group".format(N)) - if not PermutationGroup(gens = mapping[0]) == self: + if not PermutationGroup(gens=mapping[0]) == self: msg = 'the generator list must generate the calling group, {0} does not generate {1}' raise ValueError(msg.format(mapping[0], self._repr_())) if len(mapping[0]) != len(mapping[1]): @@ -3159,7 +3159,7 @@ def commutator(self, other=None): return PermutationGroup(gap_group=gap_group) @hap_decorator - def cohomology(self, n, p = 0): + def cohomology(self, n, p=0): r""" Computes the group cohomology `H^n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if `p > 0` is a prime. @@ -3208,7 +3208,7 @@ def cohomology(self, n, p = 0): return AbelianGroup(len(L), L) @hap_decorator - def cohomology_part(self, n, p = 0): + def cohomology_part(self, n, p=0): r""" Compute the p-part of the group cohomology `H^n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if @@ -3244,7 +3244,7 @@ def cohomology_part(self, n, p = 0): return AbelianGroup(len(L), L) @hap_decorator - def homology(self, n, p = 0): + def homology(self, n, p=0): r""" Computes the group homology `H_n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if @@ -3292,7 +3292,7 @@ def homology(self, n, p = 0): return AbelianGroup(len(L), L) @hap_decorator - def homology_part(self, n, p = 0): + def homology_part(self, n, p=0): r""" Computes the `p`-part of the group homology `H_n(G, F)`, where `F = \ZZ` if `p=0` and @@ -3616,7 +3616,7 @@ def _regular_subgroup_gap(self): return C @cached_method - def has_regular_subgroup(self, return_group = False): + def has_regular_subgroup(self, return_group=False): r""" Return whether the group contains a regular subgroup. @@ -3656,12 +3656,10 @@ def has_regular_subgroup(self, return_group = False): b = (C is not None) if b and return_group: G = self.subgroup(gap_group=C.Representative()) - if return_group: - return G - else: - return b - def blocks_all(self, representatives = True): + return G if return_group else b + + def blocks_all(self, representatives=True): r""" Return the list of block systems of imprimitivity. diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 037884f55da..df553df6ed8 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -1849,14 +1849,14 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): [2, 1, 1] """ cycle_type = [len(c) for c in self.cycle_tuples(singletons)] - cycle_type.sort(reverse = True) + cycle_type.sort(reverse=True) if as_list: return cycle_type else: from sage.combinat.partition import _Partitions return _Partitions(cycle_type) - def has_descent(self, i, side = "right", positive = False): + def has_descent(self, i, side="right", positive=False): """ INPUT: From d93b793c7bb627e82ce22fb4f4a4c5a278fc7dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 9 Aug 2022 21:19:24 +0200 Subject: [PATCH 147/454] details in saturation --- src/sage/schemes/elliptic_curves/saturation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/saturation.py b/src/sage/schemes/elliptic_curves/saturation.py index 1d75c076e53..9f5b59ac404 100644 --- a/src/sage/schemes/elliptic_curves/saturation.py +++ b/src/sage/schemes/elliptic_curves/saturation.py @@ -693,14 +693,14 @@ def p_projections(Eq, Plist, p, debug=False): # roots of unity with p1|p2, together with discrete log in the # multiplicative group. - zeta = g1.weil_pairing(g2,p2) # a primitive p1'th root of unity + zeta = g1.weil_pairing(g2, p2) # a primitive p1'th root of unity if debug: print("wp of gens = {} with order {}".format(zeta, zeta.multiplicative_order())) - assert zeta.multiplicative_order() == p1, "Weil pairing error during saturation: p={}, G={}, Plist={}".format(p,G,Plist) + assert zeta.multiplicative_order() == p1, "Weil pairing error during saturation: p={}, G={}, Plist={}".format(p, G, Plist) # logs are well-defined mod p1, hence mod p - return [vector(Fp, [dlog(pt.weil_pairing(g1,p2), zeta, + return [vector(Fp, [dlog(pt.weil_pairing(g1, p2), zeta, ord=p1, operation='*') for pt in pts]), - vector(Fp, [dlog(pt.weil_pairing(g2,p2), zeta, - ord=p1, operation='*') for pt in pts])] + vector(Fp, [dlog(pt.weil_pairing(g2, p2), zeta, + ord=p1, operation='*') for pt in pts])] From 499f5ce57ac696ab74f560d9494a948270f91285 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 12:46:12 -0700 Subject: [PATCH 148/454] build/pkgs/gcc/spkg-configure.m4: Reject gcc < 8 --- build/pkgs/gcc/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/gcc/spkg-configure.m4 b/build/pkgs/gcc/spkg-configure.m4 index 959e499b68a..63335eb7357 100644 --- a/build/pkgs/gcc/spkg-configure.m4 +++ b/build/pkgs/gcc/spkg-configure.m4 @@ -161,8 +161,8 @@ SAGE_SPKG_CONFIGURE_BASE([gcc], [ # Add the .0 because Debian/Ubuntu gives version numbers like # 4.6 instead of 4.6.4 (Trac #18885) AS_CASE(["$GXX_VERSION.0"], - [[[0-5]].*|6.[[0-2]].*], [ - # Install our own GCC if the system-provided one is older than gcc-6.3 + [[[0-7]].*], [ + # Install our own GCC if the system-provided one is older than gcc 8 SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]) ], [1[[3-9]].*], [ From 810ecd144d1b770901be51b09c5b9071f98393ce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 12:48:30 -0700 Subject: [PATCH 149/454] README.md, src/doc/en/installation/source.rst: Update version lower bound for gcc --- README.md | 2 +- src/doc/en/installation/source.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fd467aa85f8..abe5d2a42fe 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ in the Installation Guide. 3. [Linux, Cygwin] Install the required minimal build prerequisites. - - Compilers: `gcc`, `gfortran`, `g++` (GCC 6.3 to 12.x and recent + - Compilers: `gcc`, `gfortran`, `g++` (GCC 8.x to 12.x and recent versions of Clang (LLVM) are supported). See the Installation Manual for a discussion of suitable compilers. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index f670cd925b9..78b5ec62810 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -59,7 +59,7 @@ and the :wikipedia:`bash ` shell, the following standard command-line development tools must be installed on your computer: -- A **C/C++ compiler**: GCC versions 6.3 to 12.x are supported. +- A **C/C++ compiler**: GCC versions 8.x to 12.x are supported. Clang (LLVM) is also supported. See also `Using alternative compilers`_. - **make**: GNU make, version 3.80 or later. Version 3.82 or later is recommended. From 4e8ec602ed0653b6dbc0b19f45e48f943555e7c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:01:37 -0700 Subject: [PATCH 150/454] .github/workflows/docker.yml: Remove debian-stretch, fedora-{26,27,27} --- .github/workflows/docker.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 79fd279963c..534a0e0e273 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,7 +22,6 @@ on: "ubuntu-focal", "ubuntu-jammy", "ubuntu-kinetic", - "debian-stretch", "debian-buster", "debian-bullseye", "debian-bookworm", @@ -33,9 +32,6 @@ on: "linuxmint-20.2", "linuxmint-20.3", "linuxmint-21", - "fedora-26", - "fedora-27", - "fedora-28", "fedora-29", "fedora-30", "fedora-31", From a9f4b154cfe44116bd0aa0dc6c86d9993aaaeab7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:07:47 -0700 Subject: [PATCH 151/454] tox.ini (gcc_8), build/pkgs/_gcc8: New --- build/pkgs/_gcc8/distros/debian.txt | 3 +++ build/pkgs/_gcc8/distros/opensuse.txt | 3 +++ build/pkgs/_gcc8/type | 1 + tox.ini | 2 ++ 4 files changed, 9 insertions(+) create mode 100644 build/pkgs/_gcc8/distros/debian.txt create mode 100644 build/pkgs/_gcc8/distros/opensuse.txt create mode 100644 build/pkgs/_gcc8/type diff --git a/build/pkgs/_gcc8/distros/debian.txt b/build/pkgs/_gcc8/distros/debian.txt new file mode 100644 index 00000000000..3c56d438a9e --- /dev/null +++ b/build/pkgs/_gcc8/distros/debian.txt @@ -0,0 +1,3 @@ +gcc-8 +g++-8 +gfortran-8 diff --git a/build/pkgs/_gcc8/distros/opensuse.txt b/build/pkgs/_gcc8/distros/opensuse.txt new file mode 100644 index 00000000000..7a7764d2d5a --- /dev/null +++ b/build/pkgs/_gcc8/distros/opensuse.txt @@ -0,0 +1,3 @@ +gcc8 +gcc8-c++ +gcc8-fortran diff --git a/build/pkgs/_gcc8/type b/build/pkgs/_gcc8/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/_gcc8/type @@ -0,0 +1 @@ +optional diff --git a/tox.ini b/tox.ini index ad135de10cb..a2520b51bdd 100644 --- a/tox.ini +++ b/tox.ini @@ -523,6 +523,8 @@ setenv = # - toolchain # gcc_spkg: CONFIG_CONFIGURE_ARGS_2=--without-system-gcc + gcc_8: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-8 CXX=g++-8 FC=gfortran-8 + gcc_8: EXTRA_SAGE_PACKAGES_2=_gcc8 gcc_9: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-9 CXX=g++-9 FC=gfortran-9 gcc_9: EXTRA_SAGE_PACKAGES_2=_gcc9 gcc_10: CONFIG_CONFIGURE_ARGS_2=--with-system-gcc=force CC=gcc-10 CXX=g++-10 FC=gfortran-10 From 120ab057065a70a24c515e1a8788c96141f9db5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:08:10 -0700 Subject: [PATCH 152/454] tox.ini (envlist): Replace docker-ubuntu-trusty-minimal with docker-ubuntu-trusty-toolchain-gcc_9-minimal --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a2520b51bdd..0106e693891 100644 --- a/tox.ini +++ b/tox.ini @@ -55,7 +55,7 @@ envlist = ### - standard # Install all known system packages equivalent to standard packages that have spkg-configure.m4 ### - maximal # Install all known system packages equivalent to standard/optional packages that have spkg-configure.m4 - docker-ubuntu-trusty-minimal, + docker-ubuntu-trusty-toolchain-gcc_9-minimal, docker-debian-bullseye-standard, docker-fedora-34-standard, docker-archlinux-latest-maximal, From 9b38bad852a3d10712f7cbcad6dfbca158f623d1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:11:14 -0700 Subject: [PATCH 153/454] build/pkgs/_gcc{9,10,11}/distros/opensuse.txt: New --- build/pkgs/_gcc10/distros/opensuse.txt | 3 +++ build/pkgs/_gcc11/distros/opensuse.txt | 3 +++ build/pkgs/_gcc9/distros/opensuse.txt | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 build/pkgs/_gcc10/distros/opensuse.txt create mode 100644 build/pkgs/_gcc11/distros/opensuse.txt create mode 100644 build/pkgs/_gcc9/distros/opensuse.txt diff --git a/build/pkgs/_gcc10/distros/opensuse.txt b/build/pkgs/_gcc10/distros/opensuse.txt new file mode 100644 index 00000000000..372f607e2ab --- /dev/null +++ b/build/pkgs/_gcc10/distros/opensuse.txt @@ -0,0 +1,3 @@ +gcc10 +gcc10-c++ +gcc10-fortran diff --git a/build/pkgs/_gcc11/distros/opensuse.txt b/build/pkgs/_gcc11/distros/opensuse.txt new file mode 100644 index 00000000000..467f354c123 --- /dev/null +++ b/build/pkgs/_gcc11/distros/opensuse.txt @@ -0,0 +1,3 @@ +gcc11 +gcc11-c++ +gcc11-fortran diff --git a/build/pkgs/_gcc9/distros/opensuse.txt b/build/pkgs/_gcc9/distros/opensuse.txt new file mode 100644 index 00000000000..ea8e8a6bcaa --- /dev/null +++ b/build/pkgs/_gcc9/distros/opensuse.txt @@ -0,0 +1,3 @@ +gcc9 +gcc9-c++ +gcc9-fortran From ccce5b1e5fb7b8ac15512b079aa3ee98d7753dab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:40:23 -0700 Subject: [PATCH 154/454] .github/workflows/docker.yml: Use gcc_8 with ubuntu-bionic, linuxmint-19.x --- .github/workflows/docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 534a0e0e273..950f36f44e7 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,7 +18,7 @@ on: default: >- ["ubuntu-trusty-toolchain-gcc_9", "ubuntu-xenial-toolchain-gcc_9", - "ubuntu-bionic", + "ubuntu-bionic-gcc_8", "ubuntu-focal", "ubuntu-jammy", "ubuntu-kinetic", @@ -26,8 +26,8 @@ on: "debian-bullseye", "debian-bookworm", "debian-sid", - "linuxmint-19", - "linuxmint-19.3", + "linuxmint-19-gcc_8", + "linuxmint-19.3-gcc_8", "linuxmint-20.1", "linuxmint-20.2", "linuxmint-20.3", From 27a0882bc43a231ebecb439f80026a3cf9c526fc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 9 Aug 2022 13:40:42 -0700 Subject: [PATCH 155/454] .github/workflows/docker.yml: Use gcc_11 with opensuse-15.{3,4} --- .github/workflows/docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 950f36f44e7..8eff13405f8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -47,8 +47,8 @@ on: "gentoo-python3.9", "gentoo-python3.10", "archlinux-latest", - "opensuse-15.3", - "opensuse-15.4", + "opensuse-15.3-gcc_11", + "opensuse-15.4-gcc_11", "opensuse-tumbleweed", "conda-forge", "ubuntu-bionic-i386", From 8729071a8e49e53e0aed08997e391bcfb1b8a43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 9 Aug 2022 23:38:27 +0200 Subject: [PATCH 156/454] 33002: updating doctest to fix deprecation warning --- src/sage/misc/latex_standalone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index 8f698ffc2e6..8d9e928c012 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -158,7 +158,7 @@ sage: from sage.misc.latex_standalone import TikzPicture sage: V = [[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]] sage: P = Polyhedron(vertices=V).polar() - sage: s = P.projection().tikz([674,108,-731],112) + sage: s = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') sage: t = TikzPicture(s) Open the image in a viewer (the returned value is a string giving the From 69d1ea8445763c5190ff0edc584306d6bf346c80 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 11 Aug 2022 16:15:08 -0700 Subject: [PATCH 157/454] Add horizontal cells --- src/sage/combinat/partition.py | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 6b8b2f669e4..787f76b2591 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -4752,6 +4752,60 @@ def add_horizontal_border_strip(self, k): res.append(_Partitions(tmp)) return res + def horizontal_border_strip_cells(self, k): + """ + Return a list of all the horizontal border strips of length ``k`` + which can be added to ``self``, where each horizontal border strip is + represented as a list of cells. + + EXAMPLES:: + + sage: Partition([]).horizontal_border_strip_cells(0) + [] + sage: Partition([3,2,1]).horizontal_border_strip_cells(0) + [] + sage: Partition([]).horizontal_border_strip_cells(2) + [[(0, 0), (0, 1)]] + sage: Partition([2,2]).horizontal_border_strip_cells(2) + [[(0, 2), (0, 3)], [(0, 2), (2, 0)], [(2, 0), (2, 1)]] + sage: Partition([3,2,2]).horizontal_border_strip_cells(2) + [[(0, 3), (0, 4)], + [(0, 3), (1, 2)], + [(0, 3), (3, 0)], + [(1, 2), (3, 0)], + [(3, 0), (3, 1)]] + """ + if k == 0: + return list() + + L = self._list + res = [] + shelf = [k] # the number of boxes which will fit in a row + mapping = [0] # a record of the rows + for i in range(len(L)-1): + val = L[i] - L[i+1] + if not val: + continue + mapping.append(i+1) + shelf.append(val) + + # add the last shelf + if L: + mapping.append(len(L)) + shelf.append(L[-1]) + + L.append(0) # add room on the bottom + # list all of the positions for cells + # filling each self from the top to bottom + for iv in IntegerListsBackend_invlex(k, length=len(shelf), ceiling=shelf, check=False)._iter(): + tmp = [] + # mapping[i] is the row index, val is the number of cells added to the row. + for i, val in enumerate(iv): + tmp.extend((mapping[i], L[mapping[i]] + j) for j in range(val)) + res.append(tmp) + return res + + def remove_horizontal_border_strip(self, k): """ Return the partitions obtained from ``self`` by removing an From 3d7a37bb6971fbd8b8cdcfafc92d05e4f5ec81d1 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 11 Aug 2022 17:21:32 -0700 Subject: [PATCH 158/454] Add vertical strips --- src/sage/combinat/partition.py | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 787f76b2591..af39531c26c 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -4805,6 +4805,63 @@ def horizontal_border_strip_cells(self, k): res.append(tmp) return res + def vertical_border_strip_cells(self, k): + """ + Return a list of all the vertical border strips of length ``k`` + which can be added to ``self``, where each horizontal border strip is + represented as a list of cells. + + EXAMPLES:: + + sage: Partition([]).vertical_border_strip_cells(0) + [] + sage: Partition([3,2,1]).vertical_border_strip_cells(0) + [] + sage: Partition([]).vertical_border_strip_cells(2) + [[(0, 0), (1, 0)]] + sage: Partition([2,2]).vertical_border_strip_cells(2) + [[(0, 2), (1, 2)], + [(0, 2), (2, 0)], + [(2, 0), (3, 0)]] + sage: Partition([3,2,2]).vertical_border_strip_cells(2) + [[(0, 3), (1, 2)], + [(0, 3), (3, 0)], + [(1, 2), (2, 2)], + [(1, 2), (3, 0)], + [(3, 0), (4, 0)]] + """ + if k == 0: + return [] + + shelf = [] + res = [] + i = 0 + ell = len(self._list) + while i < ell: + tmp = 1 + while i+1 < ell and self._list[i] == self._list[i+1]: + tmp += 1 + i += 1 + if i == ell-1 and i > 0 and self._list[i] != self._list[i-1]: + tmp = 1 + shelf.append(tmp) + i += 1 + + # added the last shelf on the right side of + # the first line + shelf.append(k) + # list all of the positions for cells + for iv in IntegerListsBackend_invlex(k, length=len(shelf), ceiling=shelf, check=False)._iter(): + tmp = self._list + [0]*k + j = 0 + current_strip = [] + for t in range(len(iv)): + for _ in range(iv[t]): + current_strip.append((j, tmp[j])) + j += 1 + j = sum(shelf[:t+1]) + res.append(current_strip) + return res def remove_horizontal_border_strip(self, k): """ From bae3378fd6cb6a3b772680827c872af820d290c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 11 Aug 2022 17:37:17 +0200 Subject: [PATCH 159/454] fix various suggestions of lgtm.com --- build/pkgs/matplotlib/make-setup-config.py | 5 +- .../algebras/quatalg/quaternion_algebra.py | 19 +-- src/sage/functions/log.py | 7 +- .../groups/abelian_gps/dual_abelian_group.py | 21 ++- src/sage/interfaces/ecm.py | 10 +- src/sage/modules/quotient_module.py | 8 +- src/sage/symbolic/expression_conversions.py | 156 ++++++++++-------- src/sage/typeset/character_art.py | 2 - 8 files changed, 119 insertions(+), 109 deletions(-) diff --git a/build/pkgs/matplotlib/make-setup-config.py b/build/pkgs/matplotlib/make-setup-config.py index 98450dec2fd..4f9acf1f04c 100644 --- a/build/pkgs/matplotlib/make-setup-config.py +++ b/build/pkgs/matplotlib/make-setup-config.py @@ -1,5 +1,4 @@ from configparser import ConfigParser -import pkgconfig import os config = ConfigParser() @@ -23,7 +22,7 @@ print("NOTE: Set SAGE_MATPLOTLIB_GUI to anything but 'no' to try to build the Matplotlib GUI.") -graphical_backend='False' +graphical_backend = 'False' if os.environ.get('SAGE_MATPLOTLIB_GUI', 'no').lower() != 'no': graphical_backend = 'auto' @@ -35,7 +34,7 @@ config.add_section('gui_support') for backend in ('gtk', 'gtkagg', 'tkagg', 'wxagg', 'macosx', 'windowing'): - config.set('gui_support', backend, graphical_backend) + config.set('gui_support', backend, graphical_backend) with open('src/mplsetup.cfg', 'w') as configfile: config.write(configfile) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 3bde7b21536..1ad378eea65 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -2690,15 +2690,13 @@ def multiply_by_conjugate(self, J): R = self.quaternion_algebra() return R.ideal(basis, check=False) - def is_equivalent(I, J, B=10): + def is_equivalent(self, J, B=10) -> bool: """ - Return ``True`` if ``I`` and ``J`` are equivalent as right ideals. + Return ``True`` if ``self`` and ``J`` are equivalent as right ideals. INPUT: - - ``I`` -- a fractional quaternion ideal (self) - - - ``J`` -- a fractional quaternion ideal with same order as ``I`` + - ``J`` -- a fractional quaternion ideal with same order as ``self`` - ``B`` -- a bound to compute and compare theta series before doing the full equivalence test @@ -2718,15 +2716,16 @@ def is_equivalent(I, J, B=10): sage: R[0].is_equivalent(S) True """ - if not isinstance(I, QuaternionFractionalIdeal_rational): + # shorthand: let I be self + if not isinstance(self, QuaternionFractionalIdeal_rational): return False - if I.right_order() != J.right_order(): - raise ValueError("I and J must be right ideals") + if self.right_order() != J.right_order(): + raise ValueError("self and J must be right ideals") # Just test theta series first. If the theta series are # different, the ideals are definitely not equivalent. - if B > 0 and I.theta_series_vector(B) != J.theta_series_vector(B): + if B > 0 and self.theta_series_vector(B) != J.theta_series_vector(B): return False # The theta series are the same, so perhaps the ideals are @@ -2734,7 +2733,7 @@ def is_equivalent(I, J, B=10): # 1. Compute I * Jbar # see Prop. 1.17 in Pizer. Note that we use IJbar instead of # JbarI since we work with right ideals - IJbar = I.multiply_by_conjugate(J) + IJbar = self.multiply_by_conjugate(J) # 2. Determine if there is alpha in K such # that N(alpha) = N(I)*N(J) as explained by Pizer. diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index d322305b223..46cc279a287 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -1243,6 +1243,7 @@ def _print_latex_(self, z, m): harmonic_number = Function_harmonic_number_generalized() + class _Function_swap_harmonic(BuiltinFunction): r""" Harmonic number function with swapped arguments. For internal use only. @@ -1262,14 +1263,18 @@ class _Function_swap_harmonic(BuiltinFunction): """ def __init__(self): BuiltinFunction.__init__(self, "_swap_harmonic", nargs=2) + def _eval_(self, a, b, **kwds): - return harmonic_number(b,a,**kwds) + return harmonic_number(b, a, **kwds) + _swap_harmonic = _Function_swap_harmonic() + register_symbol(_swap_harmonic, {'maxima': 'gen_harmonic_number'}) register_symbol(_swap_harmonic, {'maple': 'harmonic'}) + class Function_harmonic_number(BuiltinFunction): r""" Harmonic number function, defined by: diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index df2b51e0d57..521267cccae 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -63,11 +63,10 @@ # http://www.gnu.org/licenses/ ########################################################################### -from sage.rings.infinity import infinity from sage.structure.category_object import normalize_names from sage.structure.unique_representation import UniqueRepresentation from sage.groups.abelian_gps.dual_abelian_group_element import ( - DualAbelianGroupElement, is_DualAbelianGroupElement ) + DualAbelianGroupElement, is_DualAbelianGroupElement) from sage.misc.mrange import mrange from sage.misc.cachefunc import cached_method from sage.groups.group import AbelianGroup as AbelianGroupBase @@ -126,7 +125,7 @@ def __init__(self, G, names, base_ring): self._group = G names = normalize_names(G.ngens(), names) self._assign_names(names) - AbelianGroupBase.__init__(self) # TODO: category=CommutativeGroups() + AbelianGroupBase.__init__(self) # TODO: category=CommutativeGroups() def group(self): """ @@ -165,7 +164,7 @@ def __str__(self): sage: print(Fd) DualAbelianGroup( AbelianGroup ( 3, (5, 64, 729) ) ) """ - s = "DualAbelianGroup( AbelianGroup ( %s, %s ) )"%(self.ngens(), self.gens_orders()) + s = "DualAbelianGroup( AbelianGroup ( %s, %s ) )" % (self.ngens(), self.gens_orders()) return s def _repr_(self): @@ -188,9 +187,9 @@ def _repr_(self): eldv = G.gens_orders() gp = "" for x in eldv: - if x!=0: - gp = gp + "Z/%sZ x "%x - if x==0: + if x != 0: + gp = gp + "Z/%sZ x " % x + if x == 0: gp = gp + "Z x " gp = gp[:-2].strip() s = 'Dual of Abelian Group isomorphic to ' + gp + ' over ' + str(self.base_ring()) @@ -235,7 +234,7 @@ def random_element(self): result = self.one() for g in self.gens(): order = g.order() - result *= g**(randint(0,order)) + result *= g**(randint(0, order)) return result def gen(self, i=0): @@ -255,8 +254,8 @@ def gen(self, i=0): """ n = self.group().ngens() if i < 0 or i >= n: - raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) - x = [0]*n + raise IndexError("Argument i (= %s) must be between 0 and %s." % (i, n - 1)) + x = [0] * n if self.gens_orders()[i] != 1: x[i] = 1 return self.element_class(self, x) @@ -324,7 +323,7 @@ def invariants(self): # TODO: deprecate return self.group().gens_orders() - def __contains__(self,X): + def __contains__(self, X): """ Implements "in". diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index 171040c77a3..81ca002056f 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -46,11 +46,11 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################### import re -import subprocess +from subprocess import Popen, PIPE, call from sage.structure.sage_object import SageObject from sage.rings.integer_ring import ZZ @@ -216,8 +216,6 @@ def _run_ecm(self, cmd, n): sage: ecm._run_ecm(['cat'], 1234) '1234' """ - from subprocess import Popen, PIPE - # Under normal usage this program only returns ASCII; anything # else mixed is garbage and an error # So just accept latin-1 without encoding errors, and let the @@ -261,7 +259,7 @@ def interact(self): """ print("Enter numbers to run ECM on them.") print("Press control-D to exit.") - subprocess.call(self._cmd) + call(self._cmd) # Recommended settings from # http://www.mersennewiki.org/index.php/Elliptic_Curve_Method @@ -661,7 +659,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): # Step 3: Call find_factor until a factorization is found n_factorization = [n] while len(n_factorization) == 1: - n_factorization = self.find_factor(n,B1=B1) + n_factorization = self.find_factor(n, B1=B1) factors.extend(n_factorization) return sorted(probable_prime_factors) diff --git a/src/sage/modules/quotient_module.py b/src/sage/modules/quotient_module.py index f7aa99210a1..10db2189997 100644 --- a/src/sage/modules/quotient_module.py +++ b/src/sage/modules/quotient_module.py @@ -19,12 +19,11 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.richcmp import rich_to_bool, richcmp - from .free_module import (Module_free_ambient, FreeModule_ambient, FreeModule_ambient_field) + ############################################################################### # # Quotients of ambient free modules over a domain @@ -80,7 +79,7 @@ def __init__(self, module, sub): v = [C(self._free_cover, x.list(), coerce=False, copy=False) for x in sub.gens()] w = [C(self._free_cover, x.list(), coerce=False, copy=False) for x in module.free_relations().gens()] self._relations = self._free_cover.submodule(v + w, check=False) - else: # Otherwise module should be a free module + else: # Otherwise module should be a free module self._free_cover = module self._relations = sub @@ -448,7 +447,7 @@ def _repr_(self): sage: Q._repr_() 'Vector space quotient V/W of dimension 1 over Finite Field in a of size 3^2 where\nV: Vector space of degree 3 and dimension 2 over Finite Field in a of size 3^2\nUser basis matrix:\n[1 0 a]\n[a a 1]\nW: Vector space of degree 3 and dimension 1 over Finite Field in a of size 3^2\nBasis matrix:\n[ 1 1 a + 2]' """ - return "%s space quotient V/W of dimension %s over %s where\nV: %s\nW: %s"%( + return "%s space quotient V/W of dimension %s over %s where\nV: %s\nW: %s" % ( "Sparse vector" if self.is_sparse() else "Vector", self.dimension(), self.base_ring(), self.V(), self.W()) @@ -675,4 +674,3 @@ def relations(self): return self._sub W = relations - diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index c5a8240cda0..430b102ba4a 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -15,7 +15,9 @@ # https://www.gnu.org/licenses/ ############################################################################### -import operator as _operator +from operator import eq, ne, gt, lt, ge, le, mul, pow, neg, add, truediv +from functools import reduce + from sage.rings.rational_field import QQ from sage.symbolic.ring import SR from sage.structure.element import Expression @@ -23,7 +25,6 @@ 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 sage.rings.universal_cyclotomic_field import UniversalCyclotomicField -from functools import reduce class FakeExpression(): @@ -59,7 +60,7 @@ def __repr__(self): sage: FakeExpression([x, y], operator.truediv) FakeExpression([x, y], ) """ - return "FakeExpression(%r, %r)"%(self._operands, self._operator) + return "FakeExpression(%r, %r)" % (self._operands, self._operator) def pyobject(self): """ @@ -200,7 +201,7 @@ def __call__(self, ex=None): return self.symbol(ex) if operator in arithmetic_operators: - if getattr(self, 'use_fake_div', False) and (operator is _operator.mul or operator is mul_vararg): + if getattr(self, 'use_fake_div', False) and (operator is mul or operator is mul_vararg): div = self.get_fake_div(ex) return self.arithmetic(div, div.operator()) return self.arithmetic(ex, operator) @@ -238,7 +239,7 @@ def get_fake_div(self, ex): for arg in ex.operands(): ops = arg.operands() try: - if arg.operator() is _operator.pow and repr(ops[1]) == '-1': + if arg.operator() is pow and repr(ops[1]) == '-1': d.append(ops[0]) else: n.append(arg) @@ -250,22 +251,22 @@ def get_fake_div(self, ex): repr_n = [repr(_) for _ in n] if len(n) == 2 and "-1" in repr_n: a = n[0] if repr_n[1] == "-1" else n[1] - return FakeExpression([a], _operator.neg) + return FakeExpression([a], neg) else: return ex elif len_d == 1: d = d[0] else: - d = FakeExpression(d, _operator.mul) + d = FakeExpression(d, mul) if len(n) == 0: - return FakeExpression([SR.one(), d], _operator.truediv) + return FakeExpression([SR.one(), d], truediv) elif len(n) == 1: n = n[0] else: - n = FakeExpression(n, _operator.mul) + n = FakeExpression(n, mul) - return FakeExpression([n,d], _operator.truediv) + return FakeExpression([n, d], truediv) def pyobject(self, ex, obj): """ @@ -379,6 +380,7 @@ def composition(self, ex, operator): """ raise NotImplementedError("composition") + class InterfaceInit(Converter): def __init__(self, interface): """ @@ -395,7 +397,7 @@ def __init__(self, interface): '(%pi)+(exp((_SAGE_VAR_x)^(2)))+(2)' """ - self.name_init = "_%s_init_"%interface.name() + self.name_init = "_%s_init_" % interface.name() self.interface = interface self.relation_symbols = interface._relation_symbols() @@ -417,12 +419,11 @@ def symbol(self, ex): sage: g.symbol(x) 'sageVARx' """ - if self.interface.name()=='maxima': - return '_SAGE_VAR_'+repr(SR(ex)) - elif self.interface.name() == 'giac': + if self.interface.name() == 'maxima': + return '_SAGE_VAR_' + repr(SR(ex)) + if self.interface.name() == 'giac': return 'sageVAR' + repr(SR(ex)) - else: - return repr(SR(ex)) + return repr(SR(ex)) def pyobject(self, ex, obj): """ @@ -440,7 +441,7 @@ def pyobject(self, ex, obj): sage: ii.pyobject(pi, pi.pyobject()) 'Pi' """ - if (self.interface.name() in ['pari','gp'] and + if (self.interface.name() in ['pari', 'gp'] and isinstance(obj, NumberFieldElement_gaussian)): return repr(obj) try: @@ -460,8 +461,8 @@ def relation(self, ex, operator): sage: m.relation(x==3, operator.lt) '_SAGE_VAR_x < 3' """ - return "%s %s %s"%(self(ex.lhs()), self.relation_symbols[operator], - self(ex.rhs())) + return "%s %s %s" % (self(ex.lhs()), self.relation_symbols[operator], + self(ex.rhs())) def tuple(self, ex): """ @@ -575,8 +576,8 @@ def derivative(self, ex, operator): sage: (gamma_inc(x,x+1).diff(x)).simplify() -(x + 1)^(x - 1)*e^(-x - 1) + D[0](gamma)(x, x + 1) """ - #This code should probably be moved into the interface - #object in a nice way. + # This code should probably be moved into the interface + # object in a nice way. from sage.symbolic.ring import is_SymbolicVariable if self.name_init != "_maxima_init_": raise NotImplementedError @@ -591,20 +592,22 @@ def derivative(self, ex, operator): # 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))] + 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)] - subs = ["%s = %s"%(t._maxima_init_(),a._maxima_init_()) for t,a in zip(temp_args,args)] - outstr = "at(diff(%s, %s), [%s])"%(f._maxima_init_(), - ", ".join(params), - ", ".join(subs)) + params = ["%s, %s" % (temp_args[i]._maxima_init_(), params.count(i)) for i in set(params)] + subs = ["%s = %s" % (t._maxima_init_(), a._maxima_init_()) + for t, a in zip(temp_args, args)] + outstr = "at(diff(%s, %s), [%s])" % (f._maxima_init_(), + ", ".join(params), + ", ".join(subs)) else: f = operator.function()(*args) params = operator.parameter_set() - params = ["%s, %s"%(args[i]._maxima_init_(), params.count(i)) for i in set(params)] - outstr = "diff(%s, %s)"%(f._maxima_init_(), - ", ".join(params)) + params = ["%s, %s" % (args[i]._maxima_init_(), params.count(i)) + for i in set(params)] + outstr = "diff(%s, %s)" % (f._maxima_init_(), + ", ".join(params)) return outstr def arithmetic(self, ex, operator): @@ -617,7 +620,7 @@ def arithmetic(self, ex, operator): sage: m.arithmetic(x+2, sage.symbolic.operators.add_vararg) '(_SAGE_VAR_x)+(2)' """ - args = ["(%s)"%self(op) for op in ex.operands()] + args = ["(%s)" % self(op) for op in ex.operands()] return arithmetic_operators[operator].join(args) def composition(self, ex, operator): @@ -636,7 +639,7 @@ def composition(self, ex, operator): 'Sin[x]' """ ops = ex.operands() - #FIXME: consider stripping pyobjects() in ops + # FIXME: consider stripping pyobjects() in ops if hasattr(operator, self.name_init + "evaled_"): return getattr(operator, self.name_init + "evaled_")(*ops) else: @@ -646,7 +649,8 @@ def composition(self, ex, operator): except (TypeError, AttributeError): op = repr(operator) - return self.interface._function_call_string(op,ops,[]) + return self.interface._function_call_string(op, ops, []) + ######### # Sympy # @@ -780,9 +784,8 @@ def relation(self, ex, op): sage: s.relation(x > 0, operator.gt) x > 0 """ - from operator import eq, ne, gt, lt, ge, le from sympy import Eq, Ne, Gt, Lt, Ge, Le - ops = {eq : Eq, ne : Ne, gt : Gt, lt : Lt, ge : Ge, le : Le} + ops = {eq: Eq, ne: Ne, gt: Gt, lt: Lt, ge: Ge, le: Le} return ops.get(op)(self(ex.lhs()), self(ex.rhs()), evaluate=False) def composition(self, ex, operator): @@ -940,6 +943,7 @@ def derivative(self, ex, operator): sympy_converter = SympyConverter() + ########## # FriCAS # ########## @@ -1096,7 +1100,7 @@ def derivative(self, ex, operator): """ from sage.symbolic.ring import is_SymbolicVariable - args = ex.operands() # the arguments the derivative is evaluated at + args = ex.operands() # the arguments the derivative is evaluated at params = operator.parameter_set() params_set = set(params) mult = ",".join(str(params.count(i)) for i in params_set) @@ -1123,8 +1127,10 @@ def derivative(self, ex, operator): return outstr + fricas_converter = FriCASConverter() + ############# # Algebraic # ############# @@ -1199,7 +1205,7 @@ def arithmetic(self, ex, operator): # can change the value of a radical expression (by changing which # root is selected). try: - if operator is _operator.pow: + if operator is pow: from sage.rings.all import Rational base, expt = ex.operands() base = self.field(base) @@ -1207,20 +1213,20 @@ def arithmetic(self, ex, operator): return self.field(base**expt) else: if operator is add_vararg: - operator = _operator.add + operator = add elif operator is mul_vararg: - operator = _operator.mul + operator = mul return reduce(operator, map(self, ex.operands())) except TypeError: pass - if operator is _operator.pow: + if operator is pow: from sage.symbolic.constants import e, pi, I base, expt = ex.operands() - if base == e and expt / (pi*I) in QQ: + if base == e and expt / (pi * I) in QQ: return exp(expt)._algebraic_(self.field) - raise TypeError("unable to convert %r to %s"%(ex, self.field)) + raise TypeError("unable to convert %r to %s" % (ex, self.field)) def composition(self, ex, operator): """ @@ -1291,17 +1297,17 @@ def composition(self, ex, operator): if func_name == 'exp': if operand.is_trivial_zero(): return self.field.one() - if not (SR(-1).sqrt()*operand).is_real(): + if not (SR(-1).sqrt() * operand).is_real(): raise ValueError("unable to represent as an algebraic number") # Coerce (not convert, see #22571) arg to a rational arg = operand.imag()/(2*ex.parent().pi()) try: rat_arg = QQ.coerce(arg.pyobject()) except TypeError: - raise TypeError("unable to convert %r to %s"%(ex, self.field)) + raise TypeError("unable to convert %r to %s" % (ex, self.field)) res = zeta(rat_arg.denom())**rat_arg.numer() elif func_name in ['sin', 'cos', 'tan']: - exp_ia = exp(SR(-1).sqrt()*operand, hold=hold)._algebraic_(QQbar) + exp_ia = exp(SR(-1).sqrt() * operand, hold=hold)._algebraic_(QQbar) if func_name == 'sin': res = (exp_ia - ~exp_ia) / (2 * zeta(4)) elif func_name == 'cos': @@ -1322,13 +1328,14 @@ def composition(self, ex, operator): res = ~self.reciprocal_trig_functions[func_name](operand)._algebraic_(QQbar) else: res = func(operand._algebraic_(self.field)) - #We have to handle the case where we get the same symbolic - #expression back. For example, QQbar(zeta(7)). See - #ticket #12665. + # We have to handle the case where we get the same symbolic + # expression back. For example, QQbar(zeta(7)). See + # ticket #12665. if (res - ex).is_trivial_zero(): - raise TypeError("unable to convert %r to %s"%(ex, self.field)) + raise TypeError("unable to convert %r to %s" % (ex, self.field)) return self.field(res) + def algebraic(ex, field): """ Returns the symbolic expression *ex* as a element of the algebraic @@ -1372,6 +1379,7 @@ def algebraic(ex, field): """ return AlgebraicConverter(field)(ex) + ############## # Polynomial # ############## @@ -1424,7 +1432,7 @@ def __init__(self, ex, base_ring=None, ring=None): self.varnames = ring.variable_names_recursive() for v in ex.variables(): if repr(v) not in self.varnames and v not in base_ring: - raise TypeError("%s is not a variable of %s" %(v, ring)) + raise TypeError("%s is not a variable of %s" % (v, ring)) self.ring = ring self.base_ring = base_ring elif base_ring is not None: @@ -1456,10 +1464,10 @@ def symbol(self, ex): y """ try: - #The symbol is one of the polynomial generators + # The symbol is one of the polynomial generators return self.ring(repr(ex)) except TypeError: - #The symbol should go into the base ring + # The symbol should go into the base ring return self.base_ring(repr(ex)) def pyobject(self, ex, obj): @@ -1541,17 +1549,18 @@ def arithmetic(self, ex, operator): """ if not any(repr(v) in self.varnames for v in ex.variables()): return self.base_ring(ex) - elif operator == _operator.pow: + elif operator == pow: from sage.rings.integer import Integer base, exp = ex.operands() return self(base)**Integer(exp) if operator == add_vararg: - operator = _operator.add + operator = add elif operator == mul_vararg: - operator = _operator.mul + operator = mul ops = [self(a) for a in ex.operands()] return reduce(operator, ops) + def polynomial(ex, base_ring=None, ring=None): """ Return a polynomial from the symbolic expression ``ex``. @@ -1739,7 +1748,7 @@ def relation(self, ex, operator): ... NotImplementedError """ - if operator is not _operator.eq: + if operator is not eq: raise NotImplementedError return self(ex.lhs() - ex.rhs()) @@ -1777,23 +1786,23 @@ def arithmetic(self, ex, operator): # exponent before the exponent gets (potentially) converted # to another type. operands = ex.operands() - if operator is _operator.pow: + if operator is pow: exponent = operands[1] if exponent == -1: - return self.etb.call(_operator.truediv, 1, operands[0]) + return self.etb.call(truediv, 1, operands[0]) elif exponent == 0.5: from sage.misc.functional import sqrt return self.etb.call(sqrt, operands[0]) elif exponent == -0.5: from sage.misc.functional import sqrt - return self.etb.call(_operator.truediv, 1, self.etb.call(sqrt, operands[0])) - elif operator is _operator.neg: + return self.etb.call(truediv, 1, self.etb.call(sqrt, operands[0])) + elif operator is neg: return self.etb.call(operator, operands[0]) if operator == add_vararg: - operator = _operator.add + operator = add elif operator == mul_vararg: - operator = _operator.mul - return reduce(lambda x,y: self.etb.call(operator, x,y), operands) + operator = mul + return reduce(lambda x, y: self.etb.call(operator, x, y), operands) def symbol(self, ex): r""" @@ -1846,6 +1855,7 @@ def tuple(self, ex): """ return ex.operands() + def fast_callable(ex, etb): """ Given an ExpressionTreeBuilder *etb*, return an Expression representing @@ -1867,6 +1877,7 @@ def fast_callable(ex, etb): """ return FastCallableConverter(ex, etb)() + class RingConverter(Converter): def __init__(self, R, subs_dict=None): """ @@ -1940,15 +1951,15 @@ def arithmetic(self, ex, operator): sage: R(a) 2*z^2 + z + 3 """ - if operator not in [_operator.pow, add_vararg, mul_vararg]: + if operator not in [pow, add_vararg, mul_vararg]: raise TypeError operands = ex.operands() - if operator is _operator.pow: + if operator is pow: from sage.all import Integer, Rational base, expt = operands - if expt == Rational(((1,2))): + if expt == Rational(((1, 2))): from sage.misc.functional import sqrt return sqrt(self(base)) try: @@ -1960,9 +1971,9 @@ def arithmetic(self, ex, operator): return base ** expt if operator == add_vararg: - operator = _operator.add + operator = add elif operator == mul_vararg: - operator = _operator.mul + operator = mul return reduce(operator, map(self, operands)) def composition(self, ex, operator): @@ -1974,7 +1985,7 @@ def composition(self, ex, operator): sage: R(cos(2)) -0.4161468365471424? """ - res = operator(*[self(_) for _ in ex.operands()]) + res = operator(*[self(op) for op in ex.operands()]) if res.parent() is not self.ring: raise TypeError else: @@ -2094,6 +2105,7 @@ def tuple(self, ex): """ return ex.operands() + class SubstituteFunction(ExpressionTreeWalker): def __init__(self, ex, *args): """ @@ -2178,6 +2190,7 @@ def derivative(self, ex, operator): else: return operator(*[self(_) for _ in ex.operands()]) + class Exponentialize(ExpressionTreeWalker): # Implementation note: this code is executed once at first # reference in the code using it, therefore avoiding rebuilding @@ -2216,7 +2229,7 @@ def __init__(self, ex): expressions. EXAMPLES:: - + sage: from sage.symbolic.expression_conversions import Exponentialize sage: d = Exponentialize(sin(x)) sage: d(sin(x)) @@ -2268,7 +2281,7 @@ def __init__(self, ex, force=False): """ self.ex = ex self.force = force - + def composition(self, ex, op): """ Return the composition of ``self`` with ``ex`` by ``op``. @@ -2306,6 +2319,7 @@ def composition(self, ex, op): return cosh(arg) + sinh(arg) return exp(arg) + class HoldRemover(ExpressionTreeWalker): def __init__(self, ex, exclude=None): """ diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index 7ec212f0631..9681f0b8c53 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -24,8 +24,6 @@ # # https://www.gnu.org/licenses/ # ****************************************************************************** - -import os import sys from sage.structure.sage_object import SageObject From a90608f7ac6cd3a63ada3ae939ec4b316a902639 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 14 Aug 2022 13:40:32 -0700 Subject: [PATCH 160/454] build/bin/write-dockerfile.sh: Invoke sage-package directly, do not go through ./sage --- build/bin/write-dockerfile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index e9ec84dc925..27b7d38b7f8 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -15,7 +15,7 @@ SAGE_ROOT=. export PATH="$SAGE_ROOT"/build/bin:$PATH SYSTEM_PACKAGES=$EXTRA_SYSTEM_PACKAGES CONFIGURE_ARGS="--enable-option-checking " -for PKG_BASE in $($SAGE_ROOT/sage -package list --has-file=distros/$SYSTEM.txt $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do +for PKG_BASE in $(sage-package list --has-file=distros/$SYSTEM.txt $SAGE_PACKAGE_LIST_ARGS) $EXTRA_SAGE_PACKAGES; do PKG_SCRIPTS="$SAGE_ROOT"/build/pkgs/$PKG_BASE if [ -d $PKG_SCRIPTS ]; then SYSTEM_PACKAGES_FILE=$PKG_SCRIPTS/distros/$SYSTEM.txt From 89fc91c0308c35f43ce4d5d79542b8f1d8547945 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 17 Aug 2022 17:13:22 +0900 Subject: [PATCH 161/454] Adding hooks for (graded) free resolutions and handling cases when given a free module. --- src/sage/homology/free_resolution.py | 89 +++++++++++++++-- src/sage/homology/graded_resolution.py | 95 +++++++++++++++++-- src/sage/modules/free_module.py | 47 +++++++++ src/sage/rings/ideal.py | 42 +++++++- src/sage/rings/polynomial/ideal.py | 1 + .../polynomial/multi_polynomial_ideal.py | 56 ++++++++++- 6 files changed, 311 insertions(+), 19 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 4468585c4f6..e3a8eab1344 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -69,10 +69,12 @@ from sage.libs.singular.singular import si2sa_resolution from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute -from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense +from sage.structure.element import Matrix +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.integral_domains import IntegralDomains from sage.modules.free_module_element import vector from sage.modules.free_module import FreeModule -from sage.modules.free_module import Module_free_ambient +from sage.modules.free_module import Module_free_ambient, FreeModule_generic from sage.rings.ideal import Ideal_generic from sage.structure.sage_object import SageObject @@ -513,15 +515,60 @@ def __init__(self, module, name='S', algorithm='heuristic'): sage: M = m.image() sage: r = FreeResolution(M, name='S') sage: TestSuite(r).run(skip=['_test_pickling']) + + sage: R. = QQ[] + sage: I = R.ideal([x^2, y^3]) + sage: Q = R.quo(I) + sage: Q.is_integral_domain() + False + sage: xb, yb = Q.gens() + sage: FreeResolution(Q.ideal([xb])) # has torsion + Traceback (most recent call last): + ... + NotImplementedError: the ring must be a polynomial ring that can be constructed in Singular + + An overdetermined system over a PID:: + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = FreeResolution(M) + sage: res + S^2 <-- S^2 <-- 0 + sage: res._m() + [ x^2 3*x^2 5*x^2] + [ 2 5 4] + sage: res._maps + [ + [x^2 0] + [ 0 1] + ] """ + # The module might still be free even if _is_free_module is False. + # This is just to handle the cases when we trivially know it is. + self._is_free_module = False if isinstance(module, Ideal_generic): S = module.ring() + if len(module.gens()) == 1 and S in IntegralDomains(): + self._is_free_module = True elif isinstance(module, Module_free_ambient): S = module.base_ring() - elif isinstance(module, Matrix_mpolynomial_dense): + self._is_free_module = (S in PrincipalIdealDomains() + or isinstance(module, FreeModule_generic)) + elif isinstance(module, Matrix): S = module.base_ring() + if S in PrincipalIdealDomains(): + self._is_free_module = True + module = module.echelon_form() + if module.nrows() > module.rank(): + module = module.submatrix(nrows=module.rank()) else: - raise TypeError('no ideal, module, or matrix') + raise TypeError('no module, matrix, or ideal') + + if not self._is_free_module: + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if not isinstance(S, MPolynomialRing_libsingular): + raise NotImplementedError("the ring must be a polynomial ring that can be constructed in Singular") self._module = module self._algorithm = algorithm @@ -554,7 +601,7 @@ def _m(self): return self._module if isinstance(self._module, Module_free_ambient): return self._module.matrix().transpose() - if isinstance(self._module, Matrix_mpolynomial_dense): + if isinstance(self._module, Matrix): return self._module.transpose() @lazy_attribute @@ -574,7 +621,37 @@ def _maps(self): [ z -y] [z^2 - y*w y*z - x*w y^2 - x*z], [-w z] ] + + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: S + Free module of degree 3 and rank 2 over Univariate Polynomial Ring in x over Rational Field + Echelon basis matrix: + [ x^2 2*x^2 3*x^2] + [ 0 x 2*x] + sage: res = S.free_resolution() + sage: res + S^3 <-- S^2 <-- 0 + sage: ascii_art(res.chain_complex()) + [ x^2 0] + [2*x^2 x] + [3*x^2 2*x] + 0 <-- C_0 <-------------- C_1 <-- 0 + sage: res._maps + [ + [ x^2 0] + [2*x^2 x] + [3*x^2 2*x] + ] """ + if self._is_free_module: + if isinstance(self._module, Ideal_generic): + return matrix([[self._module.gen()]]) + return [self._m()] + # This ensures the first component of the Singular resolution to be a # module, like the later components. This is important when the # components are converted to Sage modules. @@ -632,7 +709,7 @@ def _initial_differential(self): S = ideal.base_ring() M = ideal.ambient_module() N = ideal - elif isinstance(ideal, Matrix_mpolynomial_dense): + elif isinstance(ideal, Matrix): S = ideal.base_ring() N = ideal.row_space() M = N.ambient_module() diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index a73aa7f5c9f..cb5f0b82421 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -76,7 +76,7 @@ from sage.libs.singular.singular import si2sa_resolution_graded from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute -from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense +from sage.structure.element import Matrix from sage.modules.free_module_element import vector from sage.modules.free_module import Module_free_ambient from sage.rings.integer_ring import ZZ @@ -86,7 +86,7 @@ from sage.homology.free_resolution import FreeResolution class GradedFreeResolution(FreeResolution): - """ + r""" Graded free resolutions of ideals of multivariate polynomial rings. INPUT: @@ -166,23 +166,29 @@ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuri sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) sage: r = GradedFreeResolution(I) sage: TestSuite(r).run(skip=['_test_pickling']) + + An overdetermined system over a PID:: + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = GradedFreeResolution(M) + sage: res + S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 + sage: res._res_shifts + [[2, 0]] """ super().__init__(module, name=name, algorithm=algorithm) nvars = self._base_ring.ngens() - if isinstance(self._module, Ideal_generic): - rank = 1 - elif isinstance(self._module, Module_free_ambient): - rank = self._m().nrows() - elif isinstance(self._module, Matrix_mpolynomial_dense): - rank = self._module.ncols() - if degrees is None: degrees = nvars * (1,) # standard grading if len(degrees) != nvars: raise ValueError('the length of degrees does not match the number of generators') + if self._is_free_module and len(degrees) > 1 and any(d != 1 for d in degrees): + raise NotImplementedError("only the natural grading supported when more than one generator") if degrees[0] in ZZ: zero_deg = 0 @@ -193,6 +199,13 @@ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuri multigrade = True if shifts is None: + if isinstance(self._module, Ideal_generic): + rank = 1 + elif isinstance(self._module, Module_free_ambient): + rank = self._m().nrows() + elif isinstance(self._module, Matrix): + rank = self._module.ncols() + shifts = rank * [zero_deg] self._shifts = shifts @@ -221,7 +234,71 @@ def _maps(self): ] sage: r._res_shifts [[2, 2, 2], [3, 3]] + + sage: R. = QQ[] + sage: M = matrix([[x^3, 3*x^3, 5*x^3], + ....: [0, x, 2*x]]) + sage: res = GradedFreeResolution(M) + sage: res + S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 + sage: res._maps + [ + [ x^3 0] + [3*x^3 x] + [5*x^3 2*x] + ] + sage: res._res_shifts + [[3, 1]] + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = GradedFreeResolution(M) + sage: res + S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 + sage: res._res_shifts + [[2, 0]] + + sage: I = R.ideal([x^4]) + sage: res = I.graded_free_resolution(shifts=[1], degrees=[2]) + sage: res + S(-1) <-- S(-9) <-- 0 + sage: res._maps + [[x^4]] + sage: res._res_shifts + [[9]] """ + if self._is_free_module: + + def compute_degree(base, i): + """ + Compute the degree by ``base * deg + shift``, + where ``*`` is entry-wise multiplication, ``deg`` and + ``shift`` are the ``i``-th index. + """ + deg = self._degrees[0] + shift = self._shifts[i] + if self._multigrade: + return vector([val * d + s for val, d, s in zip(base, deg, shift)]) + return base * deg + shift + + if isinstance(self._module, Ideal_generic): + from sage.matrix.constructor import matrix + val = self._module.gen(0) + self._res_shifts = [[compute_degree(val.degree(), 0)]] + return [matrix([[val]])] + + M = self._m() + def find_deg(i): + for j in range(M.nrows()): + ret = M[j,i].degree() + if ret != -1: + return ret + raise NotImplementedError("a generator maps to 0") + self._res_shifts = [[compute_degree(find_deg(i), i) + for i in range(M.ncols())]] + return [M] + #cdef int i, j, k, ncols, nrows #cdef list res_shifts, prev_shifts, new_shifts diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 9946a588c85..b3bd30bb78f 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1759,6 +1759,53 @@ def __truediv__(self, sub): """ return self.quotient(sub, check=True) + def free_resolution(self, *args, **kwds): + r""" + Return a free resolution of ``self``. + + For input options, see + :class:`~sage.homology.free_resolution.FreeResolution`. + + EXAMPLES:: + + sage: S. = PolynomialRing(QQ) + sage: M = S**2 + sage: N = M.submodule([vector([x - y, z]), vector([y * z, x * z])]) + sage: res = N.free_resolution() + sage: res + S^2 <-- S^2 <-- 0 + sage: ascii_art(res.chain_complex()) + [x - y y*z] + [ z x*z] + 0 <-- C_0 <-------------- C_1 <-- 0 + """ + from sage.homology.free_resolution import FreeResolution + return FreeResolution(self, *args, **kwds) + + def graded_free_resolution(self, *args, **kwds): + r""" + Return a graded free resolution of ``self``. + + For input options, see + :class:`~sage.homology.graded_resolution.GradedFreeResolution`. + + EXAMPLES:: + + sage: S. = PolynomialRing(QQ) + sage: M = S**2 + sage: N = M.submodule([vector([x - y, z]), vector([y * z, x * z])]) + sage: N.graded_free_resolution(shifts=[1, -1]) + S(-1)⊕S(--1) <-- S(-2)⊕S(-3) <-- 0 + sage: N.graded_free_resolution(shifts=[2, 3]) + S(-2)⊕S(-3) <-- S(-3)⊕S(-4) <-- 0 + + sage: N = M.submodule([vector([x^3 - y^6, z^2]), vector([y * z, x])]) + sage: N.graded_free_resolution(degrees=[2, 1, 3], shifts=[2, 3]) + S(-2)⊕S(-3) <-- S(-6)⊕S(-8) <-- 0 + """ + from sage.homology.graded_resolution import GradedFreeResolution + return GradedFreeResolution(self, *args, **kwds) + class FreeModule_generic(Module_free_ambient): """ diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 8bb1de564f3..85203abed43 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1203,6 +1203,38 @@ def _macaulay2_init_(self, macaulay2=None): gens = ['0'] return macaulay2.ideal(gens) + def free_resolution(self, *args, **kwds): + r""" + Return a free resolution of ``self``. + + For input options, see + :class:`~sage.homology.free_resolution.FreeResolution`. + + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: I.free_resolution() + """ + from sage.homology.free_resolution import FreeResolution + return FreeResolution(self, *args, **kwds) + + def graded_free_resolution(self, *args, **kwds): + r""" + Return a graded free resolution of ``self``. + + For input options, see + :class:`~sage.homology.graded_resolution.GradedFreeResolution`. + + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal([x^3]) + sage: I.graded_free_resolution() + """ + from sage.homology.graded_resolution import GradedFreeResolution + return GradedFreeResolution(self, *args, **kwds) + class Ideal_principal(Ideal_generic): """ @@ -1257,10 +1289,11 @@ def is_principal(self): """ return True - def gen(self): + def gen(self, i=0): r""" - Returns the generator of the principal ideal. The generators are - elements of the ring containing the ideal. + Return the generator of the principal ideal. + + The generator is an element of the ring containing the ideal. EXAMPLES: @@ -1288,6 +1321,8 @@ def gen(self): sage: b.base_ring() Rational Field """ + if i: + raise ValueError(f"i (={i}) must be 0") return self.gens()[0] def __contains__(self, x): @@ -1816,3 +1851,4 @@ def FieldIdeal(R): raise TypeError("Cannot construct field ideal for R.base_ring().order()==infinity") return R.ideal([x**q - x for x in R.gens() ]) + diff --git a/src/sage/rings/polynomial/ideal.py b/src/sage/rings/polynomial/ideal.py index 857718c9a10..b768ed1a96b 100644 --- a/src/sage/rings/polynomial/ideal.py +++ b/src/sage/rings/polynomial/ideal.py @@ -84,3 +84,4 @@ def groebner_basis(self, algorithm=None): gb = self.gens_reduced() from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence_generic return PolynomialSequence_generic([gb], self.ring(), immutable=True) + diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 10c0db501af..46feca52b20 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1775,6 +1775,60 @@ def syzygy_module(self): S = syz(self) return matrix(self.ring(), S) + @require_field + def free_resolution(self, *args, **kwds): + r""" + Return a free resolution of ``self``. + + For input options, see + :class:`~sage.homology.free_resolution.FreeResolution`. + + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: f = 2*x^2 + y + sage: g = y + sage: h = 2*f + g + sage: I = R.ideal([f,g,h]) + sage: res = I.free_resolution() + sage: res + S^1 <-- S^2 <-- S^1 <-- 0 + sage: ascii_art(res.chain_complex()) + [-x^2] + [ y x^2] [ y] + 0 <-- C_0 <---------- C_1 <------- C_2 <-- 0 + """ + from sage.homology.free_resolution import FreeResolution + return FreeResolution(self, *args, **kwds) + + @require_field + def graded_free_resolution(self, *args, **kwds): + r""" + Return a graded free resolution of ``self``. + + For input options, see + :class:`~sage.homology.graded_resolution.GradedFreeResolution`. + + EXAMPLES:: + + sage: R. = PolynomialRing(QQ) + sage: f = 2*x^2 + y^2 + sage: g = y^2 + sage: h = 2*f + g + sage: I = R.ideal([f,g,h]) + sage: I.graded_free_resolution(shifts=[1]) + S(-1) <-- S(-3)⊕S(-3) <-- S(-5) <-- 0 + + sage: f = 2*x^2 + y + sage: g = y + sage: h = 2*f + g + sage: I = R.ideal([f,g,h]) + sage: I.graded_free_resolution(degrees=[1,2]) + S(0) <-- S(-2)⊕S(-2) <-- S(-4) <-- 0 + """ + from sage.homology.graded_resolution import GradedFreeResolution + return GradedFreeResolution(self, *args, **kwds) + @handle_AA_and_QQbar @singular_gb_standard_options @libsingular_gb_standard_options @@ -5224,7 +5278,6 @@ class MPolynomialIdeal_quotient(MPolynomialIdeal): Ideal (w^2 + x + z - 1) of Quotient of Multivariate Polynomial Ring in x, y, z, w over Rational Field by the ideal (x*y - z^2, y^2 - w^2) """ - def reduce(self, f): r""" Reduce an element modulo a Gröbner basis for this ideal. @@ -5330,3 +5383,4 @@ def __richcmp__(self, other, op): return not (contained and contains) else: # remaining case < return contained and not contains + From dfb33843f5cbfb63a73d20e2b8b9197d3a273726 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 17 Aug 2022 17:21:30 +0900 Subject: [PATCH 162/454] Rewriting the _repr_module() of graded resolutions to negate the grading. --- src/sage/homology/graded_resolution.py | 34 ++++++++++++-------------- src/sage/modules/free_module.py | 2 +- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index cb5f0b82421..4a38a347e5d 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -53,7 +53,7 @@ sage: C = P3.subscheme(I) # twisted cubic curve sage: r = GradedFreeResolution(I, degrees=[(1,0), (1,1), (1,2), (1,3)]) sage: r - S(0) <-- S(-(2, 4))⊕S(-(2, 3))⊕S(-(2, 2)) <-- S(-(3, 5))⊕S(-(3, 4)) <-- 0 + S((0, 0)) <-- S((-2, -4))⊕S((-2, -3))⊕S((-2, -2)) <-- S((-3, -5))⊕S((-3, -4)) <-- 0 sage: r.K_polynomial(names='s,t') s^3*t^5 + s^3*t^4 - s^2*t^4 - s^2*t^3 - s^2*t^2 + 1 @@ -368,28 +368,24 @@ def _repr_module(self, i): 'S(-3)⊕S(-3)' sage: r._repr_module(3) '0' + + sage: r = GradedFreeResolution(I, shifts=[-1]) + sage: r._repr_module(0) + 'S(1)' """ self._maps # to set _res_shifts if i > len(self): - m = '0' + return '0' + + if i == 0: + shifts = self._shifts else: - if i == 0: - shifts = self._shifts - else: - shifts = self._res_shifts[i - 1] - - if len(shifts) > 0: - for j in range(len(shifts)): - shift = shifts[j] - if j == 0: - m = f'{self._name}' + \ - (f'(-{shift})' if shift != self._zero_deg else '(0)') - else: - m += f'\u2295{self._name}' + \ - (f'(-{shift})' if shift != self._zero_deg else '(0)') - else: - m = '0' - return m + shifts = self._res_shifts[i - 1] + if not shifts: + return '0' + + return '\u2295'.join(f'{self._name}' + '({})'.format(-sh) + for sh in shifts) def shifts(self, i): """ diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b3bd30bb78f..1acf13bbb9e 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1795,7 +1795,7 @@ def graded_free_resolution(self, *args, **kwds): sage: M = S**2 sage: N = M.submodule([vector([x - y, z]), vector([y * z, x * z])]) sage: N.graded_free_resolution(shifts=[1, -1]) - S(-1)⊕S(--1) <-- S(-2)⊕S(-3) <-- 0 + S(-1)⊕S(1) <-- S(-2)⊕S(-3) <-- 0 sage: N.graded_free_resolution(shifts=[2, 3]) S(-2)⊕S(-3) <-- S(-3)⊕S(-4) <-- 0 From d02630a545d3b0ff037289f1d938cf302563ce3c Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 18 Aug 2022 13:35:29 +0100 Subject: [PATCH 163/454] Fixed formatting errors. --- .../schemes/riemann_surfaces/riemann_surface.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index edbb2aeb720..0e6620d11fc 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -17,8 +17,8 @@ period lattice numerically, by determining integer (near) solutions to the relevant approximate linear equations. -One can also calculate the Abel-Jacobi map on the Riemann surface, and there -is basic functionality to interface with divisors of curves to facilitate this. +One can also calculate the Abel-Jacobi map on the Riemann surface, and there +is basic functionality to interface with divisors of curves to facilitate this. AUTHORS: @@ -138,6 +138,7 @@ from sage.schemes.curves.constructor import Curve import sage.libs.mpmath.all as mpall + def voronoi_ghost(cpoints, n=6, CC=CDF): r""" Convert a set of complex points to a list of real tuples `(x,y)`, and @@ -1837,8 +1838,9 @@ def _bounding_data(self, differentials, exact=False): A tuple ``(Rzg, [(g, dgdz, F, a0_info), ...])`` where each element of the list corresponds to an element of ``differentials``. Introducing the - notation ``RBzg = PolynomialRing(self._R, ['z','g'])`` and + notation ``RBzg = PolynomialRing(self._R, ['z','g'])`` and ``CCzg = PolynomialRing(self._CC, ['z','g'])``, we have that: + - ``Rzg`` is either ``RBzg`` or ``CCzg`` depending on the value of ``exact``, - ``g`` is the full rational function in ``self._R.fraction_field()`` @@ -2834,6 +2836,7 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall mp_list = [CCzg(mp) for mp in mp_list] J = 1/z_end endscale = -z_end**(-2) + def initialise(z, i): DF = ComplexField(2*self._prec) DFw = PolynomialRing(DF,'wbar') @@ -2851,6 +2854,7 @@ def initialise(z, i): CCzg = mp_list[0].parent() J = z_end-z_start endscale = 1 + def initialise(z, i): newg = self.cohomology_basis()[i](z_start, w_start)/self._dfdw(z_start, w_start) err = mp_list[i](z, newg).abs() @@ -2892,11 +2896,13 @@ def initialise(z, i): cutoff_individually = bool(not all(ai<=0 for ai in aes) and cutoff_individually) if raise_errors: - n_steps = self._prec-1 + n_steps = self._prec-1 + def error_handle(out): raise ConvergenceError("Newton iteration fails to converge") else: n_steps = 15 + def error_handle(out): return out @@ -3583,6 +3589,7 @@ def divisor_to_divisor_list(self, divisor): raise ValueError("Numerical instability, list of wrong degree") return dl + def integer_matrix_relations(M1, M2, b=None, r=None): r""" Determine integer relations between complex matrices. From 9e6ca2a67102d4c133173cedaa5ca505c2082ddf Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 21 Aug 2022 20:05:20 +0900 Subject: [PATCH 164/454] Replace pdflatex to latex --- src/sage/features/latex.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index cf65aea6afc..c3e28a6da9c 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -37,7 +37,7 @@ def __init__(self, name): sage: isinstance(latex(), latex) True """ - Executable.__init__(self, name, executable=name, spkg=latex_spkg, url=latex_url) + super().__init__(name, executable=name, spkg=latex_spkg, url=latex_url) def is_functional(self): r""" @@ -97,7 +97,7 @@ def __init__(self): sage: isinstance(latex(), latex) True """ - LaTeX.__init__(self, "latex") + super().__init__("latex") class pdflatex(LaTeX): @@ -118,7 +118,7 @@ def __init__(self): sage: isinstance(pdflatex(), pdflatex) True """ - LaTeX.__init__(self, "pdflatex") + super().__init__("pdflatex") class xelatex(LaTeX): @@ -139,7 +139,7 @@ def __init__(self): sage: isinstance(xelatex(), xelatex) True """ - LaTeX.__init__(self, "xelatex") + super().__init__("xelatex") class lualatex(LaTeX): @@ -160,7 +160,7 @@ def __init__(self): sage: isinstance(lualatex(), lualatex) True """ - LaTeX.__init__(self, "lualatex") + super().__init__("lualatex") class TeXFile(StaticFile, JoinFeature): @@ -170,20 +170,20 @@ class TeXFile(StaticFile, JoinFeature): EXAMPLES:: sage: from sage.features.latex import TeXFile - sage: TeXFile('x', 'x.tex').is_present() # optional: pdflatex + sage: TeXFile('x', 'x.tex').is_present() # optional - latex FeatureTestResult('x', True) - sage: TeXFile('nonexisting', 'xxxxxx-nonexisting-file.tex').is_present() # optional - pdflatex + sage: TeXFile('nonexisting', 'xxxxxx-nonexisting-file.tex').is_present() # optional - latex FeatureTestResult('nonexisting', False) """ def __init__(self, name, filename, **kwds): r""" EXAMPLES:: - sage: from sage.features.latex import LaTeXPackage, pdflatex + sage: from sage.features.latex import LaTeXPackage sage: LaTeXPackage("tkz-graph")._features - [Feature('pdflatex')] + [Feature('latex')] """ - JoinFeature.__init__(self, name, [pdflatex()], + JoinFeature.__init__(self, name, [latex()], spkg=latex_spkg, url=latex_url) # see :trac:`34282` StaticFile.__init__(self, name, filename, search_path=[], **kwds) @@ -195,7 +195,7 @@ def absolute_filename(self) -> str: sage: from sage.features.latex import TeXFile sage: feature = TeXFile('latex_class_article', 'article.cls') - sage: feature.absolute_filename() # optional - pdflatex + sage: feature.absolute_filename() # optional - latex '.../latex/base/article.cls' """ from subprocess import run, CalledProcessError, PIPE @@ -214,16 +214,13 @@ def _is_present(self): EXAMPLES:: - sage: from sage.features.latex import LaTeXPackage, pdflatex + sage: from sage.features.latex import LaTeXPackage, latex sage: f = LaTeXPackage("tkz-graph") - sage: g = pdflatex() - sage: bool(f.is_present()) == bool(g.is_present()) # indirect doctest + sage: g = latex() + sage: not f.is_present() or bool(g.is_present()) # indirect doctest True """ - test = JoinFeature._is_present(self) - if not test: - return test - return super(TeXFile, self)._is_present() + return JoinFeature._is_present(self) and StaticFile._is_present(self) class LaTeXPackage(TeXFile): @@ -234,7 +231,7 @@ class LaTeXPackage(TeXFile): EXAMPLES:: sage: from sage.features.latex import LaTeXPackage - sage: LaTeXPackage('graphics').is_present() # optional - pdflatex + sage: LaTeXPackage('graphics').is_present() # optional - latex FeatureTestResult('latex_package_graphics', True) """ @staticmethod From 553f6ffc7473fabcff8e630b995bd7e3dc671207 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 21 Aug 2022 20:43:16 +0900 Subject: [PATCH 165/454] Do not use JoinFeature --- src/sage/features/latex.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index c3e28a6da9c..a3c8f3b4ab1 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -163,7 +163,7 @@ def __init__(self): super().__init__("lualatex") -class TeXFile(StaticFile, JoinFeature): +class TeXFile(StaticFile): r""" A :class:`sage.features.Feature` describing the presence of a TeX file @@ -172,19 +172,16 @@ class TeXFile(StaticFile, JoinFeature): sage: from sage.features.latex import TeXFile sage: TeXFile('x', 'x.tex').is_present() # optional - latex FeatureTestResult('x', True) - sage: TeXFile('nonexisting', 'xxxxxx-nonexisting-file.tex').is_present() # optional - latex - FeatureTestResult('nonexisting', False) """ def __init__(self, name, filename, **kwds): r""" - EXAMPLES:: + Initialize. - sage: from sage.features.latex import LaTeXPackage - sage: LaTeXPackage("tkz-graph")._features - [Feature('latex')] + TESTS:: + + sage: TeXFile('nonexisting', 'xxxxxx-nonexisting-file.tex').is_present() # optional - latex + FeatureTestResult('nonexisting', False) """ - JoinFeature.__init__(self, name, [latex()], - spkg=latex_spkg, url=latex_url) # see :trac:`34282` StaticFile.__init__(self, name, filename, search_path=[], **kwds) def absolute_filename(self) -> str: @@ -205,8 +202,8 @@ def absolute_filename(self) -> str: universal_newlines=True, check=True) return proc.stdout.strip() except CalledProcessError: - raise FeatureNotPresentError(self, - reason="{filename!r} not found by kpsewhich".format(filename=self.filename)) + reason = "{filename!r} not found by kpsewhich".format(filename=self.filename) + raise FeatureNotPresentError(self, reason) def _is_present(self): r""" @@ -220,7 +217,7 @@ def _is_present(self): sage: not f.is_present() or bool(g.is_present()) # indirect doctest True """ - return JoinFeature._is_present(self) and StaticFile._is_present(self) + return latex().is_present() and super()._is_present() class LaTeXPackage(TeXFile): From a31205abd7189dfb0f888fe78fd3bfcd4bd09eee Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 21 Aug 2022 21:38:19 +0900 Subject: [PATCH 166/454] A tiny edit --- src/sage/features/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index a3c8f3b4ab1..501c82c0539 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -207,7 +207,7 @@ def absolute_filename(self) -> str: def _is_present(self): r""" - Test for the presence of the TeX-file. + Test for the presence of the TeX file. EXAMPLES:: From d4c8b90fddd0e9aaabe99d3062b182b191700eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 20 Aug 2022 12:03:57 +0200 Subject: [PATCH 167/454] add tensor_factors method to tensor products --- src/sage/categories/modules.py | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index d37b4812209..1410f650a4d 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -551,6 +551,42 @@ def extra_super_categories(self): """ return [self.base_category()] + class ParentMethods: + """ + Implement operations on tensor products of modules. + """ + def construction(self): + """ + Return the construction of ``self``. + + EXAMPLES:: + + sage: A = algebras.Free(QQ,2) + sage: T = A.tensor(A) + sage: T.construction() + [The tensor functorial construction, + (Free Algebra on 2 generators (None0, None1) over Rational Field, + Free Algebra on 2 generators (None0, None1) over Rational Field)] + """ + return [tensor, self._sets] + + def tensor_factors(self): + """ + Return the tensor factors of this tensor product. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(ZZ, [1,2]) + sage: F.__custom_name = "F" + sage: G = CombinatorialFreeModule(ZZ, [3,4]) + sage: G.__custom_name = "G" + sage: T = tensor([F, G]); T + F # G + sage: T.tensor_factors() + (F, G) + """ + return self._sets + class FinitelyPresented(CategoryWithAxiom_over_base_ring): def extra_super_categories(self): From d0972e6183fbe3396f41dc7fba641e579bd3a30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Aug 2022 08:35:24 +0200 Subject: [PATCH 168/454] trying to use the suggestions about tensor construction --- src/sage/categories/modules.py | 8 +++++--- src/sage/combinat/free_module.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 1410f650a4d..4f48ee0e840 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -12,6 +12,7 @@ # ***************************************************************************** from sage.misc.cachefunc import cached_method +from sage.misc.abstract_method import abstract_method from sage.misc.lazy_import import LazyImport from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.morphism import SetMorphism @@ -19,7 +20,7 @@ from sage.categories.homset import Hom from .category import Category from .category_types import Category_module -from sage.categories.tensor import TensorProductsCategory, tensor +from sage.categories.tensor import TensorProductsCategory, TensorProductFunctor, tensor from .dual import DualObjectsCategory from sage.categories.cartesian_product import CartesianProductsCategory from sage.categories.sets_cat import Sets @@ -568,8 +569,10 @@ def construction(self): (Free Algebra on 2 generators (None0, None1) over Rational Field, Free Algebra on 2 generators (None0, None1) over Rational Field)] """ - return [tensor, self._sets] + return (TensorProductFunctor(self.category()), + self.tensor_factors()) + @abstract_method def tensor_factors(self): """ Return the tensor factors of this tensor product. @@ -585,7 +588,6 @@ def tensor_factors(self): sage: T.tensor_factors() (F, G) """ - return self._sets class FinitelyPresented(CategoryWithAxiom_over_base_ring): diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index a2f789a4202..e1cfa54ffdb 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1306,6 +1306,23 @@ def _repr_(self): return symb.join("%s" % module for module in self._sets) # TODO: make this overridable by setting _name + def tensor_factors(self): + """ + Return the tensor factors of this tensor product. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(ZZ, [1,2]) + sage: F.__custom_name = "F" + sage: G = CombinatorialFreeModule(ZZ, [3,4]) + sage: G.__custom_name = "G" + sage: T = tensor([F, G]); T + F # G + sage: T.tensor_factors() + (F, G) + """ + return self._sets + def _ascii_art_(self, term): """ TESTS:: From f7eff260b32155d69d1eae416ab443d9a8bc592e Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 22 Aug 2022 16:32:19 +0900 Subject: [PATCH 169/454] Fixing last details. --- src/sage/homology/free_resolution.py | 9 ++++++++- src/sage/homology/graded_resolution.py | 1 + src/sage/rings/ideal.py | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index e3a8eab1344..2f9ad448baa 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -646,10 +646,17 @@ def _maps(self): [2*x^2 x] [3*x^2 2*x] ] + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: res = I.free_resolution() + sage: res._maps + [[x^4 + 3*x^2 + 2]] """ if self._is_free_module: if isinstance(self._module, Ideal_generic): - return matrix([[self._module.gen()]]) + from sage.matrix.constructor import matrix + return [matrix([[self._module.gen()]])] return [self._m()] # This ensures the first component of the Singular resolution to be a diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index 4a38a347e5d..bc30e61ef6e 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -289,6 +289,7 @@ def compute_degree(base, i): return [matrix([[val]])] M = self._m() + def find_deg(i): for j in range(M.nrows()): ret = M[j,i].degree() diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 85203abed43..4ca750e6909 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1215,6 +1215,7 @@ def free_resolution(self, *args, **kwds): sage: R. = PolynomialRing(QQ) sage: I = R.ideal([x^4 + 3*x^2 + 2]) sage: I.free_resolution() + S^1 <-- S^1 <-- 0 """ from sage.homology.free_resolution import FreeResolution return FreeResolution(self, *args, **kwds) @@ -1231,6 +1232,7 @@ def graded_free_resolution(self, *args, **kwds): sage: R. = PolynomialRing(QQ) sage: I = R.ideal([x^3]) sage: I.graded_free_resolution() + S(0) <-- S(-3) <-- 0 """ from sage.homology.graded_resolution import GradedFreeResolution return GradedFreeResolution(self, *args, **kwds) From eba9d161893a80d65aff472ee5e26a307872482a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Aug 2022 13:51:17 +0200 Subject: [PATCH 170/454] detail fix --- src/sage/categories/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 4f48ee0e840..ca38625682d 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -569,7 +569,7 @@ def construction(self): (Free Algebra on 2 generators (None0, None1) over Rational Field, Free Algebra on 2 generators (None0, None1) over Rational Field)] """ - return (TensorProductFunctor(self.category()), + return (TensorProductFunctor(), self.tensor_factors()) @abstract_method From c862151e4b82674751fd3a3cbdfa02ffbf752b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Aug 2022 18:52:58 +0200 Subject: [PATCH 171/454] rather use rename --- src/sage/categories/modules.py | 4 ++-- src/sage/combinat/free_module.py | 41 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index ca38625682d..780ab68a44d 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -580,9 +580,9 @@ def tensor_factors(self): EXAMPLES:: sage: F = CombinatorialFreeModule(ZZ, [1,2]) - sage: F.__custom_name = "F" + sage: F.rename("F") sage: G = CombinatorialFreeModule(ZZ, [3,4]) - sage: G.__custom_name = "G" + sage: G.rename("G") sage: T = tensor([F, G]); T F # G sage: T.tensor_factors() diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index e1cfa54ffdb..ec59d021416 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -1159,8 +1159,8 @@ class CombinatorialFreeModule_Tensor(CombinatorialFreeModule): We construct two free modules, assign them short names, and construct their tensor product:: - sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.rename("G") sage: T = tensor([F, G]); T F # G @@ -1313,9 +1313,9 @@ def tensor_factors(self): EXAMPLES:: sage: F = CombinatorialFreeModule(ZZ, [1,2]) - sage: F.__custom_name = "F" + sage: F.rename("F") sage: G = CombinatorialFreeModule(ZZ, [3,4]) - sage: G.__custom_name = "G" + sage: G.rename("G") sage: T = tensor([F, G]); T F # G sage: T.tensor_factors() @@ -1447,8 +1447,8 @@ def tensor_constructor(self, modules): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.rename("G") sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H") sage: f = F.monomial(1) + 2 * F.monomial(2) @@ -1487,8 +1487,8 @@ def _tensor_of_elements(self, elements): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.rename("G") sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H") sage: f = F.monomial(1) + 2 * F.monomial(2) @@ -1622,9 +1622,9 @@ class CombinatorialFreeModule_CartesianProduct(CombinatorialFreeModule): We construct two free modules, assign them short names, and construct their Cartesian product:: - sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" - sage: H = CombinatorialFreeModule(ZZ, [4,7]); H.__custom_name = "H" + sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.rename("G") + sage: H = CombinatorialFreeModule(ZZ, [4,7]); H.rename("H") sage: S = cartesian_product([F, G]) sage: S F (+) G @@ -1696,7 +1696,7 @@ def _repr_(self): sage: F = CombinatorialFreeModule(ZZ, [2,4,5]) sage: CP = cartesian_product([F, F]); CP # indirect doctest Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 5} over Integer Ring - sage: F.__custom_name = "F"; CP + sage: F.rename("F"); CP F (+) F """ from sage.categories.cartesian_product import cartesian_product @@ -1716,8 +1716,8 @@ def cartesian_embedding(self, i): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.rename("G") sage: S = cartesian_product([F, G]) sage: phi = S.cartesian_embedding(0) sage: phi(F.monomial(4) + 2 * F.monomial(5)) @@ -1750,8 +1750,8 @@ def cartesian_projection(self, i): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.rename("G") sage: S = cartesian_product([F, G]) sage: x = S.monomial((0,4)) + 2 * S.monomial((0,5)) + 3 * S.monomial((1,6)) sage: S.cartesian_projection(0)(x) @@ -1780,8 +1780,8 @@ def _cartesian_product_of_elements(self, elements): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.rename("G") sage: S = cartesian_product([F, G]) sage: f = F.monomial(4) + 2 * F.monomial(5) sage: g = 2*G.monomial(4) + G.monomial(6) @@ -1819,12 +1819,11 @@ def cartesian_factors(self): EXAMPLES:: - sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" - sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" + sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.rename("G") sage: S = cartesian_product([F, G]) sage: S.cartesian_factors() (F, G) - """ return self._sets From 44d42db483c61cea2e5497982ad08c12b57f8d96 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 22 Aug 2022 19:53:14 -0700 Subject: [PATCH 172/454] src/sage/categories/modules.py: Deprecate classes without tensor_factors --- src/sage/categories/modules.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 780ab68a44d..58b83b8a785 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -569,10 +569,16 @@ def construction(self): (Free Algebra on 2 generators (None0, None1) over Rational Field, Free Algebra on 2 generators (None0, None1) over Rational Field)] """ + try: + factors = self.tensor_factors() + except (TypeError, NotImplementedError): + from sage.misc.superseded import deprecation + deprecation(34393, "implementations of Modules().TensorProducts() now must define the method tensor_factors") + return None return (TensorProductFunctor(), - self.tensor_factors()) + factors) - @abstract_method + @abstract_method(optional=True) def tensor_factors(self): """ Return the tensor factors of this tensor product. @@ -587,6 +593,15 @@ def tensor_factors(self): F # G sage: T.tensor_factors() (F, G) + + TESTS:: + + sage: M = CombinatorialFreeModule(ZZ, ((1, 1), (1, 2), (2, 1), (2, 2)), + ....: category=ModulesWithBasis(ZZ).FiniteDimensional().TensorProducts()) + sage: M.construction() + doctest:warning... + DeprecationWarning: implementations of Modules().TensorProducts() now must define the method tensor_factors + See https://trac.sagemath.org/34393 for details. """ class FinitelyPresented(CategoryWithAxiom_over_base_ring): From d7f05ce7d49d5cc1e4e7faceb94ead163d32039b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 23 Aug 2022 13:24:43 +0900 Subject: [PATCH 173/454] Initial implementation of q-commuting polynomials. --- src/doc/en/reference/algebras/index.rst | 1 + src/sage/algebras/catalog.py | 7 +- src/sage/algebras/q_commuting_polynomials.py | 276 +++++++++++++++++++ 3 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 src/sage/algebras/q_commuting_polynomials.py diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 898d8ace33e..b6a455a370a 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -98,6 +98,7 @@ Various associative algebras sage/algebras/associated_graded sage/algebras/cellular_basis sage/algebras/q_system + sage/algebras/q_commuting_polynomials sage/algebras/splitting_algebra Non-associative algebras diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 4a6ee3b6985..68b35506a66 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -58,6 +58,8 @@ - :class:`algebras.QSym ` - :class:`algebras.Partition ` - :class:`algebras.PlanarPartition ` +- :class:`algebras.qCommutingPolynomials + ` - :class:`algebras.QuantumGroup ` - :func:`algebras.Quaternion @@ -69,11 +71,11 @@ - :class:`algebras.Steenrod ` - :class:`algebras.TemperleyLieb ` +- :class:`algebras.Tensor ` - :class:`algebras.WQSym ` - :class:`algebras.Yangian ` - :class:`algebras.YokonumaHecke ` -- :class:`algebras.Tensor ` """ from sage.algebras.free_algebra import FreeAlgebra as Free @@ -121,9 +123,12 @@ lazy_import('sage.algebras.quantum_matrix_coordinate_algebra', 'QuantumMatrixCoordinateAlgebra', 'QuantumMatrixCoordinate') lazy_import('sage.algebras.quantum_matrix_coordinate_algebra', 'QuantumGL') +lazy_import('sage.algebras.q_commuting_polynomials', 'qCommutingPolynomials') lazy_import('sage.algebras.tensor_algebra', 'TensorAlgebra', 'Tensor') lazy_import('sage.algebras.quantum_groups.quantum_group_gap', 'QuantumGroup') lazy_import('sage.algebras.quantum_groups.ace_quantum_onsager', 'ACEQuantumOnsagerAlgebra', 'AlternatingCentralExtensionQuantumOnsager') lazy_import('sage.algebras.yangian', 'Yangian') + del lazy_import # We remove the object from here so it doesn't appear under tab completion + diff --git a/src/sage/algebras/q_commuting_polynomials.py b/src/sage/algebras/q_commuting_polynomials.py new file mode 100644 index 00000000000..c948d4e8b20 --- /dev/null +++ b/src/sage/algebras/q_commuting_polynomials.py @@ -0,0 +1,276 @@ +r""" +`q`-Commuting Polynomials + +AUTHORS: + +- Travis Scrimshaw (2022-08-23): Initial version +""" + +# **************************************************************************** +# Copyright (C) 2022 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.sets.family import Family +from sage.rings.infinity import infinity +from sage.categories.algebras import Algebras +from sage.combinat.free_module import CombinatorialFreeModule +from sage.monoids.free_abelian_monoid import FreeAbelianMonoid, FreeAbelianMonoid_class + +class qCommutingPolynomials(CombinatorialFreeModule): + r""" + The algebra of `q`-commuting polynomials. + + Let `R` be a commutative ring, and fix an element `q \in R`. We say two + distinct variables `x` and `y` `q`-*commute* if they satisfy the relation + + .. MATH:: + + x y = q \cdot y x. + + These form a graded `R`-algebra with a natural basis given by monomials + written in increasing order. These then satisfy a `q`-analog of the + classical binomial coefficient theorem: + + .. MATH:: + + (x + y)^n = \sum_{k=0}^n \binom{n}{k}_q x^k y^{n-k}. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + + We verify the `q`-binomial theorem:: + + sage: f = (x + y)^10 + sage: all(f[b] == q_binomial(10, b.list()[0]) for b in f.support()) + True + """ + @staticmethod + def __classcall_private__(cls, q, n=None, base_ring=None, names=None): + r""" + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R1. = algebras.qCommutingPolynomials(q) + sage: R2 = algebras.qCommutingPolynomials(q, base_ring=q.parent(), names='x,y,z') + sage: R3 = algebras.qCommutingPolynomials(q, names=['x', 'y', 'z']) + sage: R1 is R2 is R3 + True + """ + if base_ring is not None: + q = base_ring(q) + + if isinstance(n, FreeAbelianMonoid_class): + indices = n + else: + indices = FreeAbelianMonoid(n, names) + return super().__classcall__(cls, q, indices) + + def __init__(self, q, indices): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: TestSuite(R).run() + """ + self._q = q + base_ring = q.parent() + category = Algebras(base_ring).WithBasis().Graded() + CombinatorialFreeModule.__init__(self, base_ring, indices, + bracket=False, prefix='', + sorting_key=qCommutingPolynomials._term_key, + names=indices.variable_names(), category=category) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R + q-commuting polynomial ring in x, y, z over Fraction Field of + Univariate Polynomial Ring in q over Integer Ring + """ + names = ", ".join(self.variable_names()) + return "{}-commuting polynomial ring in {} over {}".format(self._q, names, self.base_ring()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: latex(R) + \mathrm{Frac}(\Bold{Z}[q])[x, y, z]_{q} + """ + from sage.misc.latex import latex + names = ", ".join(self.variable_names()) + return "{}[{}]_{{{}}}".format(latex(self.base_ring()), names, self._q) + + @staticmethod + def _term_key(x): + r""" + Compute a key for ``x`` for comparisons. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: elt = (x*y^3*z^2).leading_support() + sage: R._term_key(elt) + (6, [2, 3, 1]) + """ + L = x.list() + L.reverse() + return (sum(L), L) + + def gen(self, i): + """ + Return the ``i``-generator of ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.gen(0) + x + sage: R.gen(2) + z + """ + return self.monomial(self._indices.gen(i)) + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.gens() + (x, y, z) + """ + return tuple([self.monomial(g) for g in self._indices.gens()]) + + @cached_method + def algebra_generators(self): + r""" + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.algebra_generators() + Finite family {'x': x, 'y': y, 'z': z} + """ + d = {v: self.gen(i) for i,v in enumerate(self.variable_names())} + return Family(self.variable_names(), d.__getitem__, name="generator") + + @cached_method + def one_basis(self): + r""" + Return the basis index of the element `1`. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.one_basis() + 1 + """ + return self._indices.one() + + def degree_on_basis(self, m): + r""" + Return the degree of the monomial index by ``m``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.degree_on_basis(R.one_basis()) + 0 + sage: f = (x + y)^3 + z^3 + sage: f.degree() + 3 + """ + return sum(m.list()) + + def dimension(self): + r""" + Return the dimension of ``self``, which is `\infty`. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.dimension() + +Infinity + """ + return infinity + + @cached_method + def product_on_basis(self, x, y): + r""" + Return the product of two monomials given by ``x`` and ``y``. + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q) + sage: R.product_on_basis(x.leading_support(), y.leading_support()) + x*y + sage: R.product_on_basis(y.leading_support(), x.leading_support()) + q*x*y + + sage: x * y + x*y + sage: y * x + q*x*y + sage: y^2 * x + q^2*x*y^2 + sage: y * x^2 + q^2*x^2*y + sage: x * y * x + q*x^2*y + sage: y^2 * x^2 + q^4*x^2*y^2 + sage: (x + y)^2 + x^2 + (q+1)*x*y + y^2 + sage: (x + y)^3 + x^3 + (q^2+q+1)*x^2*y + (q^2+q+1)*x*y^2 + y^3 + sage: (x + y)^4 + x^4 + (q^3+q^2+q+1)*x^3*y + (q^4+q^3+2*q^2+q+1)*x^2*y^2 + (q^3+q^2+q+1)*x*y^3 + y^4 + """ + # Special case for multiplying by 1 + if x == self.one_basis(): + return self.monomial(y) + if y == self.one_basis(): + return self.monomial(x) + + Lx = x.list() + Ly = y.list() + + # This could be made more efficient + qpow = sum(exp * sum(Ly[:i]) for i,exp in enumerate(Lx)) + return self.term(x * y, self._q ** qpow) + From 1e55368b8e961c568b41ec439ecc6919e55bb267 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 23 Aug 2022 16:55:55 +0900 Subject: [PATCH 174/454] Almost done with refactoring code to separate free module cases. --- src/sage/homology/free_resolution.py | 651 +++++++++++------- src/sage/homology/graded_resolution.py | 532 ++++++++------ src/sage/modules/free_module.py | 8 +- src/sage/rings/ideal.py | 8 +- .../polynomial/multi_polynomial_ideal.py | 8 +- 5 files changed, 721 insertions(+), 486 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 2f9ad448baa..efa327d051c 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -22,16 +22,15 @@ S^1 <-- S^3 <-- S^2 <-- 0 sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) + sage: r = I.free_resolution() sage: r S^1 <-- S^3 <-- S^2 <-- 0 :: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 @@ -39,7 +38,7 @@ sage: R. = QQ[] sage: I = R.ideal([y*z - x*w, y^3 - x^2*z, x*z^2 - y^2*w, z^3 - y*w^2]) - sage: r = FreeResolution(I) + sage: r = I.free_resolution() sage: r S^1 <-- S^4 <-- S^4 <-- S^1 <-- 0 sage: len(r) @@ -69,6 +68,9 @@ from sage.libs.singular.singular import si2sa_resolution from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.abstract_method import abstract_method +from sage.structure.sage_object import SageObject +from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.structure.element import Matrix from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.integral_domains import IntegralDomains @@ -77,15 +79,12 @@ from sage.modules.free_module import Module_free_ambient, FreeModule_generic from sage.rings.ideal import Ideal_generic -from sage.structure.sage_object import SageObject - +from copy import copy -class FreeResolution_generic(SageObject): - r""" - Generic base class of finite free resolutions. - A subclass must provide a ``_maps`` attribute that contains a list of the - maps defining the resolution. +class FreeResolution(SageObject, metaclass=ClasscallMetaclass): + """ + Abstract base class for free resolutions. The matrix at index `i` in the list defines the differential map from `(i + 1)`-th free module to the `i`-th free module over the base ring by @@ -95,35 +94,96 @@ class FreeResolution_generic(SageObject): Note that the first matrix in the list defines the differential map at homological index `1`. + """ + @staticmethod + def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', **kwds): + """ + Dispatch to the correct constructor. - A subclass can define ``_initial_differential`` attribute that - contains the `0`-th differential map whose codomain is the target - of the free resolution. + TESTS:: - EXAMPLES:: + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() + sage: r = FreeResolution(m, name='S') + sage: type(r) + - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: r.differential(0) - Coercion map: - From: Ambient free module of rank 1 over the integral domain - Multivariate Polynomial Ring in x, y, z, w over Rational Field - To: Quotient module by Submodule of Ambient free module of rank 1 - over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field - Generated by the rows of the matrix: - [-z^2 + y*w] - [ y*z - x*w] - [-y^2 + x*z] - """ - def __init__(self, base_ring, name='F'): + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: type(r) + + + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: r = FreeResolution(S) + sage: type(r) + + + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: r = FreeResolution(I) + sage: type(r) + + """ + # The module might still be free even if is_free_module is False. + # This is just to handle the cases when we trivially know it is. + is_free_module = False + if isinstance(module, Ideal_generic): + S = module.ring() + if len(module.gens()) == 1 and S in IntegralDomains(): + is_free_module = True + elif isinstance(module, Module_free_ambient): + S = module.base_ring() + if (S in PrincipalIdealDomains() + or isinstance(module, FreeModule_generic)): + is_free_module = True + elif isinstance(module, Matrix): + S = module.base_ring() + if S in PrincipalIdealDomains(): + module = module.echelon_form() + if module.nrows() > module.rank(): + module = module.submatrix(nrows=module.rank()) + module.set_immutable() + is_free_module = True + if not module.is_immutable(): + # We need to make an immutable copy of the matrix + module = copy(module) + module.set_immutable() + else: + raise TypeError('no module, matrix, or ideal') + + if not is_free_module: + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if not isinstance(S, MPolynomialRing_libsingular): + raise NotImplementedError("the ring must be a free module or a polynomial ring that can be constructed in Singular") + + if degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) + + return FiniteFreeResolution_singular(module, name=name, **kwds) + + # Otherwise we know it is a free module + + if degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) + + return FiniteFreeResolution_free_module(module, name=name, **kwds) + + def __init__(self, module, name='S', **kwds): """ Initialize. INPUT: - ``base_ring`` -- a ring + - ``name`` -- (default: ``'S'``) the name of the ring for printing TESTS:: @@ -133,47 +193,32 @@ def __init__(self, base_ring, name='F'): sage: r = FreeResolution(m1, name='S') sage: TestSuite(r).run(skip=['_test_pickling']) """ - self._base_ring = base_ring + if isinstance(module, Ideal_generic): + S = module.ring() + else: #if isinstance(module, (Module_free_ambient, Matrix)): + S = module.base_ring() + + self._base_ring = S self._name = name + self._module = module - @lazy_attribute - def _length(self): - """ - The length of ``self``. + def _repr_(self): + r""" + Return a string representation of ``self``. TESTS:: sage: from sage.homology.free_resolution import FreeResolution sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: r._length - 2 - """ - return len(self._maps) - - def _repr_(self): - """ - Return the string form of this resolution. - - INPUT: - - - ``i`` -- a positive integer - - EXAMPLES:: - - sage: from sage.homology.graded_resolution import GradedFreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) - sage: r - S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: r = FreeResolution(m1, name='S') + sage: print(FreeResolution._repr_(r)) + Free resolution with initial map: + [z^2 - y*w, y*z - x*w, y^2 - x*z] """ - s = self._repr_module(0) - for i in range(1, self._length + 1): - s += ' <-- ' + self._repr_module(i) - s += ' <-- 0' - return s + if isinstance(self._module, Matrix): + return f"Free resolution with inital map:\n{self._module}" + return f"Free resolution of {self._module}" def _repr_module(self, i): r""" @@ -208,6 +253,145 @@ def _repr_module(self, i): s = '0' return s + def _m(self): + r""" + The defining matrix or ideal of the module defining ``self``. + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r._m() + Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field + + sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() + sage: r = FreeResolution(m, name='S') + sage: r._m() + [z^2 - y*w y*z - x*w y^2 - x*z] + + sage: M = m.image() + sage: r = FreeResolution(M, name='S') + sage: r._m() + [z^2 - y*w y*z - x*w y^2 - x*z] + """ + if isinstance(self._module, Ideal_generic): + return self._module + if isinstance(self._module, Module_free_ambient): + return self._module.matrix().transpose() + if isinstance(self._module, Matrix): + return self._module.transpose() + raise ValueError("unable to create a matrix/ideal to build the resolution") + + @abstract_method + def differential(self, i): + r""" + Return the ``i``-th differential map. + + INPUT: + + - ``i`` -- a positive integer + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) + sage: r = FreeResolution(m1, name='S') + sage: FreeResolution.differiental(r, 1) + Traceback (most recent call last): + ... + AttributeError: type object 'FreeResolution' has no attribute 'differiental' + """ + + def target(self): + r""" + Return the codomain of the ``0``-th differential map. + + EXAMPLES:: + + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = I.graded_free_resolution() + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: r.target() + Quotient module by Submodule of Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + return self.differential(0).codomain() + +class FiniteFreeResolution(FreeResolution): + r""" + Finite free resolutions. + + A subclass must provide a ``_maps`` attribute that contains a list of the + maps defining the resolution. + + A subclass can define ``_initial_differential`` attribute that + contains the `0`-th differential map whose codomain is the target + of the free resolution. + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r.differential(0) + Coercion map: + From: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + To: Quotient module by Submodule of Ambient free module of rank 1 + over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + @lazy_attribute + def _length(self): + """ + The length of ``self``. + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r._length + 2 + """ + return len(self._maps) + + def _repr_(self): + """ + Return the string form of this resolution. + + INPUT: + + - ``i`` -- a positive integer + + EXAMPLES:: + + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = I.graded_free_resolution() + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + """ + s = self._repr_module(0) + for i in range(1, self._length + 1): + s += ' <-- ' + self._repr_module(i) + s += ' <-- 0' + return s + def __len__(self): r""" Return the length of this resolution. @@ -216,10 +400,9 @@ def __len__(self): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 sage: len(r) @@ -237,10 +420,9 @@ def __getitem__(self, i): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 sage: r.target() @@ -250,7 +432,7 @@ def __getitem__(self, i): [-z^2 + y*w] [ y*z - x*w] [-y^2 + x*z] - """ + """ if i < 0: raise IndexError('invalid index') elif i > self._length: @@ -263,7 +445,7 @@ def __getitem__(self, i): def differential(self, i): r""" - Return the matrix representing the ``i``-th differential map. + Return the ``i``-th differential map. INPUT: @@ -271,10 +453,9 @@ def differential(self, i): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 sage: r.differential(3) @@ -332,28 +513,6 @@ def differential(self, i): m = s.hom(self._maps[i - 1], t, side='right') return m - def target(self): - r""" - Return the codomain of the ``0``-th differential map. - - EXAMPLES:: - - sage: from sage.homology.graded_resolution import GradedFreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) - sage: r - S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: r.target() - Quotient module by Submodule of Ambient free module of rank 1 over the integral domain - Multivariate Polynomial Ring in x, y, z, w over Rational Field - Generated by the rows of the matrix: - [-z^2 + y*w] - [ y*z - x*w] - [-y^2 + x*z] - """ - return self.differential(0).codomain() - def matrix(self, i): r""" Return the matrix representing the ``i``-th differential map. @@ -364,10 +523,9 @@ def matrix(self, i): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 sage: r.matrix(3) @@ -394,10 +552,9 @@ def chain_complex(self): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = I.graded_free_resolution() sage: unicode_art(r.chain_complex()) ⎛-y x⎞ ⎜ z -y⎟ @@ -410,20 +567,149 @@ def chain_complex(self): mats[i] = self.matrix(i) return ChainComplex(mats, degree_of_differential=-1) + @lazy_attribute + def _initial_differential(self): + r""" + Define the `0`-th differential map of this resolution. + + EXAMPLES:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r._initial_differential + Coercion map: + From: Ambient free module of rank 1 over the integral domain + Multivariate Polynomial Ring in x, y, z, w over Rational Field + To: Quotient module by Submodule of Ambient free module of rank 1 + over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field + Generated by the rows of the matrix: + [-z^2 + y*w] + [ y*z - x*w] + [-y^2 + x*z] + """ + ideal = self._module + if isinstance(ideal, Ideal_generic): + S = ideal.ring() + M = FreeModule(S, 1) + N = M.submodule([vector([g]) for g in ideal.gens()]) + elif isinstance(ideal, Module_free_ambient): + S = ideal.base_ring() + M = ideal.ambient_module() + N = ideal + elif isinstance(ideal, Matrix): + S = ideal.base_ring() + N = ideal.row_space() + M = N.ambient_module() + Q = M.quotient(N) + return Q.coerce_map_from(M) -class FreeResolution(FreeResolution_generic): +class FiniteFreeResolution_free_module(FiniteFreeResolution): + r""" + Free resolutions of a free module. + + INPUT: + + - ``module`` -- a free module or ideal over a PID + - ``name`` -- the name of the base ring + + EXAMPLES:: + + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: S + Free module of degree 3 and rank 2 over Univariate Polynomial Ring in x over Rational Field + Echelon basis matrix: + [ x^2 2*x^2 3*x^2] + [ 0 x 2*x] + sage: res = S.free_resolution() + sage: res + S^3 <-- S^2 <-- 0 + sage: ascii_art(res.chain_complex()) + [ x^2 0] + [2*x^2 x] + [3*x^2 2*x] + 0 <-- C_0 <-------------- C_1 <-- 0 + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: res = I.free_resolution() + sage: res + S^1 <-- S^1 <-- 0 + """ + @lazy_attribute + def _maps(self): + r""" + Return the maps that define ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: res = S.free_resolution() + sage: res + S^3 <-- S^2 <-- 0 + sage: ascii_art(res.chain_complex()) + [ x^2 0] + [2*x^2 x] + [3*x^2 2*x] + 0 <-- C_0 <-------------- C_1 <-- 0 + sage: res._maps + [ + [ x^2 0] + [2*x^2 x] + [3*x^2 2*x] + ] + + sage: R. = PolynomialRing(QQ) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: res = I.free_resolution() + sage: res._maps + [[x^4 + 3*x^2 + 2]] + + An overdetermined system over a PID:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: R. = QQ[] + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = FreeResolution(M) + sage: res + S^2 <-- S^2 <-- 0 + sage: res._m() + [ x^2 3*x^2 5*x^2] + [ 2 5 4] + sage: res._maps + [ + [x^2 0] + [ 0 1] + ] + """ + if isinstance(self._module, Ideal_generic): + from sage.matrix.constructor import matrix + return [matrix([[self._module.gen()]])] + return [self._m()] + +class FiniteFreeResolution_singular(FiniteFreeResolution): r""" Minimal free resolutions of ideals or submodules of free modules - of multivariate polynomial rings. + of multivariate polynomial rings implemented in Singular. INPUT: - ``module`` -- a submodule of a free module `M` of rank `n` over `S` or an ideal of a multi-variate polynomial ring - - - ``name`` -- a string; name of the base ring - - - ``algorithm`` -- Singular algorithm to compute a resolution of ``ideal`` + - ``name`` -- string (optional); name of the base ring + - ``algorithm`` -- (default: ``'heuristic'``) Singular algorithm + to compute a resolution of ``ideal`` OUTPUT: a minimal free resolution of the ideal @@ -496,8 +782,8 @@ class FreeResolution(FreeResolution_generic): [-y*z + x*w] [ z^2 - y*w] """ - def __init__(self, module, name='S', algorithm='heuristic'): - """ + def __init__(self, module, name='S', algorithm='heuristic', **kwds): + r""" Initialize. TESTS:: @@ -525,84 +811,10 @@ def __init__(self, module, name='S', algorithm='heuristic'): sage: FreeResolution(Q.ideal([xb])) # has torsion Traceback (most recent call last): ... - NotImplementedError: the ring must be a polynomial ring that can be constructed in Singular - - An overdetermined system over a PID:: - - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) - sage: res = FreeResolution(M) - sage: res - S^2 <-- S^2 <-- 0 - sage: res._m() - [ x^2 3*x^2 5*x^2] - [ 2 5 4] - sage: res._maps - [ - [x^2 0] - [ 0 1] - ] + NotImplementedError: the ring must be a free module or a polynomial ring that can be constructed in Singular """ - # The module might still be free even if _is_free_module is False. - # This is just to handle the cases when we trivially know it is. - self._is_free_module = False - if isinstance(module, Ideal_generic): - S = module.ring() - if len(module.gens()) == 1 and S in IntegralDomains(): - self._is_free_module = True - elif isinstance(module, Module_free_ambient): - S = module.base_ring() - self._is_free_module = (S in PrincipalIdealDomains() - or isinstance(module, FreeModule_generic)) - elif isinstance(module, Matrix): - S = module.base_ring() - if S in PrincipalIdealDomains(): - self._is_free_module = True - module = module.echelon_form() - if module.nrows() > module.rank(): - module = module.submatrix(nrows=module.rank()) - else: - raise TypeError('no module, matrix, or ideal') - - if not self._is_free_module: - from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular - if not isinstance(S, MPolynomialRing_libsingular): - raise NotImplementedError("the ring must be a polynomial ring that can be constructed in Singular") - - self._module = module self._algorithm = algorithm - super().__init__(S, name=name) - - def _m(self): - r""" - The defining module of ``self``. - - TESTS:: - - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: r._m() - Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field - - sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() - sage: r = FreeResolution(m, name='S') - sage: r._m() - [z^2 - y*w y*z - x*w y^2 - x*z] - - sage: M = m.image() - sage: r = FreeResolution(M, name='S') - sage: r._m() - [z^2 - y*w y*z - x*w y^2 - x*z] - """ - if isinstance(self._module, Ideal_generic): - return self._module - if isinstance(self._module, Module_free_ambient): - return self._module.matrix().transpose() - if isinstance(self._module, Matrix): - return self._module.transpose() + super().__init__(module, name=name, **kwds) @lazy_attribute def _maps(self): @@ -621,44 +833,7 @@ def _maps(self): [ z -y] [z^2 - y*w y*z - x*w y^2 - x*z], [-w z] ] - - sage: R. = QQ[] - sage: M = R^3 - sage: v = M([x^2, 2*x^2, 3*x^2]) - sage: w = M([0, x, 2*x]) - sage: S = M.submodule([v, w]) - sage: S - Free module of degree 3 and rank 2 over Univariate Polynomial Ring in x over Rational Field - Echelon basis matrix: - [ x^2 2*x^2 3*x^2] - [ 0 x 2*x] - sage: res = S.free_resolution() - sage: res - S^3 <-- S^2 <-- 0 - sage: ascii_art(res.chain_complex()) - [ x^2 0] - [2*x^2 x] - [3*x^2 2*x] - 0 <-- C_0 <-------------- C_1 <-- 0 - sage: res._maps - [ - [ x^2 0] - [2*x^2 x] - [3*x^2 2*x] - ] - - sage: R. = PolynomialRing(QQ) - sage: I = R.ideal([x^4 + 3*x^2 + 2]) - sage: res = I.free_resolution() - sage: res._maps - [[x^4 + 3*x^2 + 2]] """ - if self._is_free_module: - if isinstance(self._module, Ideal_generic): - from sage.matrix.constructor import matrix - return [matrix([[self._module.gen()]])] - return [self._m()] - # This ensures the first component of the Singular resolution to be a # module, like the later components. This is important when the # components are converted to Sage modules. @@ -685,41 +860,3 @@ def _maps(self): return si2sa_resolution(r) - @lazy_attribute - def _initial_differential(self): - r""" - Define the `0`-th differential map of this resolution. - - EXAMPLES:: - - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: r._initial_differential - Coercion map: - From: Ambient free module of rank 1 over the integral domain - Multivariate Polynomial Ring in x, y, z, w over Rational Field - To: Quotient module by Submodule of Ambient free module of rank 1 - over the integral domain Multivariate Polynomial Ring in x, y, z, w over Rational Field - Generated by the rows of the matrix: - [-z^2 + y*w] - [ y*z - x*w] - [-y^2 + x*z] - """ - ideal = self._module - if isinstance(ideal, Ideal_generic): - S = ideal.ring() - M = FreeModule(S, 1) - N = M.submodule([vector([g]) for g in ideal.gens()]) - elif isinstance(ideal, Module_free_ambient): - S = ideal.base_ring() - M = ideal.ambient_module() - N = ideal - elif isinstance(ideal, Matrix): - S = ideal.base_ring() - N = ideal.row_space() - M = N.ambient_module() - Q = M.quotient(N) - return Q.coerce_map_from(M) - diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index bc30e61ef6e..d9d64062a63 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -7,17 +7,17 @@ EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I, algorithm='minimal') + sage: r = GradedFiniteFreeResolution_singular(I, algorithm='minimal') sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFreeResolution(I, algorithm='shreyer') + sage: GradedFiniteFreeResolution_singular(I, algorithm='shreyer') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFreeResolution(I, algorithm='standard') + sage: GradedFiniteFreeResolution_singular(I, algorithm='standard') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFreeResolution(I, algorithm='heuristic') + sage: GradedFiniteFreeResolution_singular(I, algorithm='heuristic') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 :: @@ -39,7 +39,7 @@ [ y -z w] [ x -y z] sage: m = d.image() - sage: GradedFreeResolution(m, shifts=(2,2,2)) + sage: GradedFiniteFreeResolution_singular(m, shifts=(2,2,2)) S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 An example of multigraded resolution from Example 9.1 of [MilStu2005]_:: @@ -51,7 +51,7 @@ Ideal (c^2 - b*d, b*c - a*d, b^2 - a*c) of Multivariate Polynomial Ring in a, b, c, d over Rational Field sage: P3 = ProjectiveSpace(S) sage: C = P3.subscheme(I) # twisted cubic curve - sage: r = GradedFreeResolution(I, degrees=[(1,0), (1,1), (1,2), (1,3)]) + sage: r = GradedFiniteFreeResolution_singular(I, degrees=[(1,0), (1,1), (1,2), (1,3)]) sage: r S((0, 0)) <-- S((-2, -4))⊕S((-2, -3))⊕S((-2, -2)) <-- S((-3, -5))⊕S((-3, -4)) <-- 0 sage: r.K_polynomial(names='s,t') @@ -83,102 +83,54 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.ideal import Ideal_generic -from sage.homology.free_resolution import FreeResolution +from sage.homology.free_resolution import (FiniteFreeResolution, + FiniteFreeResolution_free_module, + FiniteFreeResolution_singular) -class GradedFreeResolution(FreeResolution): +class GradedFiniteFreeResolution(FiniteFreeResolution): r""" - Graded free resolutions of ideals of multivariate polynomial rings. + Graded finite free resolutions. INPUT: - ``module`` -- a homogeneous submodule of a free module `M` of rank `n` over `S` or a homogeneous ideal of a multivariate polynomial ring `S` - - ``degrees`` -- (default: a list with all entries `1`) a list of integers or integer vectors giving degrees of variables of `S` - - ``shifts`` -- a list of integers or integer vectors giving shifts of degrees of `n` summands of the free module `M`; this is a list of zero degrees of length `n` by default - - ``name`` -- a string; name of the base ring - - ``algorithm`` -- Singular algorithm to compute a resolution of ``ideal`` - - If ``module`` is an ideal of `S`, it is considered as a submodule of a - free module of rank `1` over `S`. - - The degrees given to the variables of `S` are integers or integer vectors of - the same length. In the latter case, `S` is said to be multigraded, and the - resolution is a multigraded free resolution. The standard grading where all - variables have degree `1` is used if the degrees are not specified. - - A summand of the graded free module `M` is a shifted (or twisted) module of - rank one over `S`, denoted `S(-d)` with shift `d`. - - The computation of the resolution is done by using ``libSingular``. - Different Singular algorithms can be chosen for best performance. - - OUTPUT: a graded minimal free resolution of ``ideal`` - - The available algorithms and the corresponding Singular commands are shown - below: - - ============= ============================ - algorithm Singular commands - ============= ============================ - ``minimal`` ``mres(ideal)`` - ``shreyer`` ``minres(sres(std(ideal)))`` - ``standard`` ``minres(nres(std(ideal)))`` - ``heuristic`` ``minres(res(std(ideal)))`` - ============= ============================ - .. WARNING:: This does not check that the module is homogeneous. - - EXAMPLES:: - - sage: from sage.homology.graded_resolution import GradedFreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) - sage: r - S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: len(r) - 2 - - sage: I = S.ideal([z^2 - y*w, y*z - x*w, y - x]) - sage: I.is_homogeneous() - True - sage: R = GradedFreeResolution(I) - sage: R - S(0) <-- S(-1)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3)⊕S(-4) <-- S(-5) <-- 0 """ - def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuristic'): + def __init__(self, module, degrees=None, shifts=None, name='S', **kwds): """ Initialize. TESTS:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = GradedFiniteFreeResolution_singular(I) sage: TestSuite(r).run(skip=['_test_pickling']) An overdetermined system over a PID:: + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module sage: M = matrix([[x^2, 2], ....: [3*x^2, 5], ....: [5*x^2, 4]]) - sage: res = GradedFreeResolution(M) + sage: res = GradedFiniteFreeResolution_free_module(M) sage: res S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 sage: res._res_shifts [[2, 0]] """ - super().__init__(module, name=name, algorithm=algorithm) + super().__init__(module, name=name, **kwds) nvars = self._base_ring.ngens() @@ -187,8 +139,6 @@ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuri if len(degrees) != nvars: raise ValueError('the length of degrees does not match the number of generators') - if self._is_free_module and len(degrees) > 1 and any(d != 1 for d in degrees): - raise NotImplementedError("only the natural grading supported when more than one generator") if degrees[0] in ZZ: zero_deg = 0 @@ -213,154 +163,14 @@ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuri self._multigrade = multigrade self._zero_deg = zero_deg - @lazy_attribute - def _maps(self): - """ - The maps that define ``self``. - - This also sets the attribute ``_res_shifts``. - - TESTS:: - - sage: from sage.homology.graded_resolution import GradedFreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) - sage: r._maps - [ - [-y x] - [ z -y] - [z^2 - y*w y*z - x*w y^2 - x*z], [-w z] - ] - sage: r._res_shifts - [[2, 2, 2], [3, 3]] - - sage: R. = QQ[] - sage: M = matrix([[x^3, 3*x^3, 5*x^3], - ....: [0, x, 2*x]]) - sage: res = GradedFreeResolution(M) - sage: res - S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 - sage: res._maps - [ - [ x^3 0] - [3*x^3 x] - [5*x^3 2*x] - ] - sage: res._res_shifts - [[3, 1]] - - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) - sage: res = GradedFreeResolution(M) - sage: res - S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 - sage: res._res_shifts - [[2, 0]] - - sage: I = R.ideal([x^4]) - sage: res = I.graded_free_resolution(shifts=[1], degrees=[2]) - sage: res - S(-1) <-- S(-9) <-- 0 - sage: res._maps - [[x^4]] - sage: res._res_shifts - [[9]] - """ - if self._is_free_module: - - def compute_degree(base, i): - """ - Compute the degree by ``base * deg + shift``, - where ``*`` is entry-wise multiplication, ``deg`` and - ``shift`` are the ``i``-th index. - """ - deg = self._degrees[0] - shift = self._shifts[i] - if self._multigrade: - return vector([val * d + s for val, d, s in zip(base, deg, shift)]) - return base * deg + shift - - if isinstance(self._module, Ideal_generic): - from sage.matrix.constructor import matrix - val = self._module.gen(0) - self._res_shifts = [[compute_degree(val.degree(), 0)]] - return [matrix([[val]])] - - M = self._m() - - def find_deg(i): - for j in range(M.nrows()): - ret = M[j,i].degree() - if ret != -1: - return ret - raise NotImplementedError("a generator maps to 0") - self._res_shifts = [[compute_degree(find_deg(i), i) - for i in range(M.ncols())]] - return [M] - - #cdef int i, j, k, ncols, nrows - #cdef list res_shifts, prev_shifts, new_shifts - - # This ensures the first component of the Singular resolution to be a - # module, like the later components. This is important when the - # components are converted to Sage modules. - module = singular_function("module") - mod = module(self._m()) - - if self._algorithm == 'minimal': - mres = singular_function('mres') # syzygy method - r = mres(mod, 0) - elif self._algorithm == 'shreyer': - std = singular_function('std') - sres = singular_function('sres') # Shreyer method - minres = singular_function('minres') - r = minres(sres(std(mod), 0)) - elif self._algorithm == 'standard': - nres = singular_function('nres') # standard basis method - minres = singular_function('minres') - r = minres(nres(mod, 0)) - elif self._algorithm == 'heuristic': - std = singular_function('std') - res = singular_function('res') # heuristic method - minres = singular_function('minres') - r = minres(res(std(mod), 0)) - - res_mats, res_degs = si2sa_resolution_graded(r, self._degrees) - - # compute shifts of free modules in the resolution - res_shifts = [] - prev_shifts = list(self._shifts) - for k in range(len(res_degs)): - new_shifts = [] - degs = res_degs[k] - ncols = len(degs) - for j in range(ncols): - col = degs[j] - nrows = len(col) - # should be enough to compute the new shifts - # from any one entry of the column vector - for i in range(nrows): - d = col[i] - if d is not None: - e = prev_shifts[i] - new_shifts.append(d + e) - break - res_shifts.append(new_shifts) - prev_shifts = new_shifts - - self._res_shifts = res_shifts - return res_mats - def _repr_module(self, i): """ EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = GradedFiniteFreeResolution_singular(I) sage: r._repr_module(0) 'S(0)' sage: r._repr_module(1) @@ -370,7 +180,7 @@ def _repr_module(self, i): sage: r._repr_module(3) '0' - sage: r = GradedFreeResolution(I, shifts=[-1]) + sage: r = GradedFiniteFreeResolution_singular(I, shifts=[-1]) sage: r._repr_module(0) 'S(1)' """ @@ -394,10 +204,10 @@ def shifts(self, i): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = GradedFiniteFreeResolution_singular(I) sage: r.shifts(0) [0] sage: r.shifts(1) @@ -431,10 +241,10 @@ def betti(self, i, a=None): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = GradedFiniteFreeResolution_singular(I) sage: r.betti(0) {0: 1} sage: r.betti(1) @@ -475,10 +285,10 @@ def K_polynomial(self, names=None): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFreeResolution + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFreeResolution(I) + sage: r = GradedFiniteFreeResolution_singular(I) sage: r.K_polynomial() 2*t^3 - 3*t^2 + 1 """ @@ -505,3 +315,291 @@ def K_polynomial(self, names=None): return kpoly +class GradedFiniteFreeResolution_free_module(GradedFiniteFreeResolution, FiniteFreeResolution_free_module): + r""" + Graded free resolution of free modules. + + .. WARNING:: + + This does not check that the module is homogeneous. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: R. = QQ[] + sage: M = matrix([[x^3, 3*x^3, 5*x^3], + ....: [0, x, 2*x]]) + sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res + S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res + S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 + """ + def __init__(self, module, degrees=None, *args, **kwds): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: R. = QQ[] + sage: M = matrix([[x^3, 3*x^3, 5*x^3], + ....: [0, x, 2*x]]) + sage: res = GradedFiniteFreeResolution_free_module(M) + sage: TestSuite(res).run(skip="_test_pickling") + """ + super().__init__(module, degrees=degrees, *args, **kwds) + + if len(self._degrees) > 1 and any(d != 1 for d in self._degrees): + raise NotImplementedError("only the natural grading supported when more than one generator") + + @lazy_attribute + def _maps(self): + r""" + The maps that define ``self``. + + This also sets the attribute ``_res_shifts``. + + TESTS:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: R. = QQ[] + sage: M = matrix([[x^3, 3*x^3, 5*x^3], + ....: [0, x, 2*x]]) + sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res + S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 + sage: res._maps + [ + [ x^3 0] + [3*x^3 x] + [5*x^3 2*x] + ] + sage: res._res_shifts + [[3, 1]] + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res._maps + sage: res._res_shifts + [[2, 0]] + + sage: I = R.ideal([x^4]) + sage: res = I.graded_free_resolution(shifts=[1], degrees=[2]) + sage: res + S(-1) <-- S(-9) <-- 0 + sage: res._maps + [[x^4]] + sage: res._res_shifts + [[9]] + """ + def compute_degree(base, i): + """ + Compute the degree by ``base * deg + shift``, + where ``*`` is entry-wise multiplication, ``deg`` and + ``shift`` are the ``i``-th index. + """ + deg = self._degrees[0] + shift = self._shifts[i] + if self._multigrade: + return vector([val * d + s for val, d, s in zip(base, deg, shift)]) + return base * deg + shift + + if isinstance(self._module, Ideal_generic): + from sage.matrix.constructor import matrix + val = self._module.gen(0) + self._res_shifts = [[compute_degree(val.degree(), 0)]] + return [matrix([[val]])] + + M = self._m() + + def find_deg(i): + for j in range(M.nrows()): + ret = M[j,i].degree() + if ret != -1: + return ret + raise NotImplementedError("a generator maps to 0") + self._res_shifts = [[compute_degree(find_deg(i), i) + for i in range(M.ncols())]] + return [M] + + +class GradedFiniteFreeResolution_singular(GradedFiniteFreeResolution, FiniteFreeResolution_singular): + r""" + Graded free resolutions of submodules and ideals of multivariate + polynomial rings implemented using Singular. + + INPUT: + + - ``module`` -- a homogeneous submodule of a free module `M` of rank `n` + over `S` or a homogeneous ideal of a multivariate polynomial ring `S` + + - ``degrees`` -- (default: a list with all entries `1`) a list of integers + or integer vectors giving degrees of variables of `S` + + - ``shifts`` -- a list of integers or integer vectors giving shifts of + degrees of `n` summands of the free module `M`; this is a list of zero + degrees of length `n` by default + + - ``name`` -- a string; name of the base ring + + - ``algorithm`` -- Singular algorithm to compute a resolution of ``ideal`` + + If ``module`` is an ideal of `S`, it is considered as a submodule of a + free module of rank `1` over `S`. + + The degrees given to the variables of `S` are integers or integer vectors of + the same length. In the latter case, `S` is said to be multigraded, and the + resolution is a multigraded free resolution. The standard grading where all + variables have degree `1` is used if the degrees are not specified. + + A summand of the graded free module `M` is a shifted (or twisted) module of + rank one over `S`, denoted `S(-d)` with shift `d`. + + The computation of the resolution is done by using ``libSingular``. + Different Singular algorithms can be chosen for best performance. + + OUTPUT: a graded minimal free resolution of ``ideal`` + + The available algorithms and the corresponding Singular commands are shown + below: + + ============= ============================ + algorithm Singular commands + ============= ============================ + ``minimal`` ``mres(ideal)`` + ``shreyer`` ``minres(sres(std(ideal)))`` + ``standard`` ``minres(nres(std(ideal)))`` + ``heuristic`` ``minres(res(std(ideal)))`` + ============= ============================ + + .. WARNING:: + + This does not check that the module is homogeneous. + + EXAMPLES:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFiniteFreeResolution_singular(I) + sage: r + S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 + sage: len(r) + 2 + + sage: I = S.ideal([z^2 - y*w, y*z - x*w, y - x]) + sage: I.is_homogeneous() + True + sage: R = GradedFiniteFreeResolution_singular(I) + sage: R + S(0) <-- S(-1)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3)⊕S(-4) <-- S(-5) <-- 0 + """ + def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuristic', **kwds): + """ + Initialize. + + TESTS:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFiniteFreeResolution_singular(I) + sage: TestSuite(r).run(skip=['_test_pickling']) + + An overdetermined system over a PID:: + + sage: M = matrix([[x^2, 2], + ....: [3*x^2, 5], + ....: [5*x^2, 4]]) + sage: res = GradedFiniteFreeResolution_singular(M) + sage: res + S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 + sage: res._res_shifts + [[2, 0]] + """ + super().__init__(module, degrees=degrees, shifts=shifts, name=name, **kwds) + self._algorithm = algorithm + + @lazy_attribute + def _maps(self): + """ + The maps that define ``self``. + + This also sets the attribute ``_res_shifts``. + + TESTS:: + + sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = GradedFiniteFreeResolution_singular(I) + sage: r._maps + [ + [-y x] + [ z -y] + [z^2 - y*w y*z - x*w y^2 - x*z], [-w z] + ] + sage: r._res_shifts + [[2, 2, 2], [3, 3]] + """ + #cdef int i, j, k, ncols, nrows + #cdef list res_shifts, prev_shifts, new_shifts + + # This ensures the first component of the Singular resolution to be a + # module, like the later components. This is important when the + # components are converted to Sage modules. + module = singular_function("module") + mod = module(self._m()) + + if self._algorithm == 'minimal': + mres = singular_function('mres') # syzygy method + r = mres(mod, 0) + elif self._algorithm == 'shreyer': + std = singular_function('std') + sres = singular_function('sres') # Shreyer method + minres = singular_function('minres') + r = minres(sres(std(mod), 0)) + elif self._algorithm == 'standard': + nres = singular_function('nres') # standard basis method + minres = singular_function('minres') + r = minres(nres(mod, 0)) + elif self._algorithm == 'heuristic': + std = singular_function('std') + res = singular_function('res') # heuristic method + minres = singular_function('minres') + r = minres(res(std(mod), 0)) + + res_mats, res_degs = si2sa_resolution_graded(r, self._degrees) + + # compute shifts of free modules in the resolution + res_shifts = [] + prev_shifts = list(self._shifts) + for k in range(len(res_degs)): + new_shifts = [] + degs = res_degs[k] + ncols = len(degs) + for j in range(ncols): + col = degs[j] + nrows = len(col) + # should be enough to compute the new shifts + # from any one entry of the column vector + for i in range(nrows): + d = col[i] + if d is not None: + e = prev_shifts[i] + new_shifts.append(d + e) + break + res_shifts.append(new_shifts) + prev_shifts = new_shifts + + self._res_shifts = res_shifts + return res_mats + diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 1acf13bbb9e..b818e36ee48 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1779,8 +1779,8 @@ def free_resolution(self, *args, **kwds): [ z x*z] 0 <-- C_0 <-------------- C_1 <-- 0 """ - from sage.homology.free_resolution import FreeResolution - return FreeResolution(self, *args, **kwds) + from sage.homology.free_resolution import FiniteFreeResolution_free_module + return FiniteFreeResolution_free_module(self, *args, **kwds) def graded_free_resolution(self, *args, **kwds): r""" @@ -1803,8 +1803,8 @@ def graded_free_resolution(self, *args, **kwds): sage: N.graded_free_resolution(degrees=[2, 1, 3], shifts=[2, 3]) S(-2)⊕S(-3) <-- S(-6)⊕S(-8) <-- 0 """ - from sage.homology.graded_resolution import GradedFreeResolution - return GradedFreeResolution(self, *args, **kwds) + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(self, *args, **kwds) class FreeModule_generic(Module_free_ambient): diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 4ca750e6909..e1bce143122 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1217,8 +1217,8 @@ def free_resolution(self, *args, **kwds): sage: I.free_resolution() S^1 <-- S^1 <-- 0 """ - from sage.homology.free_resolution import FreeResolution - return FreeResolution(self, *args, **kwds) + from sage.homology.free_resolution import FiniteFreeResolution_free_module + return FiniteFreeResolution_free_module(self, *args, **kwds) def graded_free_resolution(self, *args, **kwds): r""" @@ -1234,8 +1234,8 @@ def graded_free_resolution(self, *args, **kwds): sage: I.graded_free_resolution() S(0) <-- S(-3) <-- 0 """ - from sage.homology.graded_resolution import GradedFreeResolution - return GradedFreeResolution(self, *args, **kwds) + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(self, *args, **kwds) class Ideal_principal(Ideal_generic): diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 46feca52b20..139b942555c 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1798,8 +1798,8 @@ def free_resolution(self, *args, **kwds): [ y x^2] [ y] 0 <-- C_0 <---------- C_1 <------- C_2 <-- 0 """ - from sage.homology.free_resolution import FreeResolution - return FreeResolution(self, *args, **kwds) + from sage.homology.free_resolution import FiniteFreeResolution_singular + return FiniteFreeResolution_singular(self, *args, **kwds) @require_field def graded_free_resolution(self, *args, **kwds): @@ -1826,8 +1826,8 @@ def graded_free_resolution(self, *args, **kwds): sage: I.graded_free_resolution(degrees=[1,2]) S(0) <-- S(-2)⊕S(-2) <-- S(-4) <-- 0 """ - from sage.homology.graded_resolution import GradedFreeResolution - return GradedFreeResolution(self, *args, **kwds) + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(self, *args, **kwds) @handle_AA_and_QQbar @singular_gb_standard_options From de30ad3820c183c6f42cb4ad59a66ad21cf86d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Aug 2022 11:10:19 +0200 Subject: [PATCH 175/454] moving the code --- src/sage/categories/modules.py | 104 ++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 58b83b8a785..afe402ec818 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -552,58 +552,6 @@ def extra_super_categories(self): """ return [self.base_category()] - class ParentMethods: - """ - Implement operations on tensor products of modules. - """ - def construction(self): - """ - Return the construction of ``self``. - - EXAMPLES:: - - sage: A = algebras.Free(QQ,2) - sage: T = A.tensor(A) - sage: T.construction() - [The tensor functorial construction, - (Free Algebra on 2 generators (None0, None1) over Rational Field, - Free Algebra on 2 generators (None0, None1) over Rational Field)] - """ - try: - factors = self.tensor_factors() - except (TypeError, NotImplementedError): - from sage.misc.superseded import deprecation - deprecation(34393, "implementations of Modules().TensorProducts() now must define the method tensor_factors") - return None - return (TensorProductFunctor(), - factors) - - @abstract_method(optional=True) - def tensor_factors(self): - """ - Return the tensor factors of this tensor product. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(ZZ, [1,2]) - sage: F.rename("F") - sage: G = CombinatorialFreeModule(ZZ, [3,4]) - sage: G.rename("G") - sage: T = tensor([F, G]); T - F # G - sage: T.tensor_factors() - (F, G) - - TESTS:: - - sage: M = CombinatorialFreeModule(ZZ, ((1, 1), (1, 2), (2, 1), (2, 2)), - ....: category=ModulesWithBasis(ZZ).FiniteDimensional().TensorProducts()) - sage: M.construction() - doctest:warning... - DeprecationWarning: implementations of Modules().TensorProducts() now must define the method tensor_factors - See https://trac.sagemath.org/34393 for details. - """ - class FinitelyPresented(CategoryWithAxiom_over_base_ring): def extra_super_categories(self): @@ -944,3 +892,55 @@ def extra_super_categories(self): [Category of modules over Integer Ring] """ return [self.base_category()] + + class ParentMethods: + """ + Implement operations on tensor products of modules. + """ + def construction(self): + """ + Return the construction of ``self``. + + EXAMPLES:: + + sage: A = algebras.Free(QQ,2) + sage: T = A.tensor(A) + sage: T.construction() + [The tensor functorial construction, + (Free Algebra on 2 generators (None0, None1) over Rational Field, + Free Algebra on 2 generators (None0, None1) over Rational Field)] + """ + try: + factors = self.tensor_factors() + except (TypeError, NotImplementedError): + from sage.misc.superseded import deprecation + deprecation(34393, "implementations of Modules().TensorProducts() now must define the method tensor_factors") + return None + return (TensorProductFunctor(), + factors) + + @abstract_method(optional=True) + def tensor_factors(self): + """ + Return the tensor factors of this tensor product. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(ZZ, [1,2]) + sage: F.rename("F") + sage: G = CombinatorialFreeModule(ZZ, [3,4]) + sage: G.rename("G") + sage: T = tensor([F, G]); T + F # G + sage: T.tensor_factors() + (F, G) + + TESTS:: + + sage: M = CombinatorialFreeModule(ZZ, ((1, 1), (1, 2), (2, 1), (2, 2)), + ....: category=ModulesWithBasis(ZZ).FiniteDimensional().TensorProducts()) + sage: M.construction() + doctest:warning... + DeprecationWarning: implementations of Modules().TensorProducts() now must define the method tensor_factors + See https://trac.sagemath.org/34393 for details. + """ From bc6828fd5f265e2921aeaf4739f3fd86611a6190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Aug 2022 15:49:47 +0200 Subject: [PATCH 176/454] fix the doctest --- src/sage/categories/modules.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index afe402ec818..21ee46b24f1 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -906,9 +906,9 @@ def construction(self): sage: A = algebras.Free(QQ,2) sage: T = A.tensor(A) sage: T.construction() - [The tensor functorial construction, + (The tensor functorial construction, (Free Algebra on 2 generators (None0, None1) over Rational Field, - Free Algebra on 2 generators (None0, None1) over Rational Field)] + Free Algebra on 2 generators (None0, None1) over Rational Field)) """ try: factors = self.tensor_factors() From f1becfd9d4031a102287b7682189f2d64e958e1b Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 23 Aug 2022 14:44:59 -0700 Subject: [PATCH 177/454] raise NotImplementedError, auto-convert function-field divisors, cache curve --- src/sage/schemes/curves/affine_curve.py | 4 +- .../riemann_surfaces/riemann_surface.py | 500 ++++++++++-------- 2 files changed, 268 insertions(+), 236 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index f09cde624eb..c9eb561a7d6 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1785,7 +1785,9 @@ def riemann_surface(self, **kwargs): Riemann surface defined by polynomial f = x^3 + 3*y^3 + 5 = 0, with 53 bits of precision """ from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface - return RiemannSurface(self.defining_polynomial(),**kwargs) + S = RiemannSurface(self.defining_polynomial(),**kwargs) + S._curve = self + return S class AffinePlaneCurve_finite_field(AffinePlaneCurve_field): diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0e6620d11fc..fcef9536bf8 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -58,9 +58,9 @@ sage: all(len(a.minpoly().roots(K)) == a.minpoly().degree() for a in A) True -We can look at an extended example of the Abel-Jacobi functionality. We will -demonstrate a particular half-canonical divisor on Klein's Curve, known in -the literature.:: +We can look at an extended example of the Abel-Jacobi functionality. We will +demonstrate a particular half-canonical divisor on Klein's Curve, known in +the literature.:: sage: f = x^3*y + y^3 + x sage: S = RiemannSurface(f, integration_method='rigorous') @@ -90,7 +90,7 @@ We can also check this using our Abel-Jacobi functions:: sage: avoid = C.places_at_infinity() - sage: Zeq, _ = S.strong_approximation(Z, avoid) + sage: Zeq, _ = S.strong_approximation(Z, avoid) sage: Zlist = S.divisor_to_divisor_list(Zeq) sage: AJ = S.abel_jacobi(Zlist) # long time (1 second) sage: S.reduce_over_period_lattice(AJ).norm() < 1e-10 # long time @@ -129,6 +129,7 @@ from sage.numerical.gauss_legendre import integrate_vector, integrate_vector_N from sage.rings.complex_mpfr import ComplexField, CDF from sage.rings.function_field.constructor import FunctionField +from sage.rings.function_field.divisor import FunctionFieldDivisor from sage.rings.infinity import Infinity from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -373,10 +374,10 @@ def differential_basis_baker(f): def find_closest_element(item, List): r""" Return the index of the closest element of a list. - + Given ``List`` and ``item``, return the index of the element ``l`` of ``List`` - which minimises ``(item-l).abs()``. If there are multiple such elements, the - first is returned. + which minimises ``(item-l).abs()``. If there are multiple such elements, the + first is returned. INPUT: @@ -401,35 +402,35 @@ def find_closest_element(item, List): def reparameterise_differential_minpoly(minpoly, z0): r""" - Rewrites a minimal polynomial to write is around `z_0`. - + Rewrites a minimal polynomial to write is around `z_0`. + Given a minimal polynomial `m(z,g)`, where `g` corresponds to a differential - on the surface (that is, it is represented as a rational function, and - implicitly carries a factor `dz`), we rewrite the minpoly in terms of - variables `\bar{z}, \bar{g}` s.t now `\bar{z}=0 \Leftrightarrow z=z_0`. - + on the surface (that is, it is represented as a rational function, and + implicitly carries a factor `dz`), we rewrite the minpoly in terms of + variables `\bar{z}, \bar{g}` s.t now `\bar{z}=0 \Leftrightarrow z=z_0`. + INPUT: - - - ``minpoly`` -- a polynomial in two variables, where the first variables + + - ``minpoly`` -- a polynomial in two variables, where the first variables corresponds to the base coordinate on the Riemann surface. - - - ``z0`` -- complex number of infinity. The point about which to + + - ``z0`` -- complex number of infinity. The point about which to reparameterise. - + OUTPUT: - - A polynomial in two variables giving the reparameterise minimal polynomial. - + + A polynomial in two variables giving the reparameterise minimal polynomial. + EXAMPLES: - - On the curve given by `w^2-z^3+1=0`, we have differential + + On the curve given by `w^2-z^3+1=0`, we have differential `\frac{dz}{2w} = \frac{dz}{2\sqrt{z^3-1}}` - with minimal polynomial `g^2(z^3-1)-1/4=0`. We can make the substitution - `\bar{z}=z^{-1}` to parameterise the differential about `z=\Infty` as + with minimal polynomial `g^2(z^3-1)-1/4=0`. We can make the substitution + `\bar{z}=z^{-1}` to parameterise the differential about `z=\Infty` as `\frac{-\bar{z}^{-2} d\bar{z}}{2\sqrt{\bar{z}^{-3}-1}} = \frac{-d\bar{z}}{2\sqrt{\bar{z}(1-\bar{z}^3)}}`. - Hence the transformed differential should have minimal polynomial + Hence the transformed differential should have minimal polynomial `\bar{g}^2\bar{z}(1-\bar{z}^3)-1/4=0`, and we can check this:: - + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface, reparameterise_differential_minpoly sage: R. = QQ[] sage: S = RiemannSurface(w^2-z^3+1) @@ -438,7 +439,7 @@ def reparameterise_differential_minpoly(minpoly, z0): sage: reparameterise_differential_minpoly(minpoly, z0) -zbar^4*gbar^2 + zbar*gbar^2 - 1/4 - We can further check that reparameterising about `0` is the identity + We can further check that reparameterising about `0` is the identity operation:: sage: reparameterise_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly @@ -446,19 +447,19 @@ def reparameterise_differential_minpoly(minpoly, z0): .. NOTE:: - As part of the routine, when reparameterising about infinity, a + As part of the routine, when reparameterising about infinity, a rational function is reduced and then the numerator is taken. Over an inexact ring this is numerically unstable, and so it is advisable - to only reparameterise about infinity over an exact ring. + to only reparameterise about infinity over an exact ring. """ P = minpoly.parent() F = PolynomialRing(P.base_ring(), [str(v)+"bar" for v in P.gens()]) - + try: Inf = bool(z0==z0.parent()(Infinity)) except TypeError: Inf = False - + if Inf: F = F.fraction_field() mt = F(minpoly(F.gen(0)**(-1),-F.gen(0)**(+2)*F.gen(1))) @@ -574,8 +575,8 @@ class RiemannSurface(object): 1.2429363969691192 Note that for the above curve, the branch points are evenly distributed, and - hence the implicit assumptions in the heuristic method are more sensible, - meaning that a higher precision is required to see the heuristic method + hence the implicit assumptions in the heuristic method are more sensible, + meaning that a higher precision is required to see the heuristic method being significantly slower than the rigorous method. For a worse conditioned curve, this effect is more pronounced:: @@ -645,6 +646,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati self._CCz = PolynomialRing(self._CC, [self._R.gen(0)]) self._CCw = PolynomialRing(self._CC, [self._R.gen(1)]) self._RRz = PolynomialRing(self._RR, [self._R.gen(0)]) + self._curve = None self.f = f if differentials is not None: self._differentials = [self._R(a) for a in differentials] @@ -679,7 +681,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati exact=True) RBzg, bounding_data_list = self._cohomology_basis_bounding_data minpoly_list = [bd[2] for bd in bounding_data_list] - # We now want to calculate the additional branchpoints associated to + # We now want to calculate the additional branchpoints associated to # the differentials. discriminants = [RBzg(self._discriminant(*RBzg.gens()))] for minpoly in minpoly_list: @@ -694,9 +696,9 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati 0)).roots(multiplicities=False) # We add these branchpoints to the existing. #self.branch_locus = self.branch_locus+self._differentials_branch_locus - # We now want to also check whether Infinity is a branch point of any + # We now want to also check whether Infinity is a branch point of any # of the differentials. - # This will be useful when calculating the Abel-Jacobi map. + # This will be useful when calculating the Abel-Jacobi map. minpoly_list = [reparameterise_differential_minpoly(mp, Infinity) for mp in minpoly_list] discriminants = [] @@ -715,7 +717,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati for x0, y0 in self.voronoi_diagram.vertices] self._wvalues = [self.w_values(z0) for z0 in self._vertices] # We arbitrarily, but sensibly, set the basepoint to be the rightmost vertex - self._basepoint = (self._vertices.index(sorted(self._vertices, + self._basepoint = (self._vertices.index(sorted(self._vertices, key=lambda z:z.real())[-1]), 0) self._Sn = SymmetricGroup(range(self.degree)) self._L = {} @@ -723,7 +725,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati self._fastcall_f = fast_callable(f, domain=self._CC) self._fastcall_dfdw = fast_callable(self._dfdw, domain=self._CC) self._fastcall_dfdz = fast_callable(self._dfdz, domain=self._CC) - self._fastcall_cohomology_basis = [fast_callable(h, domain = self._CC) + self._fastcall_cohomology_basis = [fast_callable(h, domain = self._CC) for h in self.cohomology_basis()] def __repr__(self): @@ -830,7 +832,7 @@ def downstairs_graph(self): Return the Voronoi decomposition as a planar graph. The result of this routine can be useful to interpret the labelling of - the vertices. See also :meth:`upstairs_graph`. + the vertices. See also :meth:`upstairs_graph`. OUTPUT: @@ -853,17 +855,17 @@ def downstairs_graph(self): def upstairs_graph(self): r""" Return the graph of the upstairs edges. - + This method can be useful for generating paths in the surface between points labelled - by upstairs vertices, and verifying that a homology basis is likely computed correctly. + by upstairs vertices, and verifying that a homology basis is likely computed correctly. See also :meth:`downstairs_graph`. OUTPUT: - The homotopy-continued Voronoi decomposition as a graph, with appropriate 3D embedding. - + The homotopy-continued Voronoi decomposition as a graph, with appropriate 3D embedding. + EXAMPLES:: - + sage: R. = QQ[] sage: S = Curve(w^2-z^4+1).riemann_surface() sage: G = S.upstairs_graph(); G @@ -874,8 +876,8 @@ def upstairs_graph(self): True """ G = Graph(self.upstairs_edges()) - G.set_pos({(i,j): [self._vertices[i].real(), self._vertices[i].imag(), - self.w_values(self._vertices[i])[j].imag()] + G.set_pos({(i,j): [self._vertices[i].real(), self._vertices[i].imag(), + self.w_values(self._vertices[i])[j].imag()] for i in range(len(self._vertices)) for j in range(self.degree)}, dim=3) return G @@ -963,16 +965,16 @@ def homotopy_continuation(self, edge): INPUT: - ``edge`` -- a tuple ``(z_start, z_end)`` indicating the straight line - over which to perform the homotopy continutation. + over which to perform the homotopy continutation. OUTPUT: - A list containing the initialised continuation data. Each entry in the - list contains: the `t` values that entry corresponds to, a list of - complex numbers corresponding to the points which are reached when - continued along the edge when traversing along the direction of the + A list containing the initialised continuation data. Each entry in the + list contains: the `t` values that entry corresponds to, a list of + complex numbers corresponding to the points which are reached when + continued along the edge when traversing along the direction of the edge, and a value ``epsilon`` giving the minimumdistance between the - fibre values divided by 3. The ordering of these points indicates how + fibre values divided by 3. The ordering of these points indicates how they have been permuted due to the weaving of the curve. EXAMPLES: @@ -1094,7 +1096,7 @@ def _determine_new_w(self, z0, oldw, epsilon): .. NOTE:: Algorithmically, this method is nearly identical to :meth:`_newton_iteration`, - but this method takes a list of `w` values. + but this method takes a list of `w` values. """ # Tools of Newton iteration. F = self._fastcall_f @@ -1239,7 +1241,7 @@ def upstairs_edges(self): i0, i1 = e d_edge = (self._vertices[i0], self._vertices[i1]) # Epsilon for checking w-value later. - epsilon = min([abs(self._wvalues[i1][i] - self._wvalues[i1][n-j-1]) + epsilon = min([abs(self._wvalues[i1][i] - self._wvalues[i1][n-j-1]) for i in range(n) for j in range(n-i-1)])/3 # Homotopy continuation along e. self._L[e] = self.homotopy_continuation(d_edge) @@ -1608,10 +1610,10 @@ def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): INPUT: - ``upstairs_edge`` -- tuple. ``((z_start, sb), (z_end,))`` giving the - start and end values of the base coordinate along the straight-line - path and the starting branch. + start and end values of the base coordinate along the straight-line + path and the starting branch. - - ``initial_continuation`` -- list (default: None). Output of + - ``initial_continuation`` -- list (default: None). Output of ``homotopy_continuation`` initialising the continuation data. OUTPUT: @@ -1630,7 +1632,7 @@ def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): sage: u_edge = [(0, 0), (1, 0)] sage: d_edge = tuple(u[0] for u in u_edge) sage: u_edge = [(S._vertices[i], j) for i, j in u_edge] - sage: initial_continuation = S._L[d_edge] + sage: initial_continuation = S._L[d_edge] sage: g, d = S.make_zw_interpolator(u_edge, initial_continuation) sage: all(f(*g(i*0.1)).abs() < 1e-13 for i in range(10)) True @@ -1639,10 +1641,10 @@ def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): .. NOTE:: - The interpolator returned by this method can effectively hang if + The interpolator returned by this method can effectively hang if either ``z_start`` or ``z_end`` are branchpoints. In these situations it is better to take a different approach rather than continue to use - the interpolator. + the interpolator. """ downstairs_edge = tuple(u[0] for u in upstairs_edge) z_start, z_end = downstairs_edge @@ -1699,7 +1701,7 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): - ``upstairs_edge`` -- tuple. Either a pair of integer tuples corresponding to an edge of the upstairs graph, or a tuple - ``((z_start, sb), (z_end, ))`` as in the input of + ``((z_start, sb), (z_end, ))`` as in the input of ``make_zw_interpolator``. - ``differentials`` -- a list of polynomials; a polynomial `g` @@ -1730,12 +1732,12 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): Uses data that :meth:`homology_basis` initializes, and may give incorrect values if :meth:`homology_basis` has not initialized them. In practice - it is more efficient to set ``differentials`` to a fast-callable version - of differentials to speed up execution. + it is more efficient to set ``differentials`` to a fast-callable version + of differentials to speed up execution. """ d_edge = tuple(u[0] for u in upstairs_edge) # Using a try-catch here allows us to retain a certain amount of back compatibility - # for users. + # for users. try: initial_continuation = self._L[d_edge] upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), @@ -1827,33 +1829,33 @@ def _bounding_data(self, differentials, exact=False): INPUT: - ``differentials`` -- list. A list of polynomials in ``self._R`` giving - the numerators of the differentials, as per the output of + the numerators of the differentials, as per the output of :meth:`cohomology_basis`. - ``exact`` -- logical (default: False). Whether to return the minimal - polynomials over the exact base ring, or whether to return them over - ``self._CC``. + polynomials over the exact base ring, or whether to return them over + ``self._CC``. OUTPUT: - A tuple ``(Rzg, [(g, dgdz, F, a0_info), ...])`` where each element of + A tuple ``(Rzg, [(g, dgdz, F, a0_info), ...])`` where each element of the list corresponds to an element of ``differentials``. Introducing the - notation ``RBzg = PolynomialRing(self._R, ['z','g'])`` and + notation ``RBzg = PolynomialRing(self._R, ['z','g'])`` and ``CCzg = PolynomialRing(self._CC, ['z','g'])``, we have that: - - ``Rzg`` is either ``RBzg`` or ``CCzg`` depending on the value of + - ``Rzg`` is either ``RBzg`` or ``CCzg`` depending on the value of ``exact``, - - ``g`` is the full rational function in ``self._R.fraction_field()`` + - ``g`` is the full rational function in ``self._R.fraction_field()`` giving the differential, - ``dgdz`` is the derivative of ``g`` with respect to ``self._R.gen(0)``, - written in terms of ``self._R.gen(0)`` and ``g``, hence laying in + written in terms of ``self._R.gen(0)`` and ``g``, hence laying in ``RBzg``, - - ``F`` is the minimal polynomial of ``g`` over ``self._R.gen(0)``, + - ``F`` is the minimal polynomial of ``g`` over ``self._R.gen(0)``, laying in the polynomial ring ``Rzg``, - - ``a0_info`` is a tuple ``(lc, roots)`` where ``lc`` and ``roots`` are + - ``a0_info`` is a tuple ``(lc, roots)`` where ``lc`` and ``roots`` are the leading coefficient and roots of the polynomial in ``CCzg.gen(0)`` - that is the coefficient of the term of ``F`` of highest degree in - ``CCzg.gen(1)``. + that is the coefficient of the term of ``F`` of highest degree in + ``CCzg.gen(1)``. EXAMPLES:: @@ -1898,8 +1900,8 @@ def _bounding_data(self, differentials, exact=False): L = k.extension(fZW, 'Wb') dfdw_L = self._dfdw(P.gen(0), L.gen(0)) integrand_list = [h/self._dfdw for h in differentials] - # minpoly_univ gives the minimal polynomial for h, in variable x, with - # coefficients given by polynomials with coefficients in P (i.e. + # minpoly_univ gives the minimal polynomial for h, in variable x, with + # coefficients given by polynomials with coefficients in P (i.e. # rational polynomials in Z). minpoly_univ = [(h(P.gen(0), L.gen(0))/dfdw_L).minpoly().numerator() for h in differentials] @@ -1945,7 +1947,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): - ``upstairs_edge`` -- tuple. Either a pair of integer tuples corresponding to an edge of the upstairs graph, or a tuple - ``((z_start, sb), (z_end, ))`` as in the input of + ``((z_start, sb), (z_end, ))`` as in the input of ``make_zw_interpolator``. - ``differentials`` -- a list of polynomials; a polynomial `g` @@ -1980,7 +1982,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): .. NOTE:: Uses data that ``homology_basis`` initializes, and may give incorrect - values if :meth:`homology_basis` has not initialized them. + values if :meth:`homology_basis` has not initialized them. Note also that the data of the differentials is contained within ``bounding_data``. It is, however, still advantageous to have this @@ -2013,26 +2015,26 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): # Note that this, in its current formalism, makes no check that bounding # data at all corresponds to the differentials given. The onus is then # on the design of other functions which use it. - + # CCzg is required to be known as we need to know the ring which the minpolys - # lie in. + # lie in. CCzg, bounding_data_list = bounding_data d_edge = tuple(u[0] for u in upstairs_edge) - # Using a try-catch here allows us to retain a certain amount of back - # compatibility for users. + # Using a try-catch here allows us to retain a certain amount of back + # compatibility for users. try: initial_continuation = self._L[d_edge] upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), (self._vertices[d_edge[1]], )) except KeyError: initial_continuation = self.homotopy_continuation(d_edge) - - zwt, z1_minus_z0 = self.make_zw_interpolator(upstairs_edge, + + zwt, z1_minus_z0 = self.make_zw_interpolator(upstairs_edge, initial_continuation) z0 = zwt(0)[0] z1 = zwt(1)[0] - + # list of (centre, radius) pairs that still need to be processed ball_stack = [(self._RR(1/2), self._RR(1/2), 0)] alpha = self._RR(912/1000) @@ -2169,7 +2171,7 @@ def matrix_of_integral_values(self, differentials, integration_method="heuristic .. NOTE:: - If ``differentials is self.cohomology_basis()``, the calculations + If ``differentials is self.cohomology_basis()``, the calculations of the integrals along the edges are written to `self._integral_dict``. This is as this data will be required when computing the Abel-Jacobi map, and so it is helpful to have is stored rather than recomputing. @@ -2370,7 +2372,7 @@ def path(t): P += line3d([path(t[0])+(t[1][i].imag_part(),) for t in T],color=color,thickness=thickness) for z,ws in zip(self._vertices,self._wvalues): for w in ws: - P += point3d([z.real_part(), z.imag_part(), w.imag_part()], + P += point3d([z.real_part(), z.imag_part(), w.imag_part()], color="purple", size=20) return P @@ -2750,49 +2752,49 @@ def __add__(self, other): def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individually=False, raise_errors=True, prec=None): r""" - Integrate the cohomology basis along a straight line edge. - + Integrate the cohomology basis along a straight line edge. + The cohomology basis is integrated along a straight line using a version - of the double exponential quadrature. This method of integrating the - cohomology basis is especially useful when integrating out to infinity, - or near roots of `self._dfdw`. In order to aid with convergence of the - method, two main modification to a standard integrator are made, most - importantly of which is the truncation of the integral near branch points, - where the first term in the Puiseux series of the integrands are used to - approximately bound the integral. The ``cutoff_individually`` parameter - allows the user to set whether that truncation is uniform over all the - integrands, which improves the complexity of the algorithm, but loses + of the double exponential quadrature. This method of integrating the + cohomology basis is especially useful when integrating out to infinity, + or near roots of `self._dfdw`. In order to aid with convergence of the + method, two main modification to a standard integrator are made, most + importantly of which is the truncation of the integral near branch points, + where the first term in the Puiseux series of the integrands are used to + approximately bound the integral. The ``cutoff_individually`` parameter + allows the user to set whether that truncation is uniform over all the + integrands, which improves the complexity of the algorithm, but loses the ability to gain benefits where integrands vanish to a high order at - the branchpoint. - + the branchpoint. + INPUT: - + - ``upstairs_edge`` -- tuple. A tuple of complex numbers of the form - ``((z_start, w_start), z_end)`` specifying the path to integrate + ``((z_start, w_start), z_end)`` specifying the path to integrate along, where ``z_start`` may be infinite, in which case ``w_start`` - must be an integer specifying the branch. - + must be an integer specifying the branch. + - ``cutoff_individually`` -- boolean (default: False). Whether to truncate - the integrand uniformly or not. If ``None``, then no truncation is - applied. + the integrand uniformly or not. If ``None``, then no truncation is + applied. - ``raise_errors`` -- boolean (default: True). By default the code uses - convergence errors to ensure any answers returned are accurate. This + convergence errors to ensure any answers returned are accurate. This can be turned off to return answers faster that are not necessarily - correct. + correct. + + - ``prec`` -- integer (default: ``self._prec``). The precision to try + and achieve, defined as `2^{-\text{prec}+3}`. - - ``prec`` -- integer (default: ``self._prec``). The precision to try - and achieve, defined as `2^{-\text{prec}+3}`. - OUTPUT: - - Tuple ``(I, gs)`` where ``I`` is the vector of integrals, and ``gs`` are - the values of the differentials at ``z_end``. - + + Tuple ``(I, gs)`` where ``I`` is the vector of integrals, and ``gs`` are + the values of the differentials at ``z_end``. + EXAMPLES: - + We know that for the surface given by `w^2-z^4-1` a cohomology basis is - given by `\frac{dz}{2w}`. One can verify analytically that + given by `\frac{dz}{2w}`. One can verify analytically that `\int_0^1 frac{dt}{\sqrt{1-t^4}}=\frac{\sqrt{\pi}\Gamma(5/4)}{\Gamma(3/4)}`, and we check this with the integrator, being careful with signs:: @@ -2813,24 +2815,24 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall .. NOTE:: The cutoff methodology is calculating the first term in the Puiseux - series of the differentials about z_start. In future it may be + series of the differentials about z_start. In future it may be desirable to extend this further and use the truncated Puiseux series - entirely to integrate the differentials. + entirely to integrate the differentials. """ (z_start, w_start), z_end = upstairs_edge z_start = self._CC(z_start) z_end = self._CC(z_end) - + if z_end==self._CC(Infinity): raise NotImplementedError - + _, bounding_data_list = self._cohomology_basis_bounding_data mp_list = [bd[2] for bd in bounding_data_list] - + # Parameterise so zbar=0 corresponds to z=z_start mp_list = [reparameterise_differential_minpoly(mp, z_start) for mp in mp_list] - + if z_start==self._CC(Infinity): CCzg = PolynomialRing(self._CC, ['zbar','gbar']) mp_list = [CCzg(mp) for mp in mp_list] @@ -2849,7 +2851,7 @@ def initialise(z, i): rs = mp_list[i](z, DFw.gen(0)).roots(multiplicities=False) sb = find_closest_element(newg, rs) newg = rs[sb] - return newg + return newg else: CCzg = mp_list[0].parent() J = z_end-z_start @@ -2865,12 +2867,12 @@ def initialise(z, i): return newg fc_mp_list = [fast_callable(mp, domain=self._CC) for mp in mp_list] - fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) + fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) for mp in mp_list] if prec is None: prec = self._prec - # tau here is playing the role of the desired error. + # tau here is playing the role of the desired error. tau = self._RR(2)**(-prec+3) ONE = self._RR(1) LAMBDA = self._RR.pi()/2 @@ -2884,7 +2886,7 @@ def initialise(z, i): aes = [] for mp in mp_list: d = mp.dict() - mp = sum([d[k]*CCzg.gen(0)**k[0]*CCzg.gen(1)**k[1] + mp = sum([d[k]*CCzg.gen(0)**k[0]*CCzg.gen(1)**k[1] for k in d.keys() if d[k].abs()>tau]) cst = min([iz for (iz, ig) in d.keys() if ig==0]) a = QQ(max([(cst-iz)/ig for (iz,ig) in d.keys() if ig>0])) @@ -2894,10 +2896,10 @@ def initialise(z, i): cutoffs.append(((a+1)*tau/G)**(1/self._CC(a+1))/J.abs()) aes.append(a) cutoff_individually = bool(not all(ai<=0 for ai in aes) and cutoff_individually) - + if raise_errors: n_steps = self._prec-1 - + def error_handle(out): raise ConvergenceError("Newton iteration fails to converge") else: @@ -2913,7 +2915,7 @@ def error_handle(out): if cutoff_individually: z_fc_list = list(zip(fc_mp_list, fc_dmp_list)) - + def fv(hj, previous_estimate_and_validity): u2 = LAMBDA*hj.sinh() t = 1/(2*u2.exp()*u2.cosh()) @@ -2952,14 +2954,14 @@ def fv(hj, previous_estimate_and_validity): u1 = LAMBDA*hj.cosh() w = u1/(2*u2.cosh()**2) return (fj, valid), w*fj - + f0, v0 = fv(h0, (self.genus*[0], self.genus*[False])) else: cutoffs.append(1) cutoff = min(cutoffs) cutoff_z = J*cutoff J -= cutoff_z - + def fv(hj, previous_estimate): u2 = LAMBDA*hj.sinh() t = 1/(2*u2.exp()*u2.cosh()) @@ -2984,9 +2986,9 @@ def fv(hj, previous_estimate): outg.append(error_handle(newg)) fj = V(outg) u1 = LAMBDA*hj.cosh() - w = u1/(2*u2.cosh()**2) + w = u1/(2*u2.cosh()**2) return fj, w*fj - + u1, u2 = (LAMBDA*h0.cosh(),LAMBDA*h0.sinh()) y, w = (1/(2*u2.exp()*u2.cosh()), u1/(2*u2.cosh()**2)) z0 = cutoff_z+J*y @@ -2997,13 +2999,13 @@ def fv(hj, previous_estimate): D3_over_tau = v0.norm(Infinity) D4 = D3_over_tau results = [] - + for k in range(n_steps): hj = h0 val = v0 fj = f0 for j in range(2*Nh): - hj -= h + hj -= h try: fj, v = fv(hj, fj) except ConvergenceError: @@ -3020,7 +3022,7 @@ def fv(hj, previous_estimate): D1 = (results[-1]-results[-2]).norm(Infinity) D2 = (results[-1]-results[-3]).norm(Infinity) D = min(ONE,max(D1**(D1.log()/D2.log()),D2**2,tau*D3_over_tau,D4,tau)) - + if D <= tau: if cutoff_individually: fj = fj[0] @@ -3028,34 +3030,34 @@ def fv(hj, previous_estimate): h /= 2 Nh *= 2 return error_handle((J*results[-1], endscale*fj)) - + def _aj_based(self, P): r""" Return the Abel-Jacobi map to ``P`` from ``self._basepoint``. - + Computes a representative of the Abel-Jacobi map from ``self._basepoint`` to ``P`` via a well-chosen vertex ``V``. The representative given will be dependent on the path chosen. - + INPUT: - + - ``P`` -- tuple. A pair giving the endpoint of the integral, either in the form ``(z, w)`` or ``(Infinity, branch)``, where in the latter case we are using the convention that the `w` value over `\infty` is given by - the limit as ``z`` tends to `\infty` of ``self.w_values(z)[branch]``. - + the limit as ``z`` tends to `\infty` of ``self.w_values(z)[branch]``. + OUTPUT: - + A vector of length ``self.genus``. - + EXAMPLES: - + As the output of ``_aj_based`` is difficult to interpret due to its path - dependency, we look at the output of :meth:`abel_jacobi`. We check for - two hyperelliptic curves that the Abel-Jacobi map between two branch + dependency, we look at the output of :meth:`abel_jacobi`. We check for + two hyperelliptic curves that the Abel-Jacobi map between two branch points is a 2-torsion point over the lattice. Note we must remember to reduce over the period lattice, as results are path dependent:: - + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] sage: p = 100 @@ -3073,11 +3075,11 @@ def _aj_based(self, P): sage: AJx2 = [2*z for z in AJ] sage: bool(S.reduce_over_period_lattice(AJx2).norm() < 1e-10) True - + """ ##### fcd = self._fastcall_cohomology_basis - + if self._integration_method=="heuristic": line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) else: @@ -3086,19 +3088,19 @@ def _aj_based(self, P): ##### B = self._basepoint zP, wP = P - + try: Inf = bool(zP==zP.parent()(Infinity)) except TypeError: Inf = False - + if Inf: zV = self._vertices[B[0]] if zV==0: zV += 1 upstairs_edge = (P, zV) ci = bool(self._CC(Infinity) in self._differentials_branch_locus) - AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, + AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, cutoff_individually=ci) AJ = -AJ g0e = endgs[0] @@ -3106,7 +3108,7 @@ def _aj_based(self, P): g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] W_index = find_closest_element(g0e, g0s) if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") + raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") V_index = B[0] else: zP = self._CC(zP) @@ -3115,17 +3117,17 @@ def _aj_based(self, P): if zP==self._vertices[V_index]: W_index = find_closest_element(wP, self._wvalues[V_index]) - AJ = 0 + AJ = 0 else: b_index = find_closest_element(zP, self.branch_locus) b = self.branch_locus[b_index] #bl = self.branch_locus+self._differentials_branch_locus #b_index = find_closest_element(zP, bl) #b = bl[b_index] - + scale = max(b.abs() for b in self.branch_locus) d1 = self._CC(1e-2)*scale - + # We choose the first vertex we want to go to. # If the closest vertex is closer than the nearest branch point, just take that vertex # otherwise we need something smarter. @@ -3133,7 +3135,7 @@ def _aj_based(self, P): if not ((zP-self._vertices[V_index]).abs() < (zP-b).abs() or (zP-b).abs()<=delta): region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] - suitable_vertex_indices = [region[i] + suitable_vertex_indices = [region[i] for i in range(len(region)) if args[i].abs()-self._RR.pi()/2>=-self._RR(1e-15)] suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] if suitable_vertices==[]: @@ -3148,7 +3150,7 @@ def _aj_based(self, P): u_edge = ((zP, wP_index), (zV, )) initial_continuation = self.homotopy_continuation(d_edge) AJ = -line_int(u_edge) - + w_end = initial_continuation[-1][1][wP_index] W_index = find_closest_element(w_end, self._wvalues[V_index]) else: @@ -3159,14 +3161,14 @@ def _aj_based(self, P): # Here we need a block of code to change the vertex if the path # from zP to zV would go through a ramification point of the integrands fl = [c for c in self._differentials_branch_locus if not c==self._CC(Infinity)] - ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 + ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 for c in fl] ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] while (len(ds)>=1 and min(ds)=0 and ts[i]<=1)] @@ -3178,11 +3180,11 @@ def _aj_based(self, P): wP_index = find_closest_element(ws, ws_list) ws = ws_list[wP_index] upstairs_edge = ((zs, ws), zV) - AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, + AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, cutoff_individually=False) AJ = -AJ g0e = endgs[0] - + ws = self.w_values(zV) g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] W_index = find_closest_element(g0e, g0s) @@ -3206,33 +3208,33 @@ def _aj_based(self, P): except KeyError: Ie = line_int(e) self._integral_dict[e] = Ie - AJ += s*Ie + AJ += s*Ie return AJ def abel_jacobi(self, divisor, verbose=False): r""" - Return the Abel-Jacobi map of ``divisor``. - + Return the Abel-Jacobi map of ``divisor``. + Return a representative of the Abel-Jacobi map of a divisor with basepoint ``self._basepoint``. - + INPUT: - + - ``divisor`` -- list. A list with each entry a tuple of the form ``(v, P)``, - where ``v`` is the valuation of the divisor at point ``P``, ``P`` as per + where ``v`` is the valuation of the divisor at point ``P``, ``P`` as per the input to :meth:`_aj_based`. - ``verbose`` -- logical (default: False). Whether to report the progress of the computation, in terms of how many elements of the list ``divisor`` - have been completed. - + have been completed. + OUTPUT: - + A vector of length ``self.genus``. EXAMPLES: - We can test that the Abel-Jacobi map between two branchpoints of a + We can test that the Abel-Jacobi map between two branchpoints of a superelliptic curve of degree `p` is a `p`-torsion point in the Jacobian:: sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface @@ -3241,10 +3243,12 @@ def abel_jacobi(self, divisor, verbose=False): sage: S = RiemannSurface(y^p-x^4+1, prec=100) sage: divisor = [(-1, (-1, 0)), (1, (1, 0))] sage: AJ = S.abel_jacobi(divisor) # long time (15 seconds) - sage: AJxp = [p*z for z in AJ] # long time + sage: AJxp = [p*z for z in AJ] # long time sage: bool(S.reduce_over_period_lattice(AJxp).norm()<1e-7) # long time True """ + if isinstance(divisor, FunctionFieldDivisor): + divisor = self.divisor_to_divisor_list(divisor) ans = 0 n = len(divisor) for i in range(n): @@ -3258,24 +3262,24 @@ def abel_jacobi(self, divisor, verbose=False): def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normalised=False): r""" - Reduce a vector over the period lattice. - - Given a vector of length ``self.genus``, this method returns a vector - in the same orbit of the period lattice that is short. There are two - possible methods, ``'svp'`` which returns a certified shortest vector, + Reduce a vector over the period lattice. + + Given a vector of length ``self.genus``, this method returns a vector + in the same orbit of the period lattice that is short. There are two + possible methods, ``'svp'`` which returns a certified shortest vector, but can be much slower for higher genus curves, and ``'ip'``, which is faster but not guaranteed to return the shortest vector. In general the - latter will perform well when the lattice basis vectors are of similar - size. - + latter will perform well when the lattice basis vectors are of similar + size. + INPUT: - - - ``vector`` -- vector. A vector of length ``self.genus`` to reduce over + + - ``vector`` -- vector. A vector of length ``self.genus`` to reduce over the lattice. - + - ``method`` -- string (default: ``'ip'``). String specifying the method to use to reduce the vector. THe options are ``'ip'`` and ``'svp'``. - + - ``b`` -- integer (default provided): as for :meth:`homomorphism_basis`, and used in its invocation if (re)calculating said basis. @@ -3284,20 +3288,20 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal :meth:`homomorphism_basis`, and used in its invocation if (re)calculating said basis. - - ``normalised`` -- logical (default: ``False``). Whether to use the + - ``normalised`` -- logical (default: ``False``). Whether to use the period matrix with the differentials normalised s.t. the `A`-matrix - is the identity. - + is the identity. + OUTPUT: - - Complex vector of length ``self.genus`` in the same orbit as ``vector`` + + Complex vector of length ``self.genus`` in the same orbit as ``vector`` in the lattice. - + EXAMPLES: - + We can check that the lattice basis vectors themselves are reduced to zero:: - + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] sage: S = RiemannSurface(y^2-x^5+1) @@ -3309,7 +3313,7 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal True True - We can also check that the method ``'svp'`` always gives a smaller norm + We can also check that the method ``'svp'`` always gives a smaller norm than ``'ip'``:: sage: for vector in S.period_matrix().columns(): @@ -3323,7 +3327,7 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal """ if not len(vector)==self.genus: raise ValueError("Input vector needs to be of length {}".format(self.genus)) - + VR = VectorSpace(self._RR, 2*self.genus) VC = VectorSpace(self._CC, self.genus) I = self._CC(0, 1) @@ -3333,9 +3337,9 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal AM = PM[:, 0:self.genus] AInv = numerical_inverse(AM) PM = AInv*PM - + if method=="svp": - H = max(max(z.real_part().abs() for z in vector), + H = max(max(z.real_part().abs() for z in vector), max(z.imag_part().abs() for z in vector)) if b is None: b = self._prec-5-H.log2().floor() @@ -3344,24 +3348,24 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal S = 2**b if H*S > 2**(self._prec-4): raise ValueError("insufficient precision for b=%s" % b) - + def C2Z(v): vR = [(S*z.real_part()).round() for z in v] vR += [(S*z.imag_part()).round() for z in v] return vR - + M = Matrix(ZZ, 2*self.genus, 2*self.genus, [C2Z(c) for c in PM.columns()]) u = C2Z(vector) L = IntegerLattice(M) u = VR(u)-VR(L.closest_vector(u)) reduced = VC([self._CC(u[i]+I*u[i+self.genus])/S for i in range(self.genus)]) - + elif method=="ip": - + def C2R(v): return VR([z.real_part() for z in v]+[z.imag_part() for z in v]) - + u = C2R(vector) basis_vecs = [C2R(c) for c in PM.columns()] M = Matrix([[ei.dot_product(ej) for ei in basis_vecs] for ej in basis_vecs]) @@ -3371,23 +3375,47 @@ def C2R(v): reduced = VC([self._CC(u[i]+I*u[i+self.genus]) for i in range(self.genus)]) else: raise ValueError("Must give a valid method.") - + return reduced + def curve(self): + r""" + Return the curve from which this Riemann surface is obtained + + Riemann surfaces explicitly obtained from a curve return that same object. + For others, the curve is constructed and cached, so that an identical curve is + returned upon subsequent calls. + + OUTPUT: + + Curve from which Riemann surface is obtained. + + EXAMPLE: + + sage: R. = QQ[] + sage: C = Curve( y^3+x^3-1) + sage: S = C.riemann_surface() + sage: S.curve() is C + True + """ + if self._curve is None: + self._curve = Curve(self.f) + return self._curve + def places_at_branch_locus(self): r""" - Return the places above the branch locus. + Return the places above the branch locus. - Return a list of the of places above the branch locus. This must be - done over the base ring, and so the places are given in terms of the - factors of the discriminant. Currently, this method only works when - ``self._R.base_ring()==QQ`` as for other rings, the function field - for ``Curve(self.f)`` is not implemented. To go from these divisors to - a divisor list, see :meth:`divisor_to_divisor_list`. + Return a list of the of places above the branch locus. This must be + done over the base ring, and so the places are given in terms of the + factors of the discriminant. Currently, this method only works when + ``self._R.base_ring()==QQ`` as for other rings, the function field + for ``Curve(self.f)`` is not implemented. To go from these divisors to + a divisor list, see :meth:`divisor_to_divisor_list`. OUTPUT: - List of places of the functions field ``Curve(self.f).function_field()``. + List of places of the functions field ``Curve(self.f).function_field()``. EXAMPLES:: @@ -3406,7 +3434,7 @@ def places_at_branch_locus(self): K = self._R.base_ring() if not K==QQ: raise NotImplementedError - C = Curve(self.f) + C = self.curve() KC = C.function_field() g0, g1 = self._R.gens() Kb = FunctionField(K, str(g0)) @@ -3420,10 +3448,10 @@ def places_at_branch_locus(self): def strong_approximation(self, divisor, S): r""" - Apply the method of strong approximation to a divisor. + Apply the method of strong approximation to a divisor. As described in [Neu2018]_, apply the method of strong approximation to - ``divisor`` with list of places to avoid ``S``. Currently, this method + ``divisor`` with list of places to avoid ``S``. Currently, this method only works when ``self._R.base_ring()==QQ`` as for other rings, the function field for ``Curve(self.f)`` is not implemented. @@ -3431,11 +3459,11 @@ def strong_approximation(self, divisor, S): - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` - - ``S`` -- list. A list of places to avoid. + - ``S`` -- list. A list of places to avoid. OUTPUT: - A tuple ``(D, B)``, where ``D`` is a new divisor, linearly equivalent + A tuple ``(D, B)``, where ``D`` is a new divisor, linearly equivalent to ``divisor``, but not intersecting ``S``, and ``B`` is a list of tuples ``(v, b)`` where ``b`` are the functions giving the linear equivalence, added with multiplicity ``v``. @@ -3453,17 +3481,17 @@ def strong_approximation(self, divisor, S): + Place (x^2 + x + 1, y), [(1, (1/(x - 2))*y)]) """ - # One would standardly expect to run this with + # One would standardly expect to run this with # S = Curve(self.f).places_at_infinity() # or # S = Curve(self.f).places_at_infinity()+self.places_at_branch_locus() # # To avoid current implementation issues with going between divisors - # and divisor lists, we implement a method that handles only divisors + # and divisor lists, we implement a method that handles only divisors K = self._R.base_ring() if not K==QQ: raise NotImplementedError - C = Curve(self.f) + C = self.curve() KC = C.function_field() g0, g1 = self._R.gens() Kb = FunctionField(K, str(g0)) @@ -3476,7 +3504,7 @@ def strong_approximation(self, divisor, S): Fac = g0-K(rr) p0 = MO.ideal(Fac).place() q0 = KC.places_above(p0)[0] - + new_divisor = divisor B = [] for p in divisor.support(): @@ -3522,7 +3550,7 @@ def divisor_to_divisor_list(self, divisor): OUTPUT: - A list with elements of the form ``(v, (z, w))`` representing the finite places. + A list with elements of the form ``(v, (z, w))`` representing the finite places. EXAMPLES:: @@ -3538,11 +3566,11 @@ def divisor_to_divisor_list(self, divisor): .. TODO:: Currently this method can only handle places above finite points in - the base. It would be useful to extend this to allow for places at - infinity. + the base. It would be useful to extend this to allow for places at + infinity. """ - # If this error bound is too restrictive, this method might fail and - # not return. One might want to change the way this error is handled. + # If this error bound is too restrictive, this method might fail and + # not return. One might want to change the way this error is handled. eps = self._RR(2)**(-self._prec+3) dl = [] @@ -3550,20 +3578,22 @@ def divisor_to_divisor_list(self, divisor): RF = PolynomialRing(PZ, 'w') for d in divisor.support(): + if d.is_infinite_place(): + raise NotImplementedError("Conversion of infinite places not implemented yet.") v = divisor.valuation(d) gs = d._prime.gens() - + g0 = self._R(gs[0]) - gis = [sum([PZ(gi.list()[i])*RF.gen()**i + gis = [sum([PZ(gi.list()[i])*RF.gen()**i for i in range(len(gi.list()))]) for gi in gs[1:]] - + rs = self._CCz(g0).roots() rys = [] for r, m in rs: ys = [] for gi in gis: - # This test is a bit clunky, it surely can be made more efficient. + # This test is a bit clunky, it surely can be made more efficient. if len(ys): ers = min([gi(y, r).abs() for y in ys]) else: @@ -3577,7 +3607,7 @@ def divisor_to_divisor_list(self, divisor): nys = poly.roots() ys += [ny[0] for ny in nys] rys += [(v*m*n, (r, y)) for y, n in nys] - + if rys: dl.extend(rys) else: From f0727d647d18e699c709bfb7d589055877c3a227 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 1 Aug 2020 11:09:33 -0700 Subject: [PATCH 178/454] TensorFreeSubmodule_comp, TensorFreeSubmoduleBasis_comp: New --- src/sage/tensor/modules/tensor_free_module.py | 9 ++ .../tensor/modules/tensor_free_submodule.py | 93 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/sage/tensor/modules/tensor_free_submodule.py diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index dbeb1f2be25..611f54b3f02 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -686,3 +686,12 @@ def tensor_type(self): """ return self._tensor_type + + def basis(self, symbol, latex_symbol=None, from_family=None, + indices=None, latex_indices=None, symbol_dual=None, + latex_symbol_dual=None): + from sage.tensor.modules.free_module_comp_basis import FreeModuleCompTensorBasis + return FreeModuleCompTensorBasis(self, symbol, latex_symbol=latex_symbol, + indices=indices, latex_indices=latex_indices, + symbol_dual=symbol_dual, + latex_symbol_dual=latex_symbol_dual) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py new file mode 100644 index 00000000000..49020027b3c --- /dev/null +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -0,0 +1,93 @@ +r""" +Free submodules of tensor products of free modules +""" + +#****************************************************************************** +# Copyright (C) 2020 Matthias Koeppe +# +# 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.misc.cachefunc import cached_method +from .tensor_free_module import TensorFreeModule +from .finite_rank_free_module import FiniteRankFreeModule +from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_comp + +class TensorFreeSubmodule_comp(TensorFreeModule): + r""" + Class for free submodules of tensor products of free modules + that are defined by the symmetries of a + :class:`~sage.tensor.modules.comp.Components` object. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + Free module of type-(2,0) tensors + with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + on the Rank-3 free module M over the Integer Ring + """ + def __init__(self, fmodule, tensor_type, name=None, latex_name=None, + sym=None, antisym=None): + self._fmodule = fmodule + self._tensor_type = tuple(tensor_type) + # Create a tensor only because we need a Components object + tensor = fmodule.tensor(tensor_type, + name=name, latex_name=latex_name, + sym=sym, antisym=antisym) + frame = list(fmodule.irange()) + self._comp = tensor._new_comp(frame) + rank = len(list(self._comp.non_redundant_index_generator())) + # Skip TensorFreeModule.__init__ + FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, + latex_name=latex_name, + start_index=fmodule._sindex, + output_formatter=fmodule._output_formatter) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2, name='M') + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + Free module of type-(2,0) tensors + with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + on the Rank-3 free module M over the Integer Ring + + """ + return "Free module of type-({},{}) tensors with {} on the {}".format( + self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) + + def is_submodule(self, other): + r""" + Return ``True`` if ``self`` is a submodule of ``other``. + + """ + raise NotImplementedError + + @cached_method + def basis(self, symbol): + r""" + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) + sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M + + sage: for a in e_Sym2M: a.display() + e_0*e_0 + e_0*e_1 + e_0*e_2 + e_1*e_1 + e_1*e_2 + e_2*e_2 + """ + return TensorFreeSubmoduleBasis_comp(self, symbol) From 46500a50644e04d76529fa379a7870baadce181f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 1 Aug 2020 11:38:42 -0700 Subject: [PATCH 179/454] Move basis method to TensorFreeModule --- src/sage/tensor/modules/tensor_free_module.py | 39 +++++++++++++++++++ .../tensor/modules/tensor_free_submodule.py | 22 ----------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 611f54b3f02..5c7f5b1a659 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -66,6 +66,7 @@ from sage.tensor.modules.free_module_morphism import \ FiniteRankFreeModuleMorphism from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism +from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_comp class TensorFreeModule(FiniteRankFreeModule): r""" @@ -695,3 +696,41 @@ def basis(self, symbol, latex_symbol=None, from_family=None, indices=indices, latex_indices=latex_indices, symbol_dual=symbol_dual, latex_symbol_dual=latex_symbol_dual) + + + @cached_method + def basis(self, symbol, latex_symbol=None, from_family=None, + indices=None, latex_indices=None, symbol_dual=None, + latex_symbol_dual=None): + r""" + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T = M.tensor_module(1,1) + sage: e_T = T.basis('e'); e_T + + sage: for a in e_T: a.display() + e_0*e^0 + e_0*e^1 + e_0*e^2 + e_1*e^0 + e_1*e^1 + e_1*e^2 + e_2*e^0 + e_2*e^1 + e_2*e^2 + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) + sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M + + sage: for a in e_Sym2M: a.display() + e_0*e_0 + e_0*e_1 + e_0*e_2 + e_1*e_1 + e_1*e_2 + e_2*e_2 + """ + return TensorFreeSubmoduleBasis_comp(self, symbol) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 49020027b3c..dcdc7d4fd82 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -14,7 +14,6 @@ from sage.misc.cachefunc import cached_method from .tensor_free_module import TensorFreeModule from .finite_rank_free_module import FiniteRankFreeModule -from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_comp class TensorFreeSubmodule_comp(TensorFreeModule): r""" @@ -70,24 +69,3 @@ def is_submodule(self, other): """ raise NotImplementedError - - @cached_method - def basis(self, symbol): - r""" - - EXAMPLES:: - - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) - sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M - - sage: for a in e_Sym2M: a.display() - e_0*e_0 - e_0*e_1 - e_0*e_2 - e_1*e_1 - e_1*e_2 - e_2*e_2 - """ - return TensorFreeSubmoduleBasis_comp(self, symbol) From bbeaced4758750f0857478b0966033d5dcdb0aa8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 1 Aug 2020 13:12:27 -0700 Subject: [PATCH 180/454] Add missing file --- .../modules/tensor_free_submodule_basis.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/sage/tensor/modules/tensor_free_submodule_basis.py diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py new file mode 100644 index 00000000000..31f6b1bbbb2 --- /dev/null +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -0,0 +1,71 @@ +r""" +Free module bases indexed by component indices +""" + +#****************************************************************************** +# Copyright (C) 2020 Matthias Koeppe +# +# 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.tensor.modules.free_module_basis import Basis_abstract +from sage.tensor.modules.comp import Components + +class TensorFreeSubmoduleBasis_comp(Basis_abstract): + r""" + Standard basis of a tensor module with prescribed symmetries. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T11 = M.tensor_module(1,1) + sage: e11 = T11.basis('e') + sage: for a in e11: a.display() + e_0*e^0 + e_0*e^1 + e_0*e^2 + e_1*e^0 + e_1*e^1 + e_1*e^2 + e_2*e^0 + e_2*e^1 + e_2*e^2 + + """ + + def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, + latex_indices=None, symbol_dual=None, latex_symbol_dual=None): + base_module = tensor_module.base_module() + base_module_basis = base_module.basis(symbol, latex_symbol, indices, + latex_indices, symbol_dual, latex_symbol_dual) + super().__init__(tensor_module, symbol, latex_symbol, indices, latex_indices) + self._base_module_basis = base_module_basis + try: + # TensorFreeSubmodule_comp + self._comp = tensor_module._comp + except AttributeError: + # TensorFreeModule + tensor = tensor_module() + frame = list(base_module.irange()) + self._comp = tensor._new_comp(frame) + + def __iter__(self): + r""" + Generate the basis elements of ``self``. + """ + tensor_module = self._fmodule + base_module = tensor_module.base_module() + base_module_basis = self._base_module_basis + for ind in self._comp.non_redundant_index_generator(): + element = tensor_module.element_class(base_module, tensor_module._tensor_type) + element.set_comp(base_module_basis)[ind] = 1 + yield element + +# Todo: Make it a Family +# symmetrize/antisymmetrize it +# dual basis +# add test for dual +# lift/reduce/retract From 28d3c2073b403defd00e4520e886958f0cbd0af8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:20:29 -0700 Subject: [PATCH 181/454] FiniteRankFreeModule: Add methods is_submodule, ambient_module --- .../tensor/modules/finite_rank_free_module.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 091dc252ad5..ecc2e914d35 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -2824,3 +2824,30 @@ def identity_map(self, name='Id', latex_name=None): latex_name = name self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map + + def ambient_module(self): + """ + Return the ambient module associated to this module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.ambient_module() is M + True + """ + return self + + def is_submodule(self, other): + """ + Return ``True`` if ``self`` is a submodule of ``other``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: N = FiniteRankFreeModule(ZZ, 4, name='M') + sage: M.is_submodule(M) + True + sage: M.is_submodule(N) + False + """ + return self == other From 72ffe906975d5fdce4f59504e0d3b237e88610e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:21:00 -0700 Subject: [PATCH 182/454] TensorFreeModule: Remove duplicate method 'basis' --- src/sage/tensor/modules/tensor_free_module.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 5c7f5b1a659..3959b88368a 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -688,16 +688,6 @@ def tensor_type(self): """ return self._tensor_type - def basis(self, symbol, latex_symbol=None, from_family=None, - indices=None, latex_indices=None, symbol_dual=None, - latex_symbol_dual=None): - from sage.tensor.modules.free_module_comp_basis import FreeModuleCompTensorBasis - return FreeModuleCompTensorBasis(self, symbol, latex_symbol=latex_symbol, - indices=indices, latex_indices=latex_indices, - symbol_dual=symbol_dual, - latex_symbol_dual=latex_symbol_dual) - - @cached_method def basis(self, symbol, latex_symbol=None, from_family=None, indices=None, latex_indices=None, symbol_dual=None, From 8d2ef6e7634223db406ec85a2ef9c3d1bc9c1983 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:21:20 -0700 Subject: [PATCH 183/454] TensorFreeSubmodule_comp.is_submodule: New --- .../tensor/modules/tensor_free_submodule.py | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index dcdc7d4fd82..449acc81b77 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -12,6 +12,7 @@ #****************************************************************************** from sage.misc.cachefunc import cached_method +from sage.sets.disjoint_set import DisjointSet from .tensor_free_module import TensorFreeModule from .finite_rank_free_module import FiniteRankFreeModule @@ -67,5 +68,61 @@ def is_submodule(self, other): r""" Return ``True`` if ``self`` is a submodule of ``other``. + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym012345M.is_submodule(Sym012345M) + True + sage: Sym012345M.is_submodule(Sym0123x45M) + True + sage: Sym0123x45M.is_submodule(Sym012345M) + False + sage: Sym012x345M.is_submodule(Sym0123x45M) + False + sage: all(S.is_submodule(T60M) for S in (Sym0123x45M, Sym012x345M, Sym012345M)) + True + """ - raise NotImplementedError + if self == other: + return True + self_base_module = self.base_module() + self_tensor_type = self.tensor_type() + try: + other_base_module = other.base_module() + other_tensor_type = other.tensor_type() + except AttributeError: + return False + if self_base_module != other_base_module: + return False + if self_tensor_type != other_tensor_type: + return False + # Use the union-find data structure + def is_coarsening_of(self_sym_list, other_sym_list): + S = DisjointSet(self_tensor_type[0] + self_tensor_type[1]) + for index_set in self_sym_list: + i = index_set[0] + for j in index_set[1:]: + S.union(i, j) + for index_set in other_sym_list: + i = S.find(index_set[0]) + for j in index_set[1:]: + if S.find(j) != i: + return False + return True + # Similar code is in Component.contract, should refactor. + try: + other_sym = other._comp._sym + other_antisym = other._comp._antisym + except AttributeError: + # other is full tensor module (no symmetry) + return True + if not is_coarsening_of(self._comp._sym, other_sym): + return False + if not is_coarsening_of(self._comp._antisym, other._comp._antisym): + return False + return True From b132b842b137c05351820224ec78edeea6eab758 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:27:49 -0700 Subject: [PATCH 184/454] TensorFreeSubmodule_comp.ambient_module: New --- src/sage/tensor/modules/tensor_free_submodule.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 449acc81b77..bfa46091960 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -64,6 +64,21 @@ def _repr_(self): return "Free module of type-({},{}) tensors with {} on the {}".format( self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) + def ambient_module(self): + """ + Return the ambient module associated to this module. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M.ambient_module() is T60M + True + """ + return self.base_module().tensor_module(*self.tensor_type()) + def is_submodule(self, other): r""" Return ``True`` if ``self`` is a submodule of ``other``. From 677c5aecb9d6f0b3e8c521c196913d62e39204a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:27:57 -0700 Subject: [PATCH 185/454] Fix doctests --- src/sage/tensor/modules/tensor_free_module.py | 2 +- src/sage/tensor/modules/tensor_free_submodule.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 3959b88368a..a07d96ce11d 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -699,7 +699,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T = M.tensor_module(1,1) sage: e_T = T.basis('e'); e_T - + sage: for a in e_T: a.display() e_0*e^0 e_0*e^1 diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index bfa46091960..bcb083135ec 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -54,7 +54,8 @@ def _repr_(self): EXAMPLES:: - sage: M = FiniteRankFreeModule(QQ, 2, name='M') + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. [0, 1, 2] From 87d21c6c4e1655f8dd1ae650c66cdb97d55efee4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 2 Aug 2020 21:39:43 -0700 Subject: [PATCH 186/454] TensorFreeSubmodule_comp: Add alias ambient = ambient_module --- src/sage/tensor/modules/tensor_free_submodule.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index bcb083135ec..3f2ce525d5e 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -65,7 +65,7 @@ def _repr_(self): return "Free module of type-({},{}) tensors with {} on the {}".format( self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) - def ambient_module(self): + def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_generic """ Return the ambient module associated to this module. @@ -80,6 +80,8 @@ def ambient_module(self): """ return self.base_module().tensor_module(*self.tensor_type()) + ambient = ambient_module # compatible with sage.modules.with_basis.subquotient.SubmoduleWithBasis + def is_submodule(self, other): r""" Return ``True`` if ``self`` is a submodule of ``other``. From a6c6427437f4659533a72b31c212e33d7e32f84a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 13:22:05 -0700 Subject: [PATCH 187/454] TensorFreeSubmodule_comp: Add tests --- .../tensor/modules/tensor_free_submodule.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 3f2ce525d5e..5763d9567e6 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -30,6 +30,25 @@ class TensorFreeSubmodule_comp(TensorFreeModule): Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. [0, 1, 2] on the Rank-3 free module M over the Integer Ring + + Canonical injections from submodules are coercions:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym0123x45M.has_coerce_map_from(Sym012345M) + True + sage: T60M.has_coerce_map_from(Sym0123x45M) + True + + TESTS:: + + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: TestSuite(Sym0123x45M).run() + """ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, sym=None, antisym=None): From 4dc5f18857dabe6a1951d132e5b0b96b175d1361 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 17:34:51 -0700 Subject: [PATCH 188/454] FiniteRankFreeModule: Accept init arg 'ambient' --- .../tensor/modules/finite_rank_free_module.py | 24 +++++++++++++++---- .../tensor/modules/tensor_free_submodule.py | 22 ++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index ecc2e914d35..cb0500b02bb 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -757,7 +757,7 @@ class :class:`~sage.modules.module.Module`. @staticmethod def __classcall_private__(cls, ring, rank, name=None, latex_name=None, start_index=0, - output_formatter=None, category=None): + output_formatter=None, category=None, ambient=None): r""" Normalize init arguments for ``UniqueRepresentation`` @@ -791,6 +791,7 @@ def __init__( start_index: int = 0, output_formatter=None, category=None, + ambient=None, ): r""" See :class:`FiniteRankFreeModule` for documentation and examples. @@ -810,8 +811,12 @@ def __init__( if ring not in Rings().Commutative(): raise TypeError("the module base ring must be commutative") category = Modules(ring).FiniteDimensional().or_subcategory(category) - Parent.__init__(self, base=ring, category=category) + Parent.__init__(self, base=ring, category=category, facade=ambient) self._ring = ring # same as self._base + if ambient is None: + self._ambient_module = self + else: + self._ambient_module = ambient self._rank = rank self._name = name # This duplicates the normalization done in __classcall_private__, @@ -2825,7 +2830,7 @@ def identity_map(self, name='Id', latex_name=None): self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map - def ambient_module(self): + def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_generic """ Return the ambient module associated to this module. @@ -2834,8 +2839,17 @@ def ambient_module(self): sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: M.ambient_module() is M True + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M.ambient_module() is T60M + True """ - return self + return self._ambient_module + + ambient = ambient_module # compatible with sage.modules.with_basis.subquotient.SubmoduleWithBasis def is_submodule(self, other): """ @@ -2850,4 +2864,4 @@ def is_submodule(self, other): sage: M.is_submodule(N) False """ - return self == other + return self == other or self == self.ambient_module() diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 5763d9567e6..2463e062b62 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -51,7 +51,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): """ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, - sym=None, antisym=None): + sym=None, antisym=None, *, ambient=None, category=None): self._fmodule = fmodule self._tensor_type = tuple(tensor_type) # Create a tensor only because we need a Components object @@ -61,11 +61,13 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, frame = list(fmodule.irange()) self._comp = tensor._new_comp(frame) rank = len(list(self._comp.non_redundant_index_generator())) + category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, - output_formatter=fmodule._output_formatter) + output_formatter=fmodule._output_formatter, + ambient=ambient, category=category) def _repr_(self): r""" @@ -84,22 +86,6 @@ def _repr_(self): return "Free module of type-({},{}) tensors with {} on the {}".format( self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) - def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_generic - """ - Return the ambient module associated to this module. - - EXAMPLES:: - - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M.ambient_module() is T60M - True - """ - return self.base_module().tensor_module(*self.tensor_type()) - - ambient = ambient_module # compatible with sage.modules.with_basis.subquotient.SubmoduleWithBasis def is_submodule(self, other): r""" From 07291564d5dd3d19b6a8b115981523e6b7b84c74 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 17:36:51 -0700 Subject: [PATCH 189/454] TensorFreeSubmodule_comp: Factor out _is_symmetry_coarsening_of, add _element_constructor_ --- .../tensor/modules/tensor_free_submodule.py | 99 ++++++++++++++----- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 2463e062b62..daa7e3a62ae 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -35,6 +35,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) @@ -43,6 +44,11 @@ class TensorFreeSubmodule_comp(TensorFreeModule): True sage: T60M.has_coerce_map_from(Sym0123x45M) True + sage: t = e[0]^6 + sage: t.parent() + FIXME + sage: Sym012345M(t) is t + FIXME TESTS:: @@ -86,6 +92,73 @@ def _repr_(self): return "Free module of type-({},{}) tensors with {} on the {}".format( self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) + def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): + self_tensor_type = self.tensor_type() + + def sym_antisym(comp): + if isinstance(comp, tuple): + sym, antisym = tuple + if sym is None: + sym = [] + if antisym is None: + antisym = [] + return sym, antisym + # Similar code is in Component.contract, should refactor. + try: + return comp._sym, comp._antisym + except AttributeError: + return [], [] + + def is_coarsening_of(self_sym_list, other_sym_list): + # Use the union-find data structure + S = DisjointSet(self_tensor_type[0] + self_tensor_type[1]) + for index_set in self_sym_list: + i = index_set[0] + for j in index_set[1:]: + S.union(i, j) + for index_set in other_sym_list: + i = S.find(index_set[0]) + for j in index_set[1:]: + if S.find(j) != i: + return False + return True + + finer_sym, finer_antisym = sym_antisym(finer_comp) + if not finer_sym and not finer_antisym: + return True + coarser_sym, coarser_antisym = sym_antisym(coarser_comp) + if not is_coarsening_of(self._comp._sym, other_sym): + return False + if not is_coarsening_of(self._comp._antisym, other_antisym): + return False + return True + + def _element_constructor_(self, comp=[], basis=None, name=None, + latex_name=None, sym=None, antisym=None): + if sym is not None or antisym is not None: + # Refuse to create a tensor with finer symmetries + # than those defining the subspace + if not self._is_symmetry_coarsening_of((sym, antisym), self._comp): + raise ValueError("cannot create a tensor with symmetries {} as an element of {}". + format((sym, antisym), self)) + try: + comp_parent = comp.parent() + except AttributeError: + comp_parent = None + if comp_parent == self.ambient_module(): + resu = comp + # comp is already a tensor. If its declared symmetries are coarser + # than the symmetries defining self, we can use it directly. + if self._is_symmetry_coarsening_of(resu, self._comp): + return resu + if sym is None: + sym = self._comp._sym + if antisym is None: + sym = self._comp._antisym + resu = super()._element_constructor_(comp=comp, basis=basis, name=name, + latex_name=latex_name, + sym=sym, antisym=antisym) + return resu def is_submodule(self, other): r""" @@ -111,7 +184,7 @@ def is_submodule(self, other): True """ - if self == other: + if super().is_submodule(other): return True self_base_module = self.base_module() self_tensor_type = self.tensor_type() @@ -124,28 +197,10 @@ def is_submodule(self, other): return False if self_tensor_type != other_tensor_type: return False - # Use the union-find data structure - def is_coarsening_of(self_sym_list, other_sym_list): - S = DisjointSet(self_tensor_type[0] + self_tensor_type[1]) - for index_set in self_sym_list: - i = index_set[0] - for j in index_set[1:]: - S.union(i, j) - for index_set in other_sym_list: - i = S.find(index_set[0]) - for j in index_set[1:]: - if S.find(j) != i: - return False - return True - # Similar code is in Component.contract, should refactor. + try: - other_sym = other._comp._sym - other_antisym = other._comp._antisym + other_comp = other._comp except AttributeError: # other is full tensor module (no symmetry) return True - if not is_coarsening_of(self._comp._sym, other_sym): - return False - if not is_coarsening_of(self._comp._antisym, other._comp._antisym): - return False - return True + return self._is_symmetry_coarsening_of(self._comp, other_comp) From a07fe6d3ecea7266eefc8d5d7318a8a186ff0aef Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 18:18:46 -0700 Subject: [PATCH 190/454] Fixup --- src/sage/tensor/modules/finite_rank_free_module.py | 2 +- src/sage/tensor/modules/tensor_free_submodule.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index cb0500b02bb..108a845c596 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -2864,4 +2864,4 @@ def is_submodule(self, other): sage: M.is_submodule(N) False """ - return self == other or self == self.ambient_module() + return self == other or self.ambient_module() == other diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index daa7e3a62ae..1e2f0f4ae51 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -127,9 +127,9 @@ def is_coarsening_of(self_sym_list, other_sym_list): if not finer_sym and not finer_antisym: return True coarser_sym, coarser_antisym = sym_antisym(coarser_comp) - if not is_coarsening_of(self._comp._sym, other_sym): + if not is_coarsening_of(coarser_sym, finer_sym): return False - if not is_coarsening_of(self._comp._antisym, other_antisym): + if not is_coarsening_of(coarser_antisym, finer_antisym): return False return True From cff18191ec726801ed70d255abfee783df6e6366 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 19:06:18 -0700 Subject: [PATCH 191/454] TensorFreeModule._element_constructor_: Accept FreeModuleTensor of same tensor type and base module as input --- src/sage/tensor/modules/tensor_free_module.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index a07d96ce11d..07e99366b5a 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -484,6 +484,17 @@ def _element_constructor_(self, comp=[], basis=None, name=None, latex_name=autom._latex_name) for basis, comp in autom._components.items(): resu._components[basis] = comp.copy() + elif isinstance(comp, FreeModuleTensor): + tensor = comp + if self._tensor_type != tensor._tensor_type or \ + self._fmodule != tensor.base_module(): + raise TypeError("cannot coerce the {}".format(tensor) + + " to an element of {}".format(self)) + resu = self.element_class(self._fmodule, self._tensor_type, + name=name, latex_name=latex_name, + sym=sym, antisym=antisym) + for basis, comp in tensor._components.items(): + resu._components[basis] = comp.copy() else: # Standard construction: resu = self.element_class(self._fmodule, self._tensor_type, From a6f81a614c4e945bf495008bf5bc3169659fa6db Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 19:06:55 -0700 Subject: [PATCH 192/454] TensorFreeSubmodule_comp: Set ambient correctly --- src/sage/tensor/modules/tensor_free_submodule.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 1e2f0f4ae51..e42df1cbd89 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -41,14 +41,14 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: Sym0123x45M.has_coerce_map_from(Sym012345M) - True + False sage: T60M.has_coerce_map_from(Sym0123x45M) - True - sage: t = e[0]^6 + False + sage: t = e[0] * e[0] * e[0] * e[0] * e[0] * e[0] sage: t.parent() - FIXME + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring sage: Sym012345M(t) is t - FIXME + False TESTS:: @@ -69,6 +69,8 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, rank = len(list(self._comp.non_redundant_index_generator())) category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ + if ambient is None: + ambient = fmodule.tensor_module(*tensor_type) FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, @@ -155,7 +157,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None, sym = self._comp._sym if antisym is None: sym = self._comp._antisym - resu = super()._element_constructor_(comp=comp, basis=basis, name=name, + resu = super()._element_constructor_(comp=comp, + basis=basis, name=name, latex_name=latex_name, sym=sym, antisym=antisym) return resu From 22e9e1cb230de30ad3a80f5f1ee2753555c10eb6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 22:34:30 -0700 Subject: [PATCH 193/454] TensorFreeModule._coerce_map_from_: Add coercion from submodules --- src/sage/tensor/modules/tensor_free_module.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 07e99366b5a..fd5b092d41c 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -609,6 +609,13 @@ def _coerce_map_from_(self, other): sage: M.tensor_module(0,2)._coerce_map_from_(N.dual_exterior_power(2)) False + Coercion from submodules:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: Sym01M = TensorFreeSubmodule_comp(M, (2, 0), sym=((0, 1))) + sage: M.tensor_module(2,0)._coerce_map_from_(Sym01M) + True + """ from .free_module_homset import FreeModuleHomset from .ext_pow_free_module import (ExtPowerFreeModule, @@ -634,6 +641,11 @@ def _coerce_map_from_(self, other): # Coercion of an automorphism to a type-(1,1) tensor: return self._tensor_type == (1,1) and \ self._fmodule is other.base_module() + try: + if other.is_submodule(self): + return True + except AttributeError: + pass return False #### End of parent methods From 02927b83fb8d002153bb118b506d82395b99099c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 22:36:02 -0700 Subject: [PATCH 194/454] TensorFreeSubmodule_comp: Fix coerce map tests --- src/sage/tensor/modules/tensor_free_submodule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index e42df1cbd89..19cfbae44e0 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -41,9 +41,9 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: Sym0123x45M.has_coerce_map_from(Sym012345M) - False + True sage: T60M.has_coerce_map_from(Sym0123x45M) - False + True sage: t = e[0] * e[0] * e[0] * e[0] * e[0] * e[0] sage: t.parent() Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring From 4995258b766262d152b8a160c316f19c733b1849 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Aug 2020 22:51:17 -0700 Subject: [PATCH 195/454] TensorFreeSubmodule_comp.lift: New --- .../tensor/modules/tensor_free_submodule.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 19cfbae44e0..8d9090ef816 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -12,6 +12,7 @@ #****************************************************************************** from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.sets.disjoint_set import DisjointSet from .tensor_free_module import TensorFreeModule from .finite_rank_free_module import FiniteRankFreeModule @@ -207,3 +208,25 @@ def is_submodule(self, other): # other is full tensor module (no symmetry) return True return self._is_symmetry_coarsening_of(self._comp, other_comp) + + @lazy_attribute + def lift(self): + r""" + The lift (embedding) map from ``self`` to the ambient space. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M.lift + Generic morphism: + From: Free module of type-(6,0) tensors + with 6-indices components w.r.t. [0, 1, 2], + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + To: Free module of type-(6,0) tensors + on the Rank-3 free module M over the Integer Ring + """ + return self.module_morphism(function=lambda x: x, codomain=self.ambient()) From eca7a57b3ca6ecf1eab49df4b4b60f278f8de42f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 4 Aug 2020 12:28:58 -0700 Subject: [PATCH 196/454] FiniteRankFreeModule: Add _test_basis method --- src/sage/tensor/modules/finite_rank_free_module.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 108a845c596..77fcc807446 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1396,6 +1396,18 @@ def basis(self, symbol, latex_symbol=None, from_family=None, "linearly independent") return resu + def _test_basis(self, **options): + r""" + Test that the ``basis`` method works correctly. + """ + tester = self._tester(**options) + b = self.basis('test') + # Test uniqueness + b_again = self.basis('test') + tester.assertTrue(b is b_again) + # Test rank + tester.assertEqual(len(b), self.rank()) + def tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None): r""" From f633af8fb9f9d2005c1f780a9cab714385e29fc9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 4 Aug 2020 19:54:10 -0700 Subject: [PATCH 197/454] FiniteRankFreeModule._test_basis: Test indices and containment --- src/sage/tensor/modules/finite_rank_free_module.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 77fcc807446..bc4b6ca5ddc 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1407,6 +1407,12 @@ def _test_basis(self, **options): tester.assertTrue(b is b_again) # Test rank tester.assertEqual(len(b), self.rank()) + indices = list(self.irange()) + tester.assertEqual(len(b), len(indices)) + # Test basis indexing + for index, element in zip(indices, b): + tester.assertTrue(element is b[index]) + tester.assertTrue(element in self) def tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None): From ef3e7b76f0f43729351582e016089879fce14644 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 4 Aug 2020 19:58:08 -0700 Subject: [PATCH 198/454] TensorFreeSubmoduleBasis_comp: Add keys, __getitem__; compute symmetrized elements --- .../modules/tensor_free_submodule_basis.py | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 31f6b1bbbb2..33b0787e796 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -52,20 +52,52 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, frame = list(base_module.irange()) self._comp = tensor._new_comp(frame) + def keys(self): + yield from self._comp.non_redundant_index_generator() + def __iter__(self): r""" Generate the basis elements of ``self``. + """ + for ind in self.keys(): + yield self[ind] + + def __getitem__(self, index): + r""" + Return the basis element corresponding to a given index. + + INPUT: + + - ``index`` -- the index of the basis element + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T11 = M.tensor_module(1,1) + sage: e11 = T11.basis('e') + sage: e11[1, 2].display() + e_1*e^2 + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + Free module of type-(2,0) tensors + with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + on the Rank-3 free module M over the Integer Ring + sage: eSym2M = Sym2M.basis('e') + sage: eSym2M[1, 1].display() + e_1*e_1 + sage: eSym2M[1, 2].display() + e_1*e_2 + e_2*e_1 + """ tensor_module = self._fmodule base_module = tensor_module.base_module() base_module_basis = self._base_module_basis - for ind in self._comp.non_redundant_index_generator(): - element = tensor_module.element_class(base_module, tensor_module._tensor_type) - element.set_comp(base_module_basis)[ind] = 1 - yield element + element = tensor_module([]) + element.set_comp(base_module_basis)[index] = 1 + return element # Todo: Make it a Family -# symmetrize/antisymmetrize it # dual basis # add test for dual # lift/reduce/retract From 419bec6e6324b5d8232032e429859d4f132cc23c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 5 Aug 2020 11:08:11 -0700 Subject: [PATCH 199/454] FiniteRankFreeModule._test_basis: Run the test suite of the basis object --- .../tensor/modules/finite_rank_free_module.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index bc4b6ca5ddc..b1291525909 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1396,11 +1396,28 @@ def basis(self, symbol, latex_symbol=None, from_family=None, "linearly independent") return resu - def _test_basis(self, **options): + def _test_basis(self, tester=None, **options): r""" Test that the ``basis`` method works correctly. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M._test_basis(verbose=True) + + Running the test suite of self.basis('test') + running ._test_category() . . . pass + running ._test_new() . . . pass + running ._test_not_implemented_methods() . . . pass + running ._test_pickling() . . . pass + """ - tester = self._tester(**options) + from sage.misc.sage_unittest import TestSuite + # The intention is to raise an exception only if this is + # run as a sub-testsuite of a larger testsuite. + # (from _test_elements) + is_sub_testsuite = (tester is not None) + tester = self._tester(tester=tester, **options) b = self.basis('test') # Test uniqueness b_again = self.basis('test') @@ -1412,7 +1429,10 @@ def _test_basis(self, **options): # Test basis indexing for index, element in zip(indices, b): tester.assertTrue(element is b[index]) - tester.assertTrue(element in self) + # Run test suite of the basis object (similar to _test_elements) + tester.info("\n Running the test suite of self.basis('test')") + TestSuite(b).run(verbose=tester._verbose, prefix=tester._prefix + " ", + raise_on_failure=is_sub_testsuite) def tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None): From c7649e407e5812a8c00452908ae6f5586d770956 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 5 Aug 2020 12:26:44 -0700 Subject: [PATCH 200/454] FiniteRankFreeModule.isomorphism_with_fixed_basis: Make basis argument optional; add test method --- .../tensor/modules/finite_rank_free_module.py | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index b1291525909..53de21f863b 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -2620,7 +2620,7 @@ def hom(self, codomain, matrix_rep, bases=None, name=None, return homset(matrix_rep, bases=bases, name=name, latex_name=latex_name) - def isomorphism_with_fixed_basis(self, basis, codomain=None): + def isomorphism_with_fixed_basis(self, basis=None, codomain=None): r""" Construct the canonical isomorphism from the free module ``self`` to a free module in which ``basis`` of ``self`` is mapped to the @@ -2628,8 +2628,9 @@ def isomorphism_with_fixed_basis(self, basis, codomain=None): INPUT: - - ``basis`` -- the basis of ``self`` which should be mapped to the - distinguished basis on ``codomain`` + - ``basis`` -- (default: ``None``) the basis of ``self`` which + should be mapped to the distinguished basis on ``codomain``; + if ``None``, the default basis is assumed. - ``codomain`` -- (default: ``None``) the codomain of the isomorphism represented by a free module within the category :class:`~sage.categories.modules_with_basis.ModulesWithBasis` with @@ -2694,6 +2695,8 @@ def isomorphism_with_fixed_basis(self, basis, codomain=None): ValueError: domain and codomain must have the same base ring """ base_ring = self.base_ring() + if basis is None: + basis = self.default_basis() if codomain is None: from sage.combinat.free_module import CombinatorialFreeModule if isinstance(basis._symbol, str): @@ -2720,6 +2723,20 @@ def _isomorphism(x): return self.module_morphism(function=_isomorphism, codomain=codomain) + def _test_isomorphism_with_fixed_basis(self, **options): + r""" + Test that the method ``isomorphism_with_fixed_basis`` works correctly. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M._test_isomorphism_with_fixed_basis() + """ + tester = self._tester(**options) + basis = self.basis('test') + morphism = self.isomorphism_with_fixed_basis(basis) + tester.assertEqual(morphism.codomain().rank(), self.rank()) + def endomorphism(self, matrix_rep, basis=None, name=None, latex_name=None): r""" Construct an endomorphism of the free module ``self``. From fbc4445c7f73cad787bffe57195d035b7d6891ae Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 5 Aug 2020 14:13:21 -0700 Subject: [PATCH 201/454] WIP: _comp and irange --- src/sage/tensor/modules/tensor_free_module.py | 13 +++++++++ .../tensor/modules/tensor_free_submodule.py | 27 ++++++++----------- .../modules/tensor_free_submodule_basis.py | 9 +------ 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index fd5b092d41c..dae92b0a605 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -747,3 +747,16 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_2*e_2 """ return TensorFreeSubmoduleBasis_comp(self, symbol) + + @cached_method + def _basis_comp(self): + # Data for TensorFreeSubmoduleBasis_comp + frame = tuple(self.base_module().irange()) + tensor = self.ambient()() + return tensor._new_comp(frame) + + def irange(self): + r""" + Index generator, labelling the elements of a basis of ``self``. + """ + yield from self._basis_comp().non_redundant_index_generator() diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 8d9090ef816..ecd374018ee 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -61,17 +61,16 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, sym=None, antisym=None, *, ambient=None, category=None): self._fmodule = fmodule self._tensor_type = tuple(tensor_type) + if ambient is None: + ambient = fmodule.tensor_module(*tensor_type) + self._ambient_module = ambient # Create a tensor only because we need a Components object tensor = fmodule.tensor(tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) - frame = list(fmodule.irange()) - self._comp = tensor._new_comp(frame) - rank = len(list(self._comp.non_redundant_index_generator())) + rank = len(list(self.irange())) category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ - if ambient is None: - ambient = fmodule.tensor_module(*tensor_type) FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, latex_name=latex_name, start_index=fmodule._sindex, @@ -93,7 +92,7 @@ def _repr_(self): """ return "Free module of type-({},{}) tensors with {} on the {}".format( - self._tensor_type[0], self._tensor_type[1], self._comp, self._fmodule) + self._tensor_type[0], self._tensor_type[1], self._basis_comp(), self._fmodule) def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): self_tensor_type = self.tensor_type() @@ -141,7 +140,7 @@ def _element_constructor_(self, comp=[], basis=None, name=None, if sym is not None or antisym is not None: # Refuse to create a tensor with finer symmetries # than those defining the subspace - if not self._is_symmetry_coarsening_of((sym, antisym), self._comp): + if not self._is_symmetry_coarsening_of((sym, antisym), self._basis_comp()): raise ValueError("cannot create a tensor with symmetries {} as an element of {}". format((sym, antisym), self)) try: @@ -152,12 +151,12 @@ def _element_constructor_(self, comp=[], basis=None, name=None, resu = comp # comp is already a tensor. If its declared symmetries are coarser # than the symmetries defining self, we can use it directly. - if self._is_symmetry_coarsening_of(resu, self._comp): + if self._is_symmetry_coarsening_of(resu, self._basis_comp()): return resu if sym is None: - sym = self._comp._sym + sym = self._basis_comp()._sym if antisym is None: - sym = self._comp._antisym + antisym = self._basis_comp()._antisym resu = super()._element_constructor_(comp=comp, basis=basis, name=name, latex_name=latex_name, @@ -202,12 +201,8 @@ def is_submodule(self, other): if self_tensor_type != other_tensor_type: return False - try: - other_comp = other._comp - except AttributeError: - # other is full tensor module (no symmetry) - return True - return self._is_symmetry_coarsening_of(self._comp, other_comp) + other_comp = other._basis_comp() + return self._is_symmetry_coarsening_of(self._basis_comp(), other_comp) @lazy_attribute def lift(self): diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 33b0787e796..64be46ffc02 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -43,14 +43,7 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, latex_indices, symbol_dual, latex_symbol_dual) super().__init__(tensor_module, symbol, latex_symbol, indices, latex_indices) self._base_module_basis = base_module_basis - try: - # TensorFreeSubmodule_comp - self._comp = tensor_module._comp - except AttributeError: - # TensorFreeModule - tensor = tensor_module() - frame = list(base_module.irange()) - self._comp = tensor._new_comp(frame) + self._comp = tensor_module._basis_comp() def keys(self): yield from self._comp.non_redundant_index_generator() From 7715f04c94acee3879fa553c7d91e1eea0bdd9c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 16:16:48 -0700 Subject: [PATCH 202/454] Update doctest outputs --- .../tensor/modules/finite_rank_free_module.py | 1 + src/sage/tensor/modules/tensor_free_module.py | 30 +++++++++---------- .../tensor/modules/tensor_free_submodule.py | 6 ++-- .../modules/tensor_free_submodule_basis.py | 26 ++++++++-------- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 53de21f863b..0b23d770057 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1407,6 +1407,7 @@ def _test_basis(self, tester=None, **options): Running the test suite of self.basis('test') running ._test_category() . . . pass + running ._test_iter_len() . . . pass running ._test_new() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index dae92b0a605..7a9e13499cb 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -724,27 +724,27 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: e_T = T.basis('e'); e_T sage: for a in e_T: a.display() - e_0*e^0 - e_0*e^1 - e_0*e^2 - e_1*e^0 - e_1*e^1 - e_1*e^2 - e_2*e^0 - e_2*e^1 - e_2*e^2 + e_0⊗e^0 + e_0⊗e^1 + e_0⊗e^2 + e_1⊗e^0 + e_1⊗e^1 + e_1⊗e^2 + e_2⊗e^0 + e_2⊗e^1 + e_2⊗e^2 sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M sage: for a in e_Sym2M: a.display() - e_0*e_0 - e_0*e_1 - e_0*e_2 - e_1*e_1 - e_1*e_2 - e_2*e_2 + e_0⊗e_0 + e_0⊗e_1 + e_0⊗e_2 + e_1⊗e_1 + e_1⊗e_2 + e_2⊗e_2 """ return TensorFreeSubmoduleBasis_comp(self, symbol) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index ecd374018ee..16a03e272cc 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -29,7 +29,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + with Fully symmetric 2-indices components w.r.t. (0, 1, 2) on the Rank-3 free module M over the Integer Ring Canonical injections from submodules are coercions:: @@ -87,7 +87,7 @@ def _repr_(self): sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + with Fully symmetric 2-indices components w.r.t. (0, 1, 2) on the Rank-3 free module M over the Integer Ring """ @@ -217,7 +217,7 @@ def lift(self): sage: Sym0123x45M.lift Generic morphism: From: Free module of type-(6,0) tensors - with 6-indices components w.r.t. [0, 1, 2], + with 6-indices components w.r.t. (0, 1, 2), with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) on the Rank-3 free module M over the Integer Ring diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 64be46ffc02..ed4cdacd9dd 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -24,15 +24,15 @@ class TensorFreeSubmoduleBasis_comp(Basis_abstract): sage: T11 = M.tensor_module(1,1) sage: e11 = T11.basis('e') sage: for a in e11: a.display() - e_0*e^0 - e_0*e^1 - e_0*e^2 - e_1*e^0 - e_1*e^1 - e_1*e^2 - e_2*e^0 - e_2*e^1 - e_2*e^2 + e_0⊗e^0 + e_0⊗e^1 + e_0⊗e^2 + e_1⊗e^0 + e_1⊗e^1 + e_1⊗e^2 + e_2⊗e^0 + e_2⊗e^1 + e_2⊗e^2 """ @@ -69,18 +69,18 @@ def __getitem__(self, index): sage: T11 = M.tensor_module(1,1) sage: e11 = T11.basis('e') sage: e11[1, 2].display() - e_1*e^2 + e_1⊗e^2 sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. [0, 1, 2] + with Fully symmetric 2-indices components w.r.t. (0, 1, 2) on the Rank-3 free module M over the Integer Ring sage: eSym2M = Sym2M.basis('e') sage: eSym2M[1, 1].display() - e_1*e_1 + e_1⊗e_1 sage: eSym2M[1, 2].display() - e_1*e_2 + e_2*e_1 + e_1⊗e_2 + e_2⊗e_1 """ tensor_module = self._fmodule From 7caacef17846f4e5c679451ab0a46b6a1cc50e7c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 16:41:08 -0700 Subject: [PATCH 203/454] Update doctest outputs --- .../tensor/modules/finite_rank_free_module.py | 21 +++++++++++++++++++ src/sage/tensor/modules/tensor_free_module.py | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 0b23d770057..b476047d6e6 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1406,11 +1406,32 @@ def _test_basis(self, tester=None, **options): sage: M._test_basis(verbose=True) Running the test suite of self.basis('test') + running ._test_an_element() . . . pass + running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass + running ._test_elements() . . . + Running the test suite of self.an_element() + running ._test_category() . . . pass + running ._test_eq() . . . pass + running ._test_new() . . . pass + running ._test_nonzero_equal() . . . pass + running ._test_not_implemented_methods() . . . pass + running ._test_pickling() . . . pass + pass + running ._test_elements_eq_reflexive() . . . pass + running ._test_elements_eq_symmetric() . . . pass + running ._test_elements_eq_transitive() . . . pass + running ._test_elements_neq() . . . pass + running ._test_enumerated_set_contains() . . . pass + running ._test_enumerated_set_iter_cardinality() . . . pass + running ._test_enumerated_set_iter_list() . . . pass + running ._test_eq() . . . pass running ._test_iter_len() . . . pass running ._test_new() . . . pass running ._test_not_implemented_methods() . . . pass running ._test_pickling() . . . pass + running ._test_some_elements() . . . pass """ from sage.misc.sage_unittest import TestSuite diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 7a9e13499cb..d566d4ead7d 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -722,7 +722,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T = M.tensor_module(1,1) sage: e_T = T.basis('e'); e_T - + sage: for a in e_T: a.display() e_0⊗e^0 e_0⊗e^1 @@ -737,7 +737,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M - + sage: for a in e_Sym2M: a.display() e_0⊗e_0 e_0⊗e_1 From ce3ca35473f1790f7f1b88f5370578198fe7f888 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 17:02:44 -0700 Subject: [PATCH 204/454] TensorFreeSubmodule_comp: Store sym, antisym, use it in _basis_comp --- src/sage/tensor/modules/tensor_free_submodule.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 16a03e272cc..1039ee6ec26 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -64,10 +64,8 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, if ambient is None: ambient = fmodule.tensor_module(*tensor_type) self._ambient_module = ambient - # Create a tensor only because we need a Components object - tensor = fmodule.tensor(tensor_type, - name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + self._sym = sym + self._antisym = antisym rank = len(list(self.irange())) category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ @@ -77,6 +75,12 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, output_formatter=fmodule._output_formatter, ambient=ambient, category=category) + @cached_method + def _basis_comp(self): + frame = tuple(self.base_module().irange()) + tensor = self.ambient()(sym=self._sym, antisym=self._antisym) + return tensor._new_comp(frame) + def _repr_(self): r""" Return a string representation of ``self``. From e940d0ffa01c0ed51d73cd4c066ef6418aef18dd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 17:26:00 -0700 Subject: [PATCH 205/454] TensorFreeSubmodule_comp._basis_comp: Fix up --- src/sage/tensor/modules/tensor_free_submodule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 1039ee6ec26..f7ee2487c67 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -78,7 +78,8 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, @cached_method def _basis_comp(self): frame = tuple(self.base_module().irange()) - tensor = self.ambient()(sym=self._sym, antisym=self._antisym) + # Need to call _element_constructor_ explicitly, or the passed arguments are dropped + tensor = self.ambient()._element_constructor_(sym=self._sym, antisym=self._antisym) return tensor._new_comp(frame) def _repr_(self): From a6ebabf1dc2683f1cf0e4dd54c57bcb3421b4669 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 17:27:27 -0700 Subject: [PATCH 206/454] src/sage/tensor/modules/tensor_free_module.py: Fix doctest output --- src/sage/tensor/modules/tensor_free_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index d566d4ead7d..7663c8448a9 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -740,10 +740,10 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: for a in e_Sym2M: a.display() e_0⊗e_0 - e_0⊗e_1 - e_0⊗e_2 + e_0⊗e_1 + e_1⊗e_0 + e_0⊗e_2 + e_2⊗e_0 e_1⊗e_1 - e_1⊗e_2 + e_1⊗e_2 + e_2⊗e_1 e_2⊗e_2 """ return TensorFreeSubmoduleBasis_comp(self, symbol) From 8d7879577afe8a40324adfc6d6f00262d6da8273 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 18:03:20 -0700 Subject: [PATCH 207/454] TensorFreeSubmoduleBasis_comp.values: New --- src/sage/tensor/modules/tensor_free_submodule_basis.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index ed4cdacd9dd..96076a27bd3 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -48,6 +48,9 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, def keys(self): yield from self._comp.non_redundant_index_generator() + def values(self): + yield from self + def __iter__(self): r""" Generate the basis elements of ``self``. From fec6930606048747da6290ea92ec000ad26d894e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 18:55:47 -0700 Subject: [PATCH 208/454] Disable _test_basis on non-base modules --- src/sage/tensor/modules/ext_pow_free_module.py | 9 +++++++++ src/sage/tensor/modules/finite_rank_free_module.py | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index c68033fdf51..588d72db42f 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -423,6 +423,10 @@ def degree(self): """ return self._degree + def basis(self, *args, **kwds): + # We override it so that _test_basis is disabled. + # See https://trac.sagemath.org/ticket/30229#comment:6 for a better solution + raise NotImplementedError #*********************************************************************** @@ -893,3 +897,8 @@ def degree(self): """ return self._degree + + def basis(self, *args, **kwds): + # We override it so that _test_basis is disabled. + # See https://trac.sagemath.org/ticket/30229#comment:6 for a better solution + raise NotImplementedError diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index b476047d6e6..33f8ef8267f 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1440,7 +1440,10 @@ def _test_basis(self, tester=None, **options): # (from _test_elements) is_sub_testsuite = (tester is not None) tester = self._tester(tester=tester, **options) - b = self.basis('test') + try: + b = self.basis('test') + except NotImplementedError: + return # Test uniqueness b_again = self.basis('test') tester.assertTrue(b is b_again) From 5403ee0677cc9388d76b825ee1cc9e35b8a90b96 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 24 Aug 2022 12:32:59 +0900 Subject: [PATCH 209/454] Finishing the migration to the methods for building free resolutions. --- src/sage/homology/free_resolution.py | 42 ++++++++----- src/sage/homology/graded_resolution.py | 84 +++++++++++--------------- src/sage/modules/free_module.py | 16 +++-- 3 files changed, 75 insertions(+), 67 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index efa327d051c..7b7c611ca24 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -52,11 +52,12 @@ AUTHORS: - Kwankyu Lee (2022-05-13): initial version - +- Travis Scrimshaw (2022-08-23): refactored for free module inputs """ # **************************************************************************** # Copyright (C) 2022 Kwankyu Lee +# (C) 2022 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -96,7 +97,7 @@ class FreeResolution(SageObject, metaclass=ClasscallMetaclass): homological index `1`. """ @staticmethod - def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', **kwds): + def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', graded=False, **kwds): """ Dispatch to the correct constructor. @@ -158,9 +159,9 @@ def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', **kw if not is_free_module: from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular if not isinstance(S, MPolynomialRing_libsingular): - raise NotImplementedError("the ring must be a free module or a polynomial ring that can be constructed in Singular") + raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") - if degrees is not None or shifts is not None: + if graded or degrees is not None or shifts is not None: # We are computing a graded resolution from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) @@ -169,7 +170,7 @@ def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', **kw # Otherwise we know it is a free module - if degrees is not None or shifts is not None: + if graded or degrees is not None or shifts is not None: # We are computing a graded resolution from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) @@ -214,10 +215,10 @@ def _repr_(self): sage: r = FreeResolution(m1, name='S') sage: print(FreeResolution._repr_(r)) Free resolution with initial map: - [z^2 - y*w, y*z - x*w, y^2 - x*z] + [z^2 - y*w y*z - x*w y^2 - x*z] """ if isinstance(self._module, Matrix): - return f"Free resolution with inital map:\n{self._module}" + return f"Free resolution with initial map:\n{self._module}" return f"Free resolution of {self._module}" def _repr_module(self, i): @@ -674,23 +675,36 @@ def _maps(self): sage: res._maps [[x^4 + 3*x^2 + 2]] - An overdetermined system over a PID:: - sage: from sage.homology.free_resolution import FreeResolution - sage: R. = QQ[] sage: M = matrix([[x^2, 2], ....: [3*x^2, 5], ....: [5*x^2, 4]]) + sage: res = FreeResolution(M.transpose()) + sage: res + S^3 <-- S^2 <-- 0 + sage: res._m() + [ 1 0] + [ 5/2 -x^2] + [ 2 -6*x^2] + sage: res._maps + [ + [ 1 0] + [ 5/2 -x^2] + [ 2 -6*x^2] + ] + + An overdetermined system over a PID:: + sage: res = FreeResolution(M) sage: res S^2 <-- S^2 <-- 0 sage: res._m() - [ x^2 3*x^2 5*x^2] - [ 2 5 4] + [x^2 0] + [ 2 -1] sage: res._maps [ [x^2 0] - [ 0 1] + [ 2 -1] ] """ if isinstance(self._module, Ideal_generic): @@ -811,7 +825,7 @@ def __init__(self, module, name='S', algorithm='heuristic', **kwds): sage: FreeResolution(Q.ideal([xb])) # has torsion Traceback (most recent call last): ... - NotImplementedError: the ring must be a free module or a polynomial ring that can be constructed in Singular + NotImplementedError: the module must be a free module or have the base ring be a polynomial ring using Singular """ self._algorithm = algorithm super().__init__(module, name=name, **kwds) diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index d9d64062a63..6396c0ab65f 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -7,17 +7,16 @@ EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I, algorithm='minimal') + sage: r = I.graded_free_resolution(algorithm='minimal') sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFiniteFreeResolution_singular(I, algorithm='shreyer') + sage: I.graded_free_resolution(algorithm='shreyer') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFiniteFreeResolution_singular(I, algorithm='standard') + sage: I.graded_free_resolution(algorithm='standard') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 - sage: GradedFiniteFreeResolution_singular(I, algorithm='heuristic') + sage: I.graded_free_resolution(algorithm='heuristic') S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 :: @@ -39,7 +38,7 @@ [ y -z w] [ x -y z] sage: m = d.image() - sage: GradedFiniteFreeResolution_singular(m, shifts=(2,2,2)) + sage: m.graded_free_resolution(shifts=(2,2,2)) S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 An example of multigraded resolution from Example 9.1 of [MilStu2005]_:: @@ -51,7 +50,7 @@ Ideal (c^2 - b*d, b*c - a*d, b^2 - a*c) of Multivariate Polynomial Ring in a, b, c, d over Rational Field sage: P3 = ProjectiveSpace(S) sage: C = P3.subscheme(I) # twisted cubic curve - sage: r = GradedFiniteFreeResolution_singular(I, degrees=[(1,0), (1,1), (1,2), (1,3)]) + sage: r = I.graded_free_resolution(degrees=[(1,0), (1,1), (1,2), (1,3)]) sage: r S((0, 0)) <-- S((-2, -4))⊕S((-2, -3))⊕S((-2, -2)) <-- S((-3, -5))⊕S((-3, -4)) <-- 0 sage: r.K_polynomial(names='s,t') @@ -60,11 +59,12 @@ AUTHORS: - Kwankyu Lee (2022-05): initial version - +- Travis Scrimshaw (2022-08-23): refactored for free module inputs """ # **************************************************************************** # Copyright (C) 2022 Kwankyu Lee +# (C) 2022 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -112,19 +112,18 @@ def __init__(self, module, degrees=None, shifts=None, name='S', **kwds): TESTS:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: TestSuite(r).run(skip=['_test_pickling']) An overdetermined system over a PID:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: from sage.homology.free_resolution import FreeResolution sage: M = matrix([[x^2, 2], ....: [3*x^2, 5], ....: [5*x^2, 4]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 sage: res._res_shifts @@ -167,10 +166,9 @@ def _repr_module(self, i): """ EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r._repr_module(0) 'S(0)' sage: r._repr_module(1) @@ -180,7 +178,7 @@ def _repr_module(self, i): sage: r._repr_module(3) '0' - sage: r = GradedFiniteFreeResolution_singular(I, shifts=[-1]) + sage: r = I.graded_free_resolution(shifts=[-1]) sage: r._repr_module(0) 'S(1)' """ @@ -199,15 +197,14 @@ def _repr_module(self, i): for sh in shifts) def shifts(self, i): - """ + r""" Return the shifts of ``self``. EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r.shifts(0) [0] sage: r.shifts(1) @@ -241,10 +238,9 @@ def betti(self, i, a=None): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r.betti(0) {0: 1} sage: r.betti(1) @@ -285,10 +281,9 @@ def K_polynomial(self, names=None): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r.K_polynomial() 2*t^3 - 3*t^2 + 1 """ @@ -325,18 +320,18 @@ class GradedFiniteFreeResolution_free_module(GradedFiniteFreeResolution, FiniteF EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: from sage.homology.free_resolution import FreeResolution sage: R. = QQ[] sage: M = matrix([[x^3, 3*x^3, 5*x^3], ....: [0, x, 2*x]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 sage: M = matrix([[x^2, 2], ....: [3*x^2, 5], ....: [5*x^2, 4]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 """ @@ -346,11 +341,11 @@ def __init__(self, module, degrees=None, *args, **kwds): EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: from sage.homology.free_resolution import FreeResolution sage: R. = QQ[] sage: M = matrix([[x^3, 3*x^3, 5*x^3], ....: [0, x, 2*x]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: TestSuite(res).run(skip="_test_pickling") """ super().__init__(module, degrees=degrees, *args, **kwds) @@ -367,11 +362,11 @@ def _maps(self): TESTS:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + sage: from sage.homology.free_resolution import FreeResolution sage: R. = QQ[] sage: M = matrix([[x^3, 3*x^3, 5*x^3], ....: [0, x, 2*x]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 sage: res._maps @@ -386,8 +381,12 @@ def _maps(self): sage: M = matrix([[x^2, 2], ....: [3*x^2, 5], ....: [5*x^2, 4]]) - sage: res = GradedFiniteFreeResolution_free_module(M) + sage: res = FreeResolution(M, graded=True) sage: res._maps + [ + [x^2 0] + [ 2 -1] + ] sage: res._res_shifts [[2, 0]] @@ -400,6 +399,7 @@ def _maps(self): sage: res._res_shifts [[9]] """ + def compute_degree(base, i): """ Compute the degree by ``base * deg + shift``, @@ -486,10 +486,9 @@ class GradedFiniteFreeResolution_singular(GradedFiniteFreeResolution, FiniteFree EXAMPLES:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r S(0) <-- S(-2)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3) <-- 0 sage: len(r) @@ -498,8 +497,8 @@ class GradedFiniteFreeResolution_singular(GradedFiniteFreeResolution, FiniteFree sage: I = S.ideal([z^2 - y*w, y*z - x*w, y - x]) sage: I.is_homogeneous() True - sage: R = GradedFiniteFreeResolution_singular(I) - sage: R + sage: r = I.graded_free_resolution() + sage: r S(0) <-- S(-1)⊕S(-2)⊕S(-2) <-- S(-3)⊕S(-3)⊕S(-4) <-- S(-5) <-- 0 """ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuristic', **kwds): @@ -508,22 +507,10 @@ def __init__(self, module, degrees=None, shifts=None, name='S', algorithm='heuri TESTS:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: TestSuite(r).run(skip=['_test_pickling']) - - An overdetermined system over a PID:: - - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) - sage: res = GradedFiniteFreeResolution_singular(M) - sage: res - S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 - sage: res._res_shifts - [[2, 0]] """ super().__init__(module, degrees=degrees, shifts=shifts, name=name, **kwds) self._algorithm = algorithm @@ -537,10 +524,9 @@ def _maps(self): TESTS:: - sage: from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular sage: S. = PolynomialRing(QQ) sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = GradedFiniteFreeResolution_singular(I) + sage: r = I.graded_free_resolution() sage: r._maps [ [-y x] diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b818e36ee48..8f27451a4ea 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1779,8 +1779,8 @@ def free_resolution(self, *args, **kwds): [ z x*z] 0 <-- C_0 <-------------- C_1 <-- 0 """ - from sage.homology.free_resolution import FiniteFreeResolution_free_module - return FiniteFreeResolution_free_module(self, *args, **kwds) + from sage.homology.free_resolution import FreeResolution + return FreeResolution(self, *args, **kwds) def graded_free_resolution(self, *args, **kwds): r""" @@ -1803,8 +1803,16 @@ def graded_free_resolution(self, *args, **kwds): sage: N.graded_free_resolution(degrees=[2, 1, 3], shifts=[2, 3]) S(-2)⊕S(-3) <-- S(-6)⊕S(-8) <-- 0 """ - from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module - return GradedFiniteFreeResolution_free_module(self, *args, **kwds) + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if isinstance(self.base_ring(), MPolynomialRing_libsingular): + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(self, *args, **kwds) + + if isinstance(self, FreeModule_generic): + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(self, *args, **kwds) + + raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") class FreeModule_generic(Module_free_ambient): From b524b39f8e7c904b50274ceeba51c14c431aa510 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 24 Aug 2022 12:44:06 +0900 Subject: [PATCH 210/454] Catching poly rings for ideals that are implemented via Singular. --- src/sage/modules/free_module.py | 2 +- src/sage/rings/ideal.py | 2 +- .../polynomial/multi_polynomial_ideal.py | 30 ++++++++++++++++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 8f27451a4ea..8e5c3da9e17 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1787,7 +1787,7 @@ def graded_free_resolution(self, *args, **kwds): Return a graded free resolution of ``self``. For input options, see - :class:`~sage.homology.graded_resolution.GradedFreeResolution`. + :class:`~sage.homology.graded_resolution.GradedFiniteFreeResolution`. EXAMPLES:: diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index e1bce143122..b1537ee3651 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1225,7 +1225,7 @@ def graded_free_resolution(self, *args, **kwds): Return a graded free resolution of ``self``. For input options, see - :class:`~sage.homology.graded_resolution.GradedFreeResolution`. + :class:`~sage.homology.graded_resolution.GradedFiniteFreeResolution`. EXAMPLES:: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 139b942555c..18f52b0ad74 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1797,9 +1797,20 @@ def free_resolution(self, *args, **kwds): [-x^2] [ y x^2] [ y] 0 <-- C_0 <---------- C_1 <------- C_2 <-- 0 + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = q.parent()[] + sage: I = R.ideal([x^2+y^2+z^2, x*y*z^4]) + sage: I.free_resolution() + Traceback (most recent call last): + ... + NotImplementedError: the ring must be a polynomial ring using Singular """ - from sage.homology.free_resolution import FiniteFreeResolution_singular - return FiniteFreeResolution_singular(self, *args, **kwds) + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if isinstance(self.ring(), MPolynomialRing_libsingular): + from sage.homology.free_resolution import FiniteFreeResolution_singular + return FiniteFreeResolution_singular(self, *args, **kwds) + raise NotImplementedError("the ring must be a polynomial ring using Singular") @require_field def graded_free_resolution(self, *args, **kwds): @@ -1825,9 +1836,20 @@ def graded_free_resolution(self, *args, **kwds): sage: I = R.ideal([f,g,h]) sage: I.graded_free_resolution(degrees=[1,2]) S(0) <-- S(-2)⊕S(-2) <-- S(-4) <-- 0 + + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = q.parent()[] + sage: I = R.ideal([x^2+y^2+z^2, x*y*z^4]) + sage: I.graded_free_resolution() + Traceback (most recent call last): + ... + NotImplementedError: the ring must be a polynomial ring using Singular """ - from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular - return GradedFiniteFreeResolution_singular(self, *args, **kwds) + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if isinstance(self.ring(), MPolynomialRing_libsingular): + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(self, *args, **kwds) + raise NotImplementedError("the ring must be a polynomial ring using Singular") @handle_AA_and_QQbar @singular_gb_standard_options From dc7aad2bc28cbf739aecb9c28136d6fddbedfaaf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Aug 2022 23:25:26 -0700 Subject: [PATCH 211/454] src/sage/tensor/modules/tensor_free_submodule_basis.py: Add doctests --- src/sage/tensor/modules/tensor_free_module.py | 1 + .../modules/tensor_free_submodule_basis.py | 30 +++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 7663c8448a9..c356953b110 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -716,6 +716,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, indices=None, latex_indices=None, symbol_dual=None, latex_symbol_dual=None): r""" + Return the standard basis of ``self`` corresponding to a basis of the base module. EXAMPLES:: diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 96076a27bd3..45704e81ac1 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -46,14 +46,34 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, self._comp = tensor_module._basis_comp() def keys(self): + """ + Return an iterator for the keys (indices) of the family. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T11 = M.tensor_module(1,1) + sage: e11 = T11.basis('e') + sage: list(e11.keys()) + [(0, 0), (0, 1), (0, 2), + (1, 0), (1, 1), (1, 2), + (2, 0), (2, 1), (2, 2)] + """ yield from self._comp.non_redundant_index_generator() def values(self): - yield from self + """ + Return an iterator for the elements of the family. - def __iter__(self): - r""" - Generate the basis elements of ``self``. + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T11 = M.tensor_module(1,1) + sage: e11 = T11.basis('e') + sage: [b.disp() for b in e11.values()] + [e_0⊗e^0, e_0⊗e^1, e_0⊗e^2, + e_1⊗e^0, e_1⊗e^1, e_1⊗e^2, + e_2⊗e^0, e_2⊗e^1, e_2⊗e^2] """ for ind in self.keys(): yield self[ind] @@ -93,7 +113,7 @@ def __getitem__(self, index): element.set_comp(base_module_basis)[index] = 1 return element -# Todo: Make it a Family +# Todo: # dual basis # add test for dual # lift/reduce/retract From 494794d767b6393eb9b8e59a14d13e8c3d9cbc30 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 24 Aug 2022 18:26:31 +0900 Subject: [PATCH 212/454] Refactor --- src/sage/homology/free_resolution.py | 254 ++++++++++++++------------- 1 file changed, 130 insertions(+), 124 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 7b7c611ca24..a6622d8cb8d 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -83,100 +83,92 @@ from copy import copy -class FreeResolution(SageObject, metaclass=ClasscallMetaclass): +def FreeResolution(module, degrees=None, shifts=None, name='S', graded=False, **kwds): """ - Abstract base class for free resolutions. + Constructor. - The matrix at index `i` in the list defines the differential map from - `(i + 1)`-th free module to the `i`-th free module over the base ring by - multiplication on the left. The number of matrices in the list is the - length of the resolution. The number of rows and columns of the matrices - define the ranks of the free modules in the resolution. + TESTS:: - Note that the first matrix in the list defines the differential map at - homological index `1`. - """ - @staticmethod - def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', graded=False, **kwds): - """ - Dispatch to the correct constructor. - - TESTS:: - - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() - sage: r = FreeResolution(m, name='S') - sage: type(r) - + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() + sage: r = FreeResolution(m, name='S') + sage: type(r) + - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: type(r) - + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: type(r) + - sage: R. = QQ[] - sage: M = R^3 - sage: v = M([x^2, 2*x^2, 3*x^2]) - sage: w = M([0, x, 2*x]) - sage: S = M.submodule([v, w]) - sage: r = FreeResolution(S) - sage: type(r) - + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: r = FreeResolution(S) + sage: type(r) + - sage: I = R.ideal([x^4 + 3*x^2 + 2]) - sage: r = FreeResolution(I) - sage: type(r) - - """ - # The module might still be free even if is_free_module is False. - # This is just to handle the cases when we trivially know it is. - is_free_module = False - if isinstance(module, Ideal_generic): - S = module.ring() - if len(module.gens()) == 1 and S in IntegralDomains(): - is_free_module = True - elif isinstance(module, Module_free_ambient): - S = module.base_ring() - if (S in PrincipalIdealDomains() - or isinstance(module, FreeModule_generic)): - is_free_module = True - elif isinstance(module, Matrix): - S = module.base_ring() - if S in PrincipalIdealDomains(): - module = module.echelon_form() - if module.nrows() > module.rank(): - module = module.submatrix(nrows=module.rank()) - module.set_immutable() - is_free_module = True - if not module.is_immutable(): - # We need to make an immutable copy of the matrix - module = copy(module) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: r = FreeResolution(I) + sage: type(r) + + """ + # The module might still be free even if is_free_module is False. + # This is just to handle the cases when we trivially know it is. + is_free_module = False + if isinstance(module, Ideal_generic): + S = module.ring() + if len(module.gens()) == 1 and S in IntegralDomains(): + is_free_module = True + elif isinstance(module, Module_free_ambient): + S = module.base_ring() + if (S in PrincipalIdealDomains() + or isinstance(module, FreeModule_generic)): + is_free_module = True + elif isinstance(module, Matrix): + S = module.base_ring() + if S in PrincipalIdealDomains(): + module = module.echelon_form() + if module.nrows() > module.rank(): + module = module.submatrix(nrows=module.rank()) module.set_immutable() - else: - raise TypeError('no module, matrix, or ideal') + is_free_module = True + if not module.is_immutable(): + # We need to make an immutable copy of the matrix + module = copy(module) + module.set_immutable() + else: + raise TypeError('no module, matrix, or ideal') + + if not is_free_module: + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if not isinstance(S, MPolynomialRing_libsingular): + raise NotImplementedError("the module must be a free module or " + "the base ring must be a polynomial ring using Singular") - if not is_free_module: - from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular - if not isinstance(S, MPolynomialRing_libsingular): - raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") + if graded or degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) - if graded or degrees is not None or shifts is not None: - # We are computing a graded resolution - from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular - return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) + return FiniteFreeResolution_singular(module, name=name, **kwds) - return FiniteFreeResolution_singular(module, name=name, **kwds) + # Otherwise we know it is a free module - # Otherwise we know it is a free module + if graded or degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) - if graded or degrees is not None or shifts is not None: - # We are computing a graded resolution - from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module - return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) + return FiniteFreeResolution_free_module(module, name=name, **kwds) - return FiniteFreeResolution_free_module(module, name=name, **kwds) +class FreeResolution_abs(SageObject): + """ + Abstract base class for free resolutions. + """ def __init__(self, module, name='S', **kwds): """ Initialize. @@ -196,7 +188,7 @@ def __init__(self, module, name='S', **kwds): """ if isinstance(module, Ideal_generic): S = module.ring() - else: #if isinstance(module, (Module_free_ambient, Matrix)): + else: # module or matrix S = module.base_ring() self._base_ring = S @@ -218,7 +210,7 @@ def _repr_(self): [z^2 - y*w y*z - x*w y^2 - x*z] """ if isinstance(self._module, Matrix): - return f"Free resolution with initial map:\n{self._module}" + return f"Free resolution of the :\n{self._module}" return f"Free resolution of {self._module}" def _repr_module(self, i): @@ -254,37 +246,6 @@ def _repr_module(self, i): s = '0' return s - def _m(self): - r""" - The defining matrix or ideal of the module defining ``self``. - - TESTS:: - - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: r._m() - Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field - - sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() - sage: r = FreeResolution(m, name='S') - sage: r._m() - [z^2 - y*w y*z - x*w y^2 - x*z] - - sage: M = m.image() - sage: r = FreeResolution(M, name='S') - sage: r._m() - [z^2 - y*w y*z - x*w y^2 - x*z] - """ - if isinstance(self._module, Ideal_generic): - return self._module - if isinstance(self._module, Module_free_ambient): - return self._module.matrix().transpose() - if isinstance(self._module, Matrix): - return self._module.transpose() - raise ValueError("unable to create a matrix/ideal to build the resolution") - @abstract_method def differential(self, i): r""" @@ -327,13 +288,23 @@ def target(self): """ return self.differential(0).codomain() -class FiniteFreeResolution(FreeResolution): + +class FiniteFreeResolution(FreeResolution_abs): r""" Finite free resolutions. A subclass must provide a ``_maps`` attribute that contains a list of the maps defining the resolution. + The matrix at index `i` in the list defines the differential map from + `(i + 1)`-th free module to the `i`-th free module over the base ring by + multiplication on the left. The number of matrices in the list is the + length of the resolution. The number of rows and columns of the matrices + define the ranks of the free modules in the resolution. + + Note that the first matrix in the list defines the differential map at + homological index `1`. + A subclass can define ``_initial_differential`` attribute that contains the `0`-th differential map whose codomain is the target of the free resolution. @@ -590,22 +561,56 @@ def _initial_differential(self): [ y*z - x*w] [-y^2 + x*z] """ - ideal = self._module - if isinstance(ideal, Ideal_generic): - S = ideal.ring() + module = self._module + if isinstance(module, Ideal_generic): + S = module.ring() M = FreeModule(S, 1) - N = M.submodule([vector([g]) for g in ideal.gens()]) + N = M.submodule([vector([g]) for g in module.gens()]) elif isinstance(ideal, Module_free_ambient): - S = ideal.base_ring() - M = ideal.ambient_module() - N = ideal - elif isinstance(ideal, Matrix): - S = ideal.base_ring() - N = ideal.row_space() + S = module.base_ring() + M = module.ambient_module() + N = module + elif isinstance(module, Matrix): + S = module.base_ring() + N = module.row_space() M = N.ambient_module() Q = M.quotient(N) return Q.coerce_map_from(M) + def _m(self): + r""" + Return the matrix whose column space is ``self._module``. + + If ``self._module`` is an ideal, then returns just the ideal. + + TESTS:: + + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: r._m() + Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field + + sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() + sage: r = FreeResolution(m, name='S') + sage: r._m() + [z^2 - y*w y*z - x*w y^2 - x*z] + + sage: M = m.image() + sage: r = FreeResolution(M, name='S') + sage: r._m() + [z^2 - y*w y*z - x*w y^2 - x*z] + """ + if isinstance(self._module, Ideal_generic): + return self._module + if isinstance(self._module, Module_free_ambient): + return self._module.matrix().transpose() + if isinstance(self._module, Matrix): + return self._module.transpose() + raise ValueError("unable to create a matrix/ideal to build the resolution") + + class FiniteFreeResolution_free_module(FiniteFreeResolution): r""" Free resolutions of a free module. @@ -712,6 +717,7 @@ def _maps(self): return [matrix([[self._module.gen()]])] return [self._m()] + class FiniteFreeResolution_singular(FiniteFreeResolution): r""" Minimal free resolutions of ideals or submodules of free modules From 5db5d4e56243c609f44afc1f21c112b026f9e1fe Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Mon, 11 Jul 2022 21:24:01 +0100 Subject: [PATCH 213/454] Update doctests for SymPy 1.11 Doctests related to SymPy's rsolve function are updated in: src/sage/calculus/test_sympy.py src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py The form of the output from SymPy has changed since https://github.com/sympy/sympy/pull/23567 --- src/sage/calculus/test_sympy.py | 4 ++-- .../recequadiff_doctest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/calculus/test_sympy.py b/src/sage/calculus/test_sympy.py index 7cf7f3f6bfd..927e6ee4fb6 100644 --- a/src/sage/calculus/test_sympy.py +++ b/src/sage/calculus/test_sympy.py @@ -193,7 +193,7 @@ sage: u = Function('u') sage: n = Symbol('n', integer=True) sage: f = u(n+2) - u(n+1) + u(n)/4 - sage: 2**n * rsolve(f,u(n)) - C1*n + C0 + sage: expand(2**n * rsolve(f,u(n))) + 2*C1*n + C0 """ diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py index 1062f4f7e8c..f53f813d793 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/recequadiff_doctest.py @@ -382,7 +382,7 @@ sage: from sympy import rsolve_hyper sage: from sympy.abc import n sage: rsolve_hyper([-2,1],2**(n+2),n) - 2**n*C0 + 2**(n + 2)*(C0 + n/2) + 2**n*C0 + 2**(n + 1)*n """ From 16fdc52e7dee6414a36cf3e6546b4a7c9089b21d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 24 Aug 2022 21:36:24 -0700 Subject: [PATCH 214/454] build/pkgs/sympy: Update to 1.11 --- build/pkgs/sympy/checksums.ini | 6 +++--- build/pkgs/sympy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index f2b891c8a65..d92eeec4219 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,5 +1,5 @@ tarball=sympy-VERSION.tar.gz -sha1=95b5323b284a3f64414dab3b9da909eeeb1c09ea -md5=8c7a956d74a47dc439c2935fec64ac46 -cksum=1092663182 +sha1=bb3ecaa4b223097831b5f0627d6579d8bd906840 +md5=973f746dd0b0d07b01f6b2b15603cbf8 +cksum=3499849170 upstream_url=https://github.com/sympy/sympy/releases/download/sympy-VERSION/sympy-VERSION.tar.gz diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index 4dae2985b58..09601587019 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -1.10.1 +1.11 From 7e7593841298c488e2d9424740179da210459f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 25 Aug 2022 08:54:06 +0200 Subject: [PATCH 215/454] fix SchurTensormodule with construction None --- src/sage/algebras/schur_algebra.py | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/sage/algebras/schur_algebra.py b/src/sage/algebras/schur_algebra.py index ba606ded999..b716db0ce94 100644 --- a/src/sage/algebras/schur_algebra.py +++ b/src/sage/algebras/schur_algebra.py @@ -454,6 +454,19 @@ def _repr_(self): msg += " over {}" return msg.format(self._r, self._n, self.base_ring()) + def construction(self): + """ + Return ``None``. + + There is no functorial construction for ``self``. + + EXAMPLES:: + + sage: T = SchurTensorModule(QQ, 2, 3) + sage: T.construction() + """ + return None + def _monomial_product(self, xi, v): """ Result of acting by the basis element ``xi`` of the corresponding @@ -581,11 +594,11 @@ def GL_irreducible_character(n, mu, KK): A = M._schur SGA = M._sga - #make ST the superstandard tableau of shape mu + # make ST the superstandard tableau of shape mu from sage.combinat.tableau import from_shape_and_word ST = from_shape_and_word(mu, list(range(1, r + 1)), convention='English') - #make ell the reading word of the highest weight tableau of shape mu + # make ell the reading word of the highest weight tableau of shape mu ell = [i + 1 for i, l in enumerate(mu) for dummy in range(l)] e = M.basis()[tuple(ell)] # the element e_l @@ -607,17 +620,17 @@ def GL_irreducible_character(n, mu, KK): y = A.basis()[schur_rep] * e # M.action_by_Schur_alg(A.basis()[schur_rep], e) carter_lusztig.append(y.to_vector()) - #Therefore, we now have carter_lusztig as a list giving the basis - #of `V_\mu` + # Therefore, we now have carter_lusztig as a list giving the basis + # of `V_\mu` - #We want to think of expressing this character as a sum of monomial - #symmetric functions. + # We want to think of expressing this character as a sum of monomial + # symmetric functions. - #We will determine a basis element for each m_\lambda in the - #character, and we want to keep track of them by \lambda. + # We will determine a basis element for each m_\lambda in the + # character, and we want to keep track of them by \lambda. - #That means that we only want to pick out the basis elements above for - #those semistandard words whose content is a partition. + # That means that we only want to pick out the basis elements above for + # those semistandard words whose content is a partition. contents = Partitions(r, max_length=n).list() # all partitions of r, length at most n @@ -648,15 +661,15 @@ def GL_irreducible_character(n, mu, KK): except ValueError: pass - #There is an inner product on the Carter-Lusztig module V_\mu; its - #maximal submodule is exactly the kernel of the inner product. + # There is an inner product on the Carter-Lusztig module V_\mu; its + # maximal submodule is exactly the kernel of the inner product. - #Now, for each possible partition content, we look at the graded piece of - #that degree, and we record how these elements pair with each of the - #elements of carter_lusztig. + # Now, for each possible partition content, we look at the graded piece of + # that degree, and we record how these elements pair with each of the + # elements of carter_lusztig. - #The kernel of this pairing is the part of this graded piece which is - #not in the irreducible module for \mu. + # The kernel of this pairing is the part of this graded piece which is + # not in the irreducible module for \mu. length = len(carter_lusztig) From 9e6b7b2e4ec266702154fc75b81331a43a369411 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 29 Sep 2021 12:57:04 -0700 Subject: [PATCH 216/454] Polyhedra_base._repr_base_ring: Factor out from ._repr_ambient_module, do not fail if AA cannot be imported --- src/sage/geometry/polyhedron/parent.py | 41 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 881c614238a..b4601a08444 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -495,6 +495,35 @@ def Hrepresentation_space(self): from sage.modules.free_module import FreeModule return FreeModule(self.base_ring(), self.ambient_dim()+1) + def _repr_base_ring(self): + """ + Return an abbreviated string representation of the base ring. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.parent import Polyhedra + sage: Polyhedra(QQ, 3)._repr_base_ring() + 'QQ' + sage: K. = NumberField(x^2 - 3, embedding=AA(3).sqrt()) + sage: Polyhedra(K, 4)._repr_base_ring() + '(Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?)' + """ + + if self.base_ring() is ZZ: + return 'ZZ' + if self.base_ring() is QQ: + return 'QQ' + if self.base_ring() is RDF: + return 'RDF' + try: + from sage.rings.qqbar import AA + except ImportError: + pass + else: + if self.base_ring() is AA: + return 'AA' + return '({0})'.format(self.base_ring()) + def _repr_ambient_module(self): """ Return an abbreviated string representation of the ambient @@ -513,17 +542,7 @@ def _repr_ambient_module(self): sage: Polyhedra(K, 4)._repr_ambient_module() '(Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?)^4' """ - from sage.rings.qqbar import AA - if self.base_ring() is ZZ: - s = 'ZZ' - elif self.base_ring() is QQ: - s = 'QQ' - elif self.base_ring() is RDF: - s = 'RDF' - elif self.base_ring() is AA: - s = 'AA' - else: - s = '({0})'.format(self.base_ring()) + s = self._repr_base_ring() s += '^' + repr(self.ambient_dim()) return s From 22e4b7cfafdf471847e2921df6580fad1162f5dc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 5 Oct 2021 21:48:38 -0700 Subject: [PATCH 217/454] src/sage/geometry/polyhedron/parent.py: Mark doctests # optional - sage.rings.number_field --- src/sage/geometry/polyhedron/parent.py | 41 +++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index b4601a08444..de492eb6301 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -65,7 +65,7 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * EXAMPLES:: sage: from sage.geometry.polyhedron.parent import Polyhedra - sage: Polyhedra(AA, 3) + sage: Polyhedra(AA, 3) # optional - sage.rings.number_field Polyhedra in AA^3 sage: Polyhedra(ZZ, 3) Polyhedra in ZZ^3 @@ -105,11 +105,11 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * Traceback (most recent call last): ... ValueError: no default backend for computations with Real Field with 53 bits of precision - sage: Polyhedra(QQ[I], 2) + sage: Polyhedra(QQ[I], 2) # optional - sage.rings.number_field Traceback (most recent call last): ... ValueError: invalid base ring: Number Field in I with defining polynomial x^2 + 1 with I = 1*I cannot be coerced to a real field - sage: Polyhedra(AA, 3, backend='polymake') # optional - polymake + sage: Polyhedra(AA, 3, backend='polymake') # optional - polymake # optional - sage.rings.number_field Traceback (most recent call last): ... ValueError: the 'polymake' backend for polyhedron cannot be used with Algebraic Real Field @@ -264,13 +264,13 @@ def list(self): sage: P.cardinality() +Infinity - sage: P = Polyhedra(AA, 0) - sage: P.category() + sage: P = Polyhedra(AA, 0) # optional - sage.rings.number_field + sage: P.category() # optional - sage.rings.number_field Category of finite enumerated polyhedral sets over Algebraic Real Field - sage: P.list() + sage: P.list() # optional - sage.rings.number_field [The empty polyhedron in AA^0, A 0-dimensional polyhedron in AA^0 defined as the convex hull of 1 vertex] - sage: P.cardinality() + sage: P.cardinality() # optional - sage.rings.number_field 2 """ if self.ambient_dim(): @@ -504,8 +504,8 @@ def _repr_base_ring(self): sage: from sage.geometry.polyhedron.parent import Polyhedra sage: Polyhedra(QQ, 3)._repr_base_ring() 'QQ' - sage: K. = NumberField(x^2 - 3, embedding=AA(3).sqrt()) - sage: Polyhedra(K, 4)._repr_base_ring() + sage: K. = NumberField(x^2 - 3, embedding=AA(3).sqrt()) # optional - sage.rings.number_field + sage: Polyhedra(K, 4)._repr_base_ring() # optional - sage.rings.number_field '(Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?)' """ @@ -538,8 +538,8 @@ def _repr_ambient_module(self): sage: from sage.geometry.polyhedron.parent import Polyhedra sage: Polyhedra(QQ, 3)._repr_ambient_module() 'QQ^3' - sage: K. = NumberField(x^2 - 3, embedding=AA(3).sqrt()) - sage: Polyhedra(K, 4)._repr_ambient_module() + sage: K. = NumberField(x^2 - 3, embedding=AA(3).sqrt()) # optional - sage.rings.number_field + sage: Polyhedra(K, 4)._repr_ambient_module() # optional - sage.rings.number_field '(Number Field in sqrt3 with defining polynomial x^2 - 3 with sqrt3 = 1.732050807568878?)^4' """ s = self._repr_base_ring() @@ -621,11 +621,11 @@ def _element_constructor_(self, *args, **kwds): When the parent of the object is not ``self``, the default is not to copy:: - sage: Q = P.base_extend(AA) - sage: q = Q._element_constructor_(p) - sage: q is p + sage: Q = P.base_extend(AA) # optional - sage.rings.number_field + sage: q = Q._element_constructor_(p) # optional - sage.rings.number_field + sage: q is p # optional - sage.rings.number_field False - sage: q = Q._element_constructor_(p, copy=False) + sage: q = Q._element_constructor_(p, copy=False) # optional - sage.rings.number_field Traceback (most recent call last): ... ValueError: you need to make a copy when changing the parent @@ -712,9 +712,10 @@ def _element_constructor_polyhedron(self, polyhedron, **kwds): sage: P(p) A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices - sage: P = Polyhedra(AA, 3, backend='field') - sage: p = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)]) - sage: P(p) + sage: P = Polyhedra(AA, 3, backend='field') # optional - sage.rings.number_field + sage: vertices = [(0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)] + sage: p = Polyhedron(vertices=vertices) # optional - sage.rings.number_field + sage: P(p) # optional - sage.rings.number_field A 3-dimensional polyhedron in AA^3 defined as the convex hull of 4 vertices """ Vrep = None @@ -1253,9 +1254,9 @@ def does_backend_handle_base_ring(base_ring, backend): sage: from sage.geometry.polyhedron.parent import does_backend_handle_base_ring sage: does_backend_handle_base_ring(QQ, 'ppl') True - sage: does_backend_handle_base_ring(QQ[sqrt(5)], 'ppl') + sage: does_backend_handle_base_ring(QQ[sqrt(5)], 'ppl') # optional - sage.rings.number_field False - sage: does_backend_handle_base_ring(QQ[sqrt(5)], 'field') + sage: does_backend_handle_base_ring(QQ[sqrt(5)], 'field') # optional - sage.rings.number_field True """ try: From 2a0c39a8eda7cd9ae63258d54eb00c19610f0524 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Jul 2022 21:36:45 -0700 Subject: [PATCH 218/454] sage.geometry: Remove module-level imports of AA, RR, PolynomialRing --- src/sage/geometry/convex_set.py | 12 ++++--- .../geometry/polyhedron/backend_normaliz.py | 9 ++--- src/sage/geometry/polyhedron/base.py | 1 + src/sage/geometry/polyhedron/base0.py | 12 ++----- src/sage/geometry/polyhedron/base6.py | 34 +++++++++++-------- src/sage/geometry/polyhedron/base7.py | 3 +- src/sage/geometry/voronoi_diagram.py | 3 +- 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 604df756af0..82c3f3bfbe1 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -855,10 +855,14 @@ def _test_contains(self, tester=None, **options): tester.assertEqual(contains_space_point, self.contains(ambient_point)) tester.assertEqual(contains_space_point, self.contains(space_coords)) 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)) + try: + from sage.rings.qqbar import AA + except ImportError: + pass + else: + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) try: from sage.symbolic.ring import SR symbolic_space = self.ambient_vector_space(SR) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 86b89632a51..4b5902da342 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -30,8 +30,8 @@ lazy_import('PyNormaliz', ['NmzResult', 'NmzCompute', 'NmzCone', 'NmzConeCopy'], feature=sage.features.normaliz.PyNormaliz()) -from sage.rings.all import ZZ, QQ, QQbar -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ from sage.arith.functions import LCM_list from sage.misc.functional import denominator from sage.matrix.constructor import vector @@ -1082,10 +1082,11 @@ def _number_field_triple(normaliz_field): sage: Pn._number_field_triple(QuadraticField(5)) # optional - sage.rings.number_field ['a^2 - 5', 'a', '[2.236067977499789 +/- 8.06e-16]'] """ - from sage.rings.real_arb import RealBallField R = normaliz_field if R is QQ: return None + from sage.rings.real_arb import RealBallField + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing emb = RealBallField(53)(R.gen(0)) gen = 'a' R_a = PolynomialRing(QQ, gen) @@ -2336,7 +2337,7 @@ class functions. ((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1), (3*t^3 + 2*t^2 + 3*t)/(t + 1)) """ from sage.groups.conjugacy_classes import ConjugacyClassGAP - from sage.rings.all import CyclotomicField + from sage.rings.all import CyclotomicField, QQbar from sage.matrix.all import MatrixSpace from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.matrix.special import identity_matrix diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 576e073a825..5582bd190d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,6 +51,7 @@ from sage.misc.cachefunc import cached_method +import sage.rings.abc from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.matrix.constructor import matrix diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py index 2e0f6a716cf..1d844d2a073 100644 --- a/src/sage/geometry/polyhedron/base0.py +++ b/src/sage/geometry/polyhedron/base0.py @@ -1366,13 +1366,9 @@ def cdd_Hrepresentation(self): try: cdd_type = self._cdd_type except AttributeError: - from sage.rings.integer_ring import ZZ - from sage.rings.rational_field import QQ - from sage.rings.real_double import RDF - if self.base_ring() is ZZ or self.base_ring() is QQ: cdd_type = 'rational' - elif self.base_ring() is RDF: + elif isinstance(self.base_ring(), sage.rings.abc.RealDoubleField): cdd_type = 'real' else: raise TypeError('the base ring must be ZZ, QQ, or RDF') @@ -1431,13 +1427,9 @@ def cdd_Vrepresentation(self): try: cdd_type = self._cdd_type except AttributeError: - from sage.rings.integer_ring import ZZ - from sage.rings.rational_field import QQ - from sage.rings.real_double import RDF - if self.base_ring() is ZZ or self.base_ring() is QQ: cdd_type = 'rational' - elif self.base_ring() is RDF: + elif isinstance(self.base_ring(), sage.rings.abc.RealDoubleField): cdd_type = 'real' else: raise TypeError('the base ring must be ZZ, QQ, or RDF') diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index b569f40613d..9044f3db4d9 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -36,7 +36,6 @@ from sage.modules.vector_space_morphism import linear_transformation from sage.matrix.constructor import matrix from sage.modules.free_module_element import vector -from sage.rings.qqbar import AA from sage.geometry.convex_set import AffineHullProjectionData from .base5 import Polyhedron_base5 @@ -967,6 +966,7 @@ def _affine_hull_projection(self, *, except TypeError: if not extend: raise ValueError('the base ring needs to be extended; try with "extend=True"') + from sage.rings.qqbar import AA M = matrix(AA, M) A = M.gram_schmidt(orthonormal=orthonormal)[0] if minimal: @@ -1445,21 +1445,25 @@ def _test_affine_hull_projection(self, tester=None, verbose=False, **options): # Avoid very long doctests. return - data_sets = [None]*4 - data_sets[0] = self.affine_hull_projection(return_all_data=True) + try: + from sage.rings.qqbar import AA + except ImportError: + AA = None + + data_sets = [] + data_sets.append(self.affine_hull_projection(return_all_data=True)) if self.is_compact(): - data_sets[1] = self.affine_hull_projection(return_all_data=True, - orthogonal=True, - extend=True) - data_sets[2] = self.affine_hull_projection(return_all_data=True, - orthonormal=True, - extend=True) - data_sets[3] = self.affine_hull_projection(return_all_data=True, - orthonormal=True, - extend=True, - minimal=True) - else: - data_sets = data_sets[:1] + data_sets.append(self.affine_hull_projection(return_all_data=True, + orthogonal=True, + extend=True)) + if AA is not None: + data_sets.append(self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True)) + data_sets.append(self.affine_hull_projection(return_all_data=True, + orthonormal=True, + extend=True, + minimal=True)) for i, data in enumerate(data_sets): if verbose: diff --git a/src/sage/geometry/polyhedron/base7.py b/src/sage/geometry/polyhedron/base7.py index 93138e3dc4f..137acd2f61c 100644 --- a/src/sage/geometry/polyhedron/base7.py +++ b/src/sage/geometry/polyhedron/base7.py @@ -37,7 +37,6 @@ from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.qqbar import AA from .base6 import Polyhedron_base6 class Polyhedron_base7(Polyhedron_base6): @@ -714,6 +713,7 @@ def volume(self, measure='ambient', engine='auto', **kwds): if Adet.is_square(): sqrt_Adet = Adet.sqrt() else: + from sage.rings.qqbar import AA sqrt_Adet = AA(Adet).sqrt() scaled_volume = AA(scaled_volume) return scaled_volume / sqrt_Adet @@ -910,6 +910,7 @@ def integrate(self, function, measure='ambient', **kwds): A = affine_hull_data.projection_linear_map.matrix() Adet = (A.transpose() * A).det() try: + from sage.rings.qqbar import AA Adet = AA.coerce(Adet) except TypeError: pass diff --git a/src/sage/geometry/voronoi_diagram.py b/src/sage/geometry/voronoi_diagram.py index e280a41143a..1e9629aa646 100644 --- a/src/sage/geometry/voronoi_diagram.py +++ b/src/sage/geometry/voronoi_diagram.py @@ -15,7 +15,6 @@ from sage.structure.sage_object import SageObject from sage.geometry.polyhedron.constructor import Polyhedron -from sage.rings.qqbar import AA from sage.rings.rational_field import QQ import sage.rings.abc from sage.geometry.triangulation.point_configuration import PointConfiguration @@ -103,7 +102,7 @@ def __init__(self, points): self._n = self._points.n_points() if not self._n or self._points.base_ring().is_subring(QQ): self._base_ring = QQ - elif isinstance(self._points.base_ring(), sage.rings.abc.RealDoubleField) or self._points.base_ring() == AA: + elif isinstance(self._points.base_ring(), (sage.rings.abc.RealDoubleField, sage.rings.abc.AlgebraicRealField)): self._base_ring = self._points.base_ring() elif isinstance(self._points.base_ring(), sage.rings.abc.RealField): from sage.rings.real_double import RDF From 278b93f390a1caf8e511409e2ee0e39697aed8c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 17 Jul 2022 21:37:15 -0700 Subject: [PATCH 219/454] sage.geometry: More # optional - sage.rings.number_field --- src/sage/geometry/polyhedron/base3.py | 4 ++-- src/sage/geometry/polyhedron/base6.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py index 0b85c4f944c..ff144d04e39 100644 --- a/src/sage/geometry/polyhedron/base3.py +++ b/src/sage/geometry/polyhedron/base3.py @@ -136,8 +136,8 @@ def slack_matrix(self): [1 0 1 0 0 1] [1 0 0 0 1 1] - sage: P = polytopes.dodecahedron().faces(2)[0].as_polyhedron() - sage: P.slack_matrix() + sage: P = polytopes.dodecahedron().faces(2)[0].as_polyhedron() # optional - sage.rings.number_field + sage: P.slack_matrix() # optional - sage.rings.number_field [1/2*sqrt5 - 1/2 0 0 1 1/2*sqrt5 - 1/2 0] [ 0 0 1/2*sqrt5 - 1/2 1/2*sqrt5 - 1/2 1 0] [ 0 1/2*sqrt5 - 1/2 1 0 1/2*sqrt5 - 1/2 0] diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index 9044f3db4d9..c6ed839ba77 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -1176,9 +1176,9 @@ def affine_hull_projection(self, A vertex at (2, 0, 0), A vertex at (1, 3/2, 0), A vertex at (1, 1/2, 4/3)) - sage: A = S.affine_hull_projection(orthonormal=True, extend=True); A + sage: A = S.affine_hull_projection(orthonormal=True, extend=True); A # optional - sage.rings.number_field A 3-dimensional polyhedron in AA^3 defined as the convex hull of 4 vertices - sage: A.vertices() + sage: A.vertices() # optional - sage.rings.number_field (A vertex at (0.7071067811865475?, 0.4082482904638630?, 1.154700538379252?), A vertex at (0.7071067811865475?, 1.224744871391589?, 0.?e-18), A vertex at (1.414213562373095?, 0.?e-18, 0.?e-18), @@ -1187,11 +1187,11 @@ def affine_hull_projection(self, With the parameter ``minimal`` one can get a minimal base ring:: sage: s = polytopes.simplex(3) - sage: s_AA = s.affine_hull_projection(orthonormal=True, extend=True) - sage: s_AA.base_ring() + sage: s_AA = s.affine_hull_projection(orthonormal=True, extend=True) # optional - sage.rings.number_field + sage: s_AA.base_ring() # optional - sage.rings.number_field Algebraic Real Field - sage: s_full = s.affine_hull_projection(orthonormal=True, extend=True, minimal=True) - sage: s_full.base_ring() + sage: s_full = s.affine_hull_projection(orthonormal=True, extend=True, minimal=True) # optional - sage.rings.number_field + sage: s_full.base_ring() # optional - sage.rings.number_field Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a = 0.5176380902050415? More examples with the ``orthonormal`` parameter:: From 0f12ac293022f5c43c8d2497b44ea8d69a34685e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:18:08 -0700 Subject: [PATCH 220/454] src/sage/geometry/polyhedron/base0.py: Restore lost imports --- src/sage/geometry/polyhedron/base0.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py index 1d844d2a073..2cef77fd47e 100644 --- a/src/sage/geometry/polyhedron/base0.py +++ b/src/sage/geometry/polyhedron/base0.py @@ -1366,6 +1366,8 @@ def cdd_Hrepresentation(self): try: cdd_type = self._cdd_type except AttributeError: + from sage.rings.integer_ring import ZZ + from sage.rings.rational_field import QQ if self.base_ring() is ZZ or self.base_ring() is QQ: cdd_type = 'rational' elif isinstance(self.base_ring(), sage.rings.abc.RealDoubleField): @@ -1427,6 +1429,8 @@ def cdd_Vrepresentation(self): try: cdd_type = self._cdd_type except AttributeError: + from sage.rings.integer_ring import ZZ + from sage.rings.rational_field import QQ if self.base_ring() is ZZ or self.base_ring() is QQ: cdd_type = 'rational' elif isinstance(self.base_ring(), sage.rings.abc.RealDoubleField): From 74bc04b8493ec925a4cfcd33dbc73cf078d08d06 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:22:28 -0700 Subject: [PATCH 221/454] src/sage/geometry/polyhedron/base{3,6}.py: Muffle pyflakes warnings about unused imports --- src/sage/geometry/polyhedron/base3.py | 1 + src/sage/geometry/polyhedron/base6.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py index ff144d04e39..49204ed6304 100644 --- a/src/sage/geometry/polyhedron/base3.py +++ b/src/sage/geometry/polyhedron/base3.py @@ -1863,6 +1863,7 @@ def _test_combinatorial_face_as_combinatorial_polyhedron(self, tester=None, **op D2._test_bitsets(tester, **options) try: import sage.graphs.graph + assert sage.graphs.graph # to muffle pyflakes except ImportError: pass else: diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index c6ed839ba77..21045562e78 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -699,6 +699,7 @@ def _test_gale_transform(self, tester=None, **options): try: import sage.graphs.graph + assert sage.graphs.graph # to muffle pyflakes except ImportError: pass else: From 7db47e13d73818b34da78c7f6ee05005b5206bc7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:23:22 -0700 Subject: [PATCH 222/454] src/sage/geometry/polyhedron/parent.py: Muffle pyflakes warning --- src/sage/geometry/polyhedron/parent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index de492eb6301..c6a2b315c73 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -176,6 +176,7 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * try: from sage.interfaces.polymake import polymake polymake_base_field = polymake(base_field) + assert polymake_base_field # to muffle pyflakes except TypeError: raise ValueError(f"the 'polymake' backend for polyhedron cannot be used with {base_field}") return Polyhedra_polymake(base_field, ambient_dim, backend) From 6dd4f075e8a81a28292d5badbbeab2024b0c4e26 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:24:23 -0700 Subject: [PATCH 223/454] src/sage/geometry/polyhedron/base.py: Remove unused import --- src/sage/geometry/polyhedron/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 5582bd190d1..576e073a825 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,7 +51,6 @@ from sage.misc.cachefunc import cached_method -import sage.rings.abc from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.matrix.constructor import matrix From 1ede2147fc76bfb2ce9cba350f5cc579d352922a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:28:53 -0700 Subject: [PATCH 224/454] src/sage/geometry/polyhedron/backend_normaliz.py: Add missing imports --- src/sage/geometry/polyhedron/backend_normaliz.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 4b5902da342..f38498aa597 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -2337,8 +2337,9 @@ class functions. ((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1), (3*t^3 + 2*t^2 + 3*t)/(t + 1)) """ from sage.groups.conjugacy_classes import ConjugacyClassGAP - from sage.rings.all import CyclotomicField, QQbar - from sage.matrix.all import MatrixSpace + from sage.rings.number_field.number_field import CyclotomicField + from sage.rings.qqbar import QQbar + from sage.matrix.matrix_space import MatrixSpace from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.matrix.special import identity_matrix # Setting the group @@ -2476,6 +2477,8 @@ def _Hstar_as_rat_fct(self, initial_Hstar): [ 1 1 -1 -1 1] [ 2 0 0 0 -2] """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.rings.qqbar import QQbar chi_vars = ','.join('chi_{}'.format(i) for i in range(len(initial_Hstar))) Chi_ring = PolynomialRing(QQbar, chi_vars) virtual_ring = PolynomialRing(Chi_ring, initial_Hstar.base_ring().gens()) From 341475cb0bf03648c3de3d55173ba09f33cedd59 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:31:30 -0700 Subject: [PATCH 225/454] src/sage/geometry/polyhedron/base_ZZ.py: Replace .all import --- src/sage/geometry/polyhedron/base_ZZ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base_ZZ.py b/src/sage/geometry/polyhedron/base_ZZ.py index 07c7bbd6a4c..4255f0232df 100644 --- a/src/sage/geometry/polyhedron/base_ZZ.py +++ b/src/sage/geometry/polyhedron/base_ZZ.py @@ -17,7 +17,7 @@ from sage.misc.cachefunc import cached_method from sage.modules.free_module_element import vector from .base_QQ import Polyhedron_QQ -from sage.arith.all import gcd +from sage.arith.misc import gcd ######################################################################### From fb1ce4270e9d9aa1511192d6097aaa7692ab8883 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 14:59:46 -0700 Subject: [PATCH 226/454] src/sage/tensor/modules/ext_pow_free_module.py: No need to override basis() any more --- src/sage/tensor/modules/ext_pow_free_module.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index 4fe480049df..82c64faea8d 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -421,11 +421,6 @@ def degree(self): """ return self._degree - def basis(self, *args, **kwds): - # We override it so that _test_basis is disabled. - # See https://trac.sagemath.org/ticket/30229#comment:6 for a better solution - raise NotImplementedError - #*********************************************************************** @@ -893,8 +888,3 @@ def degree(self): """ return self._degree - - def basis(self, *args, **kwds): - # We override it so that _test_basis is disabled. - # See https://trac.sagemath.org/ticket/30229#comment:6 for a better solution - raise NotImplementedError From 30d4eec4cdd67c9ecd037b3edc0f9c5bcbad18d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 15:22:56 -0700 Subject: [PATCH 227/454] TensorFreeModule.basis: Pass all args --- src/sage/tensor/modules/tensor_free_module.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index ac874919901..e27f4fa6f64 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -744,7 +744,9 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_1⊗e_2 + e_2⊗e_1 e_2⊗e_2 """ - return TensorFreeSubmoduleBasis_comp(self, symbol) + return TensorFreeSubmoduleBasis_comp(self, symbol=symbol, latex_symbol=latex_symbol, + indices=indices, latex_indices=latex_indices, + symbol_dual=symbol_dual, latex_symbol_dual=latex_symbol_dual) @cached_method def _basis_comp(self): From d1376fda20910920f7333303f1466969a0d165dd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 16:01:05 -0700 Subject: [PATCH 228/454] TensorFreeModule.basis: Add documentation --- src/sage/tensor/modules/tensor_free_module.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index e27f4fa6f64..21af77745d1 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -715,6 +715,17 @@ def basis(self, symbol, latex_symbol=None, from_family=None, r""" Return the standard basis of ``self`` corresponding to a basis of the base module. + INPUT: + + - ``symbol``, ``indices`` -- passed to he base module's method + :meth:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule.basis` + to select a basis of the :meth:`base_module` of ``self``, + or to create it. + + - other parameters -- passed to + :meth:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule.basis`; when + the basis does not exist yet, it will be created using these parameters. + EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') From 2bcfc2103e48f153ef156147a613d2074e5fbf53 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 16:52:11 -0700 Subject: [PATCH 229/454] src/sage/tensor/modules: Use keyword order: category, ambient --- src/sage/tensor/modules/tensor_free_submodule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index f7ee2487c67..3a297eabae2 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -58,7 +58,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): """ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, - sym=None, antisym=None, *, ambient=None, category=None): + sym=None, antisym=None, *, category=None, ambient=None): self._fmodule = fmodule self._tensor_type = tuple(tensor_type) if ambient is None: @@ -73,7 +73,7 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, latex_name=latex_name, start_index=fmodule._sindex, output_formatter=fmodule._output_formatter, - ambient=ambient, category=category) + category=category, ambient=ambient) @cached_method def _basis_comp(self): From 6e9bec511d2b00fa1a550dd460831ae90dea9d57 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 16:58:37 -0700 Subject: [PATCH 230/454] src/sage/tensor/modules/finite_rank_free_module.py: Move ambient_module, is_submodule to FiniteRankFreeModule_abstract --- .../tensor/modules/finite_rank_free_module.py | 72 +++++++++---------- .../tensor/modules/tensor_free_submodule.py | 10 ++- 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index c04251e0ed2..4390e687377 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -693,6 +693,42 @@ def zero(self): resu.set_immutable() return resu + def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_generic + """ + Return the ambient module associated to this module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.ambient_module() is M + True + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M.ambient_module() is T60M + True + """ + return self._ambient_module + + ambient = ambient_module # compatible with sage.modules.with_basis.subquotient.SubmoduleWithBasis + + def is_submodule(self, other): + """ + Return ``True`` if ``self`` is a submodule of ``other``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: N = FiniteRankFreeModule(ZZ, 4, name='M') + sage: M.is_submodule(M) + True + sage: M.is_submodule(N) + False + """ + return self == other or self.ambient_module() == other + class FiniteRankFreeModule(FiniteRankFreeModule_abstract): r""" @@ -2941,39 +2977,3 @@ def identity_map(self, name='Id', latex_name=None): latex_name = name self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map - - def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_generic - """ - Return the ambient module associated to this module. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.ambient_module() is M - True - - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M.ambient_module() is T60M - True - """ - return self._ambient_module - - ambient = ambient_module # compatible with sage.modules.with_basis.subquotient.SubmoduleWithBasis - - def is_submodule(self, other): - """ - Return ``True`` if ``self`` is a submodule of ``other``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: N = FiniteRankFreeModule(ZZ, 4, name='M') - sage: M.is_submodule(M) - True - sage: M.is_submodule(N) - False - """ - return self == other or self.ambient_module() == other diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 3a297eabae2..814284a41de 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -15,7 +15,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.sets.disjoint_set import DisjointSet from .tensor_free_module import TensorFreeModule -from .finite_rank_free_module import FiniteRankFreeModule +from .finite_rank_free_module import FiniteRankFreeModule_abstract class TensorFreeSubmodule_comp(TensorFreeModule): r""" @@ -69,11 +69,9 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, rank = len(list(self.irange())) category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ - FiniteRankFreeModule.__init__(self, fmodule._ring, rank, name=name, - latex_name=latex_name, - start_index=fmodule._sindex, - output_formatter=fmodule._output_formatter, - category=category, ambient=ambient) + FiniteRankFreeModule_abstract.__init__(self, fmodule._ring, rank, name=name, + latex_name=latex_name, + category=category, ambient=ambient) @cached_method def _basis_comp(self): From fd5ba8cf1dbcd80fd3d364f042bb1eb44aa674fe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 17:07:21 -0700 Subject: [PATCH 231/454] src/sage/tensor/modules/tensor_free_module.py: No more need for TensorFreeModule.irange --- src/sage/tensor/modules/tensor_free_module.py | 6 ------ src/sage/tensor/modules/tensor_free_submodule.py | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 21af77745d1..11147a685ec 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -765,9 +765,3 @@ def _basis_comp(self): frame = tuple(self.base_module().irange()) tensor = self.ambient()() return tensor._new_comp(frame) - - def irange(self): - r""" - Index generator, labelling the elements of a basis of ``self``. - """ - yield from self._basis_comp().non_redundant_index_generator() diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 814284a41de..5c778c6ac0a 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -66,7 +66,8 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, self._ambient_module = ambient self._sym = sym self._antisym = antisym - rank = len(list(self.irange())) + rank = len(list(self._basis_comp().non_redundant_index_generator())) + # TODO: Good defaults for name, latex_name category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ FiniteRankFreeModule_abstract.__init__(self, fmodule._ring, rank, name=name, From a4d65bc519d8c5172a8af73387ba1307a7a10b26 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 17:34:33 -0700 Subject: [PATCH 232/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Move here from FiniteRankFreeModule, rewrite using Family --- .../tensor/modules/finite_rank_free_module.py | 241 +++++++++--------- 1 file changed, 124 insertions(+), 117 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 4390e687377..c2e90307e3d 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -538,6 +538,7 @@ class :class:`~sage.modules.free_module.FreeModule_generic` from sage.categories.rings import Rings from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer +from sage.sets.family import Family, TrivialFamily from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement @@ -729,6 +730,129 @@ def is_submodule(self, other): """ return self == other or self.ambient_module() == other + def isomorphism_with_fixed_basis(self, basis=None, codomain=None): + r""" + Construct the canonical isomorphism from the free module ``self`` + to a free module in which ``basis`` of ``self`` is mapped to the + distinguished basis of ``codomain``. + + INPUT: + + - ``basis`` -- (default: ``None``) the basis of ``self`` which + should be mapped to the distinguished basis on ``codomain``; + if ``None``, the default basis is assumed. + - ``codomain`` -- (default: ``None``) the codomain of the + isomorphism represented by a free module within the category + :class:`~sage.categories.modules_with_basis.ModulesWithBasis` with + the same rank and base ring as ``self``; if ``None`` a free module + represented by + :class:`~sage.combinat.free_module.CombinatorialFreeModule` is + constructed + + OUTPUT: + + - a module morphism represented by + :class:`~sage.modules.with_basis.morphism.ModuleMorphismFromFunction` + + EXAMPLES:: + + sage: V = FiniteRankFreeModule(QQ, 3, start_index=1); V + 3-dimensional vector space over the Rational Field + sage: basis = e = V.basis("e"); basis + Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the + Rational Field + sage: phi_e = V.isomorphism_with_fixed_basis(basis); phi_e + Generic morphism: + From: 3-dimensional vector space over the Rational Field + To: Free module generated by {1, 2, 3} over Rational Field + sage: phi_e.codomain().category() + Category of finite dimensional vector spaces with basis over + Rational Field + sage: phi_e(e[1] + 2 * e[2]) + e[1] + 2*e[2] + + sage: abc = V.basis(['a', 'b', 'c'], symbol_dual=['d', 'e', 'f']); abc + Basis (a,b,c) on the 3-dimensional vector space over the Rational Field + sage: phi_abc = V.isomorphism_with_fixed_basis(abc); phi_abc + Generic morphism: + From: 3-dimensional vector space over the Rational Field + To: Free module generated by {1, 2, 3} over Rational Field + sage: phi_abc(abc[1] + 2 * abc[2]) + B[1] + 2*B[2] + + Providing a codomain:: + + sage: W = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) + sage: phi_eW = V.isomorphism_with_fixed_basis(basis, codomain=W); phi_eW + Generic morphism: + From: 3-dimensional vector space over the Rational Field + To: Free module generated by {'a', 'b', 'c'} over Rational Field + sage: phi_eW(e[1] + 2 * e[2]) + B['a'] + 2*B['b'] + + TESTS:: + + sage: V = FiniteRankFreeModule(QQ, 3); V + 3-dimensional vector space over the Rational Field + sage: e = V.basis("e") + sage: V.isomorphism_with_fixed_basis(e, codomain=QQ^42) + Traceback (most recent call last): + ... + ValueError: domain and codomain must have the same rank + sage: V.isomorphism_with_fixed_basis(e, codomain=RR^3) + Traceback (most recent call last): + ... + ValueError: domain and codomain must have the same base ring + """ + base_ring = self.base_ring() + if basis is None: + basis = self.default_basis() + if codomain is None: + from sage.combinat.free_module import CombinatorialFreeModule + if isinstance(basis._symbol, str): + prefix = basis._symbol + else: + prefix = None + codomain = CombinatorialFreeModule(base_ring, basis.keys(), + prefix=prefix) + else: + if codomain.rank() != self.rank(): + raise ValueError("domain and codomain must have the same rank") + if codomain.base_ring() != base_ring: + raise ValueError("domain and codomain must have the same " + "base ring") + + codomain_basis = Family(codomain.basis()) + if isinstance(codomain_basis, TrivialFamily): + # assume that codomain basis keys are to be ignored + key_pairs = enumerate(basis_keys()) + else: + # assume that the keys of the codomain should be used + key_pairs = zip(codomain_basis.keys(), basis.keys()) + + def _isomorphism(x): + r""" + Concrete isomorphism from ``self`` to ``codomain``. + """ + return codomain.sum(x[basis, domain_key] * codomain_basis[codomain_key] + for codomain_key, domain_key in key_pairs) + + return self.module_morphism(function=_isomorphism, codomain=codomain) + + def _test_isomorphism_with_fixed_basis(self, **options): + r""" + Test that the method ``isomorphism_with_fixed_basis`` works correctly. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M._test_isomorphism_with_fixed_basis() + """ + tester = self._tester(**options) + basis = self.basis('test') + morphism = self.isomorphism_with_fixed_basis(basis) + tester.assertEqual(morphism.codomain().rank(), self.rank()) + class FiniteRankFreeModule(FiniteRankFreeModule_abstract): r""" @@ -2713,123 +2837,6 @@ def hom(self, codomain, matrix_rep, bases=None, name=None, return homset(matrix_rep, bases=bases, name=name, latex_name=latex_name) - def isomorphism_with_fixed_basis(self, basis=None, codomain=None): - r""" - Construct the canonical isomorphism from the free module ``self`` - to a free module in which ``basis`` of ``self`` is mapped to the - distinguished basis of ``codomain``. - - INPUT: - - - ``basis`` -- (default: ``None``) the basis of ``self`` which - should be mapped to the distinguished basis on ``codomain``; - if ``None``, the default basis is assumed. - - ``codomain`` -- (default: ``None``) the codomain of the - isomorphism represented by a free module within the category - :class:`~sage.categories.modules_with_basis.ModulesWithBasis` with - the same rank and base ring as ``self``; if ``None`` a free module - represented by - :class:`~sage.combinat.free_module.CombinatorialFreeModule` is - constructed - - OUTPUT: - - - a module morphism represented by - :class:`~sage.modules.with_basis.morphism.ModuleMorphismFromFunction` - - EXAMPLES:: - - sage: V = FiniteRankFreeModule(QQ, 3, start_index=1); V - 3-dimensional vector space over the Rational Field - sage: basis = e = V.basis("e"); basis - Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the - Rational Field - sage: phi_e = V.isomorphism_with_fixed_basis(basis); phi_e - Generic morphism: - From: 3-dimensional vector space over the Rational Field - To: Free module generated by {1, 2, 3} over Rational Field - sage: phi_e.codomain().category() - Category of finite dimensional vector spaces with basis over - Rational Field - sage: phi_e(e[1] + 2 * e[2]) - e[1] + 2*e[2] - - sage: abc = V.basis(['a', 'b', 'c'], symbol_dual=['d', 'e', 'f']); abc - Basis (a,b,c) on the 3-dimensional vector space over the Rational Field - sage: phi_abc = V.isomorphism_with_fixed_basis(abc); phi_abc - Generic morphism: - From: 3-dimensional vector space over the Rational Field - To: Free module generated by {1, 2, 3} over Rational Field - sage: phi_abc(abc[1] + 2 * abc[2]) - B[1] + 2*B[2] - - Providing a codomain:: - - sage: W = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) - sage: phi_eW = V.isomorphism_with_fixed_basis(basis, codomain=W); phi_eW - Generic morphism: - From: 3-dimensional vector space over the Rational Field - To: Free module generated by {'a', 'b', 'c'} over Rational Field - sage: phi_eW(e[1] + 2 * e[2]) - B['a'] + 2*B['b'] - - TESTS:: - - sage: V = FiniteRankFreeModule(QQ, 3); V - 3-dimensional vector space over the Rational Field - sage: e = V.basis("e") - sage: V.isomorphism_with_fixed_basis(e, codomain=QQ^42) - Traceback (most recent call last): - ... - ValueError: domain and codomain must have the same rank - sage: V.isomorphism_with_fixed_basis(e, codomain=RR^3) - Traceback (most recent call last): - ... - ValueError: domain and codomain must have the same base ring - """ - base_ring = self.base_ring() - if basis is None: - basis = self.default_basis() - if codomain is None: - from sage.combinat.free_module import CombinatorialFreeModule - if isinstance(basis._symbol, str): - prefix = basis._symbol - else: - prefix = None - codomain = CombinatorialFreeModule(base_ring, list(self.irange()), - prefix=prefix) - else: - if codomain.rank() != self.rank(): - raise ValueError("domain and codomain must have the same rank") - if codomain.base_ring() != base_ring: - raise ValueError("domain and codomain must have the same " - "base ring") - - codomain_basis = list(codomain.basis()) - - def _isomorphism(x): - r""" - Concrete isomorphism from ``self`` to ``codomain``. - """ - return codomain.sum(x[basis, i] * codomain_basis[i - self._sindex] - for i in self.irange()) - - return self.module_morphism(function=_isomorphism, codomain=codomain) - - def _test_isomorphism_with_fixed_basis(self, **options): - r""" - Test that the method ``isomorphism_with_fixed_basis`` works correctly. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M._test_isomorphism_with_fixed_basis() - """ - tester = self._tester(**options) - basis = self.basis('test') - morphism = self.isomorphism_with_fixed_basis(basis) - tester.assertEqual(morphism.codomain().rank(), self.rank()) - def endomorphism(self, matrix_rep, basis=None, name=None, latex_name=None): r""" Construct an endomorphism of the free module ``self``. From 280424ec1a8d625bdbe1b827d8c0382a1f794456 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 17:40:22 -0700 Subject: [PATCH 233/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Add example with codomain from sage.modules --- src/sage/tensor/modules/finite_rank_free_module.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index c2e90307e3d..f31d38117df 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -790,6 +790,16 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): sage: phi_eW(e[1] + 2 * e[2]) B['a'] + 2*B['b'] + Providing a :class:`~sage.modules.free_module.Module_free_ambient` as the codomain:: + + sage: W = QQ^3 + sage: phi_eW = V.isomorphism_with_fixed_basis(basis, codomain=W); phi_eW + Generic morphism: + From: 3-dimensional vector space over the Rational Field + To: Vector space of dimension 3 over Rational Field + sage: phi_eW(e[1] + 2 * e[2]) + (1, 2, 0) + TESTS:: sage: V = FiniteRankFreeModule(QQ, 3); V @@ -825,7 +835,7 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): codomain_basis = Family(codomain.basis()) if isinstance(codomain_basis, TrivialFamily): # assume that codomain basis keys are to be ignored - key_pairs = enumerate(basis_keys()) + key_pairs = enumerate(basis.keys()) else: # assume that the keys of the codomain should be used key_pairs = zip(codomain_basis.keys(), basis.keys()) From e452316628bf0982b63e1f6613f00bae313bbc52 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 18:24:45 -0700 Subject: [PATCH 234/454] FreeModuleTensor: Handle TensorFreeSubmoduleBasis_comp --- src/sage/tensor/modules/free_module_tensor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index bbcc1ecb2e0..845c9185a26 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -1101,6 +1101,12 @@ class :class:`~sage.tensor.modules.comp.Components` fmodule = self._fmodule if basis is None: basis = fmodule._def_basis + try: + # Standard bases of tensor modules are keyed to the base module's basis, + # not to the TensorFreeSubmoduleBasis_comp instance. + basis = basis._base_module_basis + except AttributeError: + pass if basis not in self._components: # The components must be computed from # those in the basis from_basis From b0b8d19851c634da9051cf91f3d05386dab71a67 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 18:25:16 -0700 Subject: [PATCH 235/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Handle codomain=MatrixSpace --- .../tensor/modules/finite_rank_free_module.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index f31d38117df..69b8b2c0874 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -800,6 +800,24 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): sage: phi_eW(e[1] + 2 * e[2]) (1, 2, 0) + Sending (1,1)-tensors to matrices:: + + sage: T11 = V.tensor_module(1, 1); T11 + Free module of type-(1,1) tensors on the 3-dimensional vector space over the Rational Field + sage: e_T11 = T11.basis("e"); e_T11 + + sage: W = MatrixSpace(QQ, 3) + sage: phi_e_T11 = T11.isomorphism_with_fixed_basis(e_T11, codomain=W); phi_e_T11 + Generic morphism: + From: Free module of type-(1,1) tensors on the 3-dimensional vector space over the Rational Field + To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field + sage: t = T11.an_element(); t.display() + 1/2 e_1⊗e^1 + sage: phi_e_T11(t) + [1/2 0 0] + [ 0 0 0] + [ 0 0 0] + TESTS:: sage: V = FiniteRankFreeModule(QQ, 3); V @@ -826,7 +844,12 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): codomain = CombinatorialFreeModule(base_ring, basis.keys(), prefix=prefix) else: - if codomain.rank() != self.rank(): + try: + codomain_rank = codomain.rank() + except AttributeError: + # https://trac.sagemath.org/ticket/34445: MatrixSpace does not have rank + codomain_rank = codomain.dimension() + if codomain_rank != self.rank(): raise ValueError("domain and codomain must have the same rank") if codomain.base_ring() != base_ring: raise ValueError("domain and codomain must have the same " From 18aa856005ef29aa3508b3ec816c0958fbaa03db Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 18:33:45 -0700 Subject: [PATCH 236/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Add example with symmetric binary forms --- .../tensor/modules/finite_rank_free_module.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 69b8b2c0874..f7e606c5255 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -818,6 +818,24 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): [ 0 0 0] [ 0 0 0] + Sending symmetric bilinear forms to matrices:: + + sage: T02 = V.tensor_module(0, 2); T02 + Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field + sage: e_T02 = T02.basis("e"); e_T02 + + sage: W = MatrixSpace(QQ, 3) + sage: phi_e_T02 = T02.isomorphism_with_fixed_basis(e_T02, codomain=W); phi_e_T02 + Generic morphism: + From: Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field + To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field + sage: t = T02.an_element(); t.display() + 1/2 e^1⊗e^1 + sage: phi_e_T02(t) + [1/2 0 0] + [ 0 0 0] + [ 0 0 0] + TESTS:: sage: V = FiniteRankFreeModule(QQ, 3); V From dafd47d72cd15666e3b325a5b93f39f2d903d53f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 18:47:18 -0700 Subject: [PATCH 237/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Better example with symmetric binary forms --- .../tensor/modules/finite_rank_free_module.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index f7e606c5255..32ad6f2aaa1 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -818,7 +818,8 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): [ 0 0 0] [ 0 0 0] - Sending symmetric bilinear forms to matrices:: + Sending symmetric bilinear forms to matrices (note that they are currently elements + of `T^{(0,2)}(M)`, not the symmetric power of `M`):: sage: T02 = V.tensor_module(0, 2); T02 Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field @@ -829,12 +830,17 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): Generic morphism: From: Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field To: Full MatrixSpace of 3 by 3 dense matrices over Rational Field - sage: t = T02.an_element(); t.display() - 1/2 e^1⊗e^1 - sage: phi_e_T02(t) - [1/2 0 0] - [ 0 0 0] - [ 0 0 0] + + sage: a = V.sym_bilinear_form() + sage: a[1,1], a[1,2], a[1,3] = 1, 2, 3 + sage: a[2,2], a[2,3] = 4, 5 + sage: a[3,3] = 6 + sage: a.display() + e^1⊗e^1 + 2 e^1⊗e^2 + 3 e^1⊗e^3 + 2 e^2⊗e^1 + 4 e^2⊗e^2 + 5 e^2⊗e^3 + 3 e^3⊗e^1 + 5 e^3⊗e^2 + 6 e^3⊗e^3 + sage: phi_e_T02(a) + [1 2 3] + [2 4 5] + [3 5 6] TESTS:: From c3547702c8ff842c0059879a35cca2de3c78704a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 19:03:10 -0700 Subject: [PATCH 238/454] FiniteRankFreeModule_abstract.isomorphism_with_fixed_basis: Add example with codomain= tensor square of a CombinatorialFreeModule --- .../tensor/modules/finite_rank_free_module.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 32ad6f2aaa1..d11c07fa405 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -842,6 +842,23 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): [2 4 5] [3 5 6] + Sending tensors to elements of the tensor square of :class:`CombinatorialFreeModule`:: + + sage: T20 = V.tensor_module(2, 0); T20 + Free module of type-(2,0) tensors on the 3-dimensional vector space over the Rational Field + sage: e_T20 = T02.basis("e"); e_T20 + + sage: W = CombinatorialFreeModule(QQ, [1, 2, 3]).tensor_square(); W + Free module generated by {1, 2, 3} over Rational Field # Free module generated by {1, 2, 3} over Rational Field + sage: phi_e_T20 = T20.isomorphism_with_fixed_basis(e_T20, codomain=W); phi_e_T20 + Generic morphism: + From: Free module of type-(2,0) tensors on the 3-dimensional vector space over the Rational Field + To: Free module generated by {1, 2, 3} over Rational Field # Free module generated by {1, 2, 3} over Rational Field + sage: t = T20.an_element(); t.display() + 1/2 e_1⊗e_1 + sage: phi_e_T20(t) + 1/2*B[1] # B[1] + TESTS:: sage: V = FiniteRankFreeModule(QQ, 3); V From 49129ea518c875cc0b793c50c84cf5dd280221e9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 27 Aug 2022 11:28:46 +0900 Subject: [PATCH 239/454] Allowing an arbitrary skew-symmetric matrix as input to determine the multiplication. --- src/sage/algebras/q_commuting_polynomials.py | 111 ++++++++++++++++--- 1 file changed, 93 insertions(+), 18 deletions(-) diff --git a/src/sage/algebras/q_commuting_polynomials.py b/src/sage/algebras/q_commuting_polynomials.py index c948d4e8b20..b772f04a57e 100644 --- a/src/sage/algebras/q_commuting_polynomials.py +++ b/src/sage/algebras/q_commuting_polynomials.py @@ -19,24 +19,31 @@ from sage.misc.cachefunc import cached_method from sage.sets.family import Family from sage.rings.infinity import infinity +from sage.rings.integer_ring import ZZ from sage.categories.algebras import Algebras from sage.combinat.free_module import CombinatorialFreeModule -from sage.monoids.free_abelian_monoid import FreeAbelianMonoid, FreeAbelianMonoid_class +from sage.monoids.free_abelian_monoid import FreeAbelianMonoid +from sage.matrix.constructor import matrix +from sage.structure.element import Matrix class qCommutingPolynomials(CombinatorialFreeModule): r""" The algebra of `q`-commuting polynomials. - Let `R` be a commutative ring, and fix an element `q \in R`. We say two - distinct variables `x` and `y` `q`-*commute* if they satisfy the relation + Let `R` be a commutative ring, and fix an element `q \in R`. Let + B = (B_{xy})_{x,y \in I}` be a skew-symmetric bilinear form with + index set `I`. Let `R[I]_{q,B}` denote the polynomial ring in the variables + `I` such that we have the `q`-*commuting* relation for `x, y \in I`: .. MATH:: - x y = q \cdot y x. + y x = q^{B_{xy}} \cdot x y. - These form a graded `R`-algebra with a natural basis given by monomials - written in increasing order. These then satisfy a `q`-analog of the - classical binomial coefficient theorem: + This is a graded `R`-algebra with a natural basis given by monomials + written in increasing order with respect to some total order on `I`. + + When `B_{xy} = 1` and `B_{yx} = -1` for all `x < y`, then we have + a `q`-analog of the classical binomial coefficient theorem: .. MATH:: @@ -47,14 +54,38 @@ class qCommutingPolynomials(CombinatorialFreeModule): sage: q = ZZ['q'].fraction_field().gen() sage: R. = algebras.qCommutingPolynomials(q) - We verify the `q`-binomial theorem:: + We verify a case of the `q`-binomial theorem:: sage: f = (x + y)^10 sage: all(f[b] == q_binomial(10, b.list()[0]) for b in f.support()) True + + We now do a computation with a non-standard `B` matrix:: + + sage: B = matrix([[0,1,2],[-1,0,3],[-2,-3,0]]) + sage: B + [ 0 1 2] + [-1 0 3] + [-2 -3 0] + sage: q = ZZ['q'].gen() + sage: R. = algebras.qCommutingPolynomials(q, B) + sage: y * x + q*x*y + sage: z * x + q^2*x*z + sage: z * y + q^3*y*z + + sage: f = (x + z)^10 + sage: all(f[b] == q_binomial(10, b.list()[0], q^2) for b in f.support()) + True + + sage: f = (y + z)^10 + sage: all(f[b] == q_binomial(10, b.list()[1], q^3) for b in f.support()) + True """ @staticmethod - def __classcall_private__(cls, q, n=None, base_ring=None, names=None): + def __classcall_private__(cls, q, n=None, B=None, base_ring=None, names=None): r""" Normalize input to ensure a unique representation. @@ -70,13 +101,34 @@ def __classcall_private__(cls, q, n=None, base_ring=None, names=None): if base_ring is not None: q = base_ring(q) - if isinstance(n, FreeAbelianMonoid_class): - indices = n + if B is None and isinstance(n, Matrix): + n, B = B, n + + if names is None: + raise ValueError("the names of the variables must be given") + from sage.structure.category_object import normalize_names + if n is None: + if isinstance(names, str): + n = names.count(',') + 1 + else: + n = len(names) + names = normalize_names(n, names) + n = len(names) + if B is None: + B = matrix.zero(ZZ, n) + for i in range(n): + for j in range(i+1, n): + B[i,j] = 1 + B[j,i] = -1 + B.set_immutable() else: - indices = FreeAbelianMonoid(n, names) - return super().__classcall__(cls, q, indices) + if not B.is_skew_symmetric(): + raise ValueError("the matrix must be skew symmetric") + B = B.change_ring(ZZ) + B.set_immutable() + return super().__classcall__(cls, q=q, B=B, names=names) - def __init__(self, q, indices): + def __init__(self, q, B, names): r""" Initialize ``self``. @@ -87,7 +139,9 @@ def __init__(self, q, indices): sage: TestSuite(R).run() """ self._q = q + self._B = B base_ring = q.parent() + indices = FreeAbelianMonoid(len(names), names) category = Algebras(base_ring).WithBasis().Graded() CombinatorialFreeModule.__init__(self, base_ring, indices, bracket=False, prefix='', @@ -104,10 +158,13 @@ def _repr_(self): sage: R. = algebras.qCommutingPolynomials(q) sage: R q-commuting polynomial ring in x, y, z over Fraction Field of - Univariate Polynomial Ring in q over Integer Ring + Univariate Polynomial Ring in q over Integer Ring with matrix: + [ 0 1 1] + [-1 0 1] + [-1 -1 0] """ names = ", ".join(self.variable_names()) - return "{}-commuting polynomial ring in {} over {}".format(self._q, names, self.base_ring()) + return "{}-commuting polynomial ring in {} over {} with matrix:\n{}".format(self._q, names, self.base_ring(), self._B) def _latex_(self): r""" @@ -142,7 +199,7 @@ def _term_key(x): return (sum(L), L) def gen(self, i): - """ + r""" Return the ``i``-generator of ``self``. EXAMPLES:: @@ -260,6 +317,24 @@ def product_on_basis(self, x, y): x^3 + (q^2+q+1)*x^2*y + (q^2+q+1)*x*y^2 + y^3 sage: (x + y)^4 x^4 + (q^3+q^2+q+1)*x^3*y + (q^4+q^3+2*q^2+q+1)*x^2*y^2 + (q^3+q^2+q+1)*x*y^3 + y^4 + + With a non-standard `B` matrix:: + + sage: B = matrix([[0,1,2],[-1,0,3],[-2,-3,0]]) + sage: q = ZZ['q'].fraction_field().gen() + sage: R. = algebras.qCommutingPolynomials(q, B=B) + sage: x * y + x*y + sage: y * x^2 + q^2*x^2*y + sage: z^2 * x + q^4*x*z^2 + sage: z^2 * x^3 + q^12*x^3*z^2 + sage: z^2 * y + q^6*y*z^2 + sage: z^2 * y^3 + q^18*y^3*z^2 """ # Special case for multiplying by 1 if x == self.one_basis(): @@ -271,6 +346,6 @@ def product_on_basis(self, x, y): Ly = y.list() # This could be made more efficient - qpow = sum(exp * sum(Ly[:i]) for i,exp in enumerate(Lx)) + qpow = sum(exp * sum(self._B[j,i] * val for j, val in enumerate(Ly[:i])) for i,exp in enumerate(Lx)) return self.term(x * y, self._q ** qpow) From 694e665430527083480ee4fa58c8b6857fc5acfe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 20:30:13 -0700 Subject: [PATCH 240/454] TensorFreeSubmoduleBasis_comp._repr_: New --- .../modules/tensor_free_submodule_basis.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 45704e81ac1..856d58554fb 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -45,6 +45,23 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, self._base_module_basis = base_module_basis self._comp = tensor_module._basis_comp() + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: T11 = M.tensor_module(1,1) + sage: e_T11 = T11.basis('e') + sage: e_T11 + Standard basis on the + Free module of type-(1,1) tensors on the Rank-3 free module M over the Integer Ring + induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + """ + return f"Standard basis on the {self._fmodule} induced by {self._base_module_basis}" + def keys(self): """ Return an iterator for the keys (indices) of the family. From 454f0b08a338a0e4dde93cc731654bff6f40269b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 20:34:12 -0700 Subject: [PATCH 241/454] src/sage/tensor/modules/finite_rank_free_module.py: Update doctest output --- src/sage/tensor/modules/finite_rank_free_module.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index d11c07fa405..d4519298210 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -805,7 +805,9 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): sage: T11 = V.tensor_module(1, 1); T11 Free module of type-(1,1) tensors on the 3-dimensional vector space over the Rational Field sage: e_T11 = T11.basis("e"); e_T11 - + Standard basis on the + Free module of type-(1,1) tensors on the 3-dimensional vector space over the Rational Field + induced by Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the Rational Field sage: W = MatrixSpace(QQ, 3) sage: phi_e_T11 = T11.isomorphism_with_fixed_basis(e_T11, codomain=W); phi_e_T11 Generic morphism: @@ -824,7 +826,9 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): sage: T02 = V.tensor_module(0, 2); T02 Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field sage: e_T02 = T02.basis("e"); e_T02 - + Standard basis on the + Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field + induced by Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the Rational Field sage: W = MatrixSpace(QQ, 3) sage: phi_e_T02 = T02.isomorphism_with_fixed_basis(e_T02, codomain=W); phi_e_T02 Generic morphism: @@ -847,7 +851,9 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): sage: T20 = V.tensor_module(2, 0); T20 Free module of type-(2,0) tensors on the 3-dimensional vector space over the Rational Field sage: e_T20 = T02.basis("e"); e_T20 - + Standard basis on the + Free module of type-(0,2) tensors on the 3-dimensional vector space over the Rational Field + induced by Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the Rational Field sage: W = CombinatorialFreeModule(QQ, [1, 2, 3]).tensor_square(); W Free module generated by {1, 2, 3} over Rational Field # Free module generated by {1, 2, 3} over Rational Field sage: phi_e_T20 = T20.isomorphism_with_fixed_basis(e_T20, codomain=W); phi_e_T20 From 50f3eeb101cf6c04d26c781fba8330352e052102 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 17 May 2022 16:29:08 +0800 Subject: [PATCH 242/454] rename .rational_reconstruct() to .rational_reconstruction() for consistency --- .../rings/polynomial/polynomial_element.pyx | 40 ++++++++++--------- .../polynomial/polynomial_zmod_flint.pxd | 2 +- .../polynomial/polynomial_zmod_flint.pyx | 8 +++- src/sage/rings/power_series_poly.pyx | 4 +- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 59f0b21fe8d..e3a00051f8c 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -126,7 +126,7 @@ from sage.misc.cachefunc import cached_function from sage.categories.map cimport Map from sage.categories.morphism cimport Morphism -from sage.misc.superseded import deprecation_cython as deprecation +from sage.misc.superseded import deprecation_cython as deprecation, deprecated_function_alias from sage.misc.cachefunc import cached_method from sage.rings.number_field.order import is_NumberFieldOrder @@ -8915,7 +8915,7 @@ cdef class Polynomial(CommutativeAlgebraElement): else: raise NotImplementedError("%s does not provide an xgcd implementation for univariate polynomials"%self.base_ring()) - def rational_reconstruct(self, m, n_deg=None, d_deg=None): + def rational_reconstruction(self, m, n_deg=None, d_deg=None): r""" Return a tuple of two polynomials ``(n, d)`` where ``self * d`` is congruent to ``n`` modulo ``m`` and @@ -8940,7 +8940,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: z = PolynomialRing(QQ, 'z').gen() sage: p = -z**16 - z**15 - z**14 + z**13 + z**12 + z**11 - z**5 - z**4 - z**3 + z**2 + z + 1 sage: m = z**21 - sage: n, d = p.rational_reconstruct(m) + sage: n, d = p.rational_reconstruction(m) sage: print((n ,d)) (z^4 + 2*z^3 + 3*z^2 + 2*z + 1, z^10 + z^9 + z^8 + z^7 + z^6 + z^5 + z^4 + z^3 + z^2 + z + 1) sage: print(((p*d - n) % m ).is_zero()) @@ -8951,7 +8951,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: z = PolynomialRing(ZZ, 'z').gen() sage: p = -z**16 - z**15 - z**14 + z**13 + z**12 + z**11 - z**5 - z**4 - z**3 + z**2 + z + 1 sage: m = z**21 - sage: n, d = p.rational_reconstruct(m) + sage: n, d = p.rational_reconstruction(m) sage: print((n ,d)) (z^4 + 2*z^3 + 3*z^2 + 2*z + 1, z^10 + z^9 + z^8 + z^7 + z^6 + z^5 + z^4 + z^3 + z^2 + z + 1) sage: print(((p*d - n) % m ).is_zero()) @@ -8963,12 +8963,12 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: x = P.gen() sage: p = 7*x^5 - 10*x^4 + 16*x^3 - 32*x^2 + 128*x + 256 sage: m = x^5 - sage: n, d = p.rational_reconstruct(m, 3, 2) + sage: n, d = p.rational_reconstruction(m, 3, 2) sage: print((n ,d)) (-32*x^3 + 384*x^2 + 2304*x + 2048, 5*x + 8) sage: print(((p*d - n) % m ).is_zero()) True - sage: n, d = p.rational_reconstruct(m, 4, 0) + sage: n, d = p.rational_reconstruction(m, 4, 0) sage: print((n ,d)) (-10*x^4 + 16*x^3 - 32*x^2 + 128*x + 256, 1) sage: print(((p*d - n) % m ).is_zero()) @@ -8983,7 +8983,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: # p = (1 + t^2*z + z^4) / (1 - t*z) sage: p = (1 + t^2*z + z^4)*(1 - t*z).inverse_mod(z^9) sage: m = z^9 - sage: n, d = p.rational_reconstruct(m) + sage: n, d = p.rational_reconstruction(m) sage: print((n ,d)) (-1/t*z^4 - t*z - 1/t, z - 1/t) sage: print(((p*d - n) % m ).is_zero()) @@ -8992,7 +8992,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: n = -10*t^2*z^4 + (-t^2 + t - 1)*z^3 + (-t - 8)*z^2 + z + 2*t^2 - t sage: d = z^4 + (2*t + 4)*z^3 + (-t + 5)*z^2 + (t^2 + 2)*z + t^2 + 2*t + 1 sage: prec = 9 - sage: nc, dc = Pz((n.subs(z = w)/d.subs(z = w) + O(w^prec)).list()).rational_reconstruct(z^prec) + sage: nc, dc = Pz((n.subs(z = w)/d.subs(z = w) + O(w^prec)).list()).rational_reconstruction(z^prec) sage: print( (nc, dc) == (n, d) ) True @@ -9004,7 +9004,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: # p = (1 + t^2*z + z^4) / (1 - t*z) mod z^9 sage: p = (1 + t^2*z + z^4) * sum((t*z)**i for i in range(9)) sage: m = z^9 - sage: n, d = p.rational_reconstruct(m,) + sage: n, d = p.rational_reconstruction(m,) sage: print((n ,d)) (-z^4 - t^2*z - 1, t*z - 1) sage: print(((p*d - n) % m ).is_zero()) @@ -9015,7 +9015,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: x = PolynomialRing(Qp(5),'x').gen() sage: p = 4*x^5 + 3*x^4 + 2*x^3 + 2*x^2 + 4*x + 2 sage: m = x^6 - sage: n, d = p.rational_reconstruct(m, 3, 2) + sage: n, d = p.rational_reconstruction(m, 3, 2) sage: print(((p*d - n) % m ).is_zero()) True @@ -9026,34 +9026,34 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: x = P.gen() sage: p = P(exp(z).list()) sage: m = x^5 - sage: n, d = p.rational_reconstruct(m, 4, 0) + sage: n, d = p.rational_reconstruction(m, 4, 0) sage: print((n ,d)) (1/24*x^4 + 1/6*x^3 + 1/2*x^2 + x + 1, 1) sage: print(((p*d - n) % m ).is_zero()) True sage: m = x^3 - sage: n, d = p.rational_reconstruct(m, 1, 1) + sage: n, d = p.rational_reconstruction(m, 1, 1) sage: print((n ,d)) (-x - 2, x - 2) sage: print(((p*d - n) % m ).is_zero()) True sage: p = P(log(1-z).list()) sage: m = x^9 - sage: n, d = p.rational_reconstruct(m, 4, 4) + sage: n, d = p.rational_reconstruction(m, 4, 4) sage: print((n ,d)) (25/6*x^4 - 130/3*x^3 + 105*x^2 - 70*x, x^4 - 20*x^3 + 90*x^2 - 140*x + 70) sage: print(((p*d - n) % m ).is_zero()) True sage: p = P(sqrt(1+z).list()) sage: m = x^6 - sage: n, d = p.rational_reconstruct(m, 3, 2) + sage: n, d = p.rational_reconstruction(m, 3, 2) sage: print((n ,d)) (1/6*x^3 + 3*x^2 + 8*x + 16/3, x^2 + 16/3*x + 16/3) sage: print(((p*d - n) % m ).is_zero()) True sage: p = P(exp(2*z).list()) sage: m = x^7 - sage: n, d = p.rational_reconstruct(m, 3, 3) + sage: n, d = p.rational_reconstruction(m, 3, 3) sage: print((n ,d)) (-x^3 - 6*x^2 - 15*x - 15, x^3 - 6*x^2 + 15*x - 15) sage: print(((p*d - n) % m ).is_zero()) @@ -9066,26 +9066,26 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: x = P.gen() sage: p = P(exp(2*z).list()) sage: m = x^7 - sage: n, d = p.rational_reconstruct( m, 3, 3) + sage: n, d = p.rational_reconstruction(m, 3, 3) sage: print((n ,d)) # absolute tolerance 1e-10 (-x^3 - 6.0*x^2 - 15.0*x - 15.0, x^3 - 6.0*x^2 + 15.0*x - 15.0) .. SEEALSO:: * :mod:`sage.matrix.berlekamp_massey`, - * :meth:`sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint.rational_reconstruct` + * :meth:`sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint.rational_reconstruction` """ P = self.parent() if not P.base_ring().is_field(): if not P.base_ring().is_integral_domain(): - raise NotImplementedError("rational_reconstruct() " + raise NotImplementedError("rational_reconstruction() " "is only implemented when the base ring is a field " "or a integral domain, " "a workaround is to do a multimodular approach") Pf = P.base_extend(P.base_ring().fraction_field()) sF = Pf(self) mF = Pf(m) - n, d = sF.rational_reconstruct( mF, n_deg, d_deg) + n, d = sF.rational_reconstruction(mF, n_deg, d_deg) l = lcm([n.denominator(), d.denominator()]) n *= l d *= l @@ -9125,6 +9125,8 @@ cdef class Polynomial(CommutativeAlgebraElement): t1 = t1 / c return t1, t0 + rational_reconstruct = deprecated_function_alias(12696, rational_reconstruction) + def variables(self): """ Return the tuple of variables occurring in this polynomial. diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pxd b/src/sage/rings/polynomial/polynomial_zmod_flint.pxd index d8cef40282f..c6a92f3df6c 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pxd +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pxd @@ -14,4 +14,4 @@ cdef class Polynomial_zmod_flint(Polynomial_template): cdef int _set_list(self, x) except -1 cdef int _set_fmpz_poly(self, fmpz_poly_t) except -1 cpdef Polynomial _mul_trunc_opposite(self, Polynomial_zmod_flint other, length) - cpdef rational_reconstruct(self, m, n_deg=?, d_deg=?) + cpdef rational_reconstruction(self, m, n_deg=?, d_deg=?) diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index 748d8ce37c7..f974916fac8 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -45,6 +45,8 @@ from sage.structure.element cimport parent from sage.structure.element import coerce_binop from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint +from sage.misc.superseded import deprecated_function_alias + # We need to define this stuff before including the templating stuff # to make sure the function get_cparent is found since it is used in # 'polynomial_template.pxi'. @@ -585,7 +587,7 @@ cdef class Polynomial_zmod_flint(Polynomial_template): nmod_poly_pow_trunc(&ans.x, &self.x, n, prec) return ans - cpdef rational_reconstruct(self, m, n_deg=0, d_deg=0): + cpdef rational_reconstruction(self, m, n_deg=0, d_deg=0): """ Construct a rational function n/d such that `p*d` is equivalent to `n` modulo `m` where `p` is this polynomial. @@ -594,7 +596,7 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sage: P. = GF(5)[] sage: p = 4*x^5 + 3*x^4 + 2*x^3 + 2*x^2 + 4*x + 2 - sage: n, d = p.rational_reconstruct(x^9, 4, 4); n, d + sage: n, d = p.rational_reconstruction(x^9, 4, 4); n, d (3*x^4 + 2*x^3 + x^2 + 2*x, x^4 + 3*x^3 + x^2 + x) sage: (p*d % x^9) == n True @@ -638,6 +640,8 @@ cdef class Polynomial_zmod_flint(Polynomial_template): return t1, t0 + rational_reconstruct = deprecated_function_alias(12696, rational_reconstruction) + @cached_method def is_irreducible(self): """ diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index 198c8e99438..58d509e8454 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -1120,7 +1120,7 @@ cdef class PowerSeries_poly(PowerSeries): .. SEEALSO:: * :mod:`sage.matrix.berlekamp_massey`, - * :meth:`sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint.rational_reconstruct` + * :meth:`sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint.rational_reconstruction` EXAMPLES:: @@ -1173,7 +1173,7 @@ cdef class PowerSeries_poly(PowerSeries): polyring = self.parent()._poly_ring() z = polyring.gen() c = self.polynomial() - u, v = c.rational_reconstruct(z**(n + m + 1), m, n) + u, v = c.rational_reconstruction(z**(n + m + 1), m, n) return u / v def _symbolic_(self, ring): From a1de06499bf16d26b13fb8c0c09116700465d917 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 26 Aug 2022 23:06:49 -0700 Subject: [PATCH 243/454] TensorFreeSubmodule_comp: Default name, latex_name for the case Sym^n(M) --- src/sage/tensor/modules/tensor_free_submodule.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 5c778c6ac0a..086010de013 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -16,6 +16,7 @@ from sage.sets.disjoint_set import DisjointSet from .tensor_free_module import TensorFreeModule from .finite_rank_free_module import FiniteRankFreeModule_abstract +from sage.tensor.modules.comp import CompFullySym, CompWithSym class TensorFreeSubmodule_comp(TensorFreeModule): r""" @@ -31,6 +32,8 @@ class TensorFreeSubmodule_comp(TensorFreeModule): Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. (0, 1, 2) on the Rank-3 free module M over the Integer Ring + sage: latex(Sym2M) + \mathrm{Sym}^{2}\left(M\right) Canonical injections from submodules are coercions:: @@ -63,11 +66,18 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, self._tensor_type = tuple(tensor_type) if ambient is None: ambient = fmodule.tensor_module(*tensor_type) + ambient_type = ambient.tensor_type() self._ambient_module = ambient self._sym = sym self._antisym = antisym - rank = len(list(self._basis_comp().non_redundant_index_generator())) - # TODO: Good defaults for name, latex_name + basis_comp = self._basis_comp() + rank = len(list(basis_comp.non_redundant_index_generator())) + # TODO: Good defaults for name, latex_name for more cases + if name is None and fmodule._name is not None: + if isinstance(basis_comp, CompFullySym) and not ambient_type[1]: + name = 'Sym^' + str(ambient_type[0]) + '(' + fmodule._name + ')' + latex_name = r'\mathrm{Sym}^{' + str(ambient_type[0]) + r'}\left(' + \ + fmodule._latex_name + r'\right)' category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ FiniteRankFreeModule_abstract.__init__(self, fmodule._ring, rank, name=name, From 08c7631cdf7356e2eb1e6cd0c04c2fb158a60145 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 27 Aug 2022 13:49:54 +0800 Subject: [PATCH 244/454] add .rational_reconstruction() wrapper to PolynomialQuotientRingElement --- .../polynomial_quotient_ring_element.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 846cd727986..c639214e5d6 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -714,3 +714,27 @@ def trace(self): 389 """ return self.matrix().trace() + + def rational_reconstruction(self, *args, **kwargs): + r""" + Compute a rational reconstruction of this polynomial quotient + ring element to its parent. + + This method is a thin convenience wrapper around + :meth:`Polynomial.rational_reconstruction`. + + EXAMPLES:: + + sage: R. = GF(65537)[] + sage: m = x^11 + 25345*x^10 + 10956*x^9 + 13873*x^8 + 23962*x^7 + 17496*x^6 + 30348*x^5 + 7440*x^4 + 65438*x^3 + 7676*x^2 + 54266*x + 47805 + sage: f = 20437*x^10 + 62630*x^9 + 63241*x^8 + 12820*x^7 + 42171*x^6 + 63091*x^5 + 15288*x^4 + 32516*x^3 + 2181*x^2 + 45236*x + 2447 + sage: f_mod_m = R.quotient(m)(f) + sage: f_mod_m.rational_reconstruction() + (51388*x^5 + 29141*x^4 + 59341*x^3 + 7034*x^2 + 14152*x + 23746, + x^5 + 15208*x^4 + 19504*x^3 + 20457*x^2 + 11180*x + 28352) + """ + m = self.parent().modulus() + R = m.parent() + f = R(self._polynomial) + return f.lift().rational_reconstruction(m, *args, **kwargs) + From 320d00b310cc27a923662b33769fa214987ff7bc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 08:30:56 -0700 Subject: [PATCH 245/454] TensorFreeSubmodule_comp: Default name, latex_name for more cases --- .../tensor/modules/tensor_free_submodule.py | 106 ++++++++++++++---- 1 file changed, 86 insertions(+), 20 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 086010de013..1ced6855eb7 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -3,7 +3,7 @@ """ #****************************************************************************** -# Copyright (C) 2020 Matthias Koeppe +# Copyright (C) 2020-2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -11,12 +11,17 @@ # http://www.gnu.org/licenses/ #****************************************************************************** +import itertools + from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.disjoint_set import DisjointSet +from sage.typeset.unicode_characters import unicode_otimes + +from .comp import CompFullySym, CompFullyAntiSym, CompWithSym from .tensor_free_module import TensorFreeModule from .finite_rank_free_module import FiniteRankFreeModule_abstract -from sage.tensor.modules.comp import CompFullySym, CompWithSym + class TensorFreeSubmodule_comp(TensorFreeModule): r""" @@ -28,22 +33,42 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M - Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. (0, 1, 2) - on the Rank-3 free module M over the Integer Ring - sage: latex(Sym2M) - \mathrm{Sym}^{2}\left(M\right) + sage: e = M.basis('e') + sage: T60M = M.tensor_module(6, 0); T60M + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring + sage: T60M._name + 'T^(6, 0)(M)' + sage: latex(T60M) + T^{(6, 0)}\left(M\right) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M + Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: Sym0123x45M._name + 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' + sage: latex(Sym0123x45M) + \mathrm{Sym}^{\{0,1,2,3\}}\left(M\right) \otimes \mathrm{Sym}^{\{4,5\}}\left(M\right) + sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M + Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1, 2), + with symmetry on the index positions (3, 4, 5) + on the Rank-3 free module M over the Integer Ring + sage: Sym012x345M._name + 'Sym^{0,1,2}(M)⊗Sym^{3,4,5}(M)' + sage: latex(Sym012x345M) + \mathrm{Sym}^{\{0,1,2\}}\left(M\right) \otimes \mathrm{Sym}^{\{3,4,5\}}\left(M\right) + sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M + Free module of type-(6,0) tensors + with Fully symmetric 6-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + sage: Sym012345M._name + 'Sym^6(M)' + sage: latex(Sym012345M) + \mathrm{Sym}^6\left(M\right) Canonical injections from submodules are coercions:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: e = M.basis('e') - sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) - sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: Sym0123x45M.has_coerce_map_from(Sym012345M) True sage: T60M.has_coerce_map_from(Sym0123x45M) @@ -58,6 +83,9 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: TestSuite(Sym0123x45M).run() + Traceback (most recent call last): + ... + The following tests failed: _test_not_implemented_methods, _test_zero """ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, @@ -66,7 +94,6 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, self._tensor_type = tuple(tensor_type) if ambient is None: ambient = fmodule.tensor_module(*tensor_type) - ambient_type = ambient.tensor_type() self._ambient_module = ambient self._sym = sym self._antisym = antisym @@ -74,10 +101,49 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, rank = len(list(basis_comp.non_redundant_index_generator())) # TODO: Good defaults for name, latex_name for more cases if name is None and fmodule._name is not None: - if isinstance(basis_comp, CompFullySym) and not ambient_type[1]: - name = 'Sym^' + str(ambient_type[0]) + '(' + fmodule._name + ')' - latex_name = r'\mathrm{Sym}^{' + str(ambient_type[0]) + r'}\left(' + \ - fmodule._latex_name + r'\right)' + if isinstance(basis_comp, CompFullySym): + sym = [tuple(range(tensor_type[0] + tensor_type[1]))] + antisym = [] + elif isinstance(basis_comp, CompFullyAntiSym): + sym = [] + antisym = [tuple(range(tensor_type[0] + tensor_type[1]))] + elif isinstance(basis_comp, CompWithSym): + sym = basis_comp._sym + antisym = basis_comp._antisym + else: + assert False, "full tensor module" + + def power_name(op, s, latex=False): + if s[0] < tensor_type[0]: + assert all(i < tensor_type[0] for i in s) + base = fmodule + full = tensor_type[0] + else: + base = fmodule.dual() + full = tensor_type[1] + if len(s) == full: + superscript = str(full) + else: + superscript = ','.join(str(i) for i in s) + if latex: + superscript = r'\{' + superscript + r'\}' + else: + superscript = '{' + superscript + '}' + if latex: + if len(superscript) != 1: + superscript = '{' + superscript + '}' + return r'\mathrm{' + op + '}^' + superscript + \ + r'\left(' + base._latex_name + r'\right)' + else: + return op + '^' + superscript + '(' + base._name + ')' + + name = unicode_otimes.join(itertools.chain( + (power_name('Sym', s, latex=False) for s in sym), + (power_name('ASym', s, latex=False) for s in antisym))) + latex_name = r' \otimes '.join(itertools.chain( + (power_name('Sym', s, latex=True) for s in sym), + (power_name('ASym', s, latex=True) for s in antisym))) + category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ FiniteRankFreeModule_abstract.__init__(self, fmodule._ring, rank, name=name, From e8ba50907ca202f62d086f3aff7aa7b6d20acee3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 09:19:47 -0700 Subject: [PATCH 246/454] TensorFreeSubmodule_comp: Fix default name, latex_name for indices without symmetries --- .../tensor/modules/tensor_free_submodule.py | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 1ced6855eb7..ec566577985 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -40,6 +40,14 @@ class TensorFreeSubmodule_comp(TensorFreeModule): 'T^(6, 0)(M)' sage: latex(T60M) T^{(6, 0)}\left(M\right) + sage: T40Sym45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((4, 5))); T40Sym45M + Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: T40Sym45M._name + 'T^{0,1,2,3}(M)⊗Sym^{4,5}(M)' + sage: latex(T40Sym45M) + T^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), with symmetry on the index positions (0, 1, 2, 3), @@ -48,7 +56,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym0123x45M._name 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(Sym0123x45M) - \mathrm{Sym}^{\{0,1,2,3\}}\left(M\right) \otimes \mathrm{Sym}^{\{4,5\}}\left(M\right) + \mathrm{Sym}^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), with symmetry on the index positions (0, 1, 2), @@ -57,7 +65,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym012x345M._name 'Sym^{0,1,2}(M)⊗Sym^{3,4,5}(M)' sage: latex(Sym012x345M) - \mathrm{Sym}^{\{0,1,2\}}\left(M\right) \otimes \mathrm{Sym}^{\{3,4,5\}}\left(M\right) + \mathrm{Sym}^{\{0,1,2\}}(M) \otimes \mathrm{Sym}^{\{3,4,5\}}(M) sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M Free module of type-(6,0) tensors with Fully symmetric 6-indices components w.r.t. (0, 1, 2) @@ -65,7 +73,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: Sym012345M._name 'Sym^6(M)' sage: latex(Sym012345M) - \mathrm{Sym}^6\left(M\right) + \mathrm{Sym}^6(M) Canonical injections from submodules are coercions:: @@ -99,19 +107,25 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, self._antisym = antisym basis_comp = self._basis_comp() rank = len(list(basis_comp.non_redundant_index_generator())) - # TODO: Good defaults for name, latex_name for more cases + if name is None and fmodule._name is not None: + all_indices = tuple(range(tensor_type[0] + tensor_type[1])) if isinstance(basis_comp, CompFullySym): - sym = [tuple(range(tensor_type[0] + tensor_type[1]))] + sym = [all_indices] antisym = [] elif isinstance(basis_comp, CompFullyAntiSym): sym = [] - antisym = [tuple(range(tensor_type[0] + tensor_type[1]))] + antisym = [all_indices] elif isinstance(basis_comp, CompWithSym): sym = basis_comp._sym antisym = basis_comp._antisym else: - assert False, "full tensor module" + sym = antisym = [] + nosym_0 = [i for i in range(tensor_type[0]) + if not any(i in s for s in sym) and not any(i in s for s in antisym)] + nosym_1 = [i for i in range(tensor_type[0], tensor_type[0] + tensor_type[1]) + if not any(i in s for s in sym) and not any(i in s for s in antisym)] + nosym = [s for s in [nosym_0, nosym_1] if s] def power_name(op, s, latex=False): if s[0] < tensor_type[0]: @@ -119,6 +133,7 @@ def power_name(op, s, latex=False): base = fmodule full = tensor_type[0] else: + assert all(i >= tensor_type[0] for i in s) base = fmodule.dual() full = tensor_type[1] if len(s) == full: @@ -132,17 +147,21 @@ def power_name(op, s, latex=False): if latex: if len(superscript) != 1: superscript = '{' + superscript + '}' - return r'\mathrm{' + op + '}^' + superscript + \ - r'\left(' + base._latex_name + r'\right)' + if len(base._latex_name) > 3: + return op + '^' + superscript + r'\left(' + base._latex_name + r'\right)' + else: + return op + '^' + superscript + '(' + base._name + ')' else: return op + '^' + superscript + '(' + base._name + ')' name = unicode_otimes.join(itertools.chain( + (power_name('T', s, latex=False) for s in nosym), (power_name('Sym', s, latex=False) for s in sym), (power_name('ASym', s, latex=False) for s in antisym))) latex_name = r' \otimes '.join(itertools.chain( - (power_name('Sym', s, latex=True) for s in sym), - (power_name('ASym', s, latex=True) for s in antisym))) + (power_name('T', s, latex=True) for s in nosym), + (power_name(r'\mathrm{Sym}', s, latex=True) for s in sym), + (power_name(r'\mathrm{ASym}', s, latex=True) for s in antisym))) category = fmodule.category().TensorProducts().FiniteDimensional().Subobjects().or_subcategory(category) # Skip TensorFreeModule.__init__ From 0277d80c8d35e467b8e4e0faaa2b0fcec3794916 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 09:32:33 -0700 Subject: [PATCH 247/454] TensorFreeSubmodule_comp: Add example for module with sym and antisym --- .../tensor/modules/tensor_free_submodule.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index ec566577985..39b70ec332e 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -89,15 +89,31 @@ class TensorFreeSubmodule_comp(TensorFreeModule): TESTS:: - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: TestSuite(Sym0123x45M).run() - Traceback (most recent call last): - ... - The following tests failed: _test_not_implemented_methods, _test_zero + sage: T = TensorFreeSubmodule_comp(M, (4, 4), sym=((0, 1)), antisym=((4, 5))); T + Free module of type-(4,4) tensors with 8-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1), + with antisymmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: T._name + 'T^{2,3}(M)⊗T^{6,7}(M*)⊗Sym^{0,1}(M)⊗ASym^{4,5}(M*)' + sage: latex(T) + T^{\{2,3\}}(M) \otimes T^{\{6,7\}}(M^*) \otimes \mathrm{Sym}^{\{0,1\}}(M) \otimes \mathrm{ASym}^{\{4,5\}}(M^*) """ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, sym=None, antisym=None, *, category=None, ambient=None): + r""" + TESTS:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: TestSuite(Sym0123x45M).run() + Traceback (most recent call last): + ... + The following tests failed: _test_not_implemented_methods, _test_zero + """ self._fmodule = fmodule self._tensor_type = tuple(tensor_type) if ambient is None: @@ -150,7 +166,7 @@ def power_name(op, s, latex=False): if len(base._latex_name) > 3: return op + '^' + superscript + r'\left(' + base._latex_name + r'\right)' else: - return op + '^' + superscript + '(' + base._name + ')' + return op + '^' + superscript + '(' + base._latex_name + ')' else: return op + '^' + superscript + '(' + base._name + ')' From 17137cf98bb41b2d1adeed0cf6d37f00117c890e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 10:24:20 -0700 Subject: [PATCH 248/454] FiniteRankFreeModule.tensor_module: Add parameters sym, antisym; add methods symmetric_power, dual_symmetric_power --- .../tensor/modules/finite_rank_free_module.py | 118 +++++++++++++++++- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 4390e687377..743cd8dae80 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1130,7 +1130,7 @@ def _Hom_(self, other, category=None): from .free_module_homset import FreeModuleHomset return FreeModuleHomset(self, other) - def tensor_module(self, k, l): + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the free module of all tensors of type `(k, l)` defined on ``self``. @@ -1141,6 +1141,18 @@ def tensor_module(self, k, l): type being `(k, l)` - ``l`` -- non-negative integer; the covariant rank, the tensor type being `(k, l)` + - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention ``position = 0`` for the first argument. For instance: + + * ``sym = (0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym = [(0,2), (1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments. + + - ``antisym`` -- (default: ``None``) antisymmetry or list of + antisymmetries among the arguments, with the same convention + as for ``sym`` OUTPUT: @@ -1170,21 +1182,115 @@ def tensor_module(self, k, l): sage: M.tensor_module(1,0) is M True + By using the arguments ``sym`` and ``antisym``, submodules of a full tensor + module can be constructed:: + + sage: T = M.tensor_module(4, 4, sym=((0, 1)), antisym=((4, 5))); T + Free module of type-(4,4) tensors with 8-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1), + with antisymmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: T._name + 'T^{2,3}(M)⊗T^{6,7}(M*)⊗Sym^{0,1}(M)⊗ASym^{4,5}(M*)' + sage: latex(T) + T^{\{2,3\}}(M) \otimes T^{\{6,7\}}(M^*) \otimes \mathrm{Sym}^{\{0,1\}}(M) \otimes \mathrm{ASym}^{\{4,5\}}(M^*) + See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` + and :class:`~sage.tensor.modules.tensor_free_module.TensorFreeSubmodule_comp` for more documentation. """ + if sym or antisym: + # TODO: Canonicalize sym, antisym, make hashable + key = (k, l, sym, antisym) + else: + key = (k, l) try: - return self._tensor_modules[(k,l)] + return self._tensor_modules[key] except KeyError: - if (k, l) == (1, 0): + if key == (1, 0): T = self + elif sym or antisym: + from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + T = TensorFreeSubmodule_comp(self, (k, l), sym=sym, antisym=antisym) else: from sage.tensor.modules.tensor_free_module import TensorFreeModule - T = TensorFreeModule(self, (k,l)) - self._tensor_modules[(k,l)] = T + T = TensorFreeModule(self, (k, l)) + self._tensor_modules[key] = T return T + def symmetric_power(self, p): + r""" + Return the `p`-th symmetric power of ``self``. + + EXAMPLES: + + Symmetric powers of a free `\ZZ`-module of rank 3:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: M.symmetric_power(0) + Free module of type-(0,0) tensors on the Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(1) # return the module itself + Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(1) is M + True + sage: M.symmetric_power(2) + Free module of type-(2,0) tensors + with Fully symmetric 2-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(2).an_element() + Type-(2,0) tensor on the Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(2).an_element().display() + e_0⊗e_0 + sage: M.symmetric_power(3) + Free module of type-(3,0) tensors + with Fully symmetric 3-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(3).an_element() + Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring + sage: M.symmetric_power(3).an_element().display() + e_0⊗e_0⊗e_0 + """ + if p <= 1: + return self.tensor_module(p, 0) + return self.tensor_module(p, 0, sym=(tuple(range(p)),)) + + def dual_symmetric_power(self, p): + r""" + Return the `p`-th symmetric power of the dual of ``self``. + + EXAMPLES: + + Symmetric powers of the dual of a free `\ZZ`-module of rank 3:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: M.dual_symmetric_power(0) + Free module of type-(0,0) tensors on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(1) # return the module itself + Free module of type-(0,1) tensors on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(2) + Free module of type-(0,2) tensors + with Fully symmetric 2-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(2).an_element() + Type-(0,2) tensor on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(2).an_element().display() + e^0⊗e^0 + sage: M.dual_symmetric_power(3) + Free module of type-(0,3) tensors + with Fully symmetric 3-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(3).an_element() + Type-(0,3) tensor on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(3).an_element().display() + e^0⊗e^0⊗e^0 + """ + if p <= 1: + return self.tensor_module(0, p) + return self.tensor_module(0, p, sym=(tuple(range(p)),)) + def exterior_power(self, p): r""" Return the `p`-th exterior power of ``self``. @@ -1216,7 +1322,7 @@ def exterior_power(self, p): EXAMPLES: - Exterior powers of the dual of a free `\ZZ`-module of rank 3:: + Exterior powers of a free `\ZZ`-module of rank 3:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') From c59c1f22768280d75383d258f73e860bf1b096f6 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Sat, 27 Aug 2022 12:39:49 -0700 Subject: [PATCH 249/454] double colon --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index fcef9536bf8..4e679b453df 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3390,7 +3390,7 @@ def curve(self): Curve from which Riemann surface is obtained. - EXAMPLE: + EXAMPLE:: sage: R. = QQ[] sage: C = Curve( y^3+x^3-1) From e2d2ea8265cfb915e55a20e89ea731eed2f16b11 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 10:59:51 -0700 Subject: [PATCH 250/454] src/sage/tensor/modules/finite_rank_free_module.py: isomorphism_with_fixed_basis with codomain=symmetric matrices --- .../tensor/modules/finite_rank_free_module.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 9da331cf3b3..2a84cee63a9 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -846,6 +846,36 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): [2 4 5] [3 5 6] + Same but explicitly in the subspace of symmetric bilinear forms:: + + sage: Sym2Vdual = V.dual_symmetric_power(2); Sym2Vdual + Free module of type-(0,2) tensors + with Fully symmetric 2-indices components w.r.t. (1, 2, 3) + on the 3-dimensional vector space over the Rational Field + sage: Sym2Vdual.is_submodule(T02) + True + sage: Sym2Vdual.rank() + 6 + sage: e_Sym2Vdual = Sym2Vdual.basis("e"); e_Sym2Vdual + Standard basis on the Free module of type-(0,2) tensors + with Fully symmetric 2-indices components w.r.t. (1, 2, 3) + on the 3-dimensional vector space over the Rational Field + induced by Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the Rational Field + sage: W_basis = [phi_e_T02(b) for b in e_Sym2Vdual]; W_basis + [ + [1 0 0] [0 1 0] [0 0 1] [0 0 0] [0 0 0] [0 0 0] + [0 0 0] [1 0 0] [0 0 0] [0 1 0] [0 0 1] [0 0 0] + [0 0 0], [0 0 0], [1 0 0], [0 0 0], [0 1 0], [0 0 1] + ] + sage: W = MatrixSpace(QQ, 3).submodule(W_basis); W + Free module generated by {0, 1, 2, 3, 4, 5} over Rational Field + sage: phi_e_Sym2Vdual = Sym2Vdual.isomorphism_with_fixed_basis(e_Sym2Vdual, codomain=W); phi_e_Sym2Vdual + Generic morphism: + From: Free module of type-(0,2) tensors + with Fully symmetric 2-indices components w.r.t. (1, 2, 3) + on the 3-dimensional vector space over the Rational Field + To: Free module generated by {0, 1, 2, 3, 4, 5} over Rational Field + Sending tensors to elements of the tensor square of :class:`CombinatorialFreeModule`:: sage: T20 = V.tensor_module(2, 0); T20 @@ -909,6 +939,8 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): else: # assume that the keys of the codomain should be used key_pairs = zip(codomain_basis.keys(), basis.keys()) + # Need them several times, can't keep as generators + key_pairs = tuple(key_pairs) def _isomorphism(x): r""" From 993b3437f31c224e2e901a6d320480318b808baa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 14:34:42 -0700 Subject: [PATCH 251/454] TensorFreeModule._element_constructor_: Pass parent=self to element_class --- src/sage/tensor/modules/tensor_free_module.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 11147a685ec..474b4e9a498 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -428,7 +428,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None, self._fmodule is endo.domain(): resu = self.element_class(self._fmodule, (1,1), name=endo._name, - latex_name=endo._latex_name) + latex_name=endo._latex_name, + parent=self) for basis, mat in endo._matrices.items(): resu.add_comp(basis[0])[:] = mat else: @@ -450,7 +451,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None, resu = self.element_class(self._fmodule, (p,0), name=tensor._name, latex_name=tensor._latex_name, - antisym=asym) + antisym=asym, + parent=self) for basis, comp in tensor._components.items(): resu._components[basis] = comp.copy() elif isinstance(comp, FreeModuleAltForm): @@ -467,7 +469,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None, asym = range(p) resu = self.element_class(self._fmodule, (0,p), name=form._name, latex_name=form._latex_name, - antisym=asym) + antisym=asym, + parent=self) for basis, comp in form._components.items(): resu._components[basis] = comp.copy() elif isinstance(comp, FreeModuleAutomorphism): @@ -478,7 +481,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None, raise TypeError("cannot coerce the {}".format(autom) + " to an element of {}".format(self)) resu = self.element_class(self._fmodule, (1,1), name=autom._name, - latex_name=autom._latex_name) + latex_name=autom._latex_name, + parent=self) for basis, comp in autom._components.items(): resu._components[basis] = comp.copy() elif isinstance(comp, FreeModuleTensor): @@ -489,14 +493,15 @@ def _element_constructor_(self, comp=[], basis=None, name=None, " to an element of {}".format(self)) resu = self.element_class(self._fmodule, self._tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + sym=sym, antisym=antisym, + parent=self) for basis, comp in tensor._components.items(): resu._components[basis] = comp.copy() else: # Standard construction: resu = self.element_class(self._fmodule, self._tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + sym=sym, antisym=antisym, parent=self) if comp: resu.set_comp(basis)[:] = comp return resu From 5b8ccdef2e7a8b9deee937e05f03a87244ef8141 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 14:38:08 -0700 Subject: [PATCH 252/454] src/sage/tensor/modules/tensor_free_module.py: Update doctest output --- src/sage/tensor/modules/tensor_free_module.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 474b4e9a498..25cfa23ca6f 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -736,7 +736,9 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T = M.tensor_module(1,1) sage: e_T = T.basis('e'); e_T - + Standard basis on the + Free module of type-(1,1) tensors on the Rank-3 free module M over the Integer Ring + induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: for a in e_T: a.display() e_0⊗e^0 e_0⊗e^1 @@ -751,7 +753,10 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M - + Standard basis on the + Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. (0, 1, 2) + on the Rank-3 free module M over the Integer Ring + induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: for a in e_Sym2M: a.display() e_0⊗e_0 e_0⊗e_1 + e_1⊗e_0 From b0812b12bcc0ba670a6963fd88310101fcde6f65 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 18:23:08 -0700 Subject: [PATCH 253/454] TensorFreeSubmoduleBasis_comp._element_constructor_: Full implementation --- .../tensor/modules/tensor_free_submodule.py | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 39b70ec332e..54eb5a92b0d 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -252,30 +252,57 @@ def is_coarsening_of(self_sym_list, other_sym_list): def _element_constructor_(self, comp=[], basis=None, name=None, latex_name=None, sym=None, antisym=None): + r""" + TESTS:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: t = Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2]); t.disp() + Traceback (most recent call last): + ... + ValueError: this tensor does not have the symmetries of + Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: t = Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1]); t.disp() + e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1 + sage: t.parent()._name + 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' + """ if sym is not None or antisym is not None: # Refuse to create a tensor with finer symmetries # than those defining the subspace if not self._is_symmetry_coarsening_of((sym, antisym), self._basis_comp()): - raise ValueError("cannot create a tensor with symmetries {} as an element of {}". - format((sym, antisym), self)) - try: - comp_parent = comp.parent() - except AttributeError: - comp_parent = None - if comp_parent == self.ambient_module(): - resu = comp - # comp is already a tensor. If its declared symmetries are coarser - # than the symmetries defining self, we can use it directly. - if self._is_symmetry_coarsening_of(resu, self._basis_comp()): - return resu + raise ValueError(f"cannot create a tensor with symmetries {sym=}, {antisym=} " + f"as an element of {self}") + if sym is None: sym = self._basis_comp()._sym if antisym is None: antisym = self._basis_comp()._antisym - resu = super()._element_constructor_(comp=comp, - basis=basis, name=name, - latex_name=latex_name, - sym=sym, antisym=antisym) + + symmetrized = resu = super()._element_constructor_(comp=comp, + basis=basis, name=name, + latex_name=latex_name, + sym=sym, antisym=antisym) + # TODO: Implement a fast symmetry check, either as part of the symmetrize method, + # or as a separate method + try: + for s in sym: + symmetrized = symmetrized.symmetrize(*s) + for s in antisym: + symmetrized = symmetrized.antisymmetrize(*s) + except TypeError: + # Averaging over the orbits of a tensor that does not have the required + # symmetries can lead to "TypeError: no conversion of this rational to integer" + raise ValueError(f"this tensor does not have the symmetries of {self}") + if symmetrized != resu: + raise ValueError(f"this tensor does not have the symmetries of {self}") + return resu def is_submodule(self, other): From 8cd3cc4517ab7a633f4e522052f103a61ccda708 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 19:01:08 -0700 Subject: [PATCH 254/454] TensorFreeSubmoduleBasis_comp._element_constructor_: Fix for zero; implement retract --- .../tensor/modules/tensor_free_submodule.py | 60 +++++++++++++++++-- .../modules/tensor_free_submodule_basis.py | 5 -- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 54eb5a92b0d..35333c9d92b 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -110,9 +110,6 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, sage: e = M.basis('e') sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: TestSuite(Sym0123x45M).run() - Traceback (most recent call last): - ... - The following tests failed: _test_not_implemented_methods, _test_zero """ self._fmodule = fmodule self._tensor_type = tuple(tensor_type) @@ -260,7 +257,7 @@ def _element_constructor_(self, comp=[], basis=None, name=None, sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: t = Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2]); t.disp() + sage: Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2]) Traceback (most recent call last): ... ValueError: this tensor does not have the symmetries of @@ -289,6 +286,10 @@ def _element_constructor_(self, comp=[], basis=None, name=None, basis=basis, name=name, latex_name=latex_name, sym=sym, antisym=antisym) + if not symmetrized._components: + # zero tensor - methods symmetrize, antisymmetrize are broken + return resu + # TODO: Implement a fast symmetry check, either as part of the symmetrize method, # or as a separate method try: @@ -367,3 +368,54 @@ def lift(self): on the Rank-3 free module M over the Integer Ring """ return self.module_morphism(function=lambda x: x, codomain=self.ambient()) + + @lazy_attribute + def retract(self): + r""" + The retract map from the ambient space. + + This is a partial map, which gives an error for elements not in the subspace. + + Calling this map on elements of the ambient space is the same as calling the + element constructor of ``self``. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: X = M.tensor_module(6, 0) + sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) + sage: e_Y = Y.basis('e') + sage: Y.retract + Generic morphism: + From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring + To: Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + + sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() + e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + sage: Y.retract(t) + Traceback (most recent call last): + ... + ValueError: this tensor does not have the symmetries of + Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + on the Rank-3 free module M over the Integer Ring + sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1] + sage: y = Y.retract(t); y + Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring + sage: y.disp() + e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1 + sage: y.parent()._name + 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' + + TESTS:: + + sage: all(Y.retract(u.lift()) == u for u in e_Y) + True + """ + return self.ambient().module_morphism(function=lambda x: self(x), codomain=self) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 856d58554fb..731242d2f50 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -129,8 +129,3 @@ def __getitem__(self, index): element = tensor_module([]) element.set_comp(base_module_basis)[index] = 1 return element - -# Todo: -# dual basis -# add test for dual -# lift/reduce/retract From ae2ff23068417561adc3c93b483b756fbc7d95c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 27 Aug 2022 19:21:06 -0700 Subject: [PATCH 255/454] TensorFreeSubmoduleBasis_comp.reduce: New; use it in _element_constructor_ --- .../tensor/modules/tensor_free_submodule.py | 90 ++++++++++++++++--- 1 file changed, 76 insertions(+), 14 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 35333c9d92b..4609c68aec9 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -282,27 +282,21 @@ def _element_constructor_(self, comp=[], basis=None, name=None, if antisym is None: antisym = self._basis_comp()._antisym - symmetrized = resu = super()._element_constructor_(comp=comp, - basis=basis, name=name, - latex_name=latex_name, - sym=sym, antisym=antisym) - if not symmetrized._components: - # zero tensor - methods symmetrize, antisymmetrize are broken + resu = super()._element_constructor_(comp=comp, + basis=basis, name=name, + latex_name=latex_name, + sym=sym, antisym=antisym) + if not resu._components: + # fast path for zero tensor return resu - # TODO: Implement a fast symmetry check, either as part of the symmetrize method, - # or as a separate method try: - for s in sym: - symmetrized = symmetrized.symmetrize(*s) - for s in antisym: - symmetrized = symmetrized.antisymmetrize(*s) + if self.reduce(resu): + raise ValueError(f"this tensor does not have the symmetries of {self}") except TypeError: # Averaging over the orbits of a tensor that does not have the required # symmetries can lead to "TypeError: no conversion of this rational to integer" raise ValueError(f"this tensor does not have the symmetries of {self}") - if symmetrized != resu: - raise ValueError(f"this tensor does not have the symmetries of {self}") return resu @@ -369,6 +363,74 @@ def lift(self): """ return self.module_morphism(function=lambda x: x, codomain=self.ambient()) + @lazy_attribute + def reduce(self): + r""" + The reduce map. + + This map reduces elements of the ambient space modulo this + submodule. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(QQ, 3, name='M') + sage: e = M.basis('e') + sage: X = M.tensor_module(6, 0) + sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) + sage: Y.reduce + Generic endomorphism of Free module of type-(6,0) tensors on the 3-dimensional vector space M over the Rational Field + sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() + e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + sage: r = Y.reduce(t); r + Type-(6,0) tensor on the 3-dimensional vector space M over the Rational Field + sage: r.disp() + 1/2 e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 - 1/2 e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1 + sage: r.parent()._name + 'T^(6, 0)(M)' + + If the base ring is not a field, this may fail:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: X = M.tensor_module(6, 0) + sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) + sage: Y.reduce + Generic endomorphism of Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring + sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() + e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + sage: Y.reduce(t) + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: X = M.tensor_module(6, 0) + sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) + sage: all(Y.reduce(u.lift()) == 0 for u in Y.basis('e')) + True + """ + sym = self._basis_comp()._sym + antisym = self._basis_comp()._antisym + + def _reduce_element(x): + if not x._components: + # zero tensor - methods symmetrize, antisymmetrize are broken + return x + # TODO: Implement a fast symmetry check, either as part of the symmetrize/antisymmetrize methods, + # or as a separate method + symmetrized = x + for s in sym: + symmetrized = symmetrized.symmetrize(*s) + for s in antisym: + symmetrized = symmetrized.antisymmetrize(*s) + return x - symmetrized + + return self.ambient().module_morphism(function=_reduce_element, codomain=self.ambient()) + @lazy_attribute def retract(self): r""" From 2e7e636881fae9a77eb394c74124cae3410f69af Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sun, 28 Aug 2022 12:24:41 +0800 Subject: [PATCH 256/454] remove incorrect call to .lift() --- src/sage/rings/polynomial/polynomial_quotient_ring_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index c639214e5d6..999665948d2 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -736,5 +736,5 @@ def rational_reconstruction(self, *args, **kwargs): m = self.parent().modulus() R = m.parent() f = R(self._polynomial) - return f.lift().rational_reconstruction(m, *args, **kwargs) + return f.rational_reconstruction(m, *args, **kwargs) From 888b4fa98d8ffac84d1db90da68712775a4b700f Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sun, 28 Aug 2022 12:25:10 +0800 Subject: [PATCH 257/454] fix terminology in docstring --- src/sage/rings/polynomial/polynomial_quotient_ring_element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 999665948d2..803e78f6e13 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -718,7 +718,7 @@ def trace(self): def rational_reconstruction(self, *args, **kwargs): r""" Compute a rational reconstruction of this polynomial quotient - ring element to its parent. + ring element to its cover ring. This method is a thin convenience wrapper around :meth:`Polynomial.rational_reconstruction`. From b7631554fcf629dba6241621c7712cb0325f337a Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sun, 28 Aug 2022 17:10:01 +0900 Subject: [PATCH 258/454] Refactor with ConstructorBaseclassMetaclass --- src/sage/homology/free_resolution.py | 12 +++++++---- .../misc/constructor_baseclass_metaclass.pyx | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 src/sage/misc/constructor_baseclass_metaclass.pyx diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index a6622d8cb8d..fb1fbb5a397 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -70,8 +70,8 @@ from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method +from sage.misc.constructor_baseclass_metaclass import ConstructorBaseclassMetaclass from sage.structure.sage_object import SageObject -from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.structure.element import Matrix from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.integral_domains import IntegralDomains @@ -83,7 +83,7 @@ from copy import copy -def FreeResolution(module, degrees=None, shifts=None, name='S', graded=False, **kwds): +def free_resolution_constructor(module, degrees=None, shifts=None, name='S', graded=False, **kwds): """ Constructor. @@ -95,6 +95,8 @@ def FreeResolution(module, degrees=None, shifts=None, name='S', graded=False, ** sage: r = FreeResolution(m, name='S') sage: type(r) + sage: isinstance(r, FreeResolution) + True sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) sage: r = FreeResolution(I) @@ -165,10 +167,12 @@ def FreeResolution(module, degrees=None, shifts=None, name='S', graded=False, ** return FiniteFreeResolution_free_module(module, name=name, **kwds) -class FreeResolution_abs(SageObject): +class FreeResolution(SageObject, metaclass=ConstructorBaseclassMetaclass): """ Abstract base class for free resolutions. """ + __constructor__ = free_resolution_constructor + def __init__(self, module, name='S', **kwds): """ Initialize. @@ -289,7 +293,7 @@ def target(self): return self.differential(0).codomain() -class FiniteFreeResolution(FreeResolution_abs): +class FiniteFreeResolution(FreeResolution): r""" Finite free resolutions. diff --git a/src/sage/misc/constructor_baseclass_metaclass.pyx b/src/sage/misc/constructor_baseclass_metaclass.pyx new file mode 100644 index 00000000000..fc3ca66f641 --- /dev/null +++ b/src/sage/misc/constructor_baseclass_metaclass.pyx @@ -0,0 +1,21 @@ +from sage.misc.classcall_metaclass cimport ClasscallMetaclass + + +cdef class ConstructorBaseclassMetaclass(ClasscallMetaclass): + + def __cinit__(self, *args, **opts): + r""" + TESTS:: + """ + if '__constructor__' in self.__dict__: + def constructor(cls, *a, **o): + return self.__constructor__(*a, **o) + self.classcall = constructor + else: + self.classcall = None + + self.classcontains = getattr(self, "__classcontains__", None) + self.classget = getattr(self, "__classget__", None) + + + From fa6162ee88a19bfdfe4723651098c0e61919512e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 09:16:24 -0700 Subject: [PATCH 259/454] src/doc/en/reference/tensor_free_modules: Add tensor_free_submodule, tensor_free_submodule_basis --- src/doc/en/reference/tensor_free_modules/tensors.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/en/reference/tensor_free_modules/tensors.rst b/src/doc/en/reference/tensor_free_modules/tensors.rst index be87fc68ad1..434ea734191 100644 --- a/src/doc/en/reference/tensor_free_modules/tensors.rst +++ b/src/doc/en/reference/tensor_free_modules/tensors.rst @@ -6,6 +6,10 @@ Tensors sage/tensor/modules/tensor_free_module + sage/tensor/modules/tensor_free_submodule + sage/tensor/modules/free_module_tensor sage/tensor/modules/tensor_with_indices + + sage/tensor/modules/tensor_free_submodule_basis From 680e3f4edb33d69503add2fd81291b395a6e74e3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 09:41:16 -0700 Subject: [PATCH 260/454] src/sage/tensor/modules/comp.py (Components._repr_): Factor out _repr_symmetry --- src/sage/tensor/modules/comp.py | 87 +++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 04f14f782f0..38ee9c58601 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -525,15 +525,48 @@ def _repr_(self): sage: c._repr_() '2-indices components w.r.t. [1, 2, 3]' + sage: from sage.tensor.modules.comp import CompWithSym + sage: CompWithSym(ZZ, [1,2,3], 4, sym=(0,1)) + 4-indices components w.r.t. [1, 2, 3], + with symmetry on the index positions (0, 1) + sage: CompWithSym(ZZ, [1,2,3], 4, sym=(0,1), antisym=(2,3)) + 4-indices components w.r.t. [1, 2, 3], + with symmetry on the index positions (0, 1), + with antisymmetry on the index positions (2, 3) + + sage: from sage.tensor.modules.comp import CompFullySym + sage: CompFullySym(ZZ, (1,2,3), 4) + Fully symmetric 4-indices components w.r.t. (1, 2, 3) + + sage: from sage.tensor.modules.comp import CompFullyAntiSym + sage: CompFullyAntiSym(ZZ, (1,2,3), 4) + Fully antisymmetric 4-indices components w.r.t. (1, 2, 3) """ - description = str(self._nid) + prefix, suffix = self._repr_symmetry() + description = prefix + description += str(self._nid) if self._nid == 1: description += "-index" else: description += "-indices" description += " components w.r.t. " + str(self._frame) + description += suffix return description + def _repr_symmetry(self): + r""" + Return a prefix and a suffix string describing the symmetry of ``self``. + + EXAMPLES:: + + sage: from sage.tensor.modules.comp import Components + sage: c = Components(ZZ, [1,2,3], 2) + sage: c._repr_symmetry() + ('', '') + + """ + return "", "" + def _new_instance(self): r""" Creates a :class:`Components` instance of the same number of indices @@ -3037,35 +3070,29 @@ def __init__(self, ring, frame, nb_indices, start_index=0, raise IndexError("incompatible lists of symmetries: the same " + "index position appears more then once") - def _repr_(self): + def _repr_symmetry(self): r""" - Return a string representation of ``self``. + Return a prefix and a suffix string describing the symmetry of ``self``. EXAMPLES:: sage: from sage.tensor.modules.comp import CompWithSym - sage: CompWithSym(ZZ, [1,2,3], 4, sym=(0,1)) - 4-indices components w.r.t. [1, 2, 3], - with symmetry on the index positions (0, 1) - sage: CompWithSym(ZZ, [1,2,3], 4, sym=(0,1), antisym=(2,3)) - 4-indices components w.r.t. [1, 2, 3], - with symmetry on the index positions (0, 1), - with antisymmetry on the index positions (2, 3) - + sage: cp = CompWithSym(QQ, [1,2,3], 4, sym=(0,1)) + sage: cp._repr_symmetry() + ('', ', with symmetry on the index positions (0, 1)') + sage: cp = CompWithSym(QQ, [1,2,3], 4, sym=((0,1),), antisym=((2,3),)) + sage: cp._repr_symmetry() + ('', + ', with symmetry on the index positions (0, 1), with antisymmetry on the index positions (2, 3)') """ - description = str(self._nid) - if self._nid == 1: - description += "-index" - else: - description += "-indices" - description += " components w.r.t. " + str(self._frame) + description = "" for isym in self._sym: description += ", with symmetry on the index positions " + \ str(tuple(isym)) for isym in self._antisym: description += ", with antisymmetry on the index positions " + \ str(tuple(isym)) - return description + return "", description def _new_instance(self): r""" @@ -4731,19 +4758,19 @@ def __init__(self, ring, frame, nb_indices, start_index=0, CompWithSym.__init__(self, ring, frame, nb_indices, start_index, output_formatter, sym=range(nb_indices)) - def _repr_(self): + def _repr_symmetry(self): r""" - Return a string representation of ``self``. + Return a prefix and a suffix string describing the symmetry of ``self``. EXAMPLES:: sage: from sage.tensor.modules.comp import CompFullySym - sage: CompFullySym(ZZ, (1,2,3), 4) - Fully symmetric 4-indices components w.r.t. (1, 2, 3) + sage: c = CompFullySym(ZZ, (1,2,3), 4) + sage: c._repr_symmetry() + ('Fully symmetric ', '') """ - return "Fully symmetric " + str(self._nid) + "-indices" + \ - " components w.r.t. " + str(self._frame) + return "Fully symmetric ", "" def _new_instance(self): r""" @@ -5190,19 +5217,19 @@ def __init__(self, ring, frame, nb_indices, start_index=0, CompWithSym.__init__(self, ring, frame, nb_indices, start_index, output_formatter, antisym=range(nb_indices)) - def _repr_(self): + def _repr_symmetry(self): r""" - Return a string representation of ``self``. + Return a prefix and a suffix string describing the symmetry of ``self``. EXAMPLES:: sage: from sage.tensor.modules.comp import CompFullyAntiSym - sage: CompFullyAntiSym(ZZ, (1,2,3), 4) - Fully antisymmetric 4-indices components w.r.t. (1, 2, 3) + sage: c = CompFullyAntiSym(ZZ, (1,2,3), 4) + sage: c._repr_symmetry() + ('Fully antisymmetric ', '') """ - return "Fully antisymmetric " + str(self._nid) + "-indices" + \ - " components w.r.t. " + str(self._frame) + return "Fully antisymmetric ", "" def _new_instance(self): r""" From 548620d491ea0d51453af69d480edc9762cb56de Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 09:58:34 -0700 Subject: [PATCH 261/454] TensorFreeSubmodule_comp._repr_: Use Components._repr_symmetry, update doctest outputs --- .../tensor/modules/finite_rank_free_module.py | 15 ++--- .../tensor/modules/tensor_free_submodule.py | 55 ++++++++----------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 743cd8dae80..08b3873dfbc 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1186,10 +1186,9 @@ def tensor_module(self, k, l, *, sym=None, antisym=None): module can be constructed:: sage: T = M.tensor_module(4, 4, sym=((0, 1)), antisym=((4, 5))); T - Free module of type-(4,4) tensors with 8-indices components w.r.t. (0, 1, 2), + Free module of type-(4,4) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1), with antisymmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: T._name 'T^{2,3}(M)⊗T^{6,7}(M*)⊗Sym^{0,1}(M)⊗ASym^{4,5}(M*)' sage: latex(T) @@ -1236,16 +1235,14 @@ def symmetric_power(self, p): sage: M.symmetric_power(1) is M True sage: M.symmetric_power(2) - Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. (0, 1, 2) + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring sage: M.symmetric_power(2).an_element() Type-(2,0) tensor on the Rank-3 free module M over the Integer Ring sage: M.symmetric_power(2).an_element().display() e_0⊗e_0 sage: M.symmetric_power(3) - Free module of type-(3,0) tensors - with Fully symmetric 3-indices components w.r.t. (0, 1, 2) + Free module of fully symmetric type-(3,0) tensors on the Rank-3 free module M over the Integer Ring sage: M.symmetric_power(3).an_element() Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring @@ -1271,16 +1268,14 @@ def dual_symmetric_power(self, p): sage: M.dual_symmetric_power(1) # return the module itself Free module of type-(0,1) tensors on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2) - Free module of type-(0,2) tensors - with Fully symmetric 2-indices components w.r.t. (0, 1, 2) + Free module of fully symmetric type-(0,2) tensors on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2).an_element() Type-(0,2) tensor on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2).an_element().display() e^0⊗e^0 sage: M.dual_symmetric_power(3) - Free module of type-(0,3) tensors - with Fully symmetric 3-indices components w.r.t. (0, 1, 2) + Free module of fully symmetric type-(0,3) tensors on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(3).an_element() Type-(0,3) tensor on the Rank-3 free module M over the Integer Ring diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 4609c68aec9..df0cee25764 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -41,34 +41,30 @@ class TensorFreeSubmodule_comp(TensorFreeModule): sage: latex(T60M) T^{(6, 0)}\left(M\right) sage: T40Sym45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((4, 5))); T40Sym45M - Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: T40Sym45M._name 'T^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(T40Sym45M) T^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M - Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: Sym0123x45M._name 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(Sym0123x45M) \mathrm{Sym}^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M - Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2), with symmetry on the index positions (3, 4, 5) - on the Rank-3 free module M over the Integer Ring sage: Sym012x345M._name 'Sym^{0,1,2}(M)⊗Sym^{3,4,5}(M)' sage: latex(Sym012x345M) \mathrm{Sym}^{\{0,1,2\}}(M) \otimes \mathrm{Sym}^{\{3,4,5\}}(M) sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M - Free module of type-(6,0) tensors - with Fully symmetric 6-indices components w.r.t. (0, 1, 2) + Free module of fully symmetric type-(6,0) tensors on the Rank-3 free module M over the Integer Ring sage: Sym012345M._name 'Sym^6(M)' @@ -90,10 +86,9 @@ class TensorFreeSubmodule_comp(TensorFreeModule): TESTS:: sage: T = TensorFreeSubmodule_comp(M, (4, 4), sym=((0, 1)), antisym=((4, 5))); T - Free module of type-(4,4) tensors with 8-indices components w.r.t. (0, 1, 2), + Free module of type-(4,4) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1), with antisymmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: T._name 'T^{2,3}(M)⊗T^{6,7}(M*)⊗Sym^{0,1}(M)⊗ASym^{4,5}(M*)' sage: latex(T) @@ -198,13 +193,13 @@ def _repr_(self): sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M - Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. (0, 1, 2) - on the Rank-3 free module M over the Integer Ring + Free module of fully symmetric type-(2,0) tensors + on the Rank-3 free module M over the Integer Ring """ - return "Free module of type-({},{}) tensors with {} on the {}".format( - self._tensor_type[0], self._tensor_type[1], self._basis_comp(), self._fmodule) + prefix, suffix = self._basis_comp()._repr_symmetry() + return "Free module of {}type-({},{}) tensors on the {}{}".format( + prefix.lower(), self._tensor_type[0], self._tensor_type[1], self._fmodule, suffix) def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): self_tensor_type = self.tensor_type() @@ -261,10 +256,9 @@ def _element_constructor_(self, comp=[], basis=None, name=None, Traceback (most recent call last): ... ValueError: this tensor does not have the symmetries of - Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: t = Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1]); t.disp() e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1 sage: t.parent()._name @@ -353,13 +347,10 @@ def lift(self): sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: Sym0123x45M.lift Generic morphism: - From: Free module of type-(6,0) tensors - with 6-indices components w.r.t. (0, 1, 2), - with symmetry on the index positions (0, 1, 2, 3), - with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring - To: Free module of type-(6,0) tensors - on the Rank-3 free module M over the Integer Ring + From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + To: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring """ return self.module_morphism(function=lambda x: x, codomain=self.ambient()) @@ -379,7 +370,8 @@ def reduce(self): sage: X = M.tensor_module(6, 0) sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: Y.reduce - Generic endomorphism of Free module of type-(6,0) tensors on the 3-dimensional vector space M over the Rational Field + Generic endomorphism of + Free module of type-(6,0) tensors on the 3-dimensional vector space M over the Rational Field sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 sage: r = Y.reduce(t); r @@ -396,7 +388,8 @@ def reduce(self): sage: X = M.tensor_module(6, 0) sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: Y.reduce - Generic endomorphism of Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring + Generic endomorphism of + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 sage: Y.reduce(t) @@ -452,10 +445,9 @@ def retract(self): sage: Y.retract Generic morphism: From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring - To: Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), - with symmetry on the index positions (0, 1, 2, 3), - with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring + To: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp() e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 @@ -463,10 +455,9 @@ def retract(self): Traceback (most recent call last): ... ValueError: this tensor does not have the symmetries of - Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2), + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) - on the Rank-3 free module M over the Integer Ring sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1] sage: y = Y.retract(t); y Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring From 2cc42fb294ebd8c7f24b3260d82c55f3338b7b1a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:00:49 -0700 Subject: [PATCH 262/454] src/sage/tensor/modules/tensor_free_submodule_basis.py: Update doctest output --- src/sage/tensor/modules/tensor_free_submodule_basis.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 731242d2f50..1474bcbf3e2 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -113,9 +113,7 @@ def __getitem__(self, index): sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M - Free module of type-(2,0) tensors - with Fully symmetric 2-indices components w.r.t. (0, 1, 2) - on the Rank-3 free module M over the Integer Ring + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring sage: eSym2M = Sym2M.basis('e') sage: eSym2M[1, 1].display() e_1⊗e_1 From 02cce265a8a17ed35a72e1bf556c19639c716c73 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:03:02 -0700 Subject: [PATCH 263/454] src/sage/tensor/modules/tensor_free_submodule_basis.py: Fix pyflakes unused warnings --- src/sage/tensor/modules/tensor_free_submodule_basis.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 1474bcbf3e2..88875a4efa7 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -3,7 +3,7 @@ """ #****************************************************************************** -# Copyright (C) 2020 Matthias Koeppe +# Copyright (C) 2020-2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -12,7 +12,7 @@ #****************************************************************************** from sage.tensor.modules.free_module_basis import Basis_abstract -from sage.tensor.modules.comp import Components + class TensorFreeSubmoduleBasis_comp(Basis_abstract): r""" @@ -122,7 +122,6 @@ def __getitem__(self, index): """ tensor_module = self._fmodule - base_module = tensor_module.base_module() base_module_basis = self._base_module_basis element = tensor_module([]) element.set_comp(base_module_basis)[index] = 1 From bff49e5ba567a7c73cf6cc89885c3252ec20492d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:10:19 -0700 Subject: [PATCH 264/454] src/sage/tensor/modules/tensor_free_module.py: Update doctest output --- src/sage/tensor/modules/tensor_free_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 25cfa23ca6f..e3dae4c71b4 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -754,8 +754,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M Standard basis on the - Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. (0, 1, 2) - on the Rank-3 free module M over the Integer Ring + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring sage: for a in e_Sym2M: a.display() e_0⊗e_0 From 89a5893fdaf7c8d26edcbd70815ecea53ef472ff Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:20:01 -0700 Subject: [PATCH 265/454] src/sage/tensor/modules/tensor_free_[sub]module.py (_basis_comp): Add docstrings --- src/sage/tensor/modules/tensor_free_module.py | 17 ++++++++++++++++- .../tensor/modules/tensor_free_submodule.py | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index e3dae4c71b4..e36bbe82071 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -770,7 +770,22 @@ def basis(self, symbol, latex_symbol=None, from_family=None, @cached_method def _basis_comp(self): - # Data for TensorFreeSubmoduleBasis_comp + r""" + Return an instance of :class:`~sage.tensor.modules.comp.Components`. + + This implementation returns an instance without symmetry. + + The subclass :class:`~sage.tensor.modules.tensor_free_submodule.TensorFreeSubmodule_comp` + overrides this method to encode the prescribed symmetry of the submodule. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T = M.tensor_module(1,1) + sage: c = T._basis_comp(); c + 2-indices components w.r.t. (0, 1, 2) + + """ frame = tuple(self.base_module().irange()) tensor = self.ambient()() return tensor._new_comp(frame) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index df0cee25764..29e99dc6a94 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -179,6 +179,21 @@ def power_name(op, s, latex=False): @cached_method def _basis_comp(self): + r""" + Return an instance of :class:`~sage.tensor.modules.comp.Components`. + + It encodes the prescribed symmetry of ``self``. + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring + sage: c = Sym2M._basis_comp(); c + Fully symmetric 2-indices components w.r.t. (0, 1, 2) + + """ frame = tuple(self.base_module().irange()) # Need to call _element_constructor_ explicitly, or the passed arguments are dropped tensor = self.ambient()._element_constructor_(sym=self._sym, antisym=self._antisym) From c8d987723dfca75236c0a45654c86e6fb40f92a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:36:45 -0700 Subject: [PATCH 266/454] TensorFreeSubmodule_comp._is_symmetry_coarsening_of: Add docstring --- .../tensor/modules/tensor_free_submodule.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 29e99dc6a94..696af271272 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -217,6 +217,33 @@ def _repr_(self): prefix.lower(), self._tensor_type[0], self._tensor_type[1], self._fmodule, suffix) def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): + r""" + Return whether ``coarser_comp`` has coarser symmetry than ``finer_comp``. + + INPUT: + + - ``coarser_comp``, ``finer_comp``: :class:`~sage.tensor.modules.comp.Components` + + EXAMPLES:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: com0123x45M = Sym0123x45M.an_element()._components[e]; com0123x45M + 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: com012x345M = Sym012x345M.an_element()._components[e]; com012x345M + 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: com012345M = Sym012345M.an_element()._components[e]; com012345M + 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + sage: Sym0123x45M._is_symmetry_coarsening_of(com0123x45M, com012x345M) + True + sage: Sym0123x45M._is_symmetry_coarsening_of(com012345M, com012x345M) + True + """ self_tensor_type = self.tensor_type() def sym_antisym(comp): From e31bdacb80a880a557448fe712aaeba4232c3693 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 10:40:18 -0700 Subject: [PATCH 267/454] TensorFreeModule._an_element_: Pass parent to element class --- src/sage/tensor/modules/tensor_free_module.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index e36bbe82071..8984e01396e 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -552,8 +552,18 @@ def _an_element_(self): sage: M.tensor_module(2,3)._an_element_().display() 1/2 e_0⊗e_0⊗e^0⊗e^0⊗e^0 + TESTS:: + + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: T60M = M.tensor_module(6, 0) + sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: t = Sym0123x45M._an_element_() + sage: t.parent() is Sym0123x45M + True """ - resu = self.element_class(self._fmodule, self._tensor_type) + resu = self.element_class(self._fmodule, self._tensor_type, parent=self) # Make sure that the base module has a default basis self._fmodule.an_element() sindex = self._fmodule._sindex From e7d5e77161b2b9b8d8a15d10e702c559bc60c19b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 11:14:13 -0700 Subject: [PATCH 268/454] TensorFreeModule._an_element_: Use element constructor --- src/sage/tensor/modules/tensor_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 8984e01396e..d4f000d7be6 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -563,7 +563,7 @@ def _an_element_(self): sage: t.parent() is Sym0123x45M True """ - resu = self.element_class(self._fmodule, self._tensor_type, parent=self) + resu = self([]) # Make sure that the base module has a default basis self._fmodule.an_element() sindex = self._fmodule._sindex From 18d3dbee5d5d2703c2a335ea5627735fe2116a25 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 11:15:41 -0700 Subject: [PATCH 269/454] TensorFreeSubmodule_comp._is_symmetry_coarsening_of: Update example --- .../tensor/modules/tensor_free_submodule.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 696af271272..dc94ace3343 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -231,16 +231,29 @@ def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: com0123x45M = Sym0123x45M.an_element()._components[e]; com0123x45M - 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + sage: ten0123x45M = Sym0123x45M.an_element(); ten0123x45M + Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring + sage: ten0123x45M.parent() + Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) + sage: com0123x45M = ten0123x45M._components[e]; com0123x45M + 6-indices components w.r.t. Basis (e_0,e_1,e_2) + on the Rank-3 free module M over the Integer Ring, + with symmetry on the index positions (0, 1, 2, 3), + with symmetry on the index positions (4, 5) sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) sage: com012x345M = Sym012x345M.an_element()._components[e]; com012x345M - 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + 6-indices components w.r.t. Basis (e_0,e_1,e_2) + on the Rank-3 free module M over the Integer Ring, + with symmetry on the index positions (0, 1, 2), + with symmetry on the index positions (3, 4, 5) sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: com012345M = Sym012345M.an_element()._components[e]; com012345M - 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + Fully symmetric 6-indices components w.r.t. Basis (e_0,e_1,e_2) + on the Rank-3 free module M over the Integer Ring sage: Sym0123x45M._is_symmetry_coarsening_of(com0123x45M, com012x345M) - True + False sage: Sym0123x45M._is_symmetry_coarsening_of(com012345M, com012x345M) True """ From 3f1c33d718a59e7fd6b0c06e032ca43fff9ecdb2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 11:17:31 -0700 Subject: [PATCH 270/454] TensorFreeSubmoduleBasis_comp.__init__: Add doctest --- src/sage/tensor/modules/tensor_free_submodule_basis.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 88875a4efa7..ae00f98971b 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -38,6 +38,15 @@ class TensorFreeSubmoduleBasis_comp(Basis_abstract): def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, latex_indices=None, symbol_dual=None, latex_symbol_dual=None): + r""" + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: T11 = M.tensor_module(1,1) + sage: e_T11 = T11.basis('e') + sage: TestSuite(e_T11).run() + """ base_module = tensor_module.base_module() base_module_basis = base_module.basis(symbol, latex_symbol, indices, latex_indices, symbol_dual, latex_symbol_dual) From 9378a78970b4202074d44e21f42550a9a597337f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 11:22:14 -0700 Subject: [PATCH 271/454] src/sage/tensor/modules/finite_rank_free_module.py: Update doctest output --- src/sage/tensor/modules/finite_rank_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 08b3873dfbc..b9a3e543eb1 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1271,7 +1271,7 @@ def dual_symmetric_power(self, p): Free module of fully symmetric type-(0,2) tensors on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2).an_element() - Type-(0,2) tensor on the Rank-3 free module M over the Integer Ring + Symmetric bilinear form on the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2).an_element().display() e^0⊗e^0 sage: M.dual_symmetric_power(3) From f49622335a78c1ae3852d2662ae39145e786b426 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 12:18:24 -0700 Subject: [PATCH 272/454] FiniteRankFreeModule_abstract._test_isomorphism_with_fixed_basis: Do nothing if there is no basis method --- src/sage/tensor/modules/finite_rank_free_module.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index ad444876994..7d25a569e96 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -961,7 +961,10 @@ def _test_isomorphism_with_fixed_basis(self, **options): sage: M._test_isomorphism_with_fixed_basis() """ tester = self._tester(**options) - basis = self.basis('test') + try: + basis = self.basis('test') + except AttributeError: + return morphism = self.isomorphism_with_fixed_basis(basis) tester.assertEqual(morphism.codomain().rank(), self.rank()) From d341422efb34d3f5ad759897ad15362263b96f33 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 12:23:30 -0700 Subject: [PATCH 273/454] src/sage/tensor/modules/finite_rank_free_module.py: Update doctest output --- .../tensor/modules/finite_rank_free_module.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 7d25a569e96..d746f922309 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -849,17 +849,14 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): Same but explicitly in the subspace of symmetric bilinear forms:: sage: Sym2Vdual = V.dual_symmetric_power(2); Sym2Vdual - Free module of type-(0,2) tensors - with Fully symmetric 2-indices components w.r.t. (1, 2, 3) - on the 3-dimensional vector space over the Rational Field + Free module of fully symmetric type-(0,2) tensors on the 3-dimensional vector space over the Rational Field sage: Sym2Vdual.is_submodule(T02) True sage: Sym2Vdual.rank() 6 sage: e_Sym2Vdual = Sym2Vdual.basis("e"); e_Sym2Vdual - Standard basis on the Free module of type-(0,2) tensors - with Fully symmetric 2-indices components w.r.t. (1, 2, 3) - on the 3-dimensional vector space over the Rational Field + Standard basis on the + Free module of fully symmetric type-(0,2) tensors on the 3-dimensional vector space over the Rational Field induced by Basis (e_1,e_2,e_3) on the 3-dimensional vector space over the Rational Field sage: W_basis = [phi_e_T02(b) for b in e_Sym2Vdual]; W_basis [ @@ -871,10 +868,8 @@ def isomorphism_with_fixed_basis(self, basis=None, codomain=None): Free module generated by {0, 1, 2, 3, 4, 5} over Rational Field sage: phi_e_Sym2Vdual = Sym2Vdual.isomorphism_with_fixed_basis(e_Sym2Vdual, codomain=W); phi_e_Sym2Vdual Generic morphism: - From: Free module of type-(0,2) tensors - with Fully symmetric 2-indices components w.r.t. (1, 2, 3) - on the 3-dimensional vector space over the Rational Field - To: Free module generated by {0, 1, 2, 3, 4, 5} over Rational Field + From: Free module of fully symmetric type-(0,2) tensors on the 3-dimensional vector space over the Rational Field + To: Free module generated by {0, 1, 2, 3, 4, 5} over Rational Field Sending tensors to elements of the tensor square of :class:`CombinatorialFreeModule`:: From c42118a437ccfd7f7975869925ef5b56d503c1bf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 14:06:48 -0700 Subject: [PATCH 274/454] src/sage/manifolds/differentiable/tangent_space.py: Add example: isomorphism with inner product space --- .../manifolds/differentiable/tangent_space.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/manifolds/differentiable/tangent_space.py b/src/sage/manifolds/differentiable/tangent_space.py index 0825f867e5e..f2a5afa73b0 100644 --- a/src/sage/manifolds/differentiable/tangent_space.py +++ b/src/sage/manifolds/differentiable/tangent_space.py @@ -162,6 +162,58 @@ class TangentSpace(FiniteRankFreeModule): sage: p2 == p True + An isomorphism of the tangent space with an inner product space with distinguished basis:: + + sage: g = M.metric('g') + sage: g[:] = ((1, 0), (0, 1)) + sage: Q_Tp_xy = g[c_xy.frame(),:](*p.coordinates(c_xy)); Q_Tp_xy + [1 0] + [0 1] + sage: W_Tp_xy = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_xy) + sage: Tp.bases()[0] + Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the 2-dimensional differentiable manifold M + sage: phi_Tp_xy = Tp.isomorphism_with_fixed_basis(Tp.bases()[0], codomain=W_Tp_xy); phi_Tp_xy + Generic morphism: + From: Tangent space at Point p on the 2-dimensional differentiable manifold M + To: Ambient quadratic space of dimension 2 over Symbolic Ring + Inner product matrix: + [1 0] + [0 1] + + sage: Q_Tp_uv = g[c_uv.frame(),:](*p.coordinates(c_uv)); Q_Tp_uv + [1/2 0] + [ 0 1/2] + sage: W_Tp_uv = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_uv) + sage: Tp.bases()[1] + Basis (∂/∂u,∂/∂v) on the Tangent space at Point p on the 2-dimensional differentiable manifold M + sage: phi_Tp_uv = Tp.isomorphism_with_fixed_basis(Tp.bases()[1], codomain=W_Tp_uv); phi_Tp_uv + Generic morphism: + From: Tangent space at Point p on the 2-dimensional differentiable manifold M + To: Ambient quadratic space of dimension 2 over Symbolic Ring + Inner product matrix: + [1/2 0] + [ 0 1/2] + + sage: t1, t2 = Tp.tensor((1,0)), Tp.tensor((1,0)) + sage: t1[:] = (8, 15) + sage: t2[:] = (47, 11) + sage: t1[Tp.bases()[0],:] + [8, 15] + sage: phi_Tp_xy(t1), phi_Tp_xy(t2) + ((8, 15), (47, 11)) + sage: phi_Tp_xy(t1).inner_product(phi_Tp_xy(t2)) + 541 + + sage: Tp_xy_to_uv = M.change_of_frame(c_xy.frame(), c_uv.frame()).at(p); Tp_xy_to_uv + Automorphism of the Tangent space at Point p on the 2-dimensional differentiable manifold M + sage: Tp.set_change_of_basis(Tp.bases()[0], Tp.bases()[1], Tp_xy_to_uv) + sage: t1[Tp.bases()[1],:] + [23, -7] + sage: phi_Tp_uv(t1), phi_Tp_uv(t2) + ((23, -7), (58, 36)) + sage: phi_Tp_uv(t1).inner_product(phi_Tp_uv(t2)) + 541 + .. SEEALSO:: :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule` From aaf46cc4c275e6fa6be216cf24525ea57d6d0367 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 16:18:10 -0700 Subject: [PATCH 275/454] DifferentiableManifold.tangent_space, TangentSpace: Accept keyword 'base_ring' --- src/sage/manifolds/differentiable/manifold.py | 6 ++++-- src/sage/manifolds/differentiable/tangent_space.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index d5e0ebc6200..7fa589be987 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -3403,7 +3403,7 @@ def is_manifestly_parallelizable(self): """ return bool(self._covering_frames) - def tangent_space(self, point): + def tangent_space(self, point, base_ring=None): r""" Tangent space to ``self`` at a given point. @@ -3412,6 +3412,8 @@ def tangent_space(self, point): - ``point`` -- :class:`~sage.manifolds.point.ManifoldPoint`; point `p` on the manifold + - ``base_ring`` -- (default: the symbolic ring) the base ring + OUTPUT: - :class:`~sage.manifolds.differentiable.tangent_space.TangentSpace` @@ -3445,7 +3447,7 @@ def tangent_space(self, point): raise TypeError("{} is not a manifold point".format(point)) if point not in self: raise ValueError("{} is not a point on the {}".format(point, self)) - return TangentSpace(point) + return TangentSpace(point, base_ring=base_ring) def curve(self, coord_expression, param, chart=None, name=None, latex_name=None): diff --git a/src/sage/manifolds/differentiable/tangent_space.py b/src/sage/manifolds/differentiable/tangent_space.py index f2a5afa73b0..a53dbd41788 100644 --- a/src/sage/manifolds/differentiable/tangent_space.py +++ b/src/sage/manifolds/differentiable/tangent_space.py @@ -222,7 +222,7 @@ class TangentSpace(FiniteRankFreeModule): """ Element = TangentVector - def __init__(self, point): + def __init__(self, point, base_ring=None): r""" Construct the tangent space at a given point. @@ -243,7 +243,9 @@ def __init__(self, point): latex_name = r"T_{%s}\,%s"%(point._latex_name, manif._latex_name) self._point = point self._manif = manif - FiniteRankFreeModule.__init__(self, SR, manif._dim, name=name, + if base_ring is None: + base_ring = SR + FiniteRankFreeModule.__init__(self, base_ring, manif._dim, name=name, latex_name=latex_name, start_index=manif._sindex) # Initialization of bases of the tangent space from existing vector From 6cd438c7e2f7f01118e8203a97c7d02f31b680e7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 28 Aug 2022 16:18:52 -0700 Subject: [PATCH 276/454] StandardSymplecticSpace: Add example: isomorphism with inner product space --- .../examples/symplectic_space.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/manifolds/differentiable/examples/symplectic_space.py b/src/sage/manifolds/differentiable/examples/symplectic_space.py index 34c557cf830..623979ed7f3 100644 --- a/src/sage/manifolds/differentiable/examples/symplectic_space.py +++ b/src/sage/manifolds/differentiable/examples/symplectic_space.py @@ -91,6 +91,28 @@ def __init__( sage: omega = M.symplectic_form() sage: omega.display() omega = -dq∧dp + + An isomomorphism of its tangent space (at any point) with an indefinite inner product space + with distinguished basis:: + + sage: Q_M_qp = omega[:]; Q_M_qp + [ 0 -1] + [ 1 0] + sage: W_M_qp = VectorSpace(RR, 2, inner_product_matrix=Q_M_qp); W_M_qp + Ambient quadratic space of dimension 2 over Real Field with 53 bits of precision + Inner product matrix: + [0.000000000000000 -1.00000000000000] + [ 1.00000000000000 0.000000000000000] + sage: T = M.tangent_space(M.point(), base_ring=RR); T + Tangent space at Point on the Standard symplectic space R2 + sage: phi_M_qp = T.isomorphism_with_fixed_basis(T.default_basis(), codomain=W_M_qp); phi_M_qp + Generic morphism: + From: Tangent space at Point on the Standard symplectic space R2 + To: Ambient quadratic space of dimension 2 over Real Field with 53 bits of precision + Inner product matrix: + [0.000000000000000 -1.00000000000000] + [ 1.00000000000000 0.000000000000000] + """ # Check that manifold is even dimensional if dimension % 2 == 1: From 47c91eac9910b090ed5dcba24cfeaea89effb3f0 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 29 Aug 2022 10:13:59 +0800 Subject: [PATCH 277/454] fix book doctests --- .../polynomes_doctest.py | 6 +++--- .../sol/polynomes_doctest.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py index 452a116401d..951bfb2b5c6 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py @@ -267,7 +267,7 @@ sage: A = Integers(101); R. = A[] sage: f6 = sum( (i+1)^2 * x^i for i in (0..5) ); f6 36*x^5 + 25*x^4 + 16*x^3 + 9*x^2 + 4*x + 1 - sage: num, den = f6.rational_reconstruct(x^6, 1, 3); num/den + sage: num, den = f6.rational_reconstruction(x^6, 1, 3); num/den (100*x + 100)/(x^3 + 98*x^2 + 3*x + 100) Sage example in ./polynomes.tex, line 1611:: @@ -283,7 +283,7 @@ Sage example in ./polynomes.tex, line 1677:: - sage: num, den = ZpZx(s).rational_reconstruct(ZpZx(x)^10,4,5) + sage: num, den = ZpZx(s).rational_reconstruction(ZpZx(x)^10,4,5) sage: num/den (1073741779*x^3 + 105*x)/(x^4 + 1073741744*x^2 + 105) @@ -304,7 +304,7 @@ sage: def mypade(pol, n, k): ....: x = ZpZx.gen(); - ....: n,d = ZpZx(pol).rational_reconstruct(x^n, k-1, n-k) + ....: n,d = ZpZx(pol).rational_reconstruction(x^n, k-1, n-k) ....: return Qx(list(map(lift_sym, n)))/Qx(list(map(lift_sym, d))) Sage example in ./polynomes.tex, line 1813:: diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py index 15f60192153..2908f38254d 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/sol/polynomes_doctest.py @@ -91,7 +91,7 @@ Sage example in ./sol/polynomes.tex, line 428:: - sage: s.rational_reconstruct(mul(x-i for i in range(4)), 1, 2) + sage: s.rational_reconstruction(mul(x-i for i in range(4)), 1, 2) (15*x + 2, x^2 + 11*x + 15) Sage example in ./sol/polynomes.tex, line 454:: From b1cdacd0fe20d4da2bb296819983979a4c04accc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:14:57 -0700 Subject: [PATCH 278/454] build/pkgs/pyproject_metadata: New --- build/pkgs/pyproject_metadata/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/pyproject_metadata/checksums.ini | 5 +++++ build/pkgs/pyproject_metadata/dependencies | 4 ++++ .../pyproject_metadata/install-requires.txt | 1 + .../pyproject_metadata/package-version.txt | 1 + build/pkgs/pyproject_metadata/spkg-install.in | 2 ++ build/pkgs/pyproject_metadata/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/pyproject_metadata/SPKG.rst create mode 100644 build/pkgs/pyproject_metadata/checksums.ini create mode 100644 build/pkgs/pyproject_metadata/dependencies create mode 100644 build/pkgs/pyproject_metadata/install-requires.txt create mode 100644 build/pkgs/pyproject_metadata/package-version.txt create mode 100644 build/pkgs/pyproject_metadata/spkg-install.in create mode 100644 build/pkgs/pyproject_metadata/type diff --git a/build/pkgs/pyproject_metadata/SPKG.rst b/build/pkgs/pyproject_metadata/SPKG.rst new file mode 100644 index 00000000000..6c0300e9a16 --- /dev/null +++ b/build/pkgs/pyproject_metadata/SPKG.rst @@ -0,0 +1,18 @@ +pyproject_metadata: PEP 621 metadata parsing +============================================ + +Description +----------- + +PEP 621 metadata parsing + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/pyproject-metadata/ + diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini new file mode 100644 index 00000000000..da299c46588 --- /dev/null +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -0,0 +1,5 @@ +tarball=pyproject-metadata-VERSION.tar.gz +sha1=5421824aa29786bde43f510365c4d035a0614ba5 +md5=85fcbd5d777809ca2217a996e06fe2e0 +cksum=1327227039 +upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/pyproject_metadata/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pyproject_metadata/install-requires.txt b/build/pkgs/pyproject_metadata/install-requires.txt new file mode 100644 index 00000000000..7ca7140f9d4 --- /dev/null +++ b/build/pkgs/pyproject_metadata/install-requires.txt @@ -0,0 +1 @@ +pyproject-metadata diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt new file mode 100644 index 00000000000..8f0916f768f --- /dev/null +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -0,0 +1 @@ +0.5.0 diff --git a/build/pkgs/pyproject_metadata/spkg-install.in b/build/pkgs/pyproject_metadata/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/pyproject_metadata/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/pyproject_metadata/type b/build/pkgs/pyproject_metadata/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pyproject_metadata/type @@ -0,0 +1 @@ +standard From 345c019deffca04f8c1e91c30c094e9cf2cdfe15 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:24:55 -0700 Subject: [PATCH 279/454] build/pkgs/pyproject_metadata/dependencies: Update --- build/pkgs/pyproject_metadata/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies index 0738c2d7777..6d5368db738 100644 --- a/build/pkgs/pyproject_metadata/dependencies +++ b/build/pkgs/pyproject_metadata/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) packaging pyparsing | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From 66366b3c1c3f7c80fb8b341af44c874a8ab57281 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:13:08 -0700 Subject: [PATCH 280/454] TensorFreeModule.basis: Fix typo in docstring --- src/sage/tensor/modules/tensor_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index d4f000d7be6..548b2f0a734 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -732,7 +732,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, INPUT: - - ``symbol``, ``indices`` -- passed to he base module's method + - ``symbol``, ``indices`` -- passed to the base module's method :meth:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule.basis` to select a basis of the :meth:`base_module` of ``self``, or to create it. From bb3bae0ad9042a70ea4428af30433e4b436d9400 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:20:59 -0700 Subject: [PATCH 281/454] TensorFreeSubmodule_sym, TensorFreeSubmoduleBasis_sym: Rename from ..._comp --- .../tensor/modules/finite_rank_free_module.py | 10 +-- src/sage/tensor/modules/tensor_free_module.py | 22 ++--- .../tensor/modules/tensor_free_submodule.py | 88 +++++++++---------- .../modules/tensor_free_submodule_basis.py | 8 +- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index b9a3e543eb1..7264944bc59 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -703,9 +703,9 @@ def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_ sage: M.ambient_module() is M True - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: T60M = M.tensor_module(6, 0) sage: Sym0123x45M.ambient_module() is T60M True @@ -1195,7 +1195,7 @@ def tensor_module(self, k, l, *, sym=None, antisym=None): T^{\{2,3\}}(M) \otimes T^{\{6,7\}}(M^*) \otimes \mathrm{Sym}^{\{0,1\}}(M) \otimes \mathrm{ASym}^{\{4,5\}}(M^*) See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` - and :class:`~sage.tensor.modules.tensor_free_module.TensorFreeSubmodule_comp` + and :class:`~sage.tensor.modules.tensor_free_module.TensorFreeSubmodule_sym` for more documentation. """ @@ -1210,8 +1210,8 @@ def tensor_module(self, k, l, *, sym=None, antisym=None): if key == (1, 0): T = self elif sym or antisym: - from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - T = TensorFreeSubmodule_comp(self, (k, l), sym=sym, antisym=antisym) + from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym + T = TensorFreeSubmodule_sym(self, (k, l), sym=sym, antisym=antisym) else: from sage.tensor.modules.tensor_free_module import TensorFreeModule T = TensorFreeModule(self, (k, l)) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 548b2f0a734..6d0b9e16af4 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -66,7 +66,7 @@ from sage.tensor.modules.free_module_morphism import \ FiniteRankFreeModuleMorphism from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism -from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_comp +from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym class TensorFreeModule(FiniteRankFreeModule_abstract): r""" @@ -554,11 +554,11 @@ def _an_element_(self): TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: t = Sym0123x45M._an_element_() sage: t.parent() is Sym0123x45M True @@ -623,8 +623,8 @@ def _coerce_map_from_(self, other): Coercion from submodules:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: Sym01M = TensorFreeSubmodule_comp(M, (2, 0), sym=((0, 1))) + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym + sage: Sym01M = TensorFreeSubmodule_sym(M, (2, 0), sym=((0, 1))) sage: M.tensor_module(2,0)._coerce_map_from_(Sym01M) True @@ -760,8 +760,8 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_2⊗e^1 e_2⊗e^2 - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)) + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym + sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M Standard basis on the Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring @@ -774,25 +774,25 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_1⊗e_2 + e_2⊗e_1 e_2⊗e_2 """ - return TensorFreeSubmoduleBasis_comp(self, symbol=symbol, latex_symbol=latex_symbol, + return TensorFreeSubmoduleBasis_sym(self, symbol=symbol, latex_symbol=latex_symbol, indices=indices, latex_indices=latex_indices, symbol_dual=symbol_dual, latex_symbol_dual=latex_symbol_dual) @cached_method - def _basis_comp(self): + def _basis_sym(self): r""" Return an instance of :class:`~sage.tensor.modules.comp.Components`. This implementation returns an instance without symmetry. - The subclass :class:`~sage.tensor.modules.tensor_free_submodule.TensorFreeSubmodule_comp` + The subclass :class:`~sage.tensor.modules.tensor_free_submodule.TensorFreeSubmodule_sym` overrides this method to encode the prescribed symmetry of the submodule. EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T = M.tensor_module(1,1) - sage: c = T._basis_comp(); c + sage: c = T._basis_sym(); c 2-indices components w.r.t. (0, 1, 2) """ diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index dc94ace3343..03d5dda185e 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -23,7 +23,7 @@ from .finite_rank_free_module import FiniteRankFreeModule_abstract -class TensorFreeSubmodule_comp(TensorFreeModule): +class TensorFreeSubmodule_sym(TensorFreeModule): r""" Class for free submodules of tensor products of free modules that are defined by the symmetries of a @@ -31,7 +31,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0); T60M @@ -40,14 +40,14 @@ class TensorFreeSubmodule_comp(TensorFreeModule): 'T^(6, 0)(M)' sage: latex(T60M) T^{(6, 0)}\left(M\right) - sage: T40Sym45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((4, 5))); T40Sym45M + sage: T40Sym45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((4, 5))); T40Sym45M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (4, 5) sage: T40Sym45M._name 'T^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(T40Sym45M) T^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) @@ -55,7 +55,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(Sym0123x45M) \mathrm{Sym}^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) - sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M + sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2), with symmetry on the index positions (3, 4, 5) @@ -63,7 +63,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): 'Sym^{0,1,2}(M)⊗Sym^{3,4,5}(M)' sage: latex(Sym012x345M) \mathrm{Sym}^{\{0,1,2\}}(M) \otimes \mathrm{Sym}^{\{3,4,5\}}(M) - sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M + sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M Free module of fully symmetric type-(6,0) tensors on the Rank-3 free module M over the Integer Ring sage: Sym012345M._name @@ -85,7 +85,7 @@ class TensorFreeSubmodule_comp(TensorFreeModule): TESTS:: - sage: T = TensorFreeSubmodule_comp(M, (4, 4), sym=((0, 1)), antisym=((4, 5))); T + sage: T = TensorFreeSubmodule_sym(M, (4, 4), sym=((0, 1)), antisym=((4, 5))); T Free module of type-(4,4) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1), with antisymmetry on the index positions (4, 5) @@ -100,10 +100,10 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, r""" TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: TestSuite(Sym0123x45M).run() """ self._fmodule = fmodule @@ -113,20 +113,20 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, self._ambient_module = ambient self._sym = sym self._antisym = antisym - basis_comp = self._basis_comp() - rank = len(list(basis_comp.non_redundant_index_generator())) + basis_sym = self._basis_sym() + rank = len(list(basis_sym.non_redundant_index_generator())) if name is None and fmodule._name is not None: all_indices = tuple(range(tensor_type[0] + tensor_type[1])) - if isinstance(basis_comp, CompFullySym): + if isinstance(basis_sym, CompFullySym): sym = [all_indices] antisym = [] - elif isinstance(basis_comp, CompFullyAntiSym): + elif isinstance(basis_sym, CompFullyAntiSym): sym = [] antisym = [all_indices] - elif isinstance(basis_comp, CompWithSym): - sym = basis_comp._sym - antisym = basis_comp._antisym + elif isinstance(basis_sym, CompWithSym): + sym = basis_sym._sym + antisym = basis_sym._antisym else: sym = antisym = [] nosym_0 = [i for i in range(tensor_type[0]) @@ -178,7 +178,7 @@ def power_name(op, s, latex=False): category=category, ambient=ambient) @cached_method - def _basis_comp(self): + def _basis_sym(self): r""" Return an instance of :class:`~sage.tensor.modules.comp.Components`. @@ -186,11 +186,11 @@ def _basis_comp(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)); Sym2M Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring - sage: c = Sym2M._basis_comp(); c + sage: c = Sym2M._basis_sym(); c Fully symmetric 2-indices components w.r.t. (0, 1, 2) """ @@ -205,14 +205,14 @@ def _repr_(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)); Sym2M Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring """ - prefix, suffix = self._basis_comp()._repr_symmetry() + prefix, suffix = self._basis_sym()._repr_symmetry() return "Free module of {}type-({},{}) tensors on the {}{}".format( prefix.lower(), self._tensor_type[0], self._tensor_type[1], self._fmodule, suffix) @@ -226,11 +226,11 @@ def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: ten0123x45M = Sym0123x45M.an_element(); ten0123x45M Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring sage: ten0123x45M.parent() @@ -242,13 +242,13 @@ def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) - sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) sage: com012x345M = Sym012x345M.an_element()._components[e]; com012x345M 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2), with symmetry on the index positions (3, 4, 5) - sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: com012345M = Sym012345M.an_element()._components[e]; com012345M Fully symmetric 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring @@ -302,11 +302,11 @@ def _element_constructor_(self, comp=[], basis=None, name=None, r""" TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2]) Traceback (most recent call last): ... @@ -322,14 +322,14 @@ def _element_constructor_(self, comp=[], basis=None, name=None, if sym is not None or antisym is not None: # Refuse to create a tensor with finer symmetries # than those defining the subspace - if not self._is_symmetry_coarsening_of((sym, antisym), self._basis_comp()): + if not self._is_symmetry_coarsening_of((sym, antisym), self._basis_sym()): raise ValueError(f"cannot create a tensor with symmetries {sym=}, {antisym=} " f"as an element of {self}") if sym is None: - sym = self._basis_comp()._sym + sym = self._basis_sym()._sym if antisym is None: - antisym = self._basis_comp()._antisym + antisym = self._basis_sym()._antisym resu = super()._element_constructor_(comp=comp, basis=basis, name=name, @@ -355,12 +355,12 @@ def is_submodule(self, other): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: Sym012x345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) - sage: Sym012345M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) sage: Sym012345M.is_submodule(Sym012345M) True sage: Sym012345M.is_submodule(Sym0123x45M) @@ -387,8 +387,8 @@ def is_submodule(self, other): if self_tensor_type != other_tensor_type: return False - other_comp = other._basis_comp() - return self._is_symmetry_coarsening_of(self._basis_comp(), other_comp) + other_comp = other._basis_sym() + return self._is_symmetry_coarsening_of(self._basis_sym(), other_comp) @lazy_attribute def lift(self): @@ -397,9 +397,9 @@ def lift(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) sage: Sym0123x45M.lift Generic morphism: From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, @@ -419,7 +419,7 @@ def reduce(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(QQ, 3, name='M') sage: e = M.basis('e') sage: X = M.tensor_module(6, 0) @@ -461,8 +461,8 @@ def reduce(self): sage: all(Y.reduce(u.lift()) == 0 for u in Y.basis('e')) True """ - sym = self._basis_comp()._sym - antisym = self._basis_comp()._antisym + sym = self._basis_sym()._sym + antisym = self._basis_sym()._antisym def _reduce_element(x): if not x._components: @@ -491,7 +491,7 @@ def retract(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: X = M.tensor_module(6, 0) diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index ae00f98971b..0ee52bb5a95 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -14,7 +14,7 @@ from sage.tensor.modules.free_module_basis import Basis_abstract -class TensorFreeSubmoduleBasis_comp(Basis_abstract): +class TensorFreeSubmoduleBasis_sym(Basis_abstract): r""" Standard basis of a tensor module with prescribed symmetries. @@ -52,7 +52,7 @@ def __init__(self, tensor_module, symbol, latex_symbol=None, indices=None, latex_indices, symbol_dual, latex_symbol_dual) super().__init__(tensor_module, symbol, latex_symbol, indices, latex_indices) self._base_module_basis = base_module_basis - self._comp = tensor_module._basis_comp() + self._comp = tensor_module._basis_sym() def _repr_(self): r""" @@ -120,8 +120,8 @@ def __getitem__(self, index): sage: e11[1, 2].display() e_1⊗e^2 - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp - sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2)); Sym2M + sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym + sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)); Sym2M Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring sage: eSym2M = Sym2M.basis('e') sage: eSym2M[1, 1].display() From a99f5c0207d4b84b329d38dde645e7fcf69e052b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:27:57 -0700 Subject: [PATCH 282/454] src/sage/tensor/modules/tensor_free_submodule[_basis].py: Deemphasize Components in the documentation --- src/sage/tensor/modules/tensor_free_submodule.py | 8 ++++---- src/sage/tensor/modules/tensor_free_submodule_basis.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 03d5dda185e..3d2ce06a01f 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -1,5 +1,5 @@ r""" -Free submodules of tensor products of free modules +Free submodules of tensor modules defined by monoterm symmetries """ #****************************************************************************** @@ -26,8 +26,7 @@ class TensorFreeSubmodule_sym(TensorFreeModule): r""" Class for free submodules of tensor products of free modules - that are defined by the symmetries of a - :class:`~sage.tensor.modules.comp.Components` object. + that are defined by some monoterm symmetries. EXAMPLES:: @@ -182,7 +181,8 @@ def _basis_sym(self): r""" Return an instance of :class:`~sage.tensor.modules.comp.Components`. - It encodes the prescribed symmetry of ``self``. + In the current implementation of :class:`~sage.tensor.modules.tensor_free_submodule.TensorFreeSubmodule_sym`, + it encodes the prescribed symmetry of ``self``. EXAMPLES:: diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index 0ee52bb5a95..d2b60298ee0 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -1,5 +1,5 @@ r""" -Free module bases indexed by component indices +Standard bases of free submodules of tensor modules defined by some monoterm symmetries """ #****************************************************************************** @@ -16,7 +16,7 @@ class TensorFreeSubmoduleBasis_sym(Basis_abstract): r""" - Standard basis of a tensor module with prescribed symmetries. + Standard basis of a free submodule of a tensor module with prescribed monoterm symmetries. EXAMPLES:: From 1e7430ab22f781ed9bc133b32f8153599e4539cc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:38:20 -0700 Subject: [PATCH 283/454] src/sage/tensor: In examples, use FiniteRankFreeModule.tensor_module(..., sym=...) instead of FiniteRankFreeModule_sym --- .../tensor/modules/finite_rank_free_module.py | 3 +- src/sage/tensor/modules/tensor_free_module.py | 9 ++-- .../tensor/modules/tensor_free_submodule.py | 42 +++++++------------ 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 7264944bc59..5f1faf48e58 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -703,9 +703,8 @@ def ambient_module(self): # compatible with sage.modules.free_module.FreeModule_ sage: M.ambient_module() is M True - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: T60M = M.tensor_module(6, 0) sage: Sym0123x45M.ambient_module() is T60M True diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 6d0b9e16af4..9ea88f3c5f0 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -554,11 +554,10 @@ def _an_element_(self): TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: t = Sym0123x45M._an_element_() sage: t.parent() is Sym0123x45M True @@ -623,8 +622,7 @@ def _coerce_map_from_(self, other): Coercion from submodules:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym - sage: Sym01M = TensorFreeSubmodule_sym(M, (2, 0), sym=((0, 1))) + sage: Sym01M = M.tensor_module(2, 0, sym=((0, 1))) sage: M.tensor_module(2,0)._coerce_map_from_(Sym01M) True @@ -760,8 +758,7 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_2⊗e^1 e_2⊗e^2 - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym - sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)) + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)) sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M Standard basis on the Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 3d2ce06a01f..cfe6b09dccf 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -30,7 +30,6 @@ class TensorFreeSubmodule_sym(TensorFreeModule): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0); T60M @@ -39,14 +38,14 @@ class TensorFreeSubmodule_sym(TensorFreeModule): 'T^(6, 0)(M)' sage: latex(T60M) T^{(6, 0)}\left(M\right) - sage: T40Sym45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((4, 5))); T40Sym45M + sage: T40Sym45M = M.tensor_module(6, 0, sym=((4, 5))); T40Sym45M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (4, 5) sage: T40Sym45M._name 'T^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(T40Sym45M) T^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))); Sym0123x45M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) @@ -54,7 +53,7 @@ class TensorFreeSubmodule_sym(TensorFreeModule): 'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)' sage: latex(Sym0123x45M) \mathrm{Sym}^{\{0,1,2,3\}}(M) \otimes \mathrm{Sym}^{\{4,5\}}(M) - sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))); Sym012x345M + sage: Sym012x345M = M.tensor_module(6, 0, sym=((0, 1, 2), (3, 4, 5))); Sym012x345M Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2), with symmetry on the index positions (3, 4, 5) @@ -62,7 +61,7 @@ class TensorFreeSubmodule_sym(TensorFreeModule): 'Sym^{0,1,2}(M)⊗Sym^{3,4,5}(M)' sage: latex(Sym012x345M) \mathrm{Sym}^{\{0,1,2\}}(M) \otimes \mathrm{Sym}^{\{3,4,5\}}(M) - sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))); Sym012345M + sage: Sym012345M = M.tensor_module(6, 0, sym=((0, 1, 2, 3, 4, 5))); Sym012345M Free module of fully symmetric type-(6,0) tensors on the Rank-3 free module M over the Integer Ring sage: Sym012345M._name @@ -84,7 +83,7 @@ class TensorFreeSubmodule_sym(TensorFreeModule): TESTS:: - sage: T = TensorFreeSubmodule_sym(M, (4, 4), sym=((0, 1)), antisym=((4, 5))); T + sage: T = M.tensor_module(4, 4, sym=((0, 1)), antisym=((4, 5))); T Free module of type-(4,4) tensors on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1), with antisymmetry on the index positions (4, 5) @@ -99,10 +98,9 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, r""" TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: TestSuite(Sym0123x45M).run() """ self._fmodule = fmodule @@ -186,9 +184,8 @@ def _basis_sym(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)); Sym2M + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring sage: c = Sym2M._basis_sym(); c Fully symmetric 2-indices components w.r.t. (0, 1, 2) @@ -205,9 +202,8 @@ def _repr_(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym2M = TensorFreeSubmodule_sym(M, (2, 0), sym=range(2)); Sym2M + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring @@ -226,11 +222,10 @@ def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: ten0123x45M = Sym0123x45M.an_element(); ten0123x45M Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring sage: ten0123x45M.parent() @@ -242,13 +237,13 @@ def _is_symmetry_coarsening_of(self, coarser_comp, finer_comp): on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2, 3), with symmetry on the index positions (4, 5) - sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012x345M = M.tensor_module(6, 0, sym=((0, 1, 2), (3, 4, 5))) sage: com012x345M = Sym012x345M.an_element()._components[e]; com012x345M 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring, with symmetry on the index positions (0, 1, 2), with symmetry on the index positions (3, 4, 5) - sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym012345M = M.tensor_module(6, 0, sym=((0, 1, 2, 3, 4, 5))) sage: com012345M = Sym012345M.an_element()._components[e]; com012345M Fully symmetric 6-indices components w.r.t. Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring @@ -302,11 +297,10 @@ def _element_constructor_(self, comp=[], basis=None, name=None, r""" TESTS:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2]) Traceback (most recent call last): ... @@ -355,12 +349,11 @@ def is_submodule(self, other): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: T60M = M.tensor_module(6, 0) - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) - sage: Sym012x345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2), (3, 4, 5))) - sage: Sym012345M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3, 4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) + sage: Sym012x345M = M.tensor_module(6, 0, sym=((0, 1, 2), (3, 4, 5))) + sage: Sym012345M = M.tensor_module(6, 0, sym=((0, 1, 2, 3, 4, 5))) sage: Sym012345M.is_submodule(Sym012345M) True sage: Sym012345M.is_submodule(Sym0123x45M) @@ -397,9 +390,8 @@ def lift(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: Sym0123x45M = TensorFreeSubmodule_sym(M, (6, 0), sym=((0, 1, 2, 3), (4, 5))) + sage: Sym0123x45M = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5))) sage: Sym0123x45M.lift Generic morphism: From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring, @@ -419,7 +411,6 @@ def reduce(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(QQ, 3, name='M') sage: e = M.basis('e') sage: X = M.tensor_module(6, 0) @@ -491,7 +482,6 @@ def retract(self): EXAMPLES:: - sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_sym sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') sage: X = M.tensor_module(6, 0) From 046887c65442ba9c7320b82ef0a9182f4fb77eed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:40:39 -0700 Subject: [PATCH 284/454] TensorFreeModule.basis: Add example from egourgoulhon --- src/sage/tensor/modules/tensor_free_module.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 9ea88f3c5f0..42a97d92b4b 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -770,6 +770,24 @@ def basis(self, symbol, latex_symbol=None, from_family=None, e_1⊗e_1 e_1⊗e_2 + e_2⊗e_1 e_2⊗e_2 + + sage: M = FiniteRankFreeModule(ZZ, 2) + sage: e = M.basis('e') + sage: f = M.basis('f', from_family=(-e[1], e[0])) + sage: for b in f: b.display() + f_0 = -e_1 + f_1 = e_0 + sage: S = M.tensor_module(2, 0, sym=(0,1)) + sage: fS = S.basis('f') + sage: for b in fS: b.display() + e_1⊗e_1 + -e_0⊗e_1 - e_1⊗e_0 + e_0⊗e_0 + sage: for b in fS: b.display(f) + f_0⊗f_0 + f_0⊗f_1 + f_1⊗f_0 + f_1⊗f_1 + """ return TensorFreeSubmoduleBasis_sym(self, symbol=symbol, latex_symbol=latex_symbol, indices=indices, latex_indices=latex_indices, From 3a65d2d2c7f89d3d74109444b1501b3709bd6fce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:56:39 -0700 Subject: [PATCH 285/454] CompWithSym._canonicalize_sym_antisym: Factor out from __init__ --- src/sage/tensor/modules/comp.py | 37 +++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 04f14f782f0..8de1f365212 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -2996,7 +2996,21 @@ def __init__(self, ring, frame, nb_indices, start_index=0, """ Components.__init__(self, ring, frame, nb_indices, start_index, output_formatter) - self._sym = [] + self._sym, self._antisym = self._canonicalize_sym_antisym( + nb_indices, sym, antisym) + + @staticmethod + def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): + r""" + Bring sym and antisym into their canonical form. + + EXAMPLES:: + + sage: from sage.tensor.modules.comp import CompWithSym + sage: CompWithSym._canonicalize_sym_antisym(6, [(2, 1)]) + ([(2, 1)], []) + """ + result_sym = [] if sym is not None and sym != []: if isinstance(sym[0], (int, Integer)): # a single symmetry is provided as a tuple or a range object; @@ -3007,11 +3021,11 @@ def __init__(self, ring, frame, nb_indices, start_index=0, raise IndexError("at least two index positions must be " + "provided to define a symmetry") for i in isym: - if i<0 or i>self._nid-1: + if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(self._nid-1) + "]") - self._sym.append(tuple(isym)) - self._antisym = [] + " not in [0," + str(nb_indices-1) + "]") + result_sym.append(tuple(isym)) + result_antisym = [] if antisym is not None and antisym != []: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range @@ -3022,20 +3036,21 @@ def __init__(self, ring, frame, nb_indices, start_index=0, raise IndexError("at least two index positions must be " + "provided to define an antisymmetry") for i in isym: - if i<0 or i>self._nid-1: + if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(self._nid-1) + "]") - self._antisym.append(tuple(isym)) + " not in [0," + str(nb_indices - 1) + "]") + result_antisym.append(tuple(isym)) # Final consistency check: index_list = [] - for isym in self._sym: + for isym in result_sym: index_list += isym - for isym in self._antisym: + for isym in result_antisym: index_list += isym if len(index_list) != len(set(index_list)): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + - "index position appears more then once") + "index position appears more than once") + return result_sym, result_antisym def _repr_(self): r""" From 07ab9914adf5418070b9f326d99d282c18ddb35a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 10:52:35 -0700 Subject: [PATCH 286/454] src/sage/tensor: Allow _sym, _antisym attributes to be tuples --- .../manifolds/differentiable/tensorfield.py | 4 +-- src/sage/tensor/modules/comp.py | 26 +++++++++++++------ src/sage/tensor/modules/free_module_tensor.py | 4 +-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index d5b63c9d81d..1370b104140 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -957,13 +957,13 @@ def symmetries(self): elif len(self._sym) == 1: s = "symmetry: {}; ".format(self._sym[0]) else: - s = "symmetries: {}; ".format(self._sym) + s = "symmetries: {}; ".format(list(self._sym)) if not self._antisym: a = "no antisymmetry" elif len(self._antisym) == 1: a = "antisymmetry: {}".format(self._antisym[0]) else: - a = "antisymmetries: {}".format(self._antisym) + a = "antisymmetries: {}".format(list(self._antisym)) print(s + a) #### End of simple accessors ##### diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 8de1f365212..d9c3dfdfe3e 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -1773,12 +1773,12 @@ def __mul__(self, other): "same starting index") if isinstance(other, CompWithSym): sym = [] - if other._sym != []: + if other._sym: for s in other._sym: ns = tuple(s[i]+self._nid for i in range(len(s))) sym.append(ns) antisym = [] - if other._antisym != []: + if other._antisym: for s in other._antisym: ns = tuple(s[i]+self._nid for i in range(len(s))) antisym.append(ns) @@ -3011,7 +3011,12 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): ([(2, 1)], []) """ result_sym = [] - if sym is not None and sym != []: + if sym is None: + sym = [] + else: + # Handle the case that sym is an iterator + sym = list(sym) + if sym: if isinstance(sym[0], (int, Integer)): # a single symmetry is provided as a tuple or a range object; # it is converted to a 1-item list: @@ -3026,7 +3031,12 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): " not in [0," + str(nb_indices-1) + "]") result_sym.append(tuple(isym)) result_antisym = [] - if antisym is not None and antisym != []: + if antisym is None: + antisym = [] + else: + # Handle the case that antisym is an iterator + antisym = list(antisym) + if antisym: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: @@ -3535,7 +3545,7 @@ def paral_sum(a, b, local_list_ind): com = tuple(set(isym).intersection(set(osym))) if len(com) > 1: common_antisym.append(com) - if common_sym != [] or common_antisym != []: + if common_sym or common_antisym: result = CompWithSym(self._ring, self._frame, self._nid, self._sindex, self._output_formatter, common_sym, common_antisym) @@ -3643,11 +3653,11 @@ def __mul__(self, other): sym = list(self._sym) antisym = list(self._antisym) if isinstance(other, CompWithSym): - if other._sym != []: + if other._sym: for s in other._sym: ns = tuple(s[i]+self._nid for i in range(len(s))) sym.append(ns) - if other._antisym != []: + if other._antisym: for s in other._antisym: ns = tuple(s[i]+self._nid for i in range(len(s))) antisym.append(ns) @@ -3973,7 +3983,7 @@ def non_redundant_index_generator(self): si = self._sindex imax = self._dim - 1 + si ind = [si for k in range(self._nid)] - sym = self._sym.copy() # we may modify this in the following + sym = list(self._sym) # we may modify this in the following antisym = self._antisym for pos in range(self._nid): for isym in antisym: diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index bbcc1ecb2e0..1fa457475a0 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -563,13 +563,13 @@ def symmetries(self): elif len(self._sym) == 1: s = "symmetry: {}; ".format(self._sym[0]) else: - s = "symmetries: {}; ".format(self._sym) + s = "symmetries: {}; ".format(list(self._sym)) if len(self._antisym) == 0: a = "no antisymmetry" elif len(self._antisym) == 1: a = "antisymmetry: {}".format(self._antisym[0]) else: - a = "antisymmetries: {}".format(self._antisym) + a = "antisymmetries: {}".format(list(self._antisym)) print(s+a) #### End of simple accessors ##### From 3619b4c163234f71c4e68d4c0442e56c5f4898dc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:04:11 -0700 Subject: [PATCH 287/454] src/sage/tensor, src/sage/manifolds: Sort _sym, _antisym, store as tuples of tuples --- src/sage/manifolds/differentiable/metric.py | 8 ++-- .../manifolds/differentiable/tensorfield.py | 39 ++----------------- src/sage/tensor/modules/comp.py | 29 ++++++++------ src/sage/tensor/modules/free_module_tensor.py | 39 ++----------------- 4 files changed, 29 insertions(+), 86 deletions(-) diff --git a/src/sage/manifolds/differentiable/metric.py b/src/sage/manifolds/differentiable/metric.py index 686af0d3505..445862d6795 100644 --- a/src/sage/manifolds/differentiable/metric.py +++ b/src/sage/manifolds/differentiable/metric.py @@ -647,7 +647,7 @@ def set(self, symbiform): raise TypeError("the argument must be a tensor field") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if not symbiform._domain.is_subset(self._domain): raise TypeError("the symmetric bilinear form is not defined " + @@ -2301,7 +2301,7 @@ def set(self, symbiform): "values on a parallelizable domain") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if symbiform._vmodule is not self._vmodule: raise TypeError("the symmetric bilinear form and the metric are " + @@ -2781,7 +2781,7 @@ def set(self, symbiform): raise TypeError("the argument must be a tensor field") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if not symbiform._domain.is_subset(self._domain): raise TypeError("the symmetric bilinear form is not defined " + @@ -3019,7 +3019,7 @@ def set(self, symbiform): "values on a parallelizable domain") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if symbiform._vmodule is not self._vmodule: raise TypeError("the symmetric bilinear form and the metric are " + diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 1370b104140..a2aad4d4937 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -58,6 +58,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.structure.element import ModuleElementWithMutability +from sage.tensor.modules.comp import CompWithSym from sage.tensor.modules.free_module_tensor import FreeModuleTensor from sage.tensor.modules.tensor_with_indices import TensorWithIndices @@ -495,40 +496,8 @@ def __init__( self._restrictions = {} # dict. of restrictions of self on subdomains # of self._domain, with the subdomains as keys # Treatment of symmetry declarations: - self._sym = [] - if sym is not None and sym != []: - if isinstance(sym[0], (int, Integer)): - # a single symmetry is provided as a tuple -> 1-item list: - sym = [tuple(sym)] - for isym in sym: - if len(isym) > 1: - for i in isym: - if i < 0 or i > self._tensor_rank - 1: - raise IndexError("invalid position: {}".format(i) + - " not in [0,{}]".format(self._tensor_rank-1)) - self._sym.append(tuple(isym)) - self._antisym = [] - if antisym is not None and antisym != []: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple -> 1-item list: - antisym = [tuple(antisym)] - for isym in antisym: - if len(isym) > 1: - for i in isym: - if i < 0 or i > self._tensor_rank - 1: - raise IndexError("invalid position: {}".format(i) + - " not in [0,{}]".format(self._tensor_rank-1)) - self._antisym.append(tuple(isym)) - # Final consistency check: - index_list = [] - for isym in self._sym: - index_list += isym - for isym in self._antisym: - index_list += isym - if len(index_list) != len(set(index_list)): - # There is a repeated index position: - raise IndexError("incompatible lists of symmetries: the same " + - "position appears more than once") + self._sym, self._antisym = CompWithSym._canonicalize_sym_antisym( + self._tensor_rank, sym, antisym) # Initialization of derived quantities: self._init_derived() @@ -591,7 +560,7 @@ def _repr_(self): """ # Special cases - if self._tensor_type == (0,2) and self._sym == [(0,1)]: + if self._tensor_type == (0,2) and self._sym == ((0,1),): description = "Field of symmetric bilinear forms " if self._name is not None: description += self._name + " " diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index d9c3dfdfe3e..3318c7b3ebf 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3008,7 +3008,7 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): sage: from sage.tensor.modules.comp import CompWithSym sage: CompWithSym._canonicalize_sym_antisym(6, [(2, 1)]) - ([(2, 1)], []) + (((1, 2),), ()) """ result_sym = [] if sym is None: @@ -3023,8 +3023,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): sym = [tuple(sym)] for isym in sym: if len(isym) < 2: - raise IndexError("at least two index positions must be " + - "provided to define a symmetry") + # Drop trivial symmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3043,8 +3043,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): antisym = [tuple(antisym)] for isym in antisym: if len(isym) < 2: - raise IndexError("at least two index positions must be " + - "provided to define an antisymmetry") + # Drop trivial antisymmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3060,6 +3060,11 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + "index position appears more than once") + # Canonicalize sort order, make tuples + result_sym = [tuple(sorted(s)) for s in result_sym] + result_antisym = [tuple(sorted(s)) for s in result_antisym] + result_sym = tuple(sorted(result_sym)) + result_antisym = tuple(sorted(result_antisym)) return result_sym, result_antisym def _repr_(self): @@ -3392,9 +3397,9 @@ def swap_adjacent_indices(self, pos1, pos2, pos3): [[0, 7, 8], [-7, 0, 9], [-8, -9, 0]]] sage: c1 = c.swap_adjacent_indices(0,1,3) sage: c._antisym # c is antisymmetric with respect to the last pair of indices... - [(1, 2)] + ((1, 2),) sage: c1._antisym #...while c1 is antisymmetric with respect to the first pair of indices - [(0, 1)] + ((0, 1),) sage: c[0,1,2] 3 sage: c1[1,2,0] @@ -3415,6 +3420,8 @@ def swap_adjacent_indices(self, pos1, pos2, pos3): for s in self._antisym: new_s = [new_lpos.index(pos) for pos in s] result._antisym.append(tuple(sorted(new_s))) + result._sym, result._antisym = self._canonicalize_sym_antisym( + self._nid, result._sym, result._antisym) # The values: for ind, val in self._comp.items(): new_ind = ind[:pos1] + ind[pos2:pos3] + ind[pos1:pos2] + ind[pos3:] @@ -4159,7 +4166,7 @@ def symmetrize(self, *pos): (0, 0, 1) ], with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) sage: a1._sym # a1 has two distinct symmetries: - [(0, 1), (2, 3)] + ((0, 1), (2, 3)) sage: a[0,1,2,0] == a[0,0,2,1] # a is symmetric w.r.t. positions 1 and 3 True sage: a1[0,1,2,0] == a1[0,0,2,1] # a1 is not @@ -4437,10 +4444,10 @@ def antisymmetrize(self, *pos): (1, 0, 0), (0, 1, 0), (0, 0, 1) - ], with antisymmetry on the index positions (1, 3), - with antisymmetry on the index positions (0, 2) + ], with antisymmetry on the index positions (0, 2), + with antisymmetry on the index positions (1, 3) sage: s._antisym # the antisymmetry (0,1,2) has been reduced to (0,2), since 1 is involved in the new antisymmetry (1,3): - [(1, 3), (0, 2)] + ((0, 2), (1, 3)) Partial antisymmetrization of 4-indices components with a symmetry on the first two indices:: diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 1fa457475a0..4d6d05c57b8 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -309,41 +309,8 @@ def __init__( # bases, with the bases as keys (initially empty) # Treatment of symmetry declarations: - self._sym = [] - if sym is not None and sym != []: - if isinstance(sym[0], (int, Integer)): - # a single symmetry is provided as a tuple -> 1-item list: - sym = [tuple(sym)] - for isym in sym: - if len(isym) > 1: - for i in isym: - if i<0 or i>self._tensor_rank-1: - raise IndexError("invalid position: " + str(i) + - " not in [0," + str(self._tensor_rank-1) + "]") - self._sym.append(tuple(isym)) - self._antisym = [] - if antisym is not None and antisym != []: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple -> 1-item list: - antisym = [tuple(antisym)] - for isym in antisym: - if len(isym) > 1: - for i in isym: - if i<0 or i>self._tensor_rank-1: - raise IndexError("invalid position: " + str(i) + - " not in [0," + str(self._tensor_rank-1) + "]") - self._antisym.append(tuple(isym)) - - # Final consistency check: - index_list = [] - for isym in self._sym: - index_list += isym - for isym in self._antisym: - index_list += isym - if len(index_list) != len(set(index_list)): - # There is a repeated index position: - raise IndexError("incompatible lists of symmetries: the same " + - "position appears more than once") + self._sym, self._antisym = CompWithSym._canonicalize_sym_antisym( + self._tensor_rank, sym, antisym) # Initialization of derived quantities: FreeModuleTensor._init_derived(self) @@ -405,7 +372,7 @@ def _repr_(self): """ # Special cases - if self._tensor_type == (0,2) and self._sym == [(0,1)]: + if self._tensor_type == (0,2) and self._sym == ((0,1),): description = "Symmetric bilinear form " else: # Generic case From 970c6c88e3b1e8efc54fccff8b68a67c83bdcf5b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:50:42 -0700 Subject: [PATCH 288/454] src/sage/tensor, src/sage/manifolds: Sort _sym, _antisym, store as tuples of tuples (fixup) --- .../manifolds/differentiable/pseudo_riemannian_submanifold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 2eaef7510c8..3aef31eb703 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -1173,7 +1173,7 @@ def ambient_second_fundamental_form(self): g.restrict(chart.domain()).contract(pf[j]) * self.scalar_field({chart: k.comp(chart.frame())[:][i, j]}) for i in range(self._dim) for j in range(self._dim)) - gam_rst._sym = [(0, 1)] + gam_rst._sym = ((0, 1),) self._ambient_second_fundamental_form.set_restriction(gam_rst) charts = iter(self.top_charts()) From 9f15e00a4396a993fe412b41993cd542d4f177a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:50:57 -0700 Subject: [PATCH 289/454] src/sage/tensor: Allow _sym, _antisym attributes to be tuples (fixup) --- src/sage/manifolds/differentiable/vectorfield_module.py | 8 ++++---- src/sage/tensor/modules/finite_rank_free_module.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index cf3f1dc687d..393218bc7c0 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -787,7 +787,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -799,7 +799,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -2102,7 +2102,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -2114,7 +2114,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 091dc252ad5..c0692104581 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1458,7 +1458,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -1470,7 +1470,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym From 04706dd4af5955e9fa38d5c9ab49d25605d344fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 12:09:01 -0700 Subject: [PATCH 290/454] FiniteRankFreeModule.tensor_module: Canonicalize sym, antisym --- src/sage/tensor/modules/finite_rank_free_module.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index a0b4973f91b..8d3c41e4e26 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1197,9 +1197,16 @@ def tensor_module(self, k, l, *, sym=None, antisym=None): and :class:`~sage.tensor.modules.tensor_free_module.TensorFreeSubmodule_sym` for more documentation. + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 2) + sage: M.tensor_module(2, 0, sym=(0,1)) is M.symmetric_power(2) + True """ + from .comp import CompWithSym, CompFullyAntiSym + + sym, antisym = CompWithSym._canonicalize_sym_antisym(k + l, sym, antisym) if sym or antisym: - # TODO: Canonicalize sym, antisym, make hashable key = (k, l, sym, antisym) else: key = (k, l) From 726e56229dd72791dc649265b73ec45bdab46c5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 20:42:31 -0700 Subject: [PATCH 291/454] src/sage/tensor/modules/finite_rank_free_module.py: Remove unused import --- src/sage/tensor/modules/finite_rank_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 8d3c41e4e26..4a99be4c88f 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1203,7 +1203,7 @@ def tensor_module(self, k, l, *, sym=None, antisym=None): sage: M.tensor_module(2, 0, sym=(0,1)) is M.symmetric_power(2) True """ - from .comp import CompWithSym, CompFullyAntiSym + from .comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym(k + l, sym, antisym) if sym or antisym: From 218b548a3920587c27b3cfd1b63d5b7f4dc431f9 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 29 Aug 2022 22:42:30 -0500 Subject: [PATCH 292/454] Make a generator, reorder, and move tmp variable --- src/sage/combinat/partition.py | 126 ++++++++++++++++----------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index af39531c26c..b6e606abf1f 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -4752,78 +4752,25 @@ def add_horizontal_border_strip(self, k): res.append(_Partitions(tmp)) return res - def horizontal_border_strip_cells(self, k): - """ - Return a list of all the horizontal border strips of length ``k`` - which can be added to ``self``, where each horizontal border strip is - represented as a list of cells. - - EXAMPLES:: - - sage: Partition([]).horizontal_border_strip_cells(0) - [] - sage: Partition([3,2,1]).horizontal_border_strip_cells(0) - [] - sage: Partition([]).horizontal_border_strip_cells(2) - [[(0, 0), (0, 1)]] - sage: Partition([2,2]).horizontal_border_strip_cells(2) - [[(0, 2), (0, 3)], [(0, 2), (2, 0)], [(2, 0), (2, 1)]] - sage: Partition([3,2,2]).horizontal_border_strip_cells(2) - [[(0, 3), (0, 4)], - [(0, 3), (1, 2)], - [(0, 3), (3, 0)], - [(1, 2), (3, 0)], - [(3, 0), (3, 1)]] - """ - if k == 0: - return list() - - L = self._list - res = [] - shelf = [k] # the number of boxes which will fit in a row - mapping = [0] # a record of the rows - for i in range(len(L)-1): - val = L[i] - L[i+1] - if not val: - continue - mapping.append(i+1) - shelf.append(val) - - # add the last shelf - if L: - mapping.append(len(L)) - shelf.append(L[-1]) - - L.append(0) # add room on the bottom - # list all of the positions for cells - # filling each self from the top to bottom - for iv in IntegerListsBackend_invlex(k, length=len(shelf), ceiling=shelf, check=False)._iter(): - tmp = [] - # mapping[i] is the row index, val is the number of cells added to the row. - for i, val in enumerate(iv): - tmp.extend((mapping[i], L[mapping[i]] + j) for j in range(val)) - res.append(tmp) - return res - def vertical_border_strip_cells(self, k): """ Return a list of all the vertical border strips of length ``k`` which can be added to ``self``, where each horizontal border strip is - represented as a list of cells. + a ``generator`` of cells. EXAMPLES:: - sage: Partition([]).vertical_border_strip_cells(0) + sage: list(Partition([]).vertical_border_strip_cells(0)) [] - sage: Partition([3,2,1]).vertical_border_strip_cells(0) + sage: list(Partition([3,2,1]).vertical_border_strip_cells(0)) [] - sage: Partition([]).vertical_border_strip_cells(2) + sage: list(Partition([]).vertical_border_strip_cells(2)) [[(0, 0), (1, 0)]] - sage: Partition([2,2]).vertical_border_strip_cells(2) + sage: list(Partition([2,2]).vertical_border_strip_cells(2)) [[(0, 2), (1, 2)], [(0, 2), (2, 0)], [(2, 0), (3, 0)]] - sage: Partition([3,2,2]).vertical_border_strip_cells(2) + sage: list(Partition([3,2,2]).vertical_border_strip_cells(2)) [[(0, 3), (1, 2)], [(0, 3), (3, 0)], [(1, 2), (2, 2)], @@ -4851,8 +4798,10 @@ def vertical_border_strip_cells(self, k): # the first line shelf.append(k) # list all of the positions for cells - for iv in IntegerListsBackend_invlex(k, length=len(shelf), ceiling=shelf, check=False)._iter(): - tmp = self._list + [0]*k + tmp = self._list + [0]*k + for iv in IntegerListsBackend_invlex(k, length=len(shelf), + ceiling=shelf, + check=False)._iter(): j = 0 current_strip = [] for t in range(len(iv)): @@ -4860,8 +4809,59 @@ def vertical_border_strip_cells(self, k): current_strip.append((j, tmp[j])) j += 1 j = sum(shelf[:t+1]) - res.append(current_strip) - return res + yield current_strip + + def horizontal_border_strip_cells(self, k): + """ + Return a list of all the horizontal border strips of length ``k`` + which can be added to ``self``, where each horizontal border strip is + a ``generator`` of cells. + + EXAMPLES:: + + sage: list(Partition([]).horizontal_border_strip_cells(0)) + [] + sage: list(Partition([3,2,1]).horizontal_border_strip_cells(0)) + [] + sage: list(Partition([]).horizontal_border_strip_cells(2)) + [[(0, 0), (0, 1)]] + sage: list(Partition([2,2]).horizontal_border_strip_cells(2)) + [[(0, 2), (0, 3)], [(0, 2), (2, 0)], [(2, 0), (2, 1)]] + sage: list(Partition([3,2,2]).horizontal_border_strip_cells(2)) + [[(0, 3), (0, 4)], + [(0, 3), (1, 2)], + [(0, 3), (3, 0)], + [(1, 2), (3, 0)], + [(3, 0), (3, 1)]] + """ + if k == 0: + return list() + + L = self._list + res = [] + shelf = [k] # the number of boxes which will fit in a row + mapping = [0] # a record of the rows + for i in range(len(L)-1): + val = L[i] - L[i+1] + if not val: + continue + mapping.append(i+1) + shelf.append(val) + + # add the last shelf + if L: + mapping.append(len(L)) + shelf.append(L[-1]) + + L.append(0) # add room on the bottom + # list all of the positions for cells + # filling each self from the top to bottom + for iv in IntegerListsBackend_invlex(k, length=len(shelf), ceiling=shelf, check=False)._iter(): + tmp = [] + # mapping[i] is the row index, val is the number of cells added to the row. + for i, val in enumerate(iv): + tmp.extend((mapping[i], L[mapping[i]] + j) for j in range(val)) + yield tmp def remove_horizontal_border_strip(self, k): """ From 4958211e8186b777705a27a31663967cdbd0e24e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 23:24:29 -0700 Subject: [PATCH 293/454] Components.items: New --- src/sage/tensor/modules/comp.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 04f14f782f0..695646d18db 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -1023,6 +1023,40 @@ def _set_value_list(self, ind, format_type, val): for i in range(si, nsi): self._set_value_list(ind + [i], format_type, val[i-si]) + def items(self): + r""" + Return an iterable of ``(indices, value)`` elements. + + This may (but is not guaranteed to) suppress zero values. + + EXAMPLES:: + + sage: from sage.tensor.modules.comp import Components, CompWithSym + + sage: c = Components(ZZ, (ZZ^2).basis(), 3) + sage: c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3 + sage: list(c.items()) + [((0, 1, 0), -2), ((1, 0, 1), 5), ((1, 1, 1), 3)] + + sage: c = CompWithSym(ZZ, (ZZ^2).basis(), 3, sym=((1, 2))) + sage: c[0,1,0], c[1,0,1], c[1,1,1] = -2, 5, 3 + sage: list(c.items()) + [((0, 0, 1), -2), + ((0, 1, 0), -2), + ((1, 0, 1), 5), + ((1, 1, 0), 5), + ((1, 1, 1), 3)] + + """ + for ind in self.index_generator(): + val = self[ind] + if hasattr(val, 'is_trivial_zero'): + zero_value = val.is_trivial_zero() + else: + zero_value = val == 0 + if not zero_value: + yield ind, val + def display(self, symbol, latex_symbol=None, index_positions=None, index_labels=None, index_latex_labels=None, format_spec=None, only_nonzero=True, only_nonredundant=False): From 74d94934cad6eaa082f5beec41dd7440c7441a63 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 23:58:01 -0700 Subject: [PATCH 294/454] Matrix.items: New --- src/sage/matrix/matrix0.pyx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 00037358faa..372ba91ec65 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -248,6 +248,22 @@ cdef class Matrix(sage.structure.element.Matrix): monomial_coefficients = dict + def items(self): + r""" + Return an iterable of ``((i,j), value)`` elements. + + This may (but is not guaranteed to) suppress zero values. + + EXAMPLES:: + + sage: a = matrix(QQ['x,y'], 2, range(6), sparse=True); a + [0 1 2] + [3 4 5] + sage: list(a.items()) + [((0, 1), 1), ((0, 2), 2), ((1, 0), 3), ((1, 1), 4), ((1, 2), 5)] + """ + return self._dict().items() + def _dict(self): """ Unsafe version of the dict method, mainly for internal use. From 90a26a118aef26b8892967dae7c42369784aedd0 Mon Sep 17 00:00:00 2001 From: Nils Bruin Date: Tue, 30 Aug 2022 11:28:12 -0700 Subject: [PATCH 295/454] S --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 4e679b453df..4bacb2545b6 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3390,7 +3390,7 @@ def curve(self): Curve from which Riemann surface is obtained. - EXAMPLE:: + EXAMPLES:: sage: R. = QQ[] sage: C = Curve( y^3+x^3-1) From cd0b9482015e3927e468a7ec045c8d5bc077f709 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 13:26:45 +0900 Subject: [PATCH 296/454] Revert to __classcall_private__ idiom --- src/sage/homology/free_resolution.py | 149 +++++++++++++-------------- src/sage/misc/constant_function.pyx | 128 ----------------------- 2 files changed, 72 insertions(+), 205 deletions(-) delete mode 100644 src/sage/misc/constant_function.pyx diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index fb1fbb5a397..bfe209b5fe0 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -83,95 +83,90 @@ from copy import copy -def free_resolution_constructor(module, degrees=None, shifts=None, name='S', graded=False, **kwds): +class FreeResolution(SageObject, metaclass=ClasscallMetaclass): """ - Constructor. + Abstract base class for free resolutions. + """ + @staticmethod + def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', graded=False, **kwds): + """ + Dispatch to the correct constructor. - TESTS:: + TESTS:: - sage: from sage.homology.free_resolution import FreeResolution - sage: S. = PolynomialRing(QQ) - sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() - sage: r = FreeResolution(m, name='S') - sage: type(r) - - sage: isinstance(r, FreeResolution) - True + sage: from sage.homology.free_resolution import FreeResolution + sage: S. = PolynomialRing(QQ) + sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() + sage: r = FreeResolution(m, name='S') + sage: type(r) + - sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) - sage: r = FreeResolution(I) - sage: type(r) - + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: r = FreeResolution(I) + sage: type(r) + - sage: R. = QQ[] - sage: M = R^3 - sage: v = M([x^2, 2*x^2, 3*x^2]) - sage: w = M([0, x, 2*x]) - sage: S = M.submodule([v, w]) - sage: r = FreeResolution(S) - sage: type(r) - + sage: R. = QQ[] + sage: M = R^3 + sage: v = M([x^2, 2*x^2, 3*x^2]) + sage: w = M([0, x, 2*x]) + sage: S = M.submodule([v, w]) + sage: r = FreeResolution(S) + sage: type(r) + - sage: I = R.ideal([x^4 + 3*x^2 + 2]) - sage: r = FreeResolution(I) - sage: type(r) - - """ - # The module might still be free even if is_free_module is False. - # This is just to handle the cases when we trivially know it is. - is_free_module = False - if isinstance(module, Ideal_generic): - S = module.ring() - if len(module.gens()) == 1 and S in IntegralDomains(): - is_free_module = True - elif isinstance(module, Module_free_ambient): - S = module.base_ring() - if (S in PrincipalIdealDomains() - or isinstance(module, FreeModule_generic)): - is_free_module = True - elif isinstance(module, Matrix): - S = module.base_ring() - if S in PrincipalIdealDomains(): - module = module.echelon_form() - if module.nrows() > module.rank(): - module = module.submatrix(nrows=module.rank()) + sage: I = R.ideal([x^4 + 3*x^2 + 2]) + sage: r = FreeResolution(I) + sage: type(r) + + """ + # The module might still be free even if is_free_module is False. + # This is just to handle the cases when we trivially know it is. + is_free_module = False + if isinstance(module, Ideal_generic): + S = module.ring() + if len(module.gens()) == 1 and S in IntegralDomains(): + is_free_module = True + elif isinstance(module, Module_free_ambient): + S = module.base_ring() + if (S in PrincipalIdealDomains() + or isinstance(module, FreeModule_generic)): + is_free_module = True + elif isinstance(module, Matrix): + S = module.base_ring() + if S in PrincipalIdealDomains(): + module = module.echelon_form() + if module.nrows() > module.rank(): + module = module.submatrix(nrows=module.rank()) + module.set_immutable() + is_free_module = True + if not module.is_immutable(): + # We need to make an immutable copy of the matrix + module = copy(module) module.set_immutable() - is_free_module = True - if not module.is_immutable(): - # We need to make an immutable copy of the matrix - module = copy(module) - module.set_immutable() - else: - raise TypeError('no module, matrix, or ideal') - - if not is_free_module: - from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular - if not isinstance(S, MPolynomialRing_libsingular): - raise NotImplementedError("the module must be a free module or " - "the base ring must be a polynomial ring using Singular") - - if graded or degrees is not None or shifts is not None: - # We are computing a graded resolution - from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular - return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) + else: + raise TypeError('no module, matrix, or ideal') - return FiniteFreeResolution_singular(module, name=name, **kwds) + if not is_free_module: + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if not isinstance(S, MPolynomialRing_libsingular): + raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") - # Otherwise we know it is a free module + if graded or degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular + return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) - if graded or degrees is not None or shifts is not None: - # We are computing a graded resolution - from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module - return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) + return FiniteFreeResolution_singular(module, name=name, **kwds) - return FiniteFreeResolution_free_module(module, name=name, **kwds) + # Otherwise we know it is a free module + if graded or degrees is not None or shifts is not None: + # We are computing a graded resolution + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) -class FreeResolution(SageObject, metaclass=ConstructorBaseclassMetaclass): - """ - Abstract base class for free resolutions. - """ - __constructor__ = free_resolution_constructor + return FiniteFreeResolution_free_module(module, name=name, **kwds) def __init__(self, module, name='S', **kwds): """ @@ -585,7 +580,7 @@ def _m(self): r""" Return the matrix whose column space is ``self._module``. - If ``self._module`` is an ideal, then returns just the ideal. + If ``self._module`` is an ideal, then just the ideal is returned. TESTS:: diff --git a/src/sage/misc/constant_function.pyx b/src/sage/misc/constant_function.pyx deleted file mode 100644 index e94f36da403..00000000000 --- a/src/sage/misc/constant_function.pyx +++ /dev/null @@ -1,128 +0,0 @@ -r""" -Constant functions -""" - -#***************************************************************************** -# Copyright (C) 2009 Nicolas M. Thiery -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.structure.richcmp cimport richcmp -from sage.structure.sage_object cimport SageObject - - -cdef class ConstantFunction(SageObject): - """ - A class for function objects implementing constant functions. - - EXAMPLES:: - - sage: f = ConstantFunction(3) - sage: f - The constant function (...) -> 3 - sage: f() - 3 - sage: f(5) - 3 - - Such a function could be implemented as a lambda expression, but - this is not (currently) picklable:: - - sage: g = lambda x: 3 - sage: g == loads(dumps(g)) - Traceback (most recent call last): - ... - PicklingError: Can't pickle ...: attribute lookup ... failed - sage: f == loads(dumps(f)) - True - - Also, in the long run, the information that this function is - constant could be used by some algorithms. - - .. TODO:: - - - Should constant functions have unique representation? - - Should the number of arguments be specified in the input? - - Should this go into ``sage.categories.maps``? - Then what should be the parent (e.g. for ``lambda x: True``)? - - TESTS: - - These tests do fail if we try to use ``UniqueRepresentation``:: - - sage: f = ConstantFunction(True) - sage: g = ConstantFunction(1) - sage: f(), g() - (True, 1) - - That's because ``1`` and ``True`` cannot be distinguished as keys - in a dictionary (argl!):: - - sage: { 1: 'a', True: 'b' } - {1: 'b'} - """ - cdef object _value - - def __init__(self, value): - """ - EXAMPLES:: - - sage: ConstantFunction(1)() - 1 - """ - self._value = value - - def __reduce__(self): - """ - TESTS:: - - sage: loads(dumps(ConstantFunction(5))) == ConstantFunction(5) # indirect doctest - True - - """ - return ConstantFunction, (self._value,) - - def _repr_(self): - """ - EXAMPLES:: - - sage: ConstantFunction(1) - The constant function (...) -> 1 - """ - return "The constant function (...) -> %s"%self._value - - def __call__(self, *args): - """ - EXAMPLES:: - - sage: ConstantFunction(1)() - 1 - sage: ConstantFunction(1)(5,3) - 1 - sage: ConstantFunction(True)() - True - """ - return self._value - - def __richcmp__(self, other, op): - """ - EXAMPLES:: - - sage: ConstantFunction(1) == ConstantFunction(1) - True - sage: ConstantFunction(1) == ConstantFunction(3) - False - sage: ConstantFunction(1) == 1 - False - sage: ConstantFunction(True) == ConstantFunction(1) # argl! - True - """ - if not isinstance(other, ConstantFunction): - return NotImplemented - return richcmp((self)._value, - (other)._value, op) From d0e11a3d9816bb5ec9034272f904ff28e939f1d4 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 13:49:05 +0900 Subject: [PATCH 297/454] Some edits --- src/sage/homology/free_resolution.py | 24 ++++++------ src/sage/homology/graded_resolution.py | 10 +++-- src/sage/libs/singular/singular.pyx | 37 +++++++++---------- .../misc/constructor_baseclass_metaclass.pyx | 21 ----------- src/sage/modules/free_module.py | 19 ++++++++-- .../polynomial/multi_polynomial_ideal.py | 4 +- 6 files changed, 54 insertions(+), 61 deletions(-) delete mode 100644 src/sage/misc/constructor_baseclass_metaclass.pyx diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index bfe209b5fe0..49fc3d7478f 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -119,6 +119,17 @@ def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', grad sage: r = FreeResolution(I) sage: type(r) + + sage: R. = QQ[] + sage: I = R.ideal([x^2, y^3]) + sage: Q = R.quo(I) + sage: Q.is_integral_domain() + False + sage: xb, yb = Q.gens() + sage: FreeResolution(Q.ideal([xb])) # has torsion + Traceback (most recent call last): + ... + NotImplementedError: the module must be a free module or have the base ring be a polynomial ring using Singular """ # The module might still be free even if is_free_module is False. # This is just to handle the cases when we trivially know it is. @@ -726,7 +737,9 @@ class FiniteFreeResolution_singular(FiniteFreeResolution): - ``module`` -- a submodule of a free module `M` of rank `n` over `S` or an ideal of a multi-variate polynomial ring + - ``name`` -- string (optional); name of the base ring + - ``algorithm`` -- (default: ``'heuristic'``) Singular algorithm to compute a resolution of ``ideal`` @@ -820,17 +833,6 @@ def __init__(self, module, name='S', algorithm='heuristic', **kwds): sage: M = m.image() sage: r = FreeResolution(M, name='S') sage: TestSuite(r).run(skip=['_test_pickling']) - - sage: R. = QQ[] - sage: I = R.ideal([x^2, y^3]) - sage: Q = R.quo(I) - sage: Q.is_integral_domain() - False - sage: xb, yb = Q.gens() - sage: FreeResolution(Q.ideal([xb])) # has torsion - Traceback (most recent call last): - ... - NotImplementedError: the module must be a free module or have the base ring be a polynomial ring using Singular """ self._algorithm = algorithm super().__init__(module, name=name, **kwds) diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index 6396c0ab65f..88b6184fb03 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -87,6 +87,7 @@ FiniteFreeResolution_free_module, FiniteFreeResolution_singular) + class GradedFiniteFreeResolution(FiniteFreeResolution): r""" Graded finite free resolutions. @@ -190,6 +191,7 @@ def _repr_module(self, i): shifts = self._shifts else: shifts = self._res_shifts[i - 1] + if not shifts: return '0' @@ -310,6 +312,7 @@ def K_polynomial(self, names=None): return kpoly + class GradedFiniteFreeResolution_free_module(GradedFiniteFreeResolution, FiniteFreeResolution_free_module): r""" Graded free resolution of free modules. @@ -351,7 +354,8 @@ def __init__(self, module, degrees=None, *args, **kwds): super().__init__(module, degrees=degrees, *args, **kwds) if len(self._degrees) > 1 and any(d != 1 for d in self._degrees): - raise NotImplementedError("only the natural grading supported when more than one generator") + raise NotImplementedError("only the natural grading supported " + "when more than one generator") @lazy_attribute def _maps(self): @@ -399,7 +403,6 @@ def _maps(self): sage: res._res_shifts [[9]] """ - def compute_degree(base, i): """ Compute the degree by ``base * deg + shift``, @@ -422,10 +425,11 @@ def compute_degree(base, i): def find_deg(i): for j in range(M.nrows()): - ret = M[j,i].degree() + ret = M[j,i].degree() if ret != -1: return ret raise NotImplementedError("a generator maps to 0") + self._res_shifts = [[compute_degree(find_deg(i), i) for i in range(M.ncols())]] return [M] diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 82d83e01212..bb729cf2db2 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -865,9 +865,9 @@ cdef number *sa2si_QQ(Rational r, ring *_ring): INPUT: - - ``r`` - a sage rational number + - ``r`` -- a sage rational number - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: @@ -895,9 +895,9 @@ cdef number *sa2si_GFqGivaro(int quo, ring *_ring): INPUT: - - ``quo`` - a sage integer + - ``quo`` -- a sage integer - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: @@ -963,9 +963,9 @@ cdef number *sa2si_GFqNTLGF2E(FFgf2eE elem, ring *_ring): INPUT: - - ``elem`` - a sage element of a ntl_gf2e finite field + - ``elem`` -- a sage element of a ntl_gf2e finite field - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: @@ -1028,9 +1028,9 @@ cdef number *sa2si_GFq_generic(object elem, ring *_ring): INPUT: - - ``elem`` - a sage element of a generic finite field + - ``elem`` -- a sage element of a generic finite field - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: @@ -1094,15 +1094,14 @@ cdef number *sa2si_transext_QQ(object elem, ring *_ring): INPUT: - - ``elem`` - a sage element of a FractionField of polynomials over the rationals + - ``elem`` -- a sage element of a FractionField of polynomials over the rationals - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: - A (pointer to) a singular number - TESTS:: sage: F = PolynomialRing(QQ,'a,b').fraction_field() @@ -1245,15 +1244,14 @@ cdef number *sa2si_transext_FF(object elem, ring *_ring): INPUT: - - ``elem`` - a sage element of a FractionField of polynomials over the rationals + - ``elem`` -- a sage element of a FractionField of polynomials over the rationals - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: - A (pointer to) a singular number - TESTS:: sage: F = PolynomialRing(FiniteField(7),'a,b').fraction_field() @@ -1346,9 +1344,9 @@ cdef number *sa2si_NF(object elem, ring *_ring): INPUT: - - ``elem`` - a sage element of a NumberField + - ``elem`` -- a sage element of a NumberField - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: @@ -1437,15 +1435,14 @@ cdef number *sa2si_ZZ(Integer d, ring *_ring): INPUT: - - ``elem`` - a sage Integer + - ``elem`` -- a sage Integer - - ``_ ring`` - a (pointer to) a singular ring, where the resul will live + - ``_ ring`` -- a (pointer to) a singular ring, where the resul will live OUTPUT: - A (pointer to) a singular number - TESTS:: sage: P. = ZZ[] @@ -1650,7 +1647,7 @@ cdef object si2sa_intvec(intvec *v): INPUT: - - ``v`` -- a (pointer to) a singular intvec + - ``v`` -- a (pointer to) singular intvec OUTPUT: diff --git a/src/sage/misc/constructor_baseclass_metaclass.pyx b/src/sage/misc/constructor_baseclass_metaclass.pyx deleted file mode 100644 index fc3ca66f641..00000000000 --- a/src/sage/misc/constructor_baseclass_metaclass.pyx +++ /dev/null @@ -1,21 +0,0 @@ -from sage.misc.classcall_metaclass cimport ClasscallMetaclass - - -cdef class ConstructorBaseclassMetaclass(ClasscallMetaclass): - - def __cinit__(self, *args, **opts): - r""" - TESTS:: - """ - if '__constructor__' in self.__dict__: - def constructor(cls, *a, **o): - return self.__constructor__(*a, **o) - self.classcall = constructor - else: - self.classcall = None - - self.classcontains = getattr(self, "__classcontains__", None) - self.classget = getattr(self, "__classget__", None) - - - diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 8e5c3da9e17..7af5a6e69b9 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1777,10 +1777,20 @@ def free_resolution(self, *args, **kwds): sage: ascii_art(res.chain_complex()) [x - y y*z] [ z x*z] - 0 <-- C_0 <-------------- C_1 <-- 0 + 0 <-- C_0 <-------------- C_1 <-- 0 """ - from sage.homology.free_resolution import FreeResolution - return FreeResolution(self, *args, **kwds) + from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular + if isinstance(self.base_ring(), MPolynomialRing_libsingular): + from sage.homology.free_resolution import FiniteFreeResolution_singular + return FiniteFreeResolution_singular(self, *args, **kwds) + + if isinstance(self, FreeModule_generic): + from sage.homology.free_resolution import FiniteFreeResolution_free_module + return FiniteFreeResolution_free_module(self, *args, **kwds) + + raise NotImplementedError("the module must be a free module or " + "have the base ring be a polynomial ring using Singular") + def graded_free_resolution(self, *args, **kwds): r""" @@ -1812,7 +1822,8 @@ def graded_free_resolution(self, *args, **kwds): from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module return GradedFiniteFreeResolution_free_module(self, *args, **kwds) - raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") + raise NotImplementedError("the module must be a free module or " + "have the base ring be a polynomial ring using Singular") class FreeModule_generic(Module_free_ambient): diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 18f52b0ad74..92d73dff1b5 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1794,8 +1794,8 @@ def free_resolution(self, *args, **kwds): sage: res S^1 <-- S^2 <-- S^1 <-- 0 sage: ascii_art(res.chain_complex()) - [-x^2] - [ y x^2] [ y] + [-x^2] + [ y x^2] [ y] 0 <-- C_0 <---------- C_1 <------- C_2 <-- 0 sage: q = ZZ['q'].fraction_field().gen() From 22dd075ca6183d0b971d311077fc31adeb650cc0 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:14:51 +0900 Subject: [PATCH 298/454] Nonhomogeneous examples --- src/sage/homology/graded_resolution.py | 28 ++++++-------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index 88b6184fb03..da04739b047 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -96,11 +96,14 @@ class GradedFiniteFreeResolution(FiniteFreeResolution): - ``module`` -- a homogeneous submodule of a free module `M` of rank `n` over `S` or a homogeneous ideal of a multivariate polynomial ring `S` + - ``degrees`` -- (default: a list with all entries `1`) a list of integers or integer vectors giving degrees of variables of `S` + - ``shifts`` -- a list of integers or integer vectors giving shifts of degrees of `n` summands of the free module `M`; this is a list of zero degrees of length `n` by default + - ``name`` -- a string; name of the base ring .. WARNING:: @@ -121,9 +124,9 @@ def __init__(self, module, degrees=None, shifts=None, name='S', **kwds): An overdetermined system over a PID:: sage: from sage.homology.free_resolution import FreeResolution - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) + sage: M = matrix([[x^2, 2*x^2], + ....: [3*x^2, 5*x^2], + ....: [5*x^2, 4*x^2]]) sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 @@ -330,13 +333,6 @@ class GradedFiniteFreeResolution_free_module(GradedFiniteFreeResolution, FiniteF sage: res = FreeResolution(M, graded=True) sage: res S(0)⊕S(0)⊕S(0) <-- S(-3)⊕S(-1) <-- 0 - - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) - sage: res = FreeResolution(M, graded=True) - sage: res - S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 """ def __init__(self, module, degrees=None, *args, **kwds): """ @@ -382,18 +378,6 @@ def _maps(self): sage: res._res_shifts [[3, 1]] - sage: M = matrix([[x^2, 2], - ....: [3*x^2, 5], - ....: [5*x^2, 4]]) - sage: res = FreeResolution(M, graded=True) - sage: res._maps - [ - [x^2 0] - [ 2 -1] - ] - sage: res._res_shifts - [[2, 0]] - sage: I = R.ideal([x^4]) sage: res = I.graded_free_resolution(shifts=[1], degrees=[2]) sage: res From 22f74a24bc8892e6d1c0b527f49f1ea8e06aa3af Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:21:47 +0900 Subject: [PATCH 299/454] Remove ConstructorBaseclassMetaclass --- src/sage/homology/free_resolution.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 49fc3d7478f..8a81073e044 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -70,7 +70,6 @@ from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method -from sage.misc.constructor_baseclass_metaclass import ConstructorBaseclassMetaclass from sage.structure.sage_object import SageObject from sage.structure.element import Matrix from sage.categories.principal_ideal_domains import PrincipalIdealDomains From 609b40d91ae190ad02078f55a554ac15e4415810 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:21:47 +0900 Subject: [PATCH 300/454] Recover ClasscallMetaclass --- src/sage/homology/free_resolution.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 8a81073e044..452a41a6de8 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -70,6 +70,7 @@ from sage.libs.singular.function import singular_function from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method +from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.structure.sage_object import SageObject from sage.structure.element import Matrix from sage.categories.principal_ideal_domains import PrincipalIdealDomains From b939bbc5cc7aba2d8f9636f16f3d35a9c02039cb Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:31:31 +0900 Subject: [PATCH 301/454] Fix in projective_morphism.py --- src/sage/schemes/projective/projective_morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 42402cfbc74..92d3b189502 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -88,7 +88,7 @@ from sage.categories.number_fields import NumberFields from sage.categories.homset import Hom, End from sage.categories.fields import Fields -from sage.homology.graded_resolution import GradedFreeResolution +from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular _NumberFields = NumberFields() _FiniteFields = FiniteFields() @@ -2691,7 +2691,7 @@ def projective_degrees(self): I = G.defining_ideal() # a bihomogeneous ideal degrees = xn*[vector([1,0])] + yn*[vector([0,1])] - res = GradedFreeResolution(I, degrees, algorithm='shreyer') + res = GradeFinitedFreeResolution_singular(I, degrees, algorithm='shreyer') kpoly = res.K_polynomial() L = kpoly.parent() From 1622605005feb13cee9956af3f2f395a2ecedb36 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:38:16 +0900 Subject: [PATCH 302/454] Fix of _repr_() --- src/sage/homology/free_resolution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 452a41a6de8..f6bc238ff4d 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -216,11 +216,11 @@ def _repr_(self): sage: m1 = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]) sage: r = FreeResolution(m1, name='S') sage: print(FreeResolution._repr_(r)) - Free resolution with initial map: + Free resolution of the row space of the matrix: [z^2 - y*w y*z - x*w y^2 - x*z] """ if isinstance(self._module, Matrix): - return f"Free resolution of the :\n{self._module}" + return f"Free resolution of the row space of the matrix:\n{self._module}" return f"Free resolution of {self._module}" def _repr_module(self, i): From 81d6afb0ac2adee48cbabe59eb1946f96161d3a3 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:46:43 +0900 Subject: [PATCH 303/454] Recover errorneously deleted constant_function.pyx --- src/sage/misc/constant_function.pyx | 128 ++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/sage/misc/constant_function.pyx diff --git a/src/sage/misc/constant_function.pyx b/src/sage/misc/constant_function.pyx new file mode 100644 index 00000000000..e94f36da403 --- /dev/null +++ b/src/sage/misc/constant_function.pyx @@ -0,0 +1,128 @@ +r""" +Constant functions +""" + +#***************************************************************************** +# Copyright (C) 2009 Nicolas M. Thiery +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.richcmp cimport richcmp +from sage.structure.sage_object cimport SageObject + + +cdef class ConstantFunction(SageObject): + """ + A class for function objects implementing constant functions. + + EXAMPLES:: + + sage: f = ConstantFunction(3) + sage: f + The constant function (...) -> 3 + sage: f() + 3 + sage: f(5) + 3 + + Such a function could be implemented as a lambda expression, but + this is not (currently) picklable:: + + sage: g = lambda x: 3 + sage: g == loads(dumps(g)) + Traceback (most recent call last): + ... + PicklingError: Can't pickle ...: attribute lookup ... failed + sage: f == loads(dumps(f)) + True + + Also, in the long run, the information that this function is + constant could be used by some algorithms. + + .. TODO:: + + - Should constant functions have unique representation? + - Should the number of arguments be specified in the input? + - Should this go into ``sage.categories.maps``? + Then what should be the parent (e.g. for ``lambda x: True``)? + + TESTS: + + These tests do fail if we try to use ``UniqueRepresentation``:: + + sage: f = ConstantFunction(True) + sage: g = ConstantFunction(1) + sage: f(), g() + (True, 1) + + That's because ``1`` and ``True`` cannot be distinguished as keys + in a dictionary (argl!):: + + sage: { 1: 'a', True: 'b' } + {1: 'b'} + """ + cdef object _value + + def __init__(self, value): + """ + EXAMPLES:: + + sage: ConstantFunction(1)() + 1 + """ + self._value = value + + def __reduce__(self): + """ + TESTS:: + + sage: loads(dumps(ConstantFunction(5))) == ConstantFunction(5) # indirect doctest + True + + """ + return ConstantFunction, (self._value,) + + def _repr_(self): + """ + EXAMPLES:: + + sage: ConstantFunction(1) + The constant function (...) -> 1 + """ + return "The constant function (...) -> %s"%self._value + + def __call__(self, *args): + """ + EXAMPLES:: + + sage: ConstantFunction(1)() + 1 + sage: ConstantFunction(1)(5,3) + 1 + sage: ConstantFunction(True)() + True + """ + return self._value + + def __richcmp__(self, other, op): + """ + EXAMPLES:: + + sage: ConstantFunction(1) == ConstantFunction(1) + True + sage: ConstantFunction(1) == ConstantFunction(3) + False + sage: ConstantFunction(1) == 1 + False + sage: ConstantFunction(True) == ConstantFunction(1) # argl! + True + """ + if not isinstance(other, ConstantFunction): + return NotImplemented + return richcmp((self)._value, + (other)._value, op) From d8bc44fb999eaba5520cdb5f238c429b7cb052c1 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 31 Aug 2022 15:53:08 +0900 Subject: [PATCH 304/454] Minor fixes --- src/sage/homology/graded_resolution.py | 4 ++-- src/sage/schemes/projective/projective_morphism.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index da04739b047..1728193dfba 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -129,9 +129,9 @@ def __init__(self, module, degrees=None, shifts=None, name='S', **kwds): ....: [5*x^2, 4*x^2]]) sage: res = FreeResolution(M, graded=True) sage: res - S(0)⊕S(0) <-- S(-2)⊕S(0) <-- 0 + S(0)⊕S(0) <-- S(-2)⊕S(-2) <-- 0 sage: res._res_shifts - [[2, 0]] + [[2, 2]] """ super().__init__(module, name=name, **kwds) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 92d3b189502..e3347c409bc 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -2691,7 +2691,7 @@ def projective_degrees(self): I = G.defining_ideal() # a bihomogeneous ideal degrees = xn*[vector([1,0])] + yn*[vector([0,1])] - res = GradeFinitedFreeResolution_singular(I, degrees, algorithm='shreyer') + res = GradedFiniteFreeResolution_singular(I, degrees, algorithm='shreyer') kpoly = res.K_polynomial() L = kpoly.parent() From 52815744bde2b682245b6f985a112f7cb8666056 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 31 Aug 2022 08:58:07 -0700 Subject: [PATCH 305/454] build/pkgs/sympy: Update to 1.11.1 --- build/pkgs/sympy/checksums.ini | 6 +++--- build/pkgs/sympy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index d92eeec4219..f59c0083e07 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,5 +1,5 @@ tarball=sympy-VERSION.tar.gz -sha1=bb3ecaa4b223097831b5f0627d6579d8bd906840 -md5=973f746dd0b0d07b01f6b2b15603cbf8 -cksum=3499849170 +sha1=9e75c8cafa4324f2803a6488ac713d87adf5cb64 +md5=232141d248ab4164e92c8ac59a996914 +cksum=2645245679 upstream_url=https://github.com/sympy/sympy/releases/download/sympy-VERSION/sympy-VERSION.tar.gz diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index 09601587019..720c7384c61 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -1.11 +1.11.1 From b81dbf016724ada9b5cb84407e7a68f18dbc7126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 31 Aug 2022 20:53:43 +0200 Subject: [PATCH 306/454] fix doctest --- src/sage/categories/modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 21ee46b24f1..46e1da5c694 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -943,4 +943,5 @@ def tensor_factors(self): doctest:warning... DeprecationWarning: implementations of Modules().TensorProducts() now must define the method tensor_factors See https://trac.sagemath.org/34393 for details. + (VectorFunctor, Integer Ring) """ From 3809a6fe0dde3a90f1b22f838d301d7125288ff5 Mon Sep 17 00:00:00 2001 From: Thierry Monteil Date: Wed, 10 Aug 2022 13:58:34 +0200 Subject: [PATCH 307/454] remove outdated source tarballs with "sage --package clean" command --- build/sage_bootstrap/app.py | 26 ++++++++++++++++++++++++++ build/sage_bootstrap/cmdline.py | 22 ++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index d48316dba46..d6094609d88 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -1,6 +1,11 @@ # -*- coding: utf-8 -*- """ Controller for the commandline actions + +AUTHORS: + + - Volker Braun (2016): initial version + - Thierry Monteil (2022): clean option to remove outdated source tarballs """ @@ -26,6 +31,7 @@ from sage_bootstrap.pypi import PyPiVersion, PyPiNotFound, PyPiError from sage_bootstrap.fileserver import FileServer from sage_bootstrap.expand_class import PackageClass +from sage_bootstrap.env import SAGE_DISTFILES class Application(object): @@ -303,3 +309,23 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre else: update = ChecksumUpdater(package_name) update.fix_checksum() + + def clean(self): + """ + Remove outdated source tarballs from the upstream/ directory + + $ sage --package clean + 42 files were removed from the .../upstream directory + """ + log.debug('Cleaning upstream/ directory') + package_names = PackageClass(':all:').names + keep = [Package(package_name).tarball.filename for package_name in package_names] + count = 0 + for filename in os.listdir(SAGE_DISTFILES): + if filename not in keep: + filepath = os.path.join(SAGE_DISTFILES, filename) + if os.path.isfile(filepath): + log.debug('Removing file {}'.format(filepath)) + os.remove(filepath) + count += 1 + print('{} files were removed from the {} directory'.format(count, SAGE_DISTFILES)) diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 8c0515c0ede..ee8b342590d 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -4,6 +4,11 @@ This module handles the main "sage-package" commandline utility, which is also exposed as "sage --package". + +AUTHORS: + + - Volker Braun (2016): initial version + - Thierry Monteil (2022): clean option to remove outdated source tarballs """ # **************************************************************************** @@ -174,6 +179,16 @@ Creating new package "foo" """ +epilog_clean = \ +""" +Remove outdated source tarballs from the upstream/ directory + +EXAMPLE: + + $ sage --package clean + 42 files were removed from the .../upstream directory +""" + def make_parser(): """ @@ -316,6 +331,11 @@ def make_parser(): '--pypi', action="store_true", help='Create a package for a Python package available on PyPI') + parser_clean = subparsers.add_parser( + 'clean', epilog=epilog_clean, + formatter_class=argparse.RawDescriptionHelpFormatter, + help='Remove outdated source tarballs from the upstream/ directory') + return parser @@ -356,6 +376,8 @@ def run(): app.upload_cls(args.package_name) elif args.subcommand == 'fix-checksum': app.fix_checksum_cls(*args.package_class) + elif args.subcommand == 'clean': + app.clean() else: raise RuntimeError('unknown subcommand: {0}'.format(args)) From 6e5a36871fa519802d47b0759a270df91063e5b6 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 1 Sep 2022 08:25:42 +0800 Subject: [PATCH 308/454] fix various complaints from linters --- src/sage/algebras/clifford_algebra.py | 2 +- src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py | 6 +++--- src/sage/functions/special.py | 2 +- src/sage/groups/cubic_braid.py | 1 + src/sage/schemes/elliptic_curves/hom_velusqrt.py | 3 +++ 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 0def7ae087b..8fc5d750dd6 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -2984,7 +2984,7 @@ def groebner_basis(self, term_order=None, reduced=True): from sage.algebras.exterior_algebra_groebner import GroebnerStrategyDegLex as strategy else: raise ValueError("invalid term order") - if strategy == type(self._groebner_strategy): + if isinstance(self._groebner_strategy, strategy): if self._reduced or not reduced: return self._groebner_strategy.groebner_basis self._reduced = reduced diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py index 467313ce9b9..29ed81ff47d 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py @@ -1628,7 +1628,7 @@ def _tietze_to_finite_sub_basis_monomial(self, tietze_tup): def _create_matrix_list_for_one(self, representation_type): r""" Return the matrix list for the given representation type - for ``self.one()`. + for ``self.one()``. EXAMPLES:: @@ -2366,8 +2366,8 @@ def _reduce_all_gen_powers(self, braid_tietze): @cached_method def _reduce_gen_power(self, k): r""" - Return the ``k``-th power on an arbitrary generator, - for example ``c0^k` . + Return the `k`-th power on an arbitrary generator, + for example `c_0^k`. INPUT: diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index 02596e49620..901f02f9bee 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -849,7 +849,7 @@ class EllipticF(BuiltinFunction): - :wikipedia:`Elliptic_integral#Incomplete_elliptic_integral_of_the_first_kind` """ def __init__(self): - """ + r""" EXAMPLES:: sage: loads(dumps(elliptic_f)) diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index 6abc64d782b..7f6c89748e9 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -963,6 +963,7 @@ def _test_matrix_group(self, **options): - Construction of matrix group was faithful. - Coercion maps to and from matrix group exist and are inverse to each other. + EXAMPLES:: sage: CBG2 = CubicBraidGroup(2) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 86ca9e51de0..894608fd3a4 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -322,11 +322,14 @@ def prod_with_derivative(pairs): class _aux: def __init__(self, f, df): self.f, self.df = f, df + def __mul__(self, other): return _aux(self.f * other.f, self.df * other.f + self.f * other.df) + def __iter__(self): yield self.f yield self.df + return tuple(prod(_aux(*tup) for tup in pairs)) From b0de177897ed1c4c9b4bbe39cb310313ad189a62 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 31 Aug 2022 21:23:02 -0700 Subject: [PATCH 309/454] src/sage/modules/free_module.py: Pass category through all __init__s, use Subobjects() for submodules --- src/sage/modules/free_module.py | 64 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index d508b22fcc7..54c500c4403 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3500,7 +3500,7 @@ class FreeModule_generic_domain(FreeModule_generic): """ Base class for free modules over an integral domain. """ - def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None): + def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None, category=None): """ Create a free module over an integral domain. @@ -3511,7 +3511,7 @@ def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None): sage: FreeModule(PolynomialRing(GF(7),'x'), 2) Ambient free module of rank 2 over the principal ideal domain Univariate Polynomial Ring in x over Finite Field of size 7 """ - FreeModule_generic.__init__(self, base_ring, rank, degree, sparse, coordinate_ring) + FreeModule_generic.__init__(self, base_ring, rank, degree, sparse, coordinate_ring, category=category) def __add__(self, other): r""" @@ -3594,7 +3594,7 @@ class FreeModule_generic_pid(FreeModule_generic_domain): """ Base class for all free modules over a PID. """ - def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None): + def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None, category=None): """ Create a free module over a PID. @@ -3605,7 +3605,7 @@ def __init__(self, base_ring, rank, degree, sparse=False, coordinate_ring=None): sage: FreeModule(PolynomialRing(GF(7),'x'), 2) Ambient free module of rank 2 over the principal ideal domain Univariate Polynomial Ring in x over Finite Field of size 7 """ - super().__init__(base_ring, rank, degree, sparse, coordinate_ring) + super().__init__(base_ring, rank, degree, sparse, coordinate_ring, category=category) def index_in(self, other): """ @@ -4219,7 +4219,7 @@ class FreeModule_generic_field(FreeModule_generic_pid): """ Base class for all free modules over fields. """ - def __init__(self, base_field, dimension, degree, sparse=False): + def __init__(self, base_field, dimension, degree, sparse=False, category=None): """ Creates a vector space over a field. @@ -4240,7 +4240,7 @@ def __init__(self, base_field, dimension, degree, sparse=False): """ if not isinstance(base_field, ring.Field): raise TypeError("The base_field (=%s) must be a field"%base_field) - super().__init__(base_field, dimension, degree, sparse=sparse) + super().__init__(base_field, dimension, degree, sparse=sparse, category=category) def _Hom_(self, Y, category): r""" @@ -5207,7 +5207,7 @@ class FreeModule_ambient(FreeModule_generic): """ Ambient free module over a commutative ring. """ - def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): + def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None, category=None): """ The free module of given rank over the given base_ring. @@ -5243,7 +5243,7 @@ def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): True """ FreeModule_generic.__init__(self, base_ring, rank=rank, - degree=rank, sparse=sparse, coordinate_ring=coordinate_ring) + degree=rank, sparse=sparse, coordinate_ring=coordinate_ring, category=category) def __hash__(self): """ @@ -5912,7 +5912,7 @@ class FreeModule_ambient_domain(FreeModule_generic_domain, FreeModule_ambient): Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in x over Finite Field of size 5 """ - def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): + def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None, category=None): """ Create the ambient free module of given rank over the given integral domain. @@ -5922,7 +5922,7 @@ def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): sage: A = FreeModule(PolynomialRing(GF(5),'x'), 3) sage: TestSuite(A).run() """ - FreeModule_ambient.__init__(self, base_ring, rank, sparse, coordinate_ring) + FreeModule_ambient.__init__(self, base_ring, rank, sparse, coordinate_ring, category=category) def _repr_(self): """ @@ -6081,7 +6081,7 @@ class FreeModule_ambient_pid(FreeModule_generic_pid, FreeModule_ambient_domain): """ Ambient free module over a principal ideal domain. """ - def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): + def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None, category=None): """ Create the ambient free module of given rank over the given principal ideal domain. @@ -6114,7 +6114,7 @@ def __init__(self, base_ring, rank, sparse=False, coordinate_ring=None): """ FreeModule_ambient_domain.__init__(self, base_ring=base_ring, - rank=rank, sparse=sparse, coordinate_ring=coordinate_ring) + rank=rank, sparse=sparse, coordinate_ring=coordinate_ring, category=category) def _repr_(self): """ @@ -6173,7 +6173,7 @@ class FreeModule_ambient_field(FreeModule_generic_field, FreeModule_ambient_pid) """ """ - def __init__(self, base_field, dimension, sparse=False): + def __init__(self, base_field, dimension, sparse=False, category=None): """ Create the ambient vector space of given dimension over the given field. @@ -6191,7 +6191,7 @@ def __init__(self, base_field, dimension, sparse=False): sage: QQ^3 Vector space of dimension 3 over Rational Field """ - FreeModule_ambient_pid.__init__(self, base_field, dimension, sparse=sparse) + FreeModule_ambient_pid.__init__(self, base_field, dimension, sparse=sparse, category=category) def _repr_(self): """ @@ -6352,7 +6352,8 @@ class FreeModule_submodule_with_basis_pid(FreeModule_generic_pid): [ 4 5 6] """ def __init__(self, ambient, basis, check=True, - echelonize=False, echelonized_basis=None, already_echelonized=False): + echelonize=False, echelonized_basis=None, already_echelonized=False, + category=None): r""" See :class:`FreeModule_submodule_with_basis_pid` for documentation. @@ -6405,9 +6406,20 @@ def __init__(self, ambient, basis, check=True, if echelonize and not already_echelonized: basis = self._echelonized_basis(ambient, basis) + # Adapted from Module_free_ambient.__init__ + if category is None: + from sage.categories.all import FreeModules + category = FreeModules(R.category()).FiniteDimensional() + try: + if base_ring.is_finite() or len(basis) == 0: + category = category.Enumerated().Finite() + except Exception: + pass + category = category.Subobjects() + FreeModule_generic_pid.__init__(self, base_ring=R, coordinate_ring=R_coord, rank=len(basis), degree=ambient.degree(), - sparse=ambient.is_sparse()) + sparse=ambient.is_sparse(), category=category) C = self.element_class w = [C(self, x.list(), coerce=False, copy=False) for x in basis] self.__basis = basis_seq(self, w) @@ -6698,6 +6710,9 @@ def ambient_module(self): """ return self.__ambient_module + # Subobjects.ParentMethods + ambient = ambient_module + def relations(self): r""" Return the submodule defining the relations of ``self`` as a @@ -7321,7 +7336,8 @@ class FreeModule_submodule_pid(FreeModule_submodule_with_basis_pid): sage: v = W.0 + W.1 sage: TestSuite(v).run() """ - def __init__(self, ambient, gens, check=True, already_echelonized=False): + def __init__(self, ambient, gens, check=True, already_echelonized=False, + category=None): """ Create an embedded free module over a PID. @@ -7336,7 +7352,8 @@ def __init__(self, ambient, gens, check=True, already_echelonized=False): [0 3 6] """ FreeModule_submodule_with_basis_pid.__init__(self, ambient, basis=gens, - echelonize=True, already_echelonized=already_echelonized) + echelonize=True, already_echelonized=already_echelonized, + category=category) def _repr_(self): """ @@ -7474,7 +7491,8 @@ class FreeModule_submodule_with_basis_field(FreeModule_generic_field, FreeModule sage: TestSuite(W).run() """ def __init__(self, ambient, basis, check=True, - echelonize=False, echelonized_basis=None, already_echelonized=False): + echelonize=False, echelonized_basis=None, already_echelonized=False, + category=None): """ Create a vector space with given basis. @@ -7490,7 +7508,8 @@ def __init__(self, ambient, basis, check=True, """ FreeModule_submodule_with_basis_pid.__init__( self, ambient, basis=basis, check=check, echelonize=echelonize, - echelonized_basis=echelonized_basis, already_echelonized=already_echelonized) + echelonized_basis=echelonized_basis, already_echelonized=already_echelonized, + category=category) def _repr_(self): """ @@ -7671,7 +7690,7 @@ class FreeModule_submodule_field(FreeModule_submodule_with_basis_field): sage: vector(QQ, W.coordinates(v)) * W.basis_matrix() (1, 5, 9) """ - def __init__(self, ambient, gens, check=True, already_echelonized=False): + def __init__(self, ambient, gens, check=True, already_echelonized=False, category=None): """ Create an embedded vector subspace with echelonized basis. @@ -7688,7 +7707,8 @@ def __init__(self, ambient, gens, check=True, already_echelonized=False): if is_FreeModule(gens): gens = gens.gens() FreeModule_submodule_with_basis_field.__init__(self, ambient, basis=gens, check=check, - echelonize=not already_echelonized, already_echelonized=already_echelonized) + echelonize=not already_echelonized, already_echelonized=already_echelonized, + category=category) def _repr_(self): """ From 653416a375abb25813bb9e694d1849e49919feda Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 31 Aug 2022 21:47:07 -0700 Subject: [PATCH 310/454] FreeModule_submodule_with_basis_pid.ambient: Implement general case, add examples --- src/sage/modules/free_module.py | 50 ++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 54c500c4403..8e48710511a 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -6710,8 +6710,56 @@ def ambient_module(self): """ return self.__ambient_module + # Sets.Subquotients.ParentMethods + def ambient(self): + """ + Return the ambient module or space for ``self``. + + EXAMPLES:: + + sage: M = ZZ^3 + sage: W = M.span_of_basis([[1,2,3],[4,5,6]]); W + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + sage: W.ambient() + Ambient free module of rank 3 over the principal ideal domain Integer Ring + + Now we create a submodule of the ambient vector space, rather than + ``M`` itself:: + + sage: W = M.span_of_basis([[1,2,3/2],[4,5,6]]); W + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [ 1 2 3/2] + [ 4 5 6] + sage: W.ambient() + Vector space of dimension 3 over Rational Field + + A submodule of a submodule:: + + sage: M = ZZ^3 + sage: W = M.span_of_basis([[1,2,3],[4,5,6]]); W + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + sage: U = W.span_of_basis([[5,7,9]]); U + Free module of degree 3 and rank 1 over Integer Ring + User basis matrix: + [5 7 9] + sage: U.ambient() + Ambient free module of rank 3 over the principal ideal domain Integer Ring + + """ + if self.base_ring() == self.coordinate_ring(): + return self.ambient_module() + else: + return self.ambient_vector_space() + # Subobjects.ParentMethods - ambient = ambient_module + # lift, retract def relations(self): r""" From 88c67d9704e6100b9585336802e2a746e7c15104 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 31 Aug 2022 22:20:48 -0700 Subject: [PATCH 311/454] FreeModule_submodule_with_basis_pid.{lift,retract}: New --- src/sage/modules/free_module.py | 69 ++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 8e48710511a..f069cf552a3 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -193,6 +193,7 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.integral_domains import IntegralDomains from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.misc.lazy_attribute import lazy_attribute from sage.misc.randstate import current_randstate from sage.structure.factory import UniqueFactory from sage.structure.sequence import Sequence @@ -6758,8 +6759,72 @@ def ambient(self): else: return self.ambient_vector_space() - # Subobjects.ParentMethods - # lift, retract + # Sets.Subquotients.ParentMethods + @lazy_attribute + def lift(self): + r""" + The lift (embedding) map from ``self`` to the ambient module or space. + + EXAMPLES:: + + sage: M = ZZ^3 + sage: W = M.span_of_basis([[1,2,3],[4,5,6]]); W + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + sage: W.lift + Generic morphism: + From: Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + To: Ambient free module of rank 3 over the principal ideal domain Integer Ring + sage: w = W([5,7,9]) + sage: m = W.lift(w); m + (5, 7, 9) + sage: m.parent() + Ambient free module of rank 3 over the principal ideal domain Integer Ring + """ + ambient = self.ambient() + return self.module_morphism(function=ambient, codomain=ambient) + + # Sets.Subquotients.ParentMethods + @lazy_attribute + def retract(self): + r""" + The retract map from the ambient space. + + This is a partial map, which gives an error for elements not in the subspace. + + Calling this map on elements of the ambient space is the same as calling the + element constructor of ``self``. + + EXAMPLES:: + + sage: M = ZZ^3 + sage: W = M.span_of_basis([[1,2,3],[4,5,6]]); W + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + sage: W.retract + Generic morphism: + From: Ambient free module of rank 3 over the principal ideal domain Integer Ring + To: Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + sage: m = M([5, 7, 9]) + sage: w = W.retract(m); w + (5, 7, 9) + sage: w.parent() + Free module of degree 3 and rank 2 over Integer Ring + User basis matrix: + [1 2 3] + [4 5 6] + """ + return self.ambient().module_morphism(function=self, codomain=self) def relations(self): r""" From 1b4745764a283f35f6b4f85360fdae3f21033ac8 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 1 Sep 2022 17:13:38 +0800 Subject: [PATCH 312/454] preempt merge conflict --- src/sage/functions/special.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index 901f02f9bee..02596e49620 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -849,7 +849,7 @@ class EllipticF(BuiltinFunction): - :wikipedia:`Elliptic_integral#Incomplete_elliptic_integral_of_the_first_kind` """ def __init__(self): - r""" + """ EXAMPLES:: sage: loads(dumps(elliptic_f)) From 111f376bf91a9e153a3e563ebfe63e7ad0afd229 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 1 Sep 2022 11:16:51 +0100 Subject: [PATCH 313/454] Update based on comment 52 by tscrim. Mostly PEP8 formatting and improving some checks / way lists are handled. --- .../riemann_surfaces/riemann_surface.py | 91 ++++++++++--------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 4bacb2545b6..71d4d0f628b 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -60,7 +60,7 @@ We can look at an extended example of the Abel-Jacobi functionality. We will demonstrate a particular half-canonical divisor on Klein's Curve, known in -the literature.:: +the literature:: sage: f = x^3*y + y^3 + x sage: S = RiemannSurface(f, integration_method='rigorous') @@ -81,7 +81,7 @@ sage: if K-3*Pinf-1*Pinf_prime: Pinf, Pinf_prime = (Pinf_prime, Pinf); sage: D = P0 + 2*Pinf - Pinf_prime -Note we could check using exact techniques that `2D=K`:: +Note we could check using exact techniques that `2D = K`:: sage: Z = K - 2*D sage: (Z.degree()==0, len(Z.basis_differential_space())==S.genus, len(Z.basis_function_space())==1) @@ -371,7 +371,7 @@ def differential_basis_baker(f): if P.interior_contains(a)] -def find_closest_element(item, List): +def find_closest_element(item, lst): r""" Return the index of the closest element of a list. @@ -381,9 +381,9 @@ def find_closest_element(item, List): INPUT: - - ``item`` -- value to minimise the distance to over the list. + - ``item`` -- value to minimize the distance to over the list - - ``List`` -- list. List to look for closest element in. + - ``lst`` -- list to look for closest element in EXAMPLES:: @@ -396,11 +396,11 @@ def find_closest_element(item, List): Note that this method does no checks on the input, but will fail for inputs where the absolute value or subtraction do not make sense. """ - dists = [(item-l).abs() for l in List] + dists = [(item-l).abs() for l in lst] return dists.index(min(dists)) -def reparameterise_differential_minpoly(minpoly, z0): +def reparameterize_differential_minpoly(minpoly, z0): r""" Rewrites a minimal polynomial to write is around `z_0`. @@ -412,14 +412,13 @@ def reparameterise_differential_minpoly(minpoly, z0): INPUT: - ``minpoly`` -- a polynomial in two variables, where the first variables - corresponds to the base coordinate on the Riemann surface. - - - ``z0`` -- complex number of infinity. The point about which to - reparameterise. + corresponds to the base coordinate on the Riemann surface + - ``z0`` -- complex number or infinity; the point about which to + reparameterize OUTPUT: - A polynomial in two variables giving the reparameterise minimal polynomial. + A polynomial in two variables giving the reparameterize minimal polynomial. EXAMPLES: @@ -431,18 +430,18 @@ def reparameterise_differential_minpoly(minpoly, z0): Hence the transformed differential should have minimal polynomial `\bar{g}^2\bar{z}(1-\bar{z}^3)-1/4=0`, and we can check this:: - sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface, reparameterise_differential_minpoly + sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface, reparameterize_differential_minpoly sage: R. = QQ[] sage: S = RiemannSurface(w^2-z^3+1) sage: minpoly = S._cohomology_basis_bounding_data[1][0][2] sage: z0 = Infinity - sage: reparameterise_differential_minpoly(minpoly, z0) + sage: reparameterize_differential_minpoly(minpoly, z0) -zbar^4*gbar^2 + zbar*gbar^2 - 1/4 We can further check that reparameterising about `0` is the identity operation:: - sage: reparameterise_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly + sage: reparameterize_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly True .. NOTE:: @@ -450,7 +449,7 @@ def reparameterise_differential_minpoly(minpoly, z0): As part of the routine, when reparameterising about infinity, a rational function is reduced and then the numerator is taken. Over an inexact ring this is numerically unstable, and so it is advisable - to only reparameterise about infinity over an exact ring. + to only reparameterize about infinity over an exact ring. """ P = minpoly.parent() F = PolynomialRing(P.base_ring(), [str(v)+"bar" for v in P.gens()]) @@ -699,7 +698,7 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati # We now want to also check whether Infinity is a branch point of any # of the differentials. # This will be useful when calculating the Abel-Jacobi map. - minpoly_list = [reparameterise_differential_minpoly(mp, Infinity) + minpoly_list = [reparameterize_differential_minpoly(mp, Infinity) for mp in minpoly_list] discriminants = [] for minpoly in minpoly_list: @@ -875,7 +874,7 @@ def upstairs_graph(self): sage: G.is_connected() True """ - G = Graph(self.upstairs_edges()) + G = Graph(self.upstairs_edges(), immutable=True) G.set_pos({(i,j): [self._vertices[i].real(), self._vertices[i].imag(), self.w_values(self._vertices[i])[j].imag()] for i in range(len(self._vertices)) for j in range(self.degree)}, dim=3) @@ -1241,8 +1240,9 @@ def upstairs_edges(self): i0, i1 = e d_edge = (self._vertices[i0], self._vertices[i1]) # Epsilon for checking w-value later. - epsilon = min([abs(self._wvalues[i1][i] - self._wvalues[i1][n-j-1]) - for i in range(n) for j in range(n-i-1)])/3 + val = self._wvalues[i1] + epsilon = min(abs(val[i] - val[n-j-1]) + for i in range(n) for j in range(n-i-1)) / 3 # Homotopy continuation along e. self._L[e] = self.homotopy_continuation(d_edge) homotopycont = self._L[e][-1][1] @@ -1609,12 +1609,11 @@ def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): INPUT: - - ``upstairs_edge`` -- tuple. ``((z_start, sb), (z_end,))`` giving the + - ``upstairs_edge`` -- tuple ``((z_start, sb), (z_end,))`` giving the start and end values of the base coordinate along the straight-line - path and the starting branch. - - - ``initial_continuation`` -- list (default: None). Output of - ``homotopy_continuation`` initialising the continuation data. + path and the starting branch + - ``initial_continuation`` -- list (optional); output of + ``homotopy_continuation`` initialising the continuation data OUTPUT: @@ -2757,7 +2756,7 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall The cohomology basis is integrated along a straight line using a version of the double exponential quadrature. This method of integrating the cohomology basis is especially useful when integrating out to infinity, - or near roots of `self._dfdw`. In order to aid with convergence of the + or near roots of ``self._dfdw``. In order to aid with convergence of the method, two main modification to a standard integrator are made, most importantly of which is the truncation of the integral near branch points, where the first term in the Puiseux series of the integrands are used to @@ -2830,7 +2829,7 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall mp_list = [bd[2] for bd in bounding_data_list] # Parameterise so zbar=0 corresponds to z=z_start - mp_list = [reparameterise_differential_minpoly(mp, z_start) + mp_list = [reparameterize_differential_minpoly(mp, z_start) for mp in mp_list] if z_start==self._CC(Infinity): @@ -2874,8 +2873,8 @@ def initialise(z, i): prec = self._prec # tau here is playing the role of the desired error. tau = self._RR(2)**(-prec+3) - ONE = self._RR(1) - LAMBDA = self._RR.pi()/2 + one = self._RR(1) + la = self._RR.pi()/2 if cutoff_individually is None: cutoffs = [0] @@ -2909,15 +2908,15 @@ def error_handle(out): return out V = VectorSpace(self._CC, self.genus) - h = ONE - Nh = (-lambert_w(-1,-tau/2)/LAMBDA).log().ceil() + h = one + Nh = (-lambert_w(-1,-tau/2)/la).log().ceil() h0 = Nh*h if cutoff_individually: z_fc_list = list(zip(fc_mp_list, fc_dmp_list)) def fv(hj, previous_estimate_and_validity): - u2 = LAMBDA*hj.sinh() + u2 = la*hj.sinh() t = 1/(2*u2.exp()*u2.cosh()) z0 = J*t outg = [] @@ -2951,7 +2950,7 @@ def fv(hj, previous_estimate_and_validity): if j==99: outg.append(error_handle(newg)) fj = V(outg) - u1 = LAMBDA*hj.cosh() + u1 = la*hj.cosh() w = u1/(2*u2.cosh()**2) return (fj, valid), w*fj @@ -2963,7 +2962,7 @@ def fv(hj, previous_estimate_and_validity): J -= cutoff_z def fv(hj, previous_estimate): - u2 = LAMBDA*hj.sinh() + u2 = la*hj.sinh() t = 1/(2*u2.exp()*u2.cosh()) z0 = cutoff_z+J*t outg = [] @@ -2985,11 +2984,11 @@ def fv(hj, previous_estimate): if j==99: outg.append(error_handle(newg)) fj = V(outg) - u1 = LAMBDA*hj.cosh() + u1 = la*hj.cosh() w = u1/(2*u2.cosh()**2) return fj, w*fj - u1, u2 = (LAMBDA*h0.cosh(),LAMBDA*h0.sinh()) + u1, u2 = (la*h0.cosh(),la*h0.sinh()) y, w = (1/(2*u2.exp()*u2.cosh()), u1/(2*u2.cosh()**2)) z0 = cutoff_z+J*y f0 = [initialise(z0, i) for i in range(self.genus)] @@ -3021,7 +3020,7 @@ def fv(hj, previous_estimate): else: D1 = (results[-1]-results[-2]).norm(Infinity) D2 = (results[-1]-results[-3]).norm(Infinity) - D = min(ONE,max(D1**(D1.log()/D2.log()),D2**2,tau*D3_over_tau,D4,tau)) + D = min(one, max(D1**(D1.log()/D2.log()),D2**2,tau*D3_over_tau,D4,tau)) if D <= tau: if cutoff_individually: @@ -3432,7 +3431,7 @@ def places_at_branch_locus(self): """ BP = [] K = self._R.base_ring() - if not K==QQ: + if K is not QQ: raise NotImplementedError C = self.curve() KC = C.function_field() @@ -3513,7 +3512,7 @@ def strong_approximation(self, divisor, S): i = S.index(p) Q = S[i] D = D_base+Q - if D==0: + if not D: ios = self.genus else: ios = len(D.basis_differential_space()) @@ -3536,7 +3535,7 @@ def strong_approximation(self, divisor, S): new_divisor += v*b.divisor() return new_divisor, B - def divisor_to_divisor_list(self, divisor): + def divisor_to_divisor_list(self, divisor, eps=None): r""" Turn a divisor into a list for :meth:`abel_jacobi`. @@ -3547,6 +3546,8 @@ def divisor_to_divisor_list(self, divisor): INPUT: - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` + - ``eps`` -- real number (optional); tolerance used to determine whether a complex + number is close enough to a root of a polynomial. OUTPUT: @@ -3571,7 +3572,8 @@ def divisor_to_divisor_list(self, divisor): """ # If this error bound is too restrictive, this method might fail and # not return. One might want to change the way this error is handled. - eps = self._RR(2)**(-self._prec+3) + if not eps: + eps = self._RR(2)**(-self._prec+3) dl = [] PZ = PolynomialRing(self._R.base(), 'z').fraction_field() @@ -3605,8 +3607,8 @@ def divisor_to_divisor_list(self, divisor): nys = [] else: nys = poly.roots() - ys += [ny[0] for ny in nys] - rys += [(v*m*n, (r, y)) for y, n in nys] + ys.extend(ny[0] for ny in nys) + rys.extend((v*m*n, (r, y)) for y, n in nys) if rys: dl.extend(rys) @@ -3615,8 +3617,7 @@ def divisor_to_divisor_list(self, divisor): ys = self._CCw(self.f(r, self._CCw.gen(0))).roots() dl.extend([(v*m*n, (r, y)) for y, n in ys]) if not sum([v[0] for v in dl])==divisor.degree(): - print(dl) - raise ValueError("Numerical instability, list of wrong degree") + raise ValueError("numerical instability, list of wrong degree, returning list {}".format(dl)) return dl From 803f7e4437d853189d9c21ea73345fa672a9df5b Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Thu, 1 Sep 2022 15:14:33 +0200 Subject: [PATCH 314/454] Make FiniteRankFreeModule.tensor_module(0, 1) return the dual (#34474) --- .../tensor/modules/ext_pow_free_module.py | 74 +------------------ .../tensor/modules/finite_rank_free_module.py | 9 ++- src/sage/tensor/modules/tensor_free_module.py | 41 +--------- 3 files changed, 15 insertions(+), 109 deletions(-) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index a5938417f0f..f1ce40c078a 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -573,21 +573,12 @@ class ExtPowerDualFreeModule(FiniteRankFreeModule_abstract): sage: latex(M.dual()) M^* - Since any tensor of type (0,1) is a linear form, there is a coercion map - from the set `T^{(0,1)}(M)` of such tensors to `M^*`:: + It also coincides with the module of type-`(0,1)` tensors:: - sage: T01 = M.tensor_module(0,1) ; T01 - Free module of type-(0,1) tensors on the Rank-3 free module M over the - Integer Ring - sage: M.dual().has_coerce_map_from(T01) - True - - There is also a coercion map in the reverse direction:: - - sage: T01.has_coerce_map_from(M.dual()) + sage: M.dual_exterior_power(1) is M.tensor_module(0,1) True - For a degree `p\geq 2`, the coercion holds only in the direction + For a degree `p\geq 2`, there is a coercion map `\Lambda^p(M^*)\rightarrow T^{(0,p)}(M)`:: sage: T02 = M.tensor_module(0,2) ; T02 @@ -598,24 +589,6 @@ class ExtPowerDualFreeModule(FiniteRankFreeModule_abstract): sage: A.has_coerce_map_from(T02) False - The coercion map `T^{(0,1)}(M) \rightarrow M^*` in action:: - - sage: b = T01([-2,1,4], basis=e, name='b') ; b - Type-(0,1) tensor b on the Rank-3 free module M over the Integer Ring - sage: b.display(e) - b = -2 e^0 + e^1 + 4 e^2 - sage: lb = M.dual()(b) ; lb - Linear form b on the Rank-3 free module M over the Integer Ring - sage: lb.display(e) - b = -2 e^0 + e^1 + 4 e^2 - - The coercion map `M^* \rightarrow T^{(0,1)}(M)` in action:: - - sage: tlb = T01(lb) ; tlb - Type-(0,1) tensor b on the Rank-3 free module M over the Integer Ring - sage: tlb == b - True - The coercion map `\Lambda^2(M^*)\rightarrow T^{(0,2)}(M)` in action:: sage: ta = T02(a) ; ta @@ -783,47 +756,6 @@ def _an_element_(self): resu.set_comp()[ind] = self._fmodule._ring.an_element() return resu - def _coerce_map_from_(self, other): - r""" - Determine whether coercion to ``self`` exists from other parent. - - EXAMPLES: - - Sets of type-`(0,1)` tensors coerce to ``self`` if the degree is 1:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: L1 = M.dual_exterior_power(1) ; L1 - Dual of the Rank-3 free module M over the Integer Ring - sage: T01 = M.tensor_module(0,1) ; T01 - Free module of type-(0,1) tensors on the Rank-3 free module M over - the Integer Ring - sage: L1._coerce_map_from_(T01) - True - - Of course, coercions from other tensor types are meaningless:: - - sage: L1._coerce_map_from_(M.tensor_module(1,0)) - False - sage: L1._coerce_map_from_(M.tensor_module(0,2)) - False - - If the degree is larger than 1, there is no coercion:: - - sage: L2 = M.dual_exterior_power(2) ; L2 - 2nd exterior power of the dual of the Rank-3 free module M over - the Integer Ring - sage: L2._coerce_map_from_(M.tensor_module(0,2)) - False - - """ - from sage.tensor.modules.tensor_free_module import TensorFreeModule - if isinstance(other, TensorFreeModule): - # coercion of a type-(0,1) tensor to a linear form - if self._fmodule is other._fmodule and self._degree == 1 and \ - other.tensor_type() == (0,1): - return True - return False - #### End of parent methods @cached_method diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index a49eda75404..407a01e82d1 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1195,11 +1195,16 @@ def tensor_module(self, k, l): sage: M.tensor_module(1,2) is T True - The base module is itself the module of all type-`(1,0)` tensors:: + The module of type-`(1,0)` tensors is the base module itself:: sage: M.tensor_module(1,0) is M True + while the module of type-`(0,1)` tensors is the dual of the base module:: + + sage: M.tensor_module(0, 1) is M.dual() + True + See :class:`~sage.tensor.modules.tensor_free_module.TensorFreeModule` for more documentation. @@ -1209,6 +1214,8 @@ def tensor_module(self, k, l): except KeyError: if (k, l) == (1, 0): T = self + elif (k, l) == (0, 1): + T = self.dual() else: from sage.tensor.modules.tensor_free_module import TensorFreeModule T = TensorFreeModule(self, (k,l)) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 0566d4de98d..a6b13d6c162 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -237,39 +237,11 @@ class TensorFreeModule(FiniteRankFreeModule_abstract): sage: ta.symmetries() # the antisymmetry is of course preserved no symmetry; antisymmetry: (0, 1) - For the degree `p=1`, there is a coercion in both directions:: + For the degree `p=1`, we have the identity `\Lambda^1(M^*) = T^{(0,1)}(M) = M^*`:: - sage: L1 = M.dual_exterior_power(1) ; L1 - Dual of the Rank-3 free module M over the Integer Ring - sage: T01 = M.tensor_module(0,1) ; T01 - Free module of type-(0,1) tensors on the Rank-3 free module M over the - Integer Ring - sage: T01.has_coerce_map_from(L1) - True - sage: L1.has_coerce_map_from(T01) + sage: M.dual_exterior_power(1) is M.tensor_module(0,1) True - - The coercion map `\Lambda^1(M^*)\rightarrow T^{(0,1)}(M)` in action:: - - sage: a = M.linear_form('a') - sage: a[:] = -2, 4, 1 ; a.display(e) - a = -2 e^0 + 4 e^1 + e^2 - sage: a.parent() is L1 - True - sage: ta = T01(a) ; ta - Type-(0,1) tensor a on the Rank-3 free module M over the Integer Ring - sage: ta.display(e) - a = -2 e^0 + 4 e^1 + e^2 - - The coercion map `T^{(0,1)}(M) \rightarrow \Lambda^1(M^*)` in action:: - - sage: ta.parent() is T01 - True - sage: lta = L1(ta) ; lta - Linear form a on the Rank-3 free module M over the Integer Ring - sage: lta.display(e) - a = -2 e^0 + 4 e^1 + e^2 - sage: lta == a + sage: M.tensor_module(0,1) is M.dual() True There is a canonical identification between tensors of type `(1,1)` and @@ -571,7 +543,7 @@ def _coerce_map_from_(self, other): but not to tensor modules of other types:: - sage: M.tensor_module(0,1)._coerce_map_from_(End(M)) + sage: M.tensor_module(0,2)._coerce_map_from_(End(M)) False and not to type-`(1,1)` tensor modules defined on another free module:: @@ -597,8 +569,6 @@ def _coerce_map_from_(self, other): Coercion from alternating forms:: - sage: M.tensor_module(0,1)._coerce_map_from_(M.dual_exterior_power(1)) - True sage: M.tensor_module(0,2)._coerce_map_from_(M.dual_exterior_power(2)) True sage: M.tensor_module(0,2)._coerce_map_from_(M.dual_exterior_power(3)) @@ -645,9 +615,6 @@ def _repr_(self): sage: M.tensor_module(1,1) Free module of type-(1,1) tensors on the 2-dimensional vector space M over the Rational Field - sage: M.tensor_module(0,1) - Free module of type-(0,1) tensors on the 2-dimensional vector space - M over the Rational Field """ description = "Free module of type-({},{}) tensors on the {}".format( From 3b215c0e7105fe9d3046409fa6462210cba6a008 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 1 Sep 2022 14:34:56 +0100 Subject: [PATCH 315/454] Added comments to integrate_differentials_iteratively Comments added to clarify the functionality, and explain how the flags cutoff_individually and riase_errors work. --- .../riemann_surfaces/riemann_surface.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 71d4d0f628b..d5c25ddb81d 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -2832,6 +2832,11 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall mp_list = [reparameterize_differential_minpoly(mp, z_start) for mp in mp_list] + # Depending on whether we have reparameterized about infinity or not, + # we initialise some values we will need in the calculation, inclduing + # the function `initalize', which at a given value of zbar, calculates + # the starting value for the i-th differential so it can be iterated + # from via homotopy continuation. if z_start==self._CC(Infinity): CCzg = PolynomialRing(self._CC, ['zbar','gbar']) mp_list = [CCzg(mp) for mp in mp_list] @@ -2865,6 +2870,9 @@ def initialise(z, i): newg = rs[sb] return newg + # As multiple calls of the minimal polynomial and it's derivative will + # be required for the homotopy continuaiton, we create fast-callable + # versions of these. fc_mp_list = [fast_callable(mp, domain=self._CC) for mp in mp_list] fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) for mp in mp_list] @@ -2876,6 +2884,15 @@ def initialise(z, i): one = self._RR(1) la = self._RR.pi()/2 + # Cutoffs are used to allow us to not have to integrate as close into + # a singularity as we might otherwise have to, by knowing that we can + # truncate the integration interval and only introduce a finite error + # that can be bounded by knowledge of the asymptotics of the integrands, + # which we have from their minimal polynomials. This is really a + # precursor to what would be ideal to implement eventually, namely + # a method that uses Puiseux series to integrate into singularities. + # We allow for cutoffs to be tailored to each integrand, or we take a + # uniform value. if cutoff_individually is None: cutoffs = [0] cutoff_individually = False @@ -2896,6 +2913,14 @@ def initialise(z, i): aes.append(a) cutoff_individually = bool(not all(ai<=0 for ai in aes) and cutoff_individually) + # The `raise_errors' variable toggles what we do in the event that + # newton iteration hasn't converged to the desired precision in a + # fixed number of steps, here set to 100. + # If the default value of True is taken, then the failure to converge + # raises an error. If the value of False is taken, this failure to + # converge happens silently, thus allowing the user to get *an* + # answer out of the integration, but numerical imprecision is to be + # expected. if raise_errors: n_steps = self._prec-1 @@ -2912,6 +2937,11 @@ def error_handle(out): Nh = (-lambert_w(-1,-tau/2)/la).log().ceil() h0 = Nh*h + # Depending on how the cutoffs were defined, we now create the function + # which calculates the integrand we want to integrate via double- + # exponential methods. This will get the value at the next node by + # homotopy-continuing from the last node value. There is also a slight + # technical condition which implements the cutoffs. if cutoff_individually: z_fc_list = list(zip(fc_mp_list, fc_dmp_list)) @@ -2999,6 +3029,12 @@ def fv(hj, previous_estimate): D4 = D3_over_tau results = [] + # we now calculate the integral via double-exponential methods + # repeatedly halving the step size and then using a heuristic + # convergence check. We again use the error_handle function to deal + # with the case where we exceed the maximum number of steps allowed, + # currently set by to make sure the step size does not fall below the + # resolution set by the binary precision used. for k in range(n_steps): hj = h0 val = v0 From 319ecfaa335f8ba214ad59033fd91bf95579918f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:03:58 -0700 Subject: [PATCH 316/454] FiniteRankFreeModule.tensor: Use CompWithSym._canonicalize_sym_antisym --- .../tensor/modules/finite_rank_free_module.py | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 73b260a07c2..bb55959f4ed 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1609,7 +1609,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the tensor; if none is provided, the LaTeX symbol is set to ``name`` - - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries + - ``sym`` -- (default: ``None``) a symmetry or an iterable of symmetries among the tensor arguments: each symmetry is described by a tuple containing the positions of the involved arguments, with the convention ``position = 0`` for the first argument. For instance: @@ -1618,7 +1618,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, * ``sym = [(0,2), (1,3,4)]`` for a symmetry between the 1st and 3rd arguments and a symmetry between the 2nd, 4th and 5th arguments. - - ``antisym`` -- (default: ``None``) antisymmetry or list of + - ``antisym`` -- (default: ``None``) antisymmetry or iterable of antisymmetries among the arguments, with the same convention as for ``sym`` @@ -1653,33 +1653,20 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, for more examples and documentation. """ + from .comp import CompWithSym + sym, antisym = CompWithSym._canonicalize_sym_antisym( + tensor_type[0] + tensor_type[1], sym, antisym) # Special cases: if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) elif tensor_type == (0,1): return self.linear_form(name=name, latex_name=latex_name) elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a range - # object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[1]: + if len(antisym[0]) == tensor_type[1]: return self.alternating_form(tensor_type[1], name=name, latex_name=latex_name) elif tensor_type[0] > 1 and tensor_type[1] == 0 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a range - # object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[0]: + if len(antisym[0]) == tensor_type[0]: return self.alternating_contravariant_tensor(tensor_type[0], name=name, latex_name=latex_name) # Generic case: From 8e93626a08496c71387cacd7bf9dae075803ebdc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:04:55 -0700 Subject: [PATCH 317/454] CompWithSym._canonicalize_sym_antisym: Add documentation --- src/sage/tensor/modules/comp.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 8e9cadbaa4a..9eea2bd1770 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3000,10 +3000,28 @@ def __init__(self, ring, frame, nb_indices, start_index=0, nb_indices, sym, antisym) @staticmethod - def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): + def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, + trivial_symmetries='drop'): r""" Bring sym and antisym into their canonical form. + INPUT: + + - ``nb_indices`` -- number of integer indices labeling the components + + - ``sym`` -- (default: ``None``) a symmetry or an iterable of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention ``position = 0`` for the first argument. For instance: + + * ``sym = (0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym = [(0,2), (1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments. + + - ``antisym`` -- (default: ``None``) antisymmetry or iterable of + antisymmetries among the arguments, with the same convention + as for ``sym`` + EXAMPLES:: sage: from sage.tensor.modules.comp import CompWithSym From 564be353b347cecfd4496a169b5eaff0642de03d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:26:30 -0700 Subject: [PATCH 318/454] FiniteRankFreeModule.tensor: Restore error when trivial symmetries are passed --- src/sage/tensor/modules/comp.py | 24 ++++++++++++++----- .../tensor/modules/finite_rank_free_module.py | 3 ++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 9eea2bd1770..d9a676711e1 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3022,6 +3022,10 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, antisymmetries among the arguments, with the same convention as for ``sym`` + - ``trivial_symmetries`` -- (default: ``"drop"``) if ``"error"``, raise + an :class:`IndexError` if any trivial symmetries are provided; + otherwise, silently drop them + EXAMPLES:: sage: from sage.tensor.modules.comp import CompWithSym @@ -3041,8 +3045,12 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, sym = [tuple(sym)] for isym in sym: if len(isym) < 2: - # Drop trivial symmetry - continue + if trivial_symmetries == 'error': + raise IndexError("at least two index positions must be " + + "provided to define a symmetry") + else: + # Drop trivial symmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3053,16 +3061,20 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, antisym = [] else: # Handle the case that antisym is an iterator - antisym = list(antisym) + antisym = tuple(antisym) if antisym: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: - antisym = [tuple(antisym)] + antisym = (tuple(antisym),) for isym in antisym: if len(isym) < 2: - # Drop trivial antisymmetry - continue + if trivial_symmetries == 'error': + raise IndexError("at least two index positions must be " + + "provided to define an antisymmetry") + else: + # Drop trivial antisymmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index bb55959f4ed..307605b2df4 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1655,7 +1655,8 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, """ from .comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym) + tensor_type[0] + tensor_type[1], sym, antisym, + trivial_symmetries='error') # Special cases: if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) From ffea896b41332047c9f6594358b8706873023c2e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:27:11 -0700 Subject: [PATCH 319/454] CompWithSym._canonicalize_sym_antisym: Code optimization --- src/sage/tensor/modules/comp.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index d9a676711e1..0854361076e 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3032,17 +3032,20 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, sage: CompWithSym._canonicalize_sym_antisym(6, [(2, 1)]) (((1, 2),), ()) """ + if not sym and not antisym: + # fast path + return (), () result_sym = [] if sym is None: - sym = [] + sym = () else: # Handle the case that sym is an iterator - sym = list(sym) + sym = tuple(sym) if sym: if isinstance(sym[0], (int, Integer)): # a single symmetry is provided as a tuple or a range object; # it is converted to a 1-item list: - sym = [tuple(sym)] + sym = (tuple(sym),) for isym in sym: if len(isym) < 2: if trivial_symmetries == 'error': @@ -3083,9 +3086,9 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, # Final consistency check: index_list = [] for isym in result_sym: - index_list += isym + index_list.extend(isym) for isym in result_antisym: - index_list += isym + index_list.extend(isym) if len(index_list) != len(set(index_list)): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + From fed09d56502962e93ee9f1981a3eb6af70dbe42f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:35:42 -0700 Subject: [PATCH 320/454] CompWithSym._canonicalize_sym_antisym: Sort earlier to validate indices faster --- src/sage/tensor/modules/comp.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 0854361076e..330fe576168 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3054,11 +3054,11 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, else: # Drop trivial symmetry continue - for i in isym: - if i < 0 or i > nb_indices - 1: - raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(nb_indices-1) + "]") - result_sym.append(tuple(isym)) + isym = tuple(sorted(isym)) + if isym[0] < 0 or isym[-1] > nb_indices - 1: + raise IndexError("invalid index position: " + str(i) + + " not in [0," + str(nb_indices-1) + "]") + result_sym.append(isym) result_antisym = [] if antisym is None: antisym = [] @@ -3078,11 +3078,11 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, else: # Drop trivial antisymmetry continue - for i in isym: - if i < 0 or i > nb_indices - 1: - raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(nb_indices - 1) + "]") - result_antisym.append(tuple(isym)) + isym = tuple(sorted(isym)) + if isym[0] < 0 or isym[-1] > nb_indices - 1: + raise IndexError("invalid index position: " + str(i) + + " not in [0," + str(nb_indices - 1) + "]") + result_antisym.append(isym) # Final consistency check: index_list = [] for isym in result_sym: @@ -3094,8 +3094,6 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, raise IndexError("incompatible lists of symmetries: the same " + "index position appears more than once") # Canonicalize sort order, make tuples - result_sym = [tuple(sorted(s)) for s in result_sym] - result_antisym = [tuple(sorted(s)) for s in result_antisym] result_sym = tuple(sorted(result_sym)) result_antisym = tuple(sorted(result_antisym)) return result_sym, result_antisym From 421c660af21de39acfcf53dc895d51064024fd6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:38:59 -0700 Subject: [PATCH 321/454] src/sage/tensor/modules/finite_rank_free_module.py: Add doctest --- src/sage/tensor/modules/finite_rank_free_module.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 307605b2df4..5ffce01fe4a 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1652,6 +1652,20 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, See :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor` for more examples and documentation. + TESTS: + + Errors are raised if trivial symmetries appear in the list of symmetries or + antisymmetries. + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.tensor((3,0), sym=[[1]]) + Traceback (most recent call last): + ... + IndexError: at least two index positions must be provided to define a symmetry + sage: M.tensor((3,0), antisym=[[]]) + Traceback (most recent call last): + ... + IndexError: at least two index positions must be provided to define an antisymmetry """ from .comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( From a1a3f364657633f837a59022e85f0a4ba72c2512 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:49:45 -0700 Subject: [PATCH 322/454] CompWithSym.__init__: Accept keyword 'trivial_symmetries', add tests --- src/sage/tensor/modules/comp.py | 19 +++++++++++++++++-- .../tensor/modules/finite_rank_free_module.py | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 330fe576168..060295be280 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -2983,9 +2983,24 @@ class CompWithSym(Components): sage: e + d == d + e True + TESTS: + + Errors are raised if trivial symmetries appear in the list of symmetries or + antisymmetries:: + + sage: CompWithSym(QQ, V.basis(), 3, sym=[[1]]) + Traceback (most recent call last): + ... + IndexError: at least two index positions must be provided to define a symmetry + sage: CompWithSym(QQ, V.basis(), 2, antisym=[[]]) + Traceback (most recent call last): + ... + IndexError: at least two index positions must be provided to define an antisymmetry + """ def __init__(self, ring, frame, nb_indices, start_index=0, - output_formatter=None, sym=None, antisym=None): + output_formatter=None, sym=None, antisym=None, + trivial_symmetries='error'): r""" TESTS:: @@ -2997,7 +3012,7 @@ def __init__(self, ring, frame, nb_indices, start_index=0, Components.__init__(self, ring, frame, nb_indices, start_index, output_formatter) self._sym, self._antisym = self._canonicalize_sym_antisym( - nb_indices, sym, antisym) + nb_indices, sym, antisym, trivial_symmetries=trivial_symmetries) @staticmethod def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 5ffce01fe4a..889bc3fb7b0 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1655,7 +1655,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, TESTS: Errors are raised if trivial symmetries appear in the list of symmetries or - antisymmetries. + antisymmetries:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: M.tensor((3,0), sym=[[1]]) From 8a2c71a6d56ca08d8fb5793ec0cd3faa4682850f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 09:55:28 -0700 Subject: [PATCH 323/454] VectorFieldModule.tensor, VectorFieldFreeModule.tensor: Use CompWithSym._canonicalize_sym_antisym --- .../differentiable/vectorfield_module.py | 72 ++++++------------- 1 file changed, 23 insertions(+), 49 deletions(-) diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 393218bc7c0..fbdd39b7aef 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -773,7 +773,11 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, AutomorphismField from sage.manifolds.differentiable.metric import (PseudoRiemannianMetric, DegenerateMetric) - if tensor_type==(1,0): + from sage.tensor.modules.comp import CompWithSym + sym, antisym = CompWithSym._canonicalize_sym_antisym( + tensor_type[0] + tensor_type[1], sym, antisym, + trivial_symmetries='error') + if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) elif tensor_type == (0,1): @@ -783,31 +787,14 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, return self.automorphism(name=name, latex_name=latex_name) elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a - # range object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[1]: + if len(antisym[0]) == tensor_type[1]: return self.alternating_form(tensor_type[1], name=name, latex_name=latex_name) elif tensor_type[0] > 1 and tensor_type[1] == 0 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a - # range object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[0]: + if len(antisym[0]) == tensor_type[0]: return self.alternating_contravariant_tensor( - tensor_type[0], name=name, - latex_name=latex_name) - elif tensor_type==(0,2) and specific_type is not None: + tensor_type[0], name=name, latex_name=latex_name) + elif tensor_type == (0,2) and specific_type is not None: if issubclass(specific_type, PseudoRiemannianMetric): return self.metric(name, latex_name=latex_name) # NB: the signature is not treated @@ -816,9 +803,9 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, return self.metric(name, latex_name=latex_name, signature=(0, sign-1, 1)) # Generic case - return self.tensor_module(*tensor_type).element_class(self, - tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + return self.tensor_module(*tensor_type).element_class( + self, tensor_type, name=name, latex_name=latex_name, + sym=sym, antisym=antisym) def alternating_contravariant_tensor(self, degree, name=None, latex_name=None): @@ -2088,6 +2075,10 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, AutomorphismField, AutomorphismFieldParal) from sage.manifolds.differentiable.metric import (PseudoRiemannianMetric, DegenerateMetric) + from sage.tensor.modules.comp import CompWithSym + sym, antisym = CompWithSym._canonicalize_sym_antisym( + tensor_type[0] + tensor_type[1], sym, antisym, + trivial_symmetries='error') if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) @@ -2098,31 +2089,14 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, (AutomorphismField, AutomorphismFieldParal)): return self.automorphism(name=name, latex_name=latex_name) elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a - # range object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[1]: + if len(antisym[0]) == tensor_type[1]: return self.alternating_form(tensor_type[1], name=name, latex_name=latex_name) elif tensor_type[0] > 1 and tensor_type[1] == 0 and antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a - # range object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - if isinstance(antisym, (tuple, list)): - antisym0 = antisym[0] - else: - antisym0 = antisym - if len(antisym0) == tensor_type[0]: + if len(antisym[0]) == tensor_type[0]: return self.alternating_contravariant_tensor( - tensor_type[0], name=name, - latex_name=latex_name) - elif tensor_type==(0,2) and specific_type is not None: + tensor_type[0], name=name, latex_name=latex_name) + elif tensor_type == (0,2) and specific_type is not None: if issubclass(specific_type, PseudoRiemannianMetric): return self.metric(name, latex_name=latex_name) # NB: the signature is not treated @@ -2131,9 +2105,9 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, return self.metric(name, latex_name=latex_name, signature=(0, sign-1, 1)) # Generic case - return self.tensor_module(*tensor_type).element_class(self, - tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + return self.tensor_module(*tensor_type).element_class( + self, tensor_type, name=name, latex_name=latex_name, + sym=sym, antisym=antisym) def tensor_from_comp(self, tensor_type, comp, name=None, latex_name=None): From e34306bf0471fe2fa9da9e7b9bb86d5bf18cb8d9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 12:12:44 -0700 Subject: [PATCH 324/454] Revert "FiniteRankFreeModule.tensor: Restore error when trivial symmetries are passed" This reverts commit 564be353b347cecfd4496a169b5eaff0642de03d. --- src/sage/tensor/modules/comp.py | 24 +++++-------------- .../tensor/modules/finite_rank_free_module.py | 3 +-- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 060295be280..87d2dc0b473 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3037,10 +3037,6 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, antisymmetries among the arguments, with the same convention as for ``sym`` - - ``trivial_symmetries`` -- (default: ``"drop"``) if ``"error"``, raise - an :class:`IndexError` if any trivial symmetries are provided; - otherwise, silently drop them - EXAMPLES:: sage: from sage.tensor.modules.comp import CompWithSym @@ -3063,12 +3059,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, sym = (tuple(sym),) for isym in sym: if len(isym) < 2: - if trivial_symmetries == 'error': - raise IndexError("at least two index positions must be " + - "provided to define a symmetry") - else: - # Drop trivial symmetry - continue + # Drop trivial symmetry + continue isym = tuple(sorted(isym)) if isym[0] < 0 or isym[-1] > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3079,20 +3071,16 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, antisym = [] else: # Handle the case that antisym is an iterator - antisym = tuple(antisym) + antisym = list(antisym) if antisym: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: - antisym = (tuple(antisym),) + antisym = [tuple(antisym)] for isym in antisym: if len(isym) < 2: - if trivial_symmetries == 'error': - raise IndexError("at least two index positions must be " + - "provided to define an antisymmetry") - else: - # Drop trivial antisymmetry - continue + # Drop trivial antisymmetry + continue isym = tuple(sorted(isym)) if isym[0] < 0 or isym[-1] > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 889bc3fb7b0..442b5576b49 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1669,8 +1669,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, """ from .comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym, - trivial_symmetries='error') + tensor_type[0] + tensor_type[1], sym, antisym) # Special cases: if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) From c6b6b2ed6ddc44044b8e0915d768cd22672f0dab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Sep 2022 12:16:19 -0700 Subject: [PATCH 325/454] src/sage/tensor, src/sage/manifolds: Remove parameter 'trivial_symmetries' again --- .../differentiable/vectorfield_module.py | 6 ++--- src/sage/tensor/modules/comp.py | 23 +++---------------- .../tensor/modules/finite_rank_free_module.py | 12 ++++------ 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index fbdd39b7aef..1e097907967 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -775,8 +775,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, DegenerateMetric) from sage.tensor.modules.comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym, - trivial_symmetries='error') + tensor_type[0] + tensor_type[1], sym, antisym) if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) @@ -2077,8 +2076,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, DegenerateMetric) from sage.tensor.modules.comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym, - trivial_symmetries='error') + tensor_type[0] + tensor_type[1], sym, antisym) if tensor_type == (1,0): return self.element_class(self, name=name, latex_name=latex_name) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 87d2dc0b473..0bc56fe728c 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -2982,25 +2982,9 @@ class CompWithSym(Components): ) sage: e + d == d + e True - - TESTS: - - Errors are raised if trivial symmetries appear in the list of symmetries or - antisymmetries:: - - sage: CompWithSym(QQ, V.basis(), 3, sym=[[1]]) - Traceback (most recent call last): - ... - IndexError: at least two index positions must be provided to define a symmetry - sage: CompWithSym(QQ, V.basis(), 2, antisym=[[]]) - Traceback (most recent call last): - ... - IndexError: at least two index positions must be provided to define an antisymmetry - """ def __init__(self, ring, frame, nb_indices, start_index=0, - output_formatter=None, sym=None, antisym=None, - trivial_symmetries='error'): + output_formatter=None, sym=None, antisym=None): r""" TESTS:: @@ -3012,11 +2996,10 @@ def __init__(self, ring, frame, nb_indices, start_index=0, Components.__init__(self, ring, frame, nb_indices, start_index, output_formatter) self._sym, self._antisym = self._canonicalize_sym_antisym( - nb_indices, sym, antisym, trivial_symmetries=trivial_symmetries) + nb_indices, sym, antisym) @staticmethod - def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None, *, - trivial_symmetries='drop'): + def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): r""" Bring sym and antisym into their canonical form. diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 442b5576b49..53f1aa054b0 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1654,18 +1654,14 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, TESTS: - Errors are raised if trivial symmetries appear in the list of symmetries or - antisymmetries:: + Trivial symmetries in the list of symmetries or antisymmetries are silently + ignored:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: M.tensor((3,0), sym=[[1]]) - Traceback (most recent call last): - ... - IndexError: at least two index positions must be provided to define a symmetry + Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring sage: M.tensor((3,0), antisym=[[]]) - Traceback (most recent call last): - ... - IndexError: at least two index positions must be provided to define an antisymmetry + Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring """ from .comp import CompWithSym sym, antisym = CompWithSym._canonicalize_sym_antisym( From bec2f6540e63cca9b1c192ceb9ea1c15b7aee762 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 09:14:16 -0700 Subject: [PATCH 326/454] CompWithSym._canonicalize_sym_antisym: Refactor, fix index validation, add tests --- src/sage/tensor/modules/comp.py | 98 ++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 0bc56fe728c..dec039e5d1c 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -2998,10 +2998,61 @@ def __init__(self, ring, frame, nb_indices, start_index=0, self._sym, self._antisym = self._canonicalize_sym_antisym( nb_indices, sym, antisym) + @staticmethod + def _canonicalize_sym_or_antisym(nb_indices, sym_or_antisym): + r""" + Bring ``sym`` or ``antisym`` to its canonical form. + + INPUT: + + - ``nb_indices`` -- number of integer indices labeling the components + + - ``sym_or_antisym`` -- (default: ``None``) a symmetry/antisymmetry + or an iterable of symmetries or an iterable of antisymmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention ``position = 0`` for the first argument. For instance: + + TESTS:: + + sage: from sage.tensor.modules.comp import CompWithSym + sage: CompWithSym._canonicalize_sym_or_antisym(3, [0, -1]) + Traceback (most recent call last): + ... + IndexError: invalid index position: -1 not in [0,2] + sage: CompWithSym._canonicalize_sym_or_antisym(3, [3, 1]) + Traceback (most recent call last): + ... + IndexError: invalid index position: 3 not in [0,2] + """ + if not sym_or_antisym: + return () + # Handle the case that sym_or_antisym is an iterator + sym_or_antisym = tuple(sym_or_antisym) + result_sym_or_antisym = [] + if isinstance(sym_or_antisym[0], (int, Integer)): + # a single symmetry is provided as a tuple or a range object; + # it is converted to a 1-item list: + sym_or_antisym = (tuple(sym_or_antisym),) + for isym in sym_or_antisym: + if len(isym) < 2: + # Drop trivial symmetry + continue + isym = tuple(sorted(isym)) + if isym[0] < 0: + raise IndexError("invalid index position: " + str(isym[0]) + + " not in [0," + str(nb_indices-1) + "]") + if isym[-1] > nb_indices - 1: + raise IndexError("invalid index position: " + str(isym[-1]) + + " not in [0," + str(nb_indices-1) + "]") + result_sym_or_antisym.append(isym) + # Canonicalize sort order, make tuples + return tuple(sorted(result_sym_or_antisym)) + @staticmethod def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): r""" - Bring sym and antisym into their canonical form. + Bring ``sym`` and ``antisym`` into their canonical form. INPUT: @@ -3029,46 +3080,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): if not sym and not antisym: # fast path return (), () - result_sym = [] - if sym is None: - sym = () - else: - # Handle the case that sym is an iterator - sym = tuple(sym) - if sym: - if isinstance(sym[0], (int, Integer)): - # a single symmetry is provided as a tuple or a range object; - # it is converted to a 1-item list: - sym = (tuple(sym),) - for isym in sym: - if len(isym) < 2: - # Drop trivial symmetry - continue - isym = tuple(sorted(isym)) - if isym[0] < 0 or isym[-1] > nb_indices - 1: - raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(nb_indices-1) + "]") - result_sym.append(isym) - result_antisym = [] - if antisym is None: - antisym = [] - else: - # Handle the case that antisym is an iterator - antisym = list(antisym) - if antisym: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple or a range - # object; it is converted to a 1-item list: - antisym = [tuple(antisym)] - for isym in antisym: - if len(isym) < 2: - # Drop trivial antisymmetry - continue - isym = tuple(sorted(isym)) - if isym[0] < 0 or isym[-1] > nb_indices - 1: - raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(nb_indices - 1) + "]") - result_antisym.append(isym) + result_sym = CompWithSym._canonicalize_sym_or_antisym(nb_indices, sym) + result_antisym = CompWithSym._canonicalize_sym_or_antisym(nb_indices, antisym) # Final consistency check: index_list = [] for isym in result_sym: @@ -3079,9 +3092,6 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + "index position appears more than once") - # Canonicalize sort order, make tuples - result_sym = tuple(sorted(result_sym)) - result_antisym = tuple(sorted(result_antisym)) return result_sym, result_antisym def _repr_(self): From 66009e7700f8fc0610dc494a09089070bb6f609a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 09:24:03 -0700 Subject: [PATCH 327/454] src/sage/tensor/modules/comp.py: Fix docstring --- src/sage/tensor/modules/comp.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index dec039e5d1c..40f2cf292c7 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3009,9 +3009,9 @@ def _canonicalize_sym_or_antisym(nb_indices, sym_or_antisym): - ``sym_or_antisym`` -- (default: ``None``) a symmetry/antisymmetry or an iterable of symmetries or an iterable of antisymmetries - among the tensor arguments: each symmetry is described by a tuple - containing the positions of the involved arguments, with the - convention ``position = 0`` for the first argument. For instance: + among the tensor arguments: each symmetry/antisymmetry is described + by a tuple containing the positions of the involved arguments, with + the convention ``position = 0`` for the first argument. TESTS:: From 7474abbe05d4ab21978b268669a3cd9e205dcec2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 10:35:11 -0700 Subject: [PATCH 328/454] FiniteRankFreeModule_abstract.tensor_product: Handle submodules with symmetries --- .../tensor/modules/finite_rank_free_module.py | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index b983789b62d..bfdca5c4846 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -644,13 +644,70 @@ def tensor_product(self, *others): sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) + sage: Sym01x23M._index_maps + ((0, 1), (2, 3)) + + sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N + Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), + with antisymmetry on the index positions (3, 4) + sage: NxN = N.tensor_product(N); NxN + Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), + with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) + sage: NxN._index_maps + ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) """ from sage.modules.free_module_element import vector + from .comp import CompFullySym, CompFullyAntiSym, CompWithSym + base_module = self.base_module() if not all(module.base_module() == base_module for module in others): raise NotImplementedError('all factors must be tensor modules over the same base module') - tensor_type = sum(vector(module.tensor_type()) for module in [self] + list(others)) - return base_module.tensor_module(*tensor_type) + factors = [self] + list(others) + result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) + index_maps = [] + running_indices = vector([0, result_tensor_type[0]]) + result_sym = [] + result_antisym = [] + for factor in factors: + tensor_type = factor.tensor_type() + index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) + index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) + index_maps.append(index_map) + + if tensor_type[0] + tensor_type[1] > 1: + basis_sym = factor._basis_sym() + all_indices = tuple(range(tensor_type[0] + tensor_type[1])) + if isinstance(basis_sym, CompFullySym): + sym = [all_indices] + antisym = [] + elif isinstance(basis_sym, CompFullyAntiSym): + sym = [] + antisym = [all_indices] + elif isinstance(basis_sym, CompWithSym): + sym = basis_sym._sym + antisym = basis_sym._antisym + else: + sym = antisym = [] + + def map_isym(isym): + return tuple(index_map[i] for i in isym) + + result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) + result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) + + running_indices += vector(tensor_type) + + result = base_module.tensor_module(*result_tensor_type, + sym=result_sym, antisym=result_antisym) + result._index_maps = tuple(index_maps) + return result def rank(self) -> int: r""" From ed3c8d91bf060db1a4f38ed478fa96dabb14ae9d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 10:37:29 -0700 Subject: [PATCH 329/454] src/sage/tensor/modules/finite_rank_free_module.py (FiniteRankFreeModule_abstract.is_submodule): Fix typo in doctest --- src/sage/tensor/modules/finite_rank_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index bfdca5c4846..e81c0be5762 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -816,7 +816,7 @@ def is_submodule(self, other): EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: N = FiniteRankFreeModule(ZZ, 4, name='M') + sage: N = FiniteRankFreeModule(ZZ, 4, name='N') sage: M.is_submodule(M) True sage: M.is_submodule(N) From fd89892e620cd4b81d46e9e441e9809eaea2685a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 10:47:55 -0700 Subject: [PATCH 330/454] src/sage/tensor/modules/tensor_free_submodule[_basis].py, finite_rank_free_module.py: Update AUTHORS --- src/sage/tensor/modules/finite_rank_free_module.py | 3 ++- src/sage/tensor/modules/tensor_free_submodule.py | 8 ++++++-- src/sage/tensor/modules/tensor_free_submodule_basis.py | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index e81c0be5762..4f5b99fbc84 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -38,10 +38,11 @@ class :class:`~sage.modules.free_module.FreeModule_generic` AUTHORS: - Eric Gourgoulhon, Michal Bejger (2014-2015): initial version -- Travis Scrimshaw (2016): category set to Modules(ring).FiniteDimensional() +- Travis Scrimshaw (2016): category set to ``Modules(ring).FiniteDimensional()`` (:trac:`20770`) - Michael Jung (2019): improve treatment of the zero element - Eric Gourgoulhon (2021): unicode symbols for tensor and exterior products +- Matthias Koeppe (2022): ``FiniteRankFreeModule_abstract``, symmetric powers REFERENCES: diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index cfe6b09dccf..5f6964f8546 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -1,15 +1,19 @@ r""" Free submodules of tensor modules defined by monoterm symmetries + +AUTHORS: + +- Matthias Koeppe (2020-2022): initial version """ -#****************************************************************************** +# ****************************************************************************** # Copyright (C) 2020-2022 Matthias Koeppe # # 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/ -#****************************************************************************** +# ****************************************************************************** import itertools diff --git a/src/sage/tensor/modules/tensor_free_submodule_basis.py b/src/sage/tensor/modules/tensor_free_submodule_basis.py index d2b60298ee0..6c88b05af23 100644 --- a/src/sage/tensor/modules/tensor_free_submodule_basis.py +++ b/src/sage/tensor/modules/tensor_free_submodule_basis.py @@ -1,15 +1,19 @@ r""" Standard bases of free submodules of tensor modules defined by some monoterm symmetries + +AUTHORS: + +- Matthias Koeppe (2020-2022): initial version """ -#****************************************************************************** +# ****************************************************************************** # Copyright (C) 2020-2022 Matthias Koeppe # # 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.tensor.modules.free_module_basis import Basis_abstract From 644933a6eadec662ebdf59fcb93add2bc4190ca6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 10:48:21 -0700 Subject: [PATCH 331/454] src/sage/tensor/modules/finite_rank_free_module.py: Update copyright according to git blame -w --date=format:%Y FILE | sort -k2 --- src/sage/tensor/modules/finite_rank_free_module.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 4f5b99fbc84..4dfbcbbb41a 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -520,16 +520,19 @@ class :class:`~sage.modules.free_module.FreeModule_generic` [2, 0, -5] """ -#****************************************************************************** -# Copyright (C) 2015-2021 Eric Gourgoulhon -# Copyright (C) 2015 Michal Bejger -# Copyright (C) 2016 Travis Scrimshaw +# ****************************************************************************** +# Copyright (C) 2014-2021 Eric Gourgoulhon +# 2014-2016 Travis Scrimshaw +# 2015 Michal Bejger +# 2016 Frédéric Chapoton +# 2020 Michael Jung +# 2020-2022 Matthias Koeppe # # 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. # https://www.gnu.org/licenses/ -#****************************************************************************** +# ****************************************************************************** from __future__ import annotations from typing import Generator, Optional From 45bf6f359dba548b7749c6754ebe56b7c16db170 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 11:06:17 -0700 Subject: [PATCH 332/454] FiniteRankFreeModule_abstract.tensor_product: Add comment --- src/sage/tensor/modules/finite_rank_free_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 4dfbcbbb41a..f70d603d1b1 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -675,10 +675,12 @@ def tensor_product(self, *others): raise NotImplementedError('all factors must be tensor modules over the same base module') factors = [self] + list(others) result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) - index_maps = [] - running_indices = vector([0, result_tensor_type[0]]) result_sym = [] result_antisym = [] + # Keep track of reordering of the contravariant and covariant indices + # (compatible with FreeModuleTensor.__mul__) + index_maps = [] + running_indices = vector([0, result_tensor_type[0]]) for factor in factors: tensor_type = factor.tensor_type() index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) From 94f59b890768bc21c0176398f279385a9010d2b2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 13:53:02 -0700 Subject: [PATCH 333/454] FiniteRankDualFreeModule: New, remove special case from ExtPowerDualFreeModule --- .../tensor/modules/ext_pow_free_module.py | 34 +-- .../tensor/modules/finite_rank_free_module.py | 285 +++++++++++++++++- 2 files changed, 290 insertions(+), 29 deletions(-) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index f1ce40c078a..92483ffe422 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -622,18 +622,12 @@ def __init__(self, fmodule, degree, name=None, latex_name=None): self._fmodule = fmodule self._degree = ZZ(degree) rank = binomial(fmodule._rank, degree) - if degree == 1: # case of the dual - if name is None and fmodule._name is not None: - name = fmodule._name + '*' - if latex_name is None and fmodule._latex_name is not None: - latex_name = fmodule._latex_name + r'^*' - else: - if name is None and fmodule._name is not None: - name = unicode_bigwedge + r'^{}('.format(degree) \ - + fmodule._name + '*)' - if latex_name is None and fmodule._latex_name is not None: - latex_name = r'\Lambda^{' + str(degree) + r'}\left(' \ - + fmodule._latex_name + r'^*\right)' + if name is None and fmodule._name is not None: + name = unicode_bigwedge + r'^{}('.format(degree) \ + + fmodule._name + '*)' + if latex_name is None and fmodule._latex_name is not None: + latex_name = r'\Lambda^{' + str(degree) + r'}\left(' \ + + fmodule._latex_name + r'^*\right)' super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name) fmodule._all_modules.add(self) @@ -664,13 +658,6 @@ def _element_constructor_(self, comp=[], basis=None, name=None, sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') - sage: A = M.dual_exterior_power(1) - sage: a = A._element_constructor_(0) ; a - Linear form zero on the Rank-3 free module M over the Integer Ring - sage: a = A._element_constructor_([2,0,-1], name='a') ; a - Linear form a on the Rank-3 free module M over the Integer Ring - sage: a.display() - a = 2 e^0 - e^2 sage: A = M.dual_exterior_power(2) sage: a = A._element_constructor_(0) ; a Alternating form zero of degree 2 on the Rank-3 free module M over @@ -713,11 +700,6 @@ def _an_element_(self): sage: M = FiniteRankFreeModule(QQ, 4, name='M') sage: e = M.basis('e') - sage: a = M.dual_exterior_power(1)._an_element_() ; a - Linear form on the 4-dimensional vector space M over the Rational - Field - sage: a.display() - 1/2 e^0 sage: a = M.dual_exterior_power(2)._an_element_() ; a Alternating form of degree 2 on the 4-dimensional vector space M over the Rational Field @@ -790,8 +772,6 @@ def _repr_(self): EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 5, name='M') - sage: M.dual_exterior_power(1)._repr_() - 'Dual of the Rank-5 free module M over the Integer Ring' sage: M.dual_exterior_power(2)._repr_() '2nd exterior power of the dual of the Rank-5 free module M over the Integer Ring' sage: M.dual_exterior_power(3)._repr_() @@ -804,8 +784,6 @@ def _repr_(self): '21st exterior power of the dual of the Rank-5 free module M over the Integer Ring' """ - if self._degree == 1: - return "Dual of the {}".format(self._fmodule) description = "{}".format(self._degree.ordinal_str()) description += " exterior power of the dual of the {}".format( self._fmodule) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 407a01e82d1..654d6d3858b 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -540,8 +540,9 @@ class :class:`~sage.modules.free_module.FreeModule_generic` from sage.rings.integer import Integer from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation +from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement - +from sage.tensor.modules.free_module_tensor import FreeModuleTensor class FiniteRankFreeModule_abstract(UniqueRepresentation, Parent): r""" @@ -1321,6 +1322,9 @@ def dual_exterior_power(self, p): OUTPUT: - for `p=0`, the base ring `R` + - for `p=1`, instance of + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankDualFreeModule` + representing the dual `M^*` - for `p\geq 1`, instance of :class:`~sage.tensor.modules.ext_pow_free_module.ExtPowerDualFreeModule` representing the free module `\Lambda^p(M^*)` @@ -1359,6 +1363,8 @@ def dual_exterior_power(self, p): except KeyError: if p == 0: L = self._ring + elif p == 1: + L = FiniteRankDualFreeModule(self) else: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule L = ExtPowerDualFreeModule(self, p) @@ -2960,3 +2966,280 @@ def tensor_type(self): """ return (1, 0) + + +class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): + r""" + Dual of a free module of finite rank over a commutative ring. + + Given a free module `M` of finite rank over a commutative ring `R`, + the *dual of* `M` is the set `M^*` of all linear forms `p` on `M`, + i.e., linear maps + + .. MATH:: + + M \longrightarrow R + + This is a Sage *parent* class, whose *element* class is + :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`. + + INPUT: + + - ``fmodule`` -- free module `M` of finite rank, as an instance of + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule` + - ``name`` -- (default: ``None``) string; name given to `\Lambda^p(M^*)` + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote `M^*` + + EXAMPLES: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual(); A + Dual of the Rank-3 free module M over the Integer Ring + + ``A`` is a module (actually a free module) over `\ZZ`:: + + sage: A.category() + Category of finite dimensional modules over Integer Ring + sage: A in Modules(ZZ) + True + sage: A.rank() + 3 + sage: A.base_ring() + Integer Ring + sage: A.base_module() + Rank-3 free module M over the Integer Ring + + ``A`` is a *parent* object, whose elements are linear forms, + represented by instances of the class + :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`:: + + sage: a = A.an_element() ; a + Linear form on the Rank-3 free module M over the Integer Ring + sage: a.display() # expansion with respect to M's default basis (e) + e^0 + sage: from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm + sage: isinstance(a, FreeModuleAltForm) + True + sage: a in A + True + sage: A.is_parent_of(a) + True + + Elements can be constructed from ``A``. In particular, 0 yields + the zero element of ``A``:: + + sage: A(0) + Linear form zero on the Rank-3 free module M over the Integer Ring + sage: A(0) is A.zero() + True + + while non-zero elements are constructed by providing their components in a + given basis:: + + sage: e + Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring + sage: comp = [0,3,-1] + sage: a = A(comp, basis=e, name='a') ; a + Linear form a on the Rank-3 free module M over the Integer Ring + sage: a.display(e) + a = 3 e^1 - e^2 + + An alternative is to construct the alternating form from an empty list of + components and to set the nonzero components afterwards:: + + sage: a = A([], name='a') + sage: a.set_comp(e)[0] = 3 + sage: a.set_comp(e)[1] = -1 + sage: a.set_comp(e)[2] = 4 + sage: a.display(e) + a = 3 e^0 - e^1 + 4 e^2 + + The dual is unique:: + + sage: A is M.dual() + True + + The exterior power `\Lambda^1(M^*)` is nothing but `M^*`:: + + sage: M.dual_exterior_power(1) is M.dual() + True + + It also coincides with the module of type-`(0,1)` tensors:: + + sage: M.dual_exterior_power(1) is M.tensor_module(0,1) + True + """ + + Element = FreeModuleAltForm + + def __init__(self, fmodule, name=None, latex_name=None): + r""" + TESTS:: + + sage: from sage.tensor.modules.finite_rank_free_module import FiniteRankDualFreeModule + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = FiniteRankDualFreeModule(M) ; A + Dual of the Rank-3 free module M over the Integer Ring + sage: TestSuite(A).run() + + """ + self._fmodule = fmodule + rank = fmodule._rank + if name is None and fmodule._name is not None: + name = fmodule._name + '*' + if latex_name is None and fmodule._latex_name is not None: + latex_name = fmodule._latex_name + r'^*' + super().__init__(fmodule._ring, rank, name=name, + latex_name=latex_name) + fmodule._all_modules.add(self) + + def construction(self): + r""" + TESTS:: + + sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: A.construction() is None + True + """ + # No construction until we extend VectorFunctor with a parameter 'dual' + return None + + #### Parent methods + + def _element_constructor_(self, comp=[], basis=None, name=None, + latex_name=None): + r""" + Construct a linear form. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: a = A._element_constructor_(0) ; a + Linear form zero on the Rank-3 free module M over the Integer Ring + sage: a = A._element_constructor_([2,0,-1], name='a') ; a + Linear form a on the Rank-3 free module M over the Integer Ring + sage: a.display() + a = 2 e^0 - e^2 + """ + if isinstance(comp, (int, Integer)) and comp == 0: + return self.zero() + if isinstance(comp, FreeModuleTensor): + # coercion of a tensor of type (0,1) to a linear form + tensor = comp # for readability + if tensor.tensor_type() == (0,1) and self._degree == 1 and \ + tensor.base_module() is self._fmodule: + resu = self.element_class(self._fmodule, 1, name=tensor._name, + latex_name=tensor._latex_name) + for basis, comp in tensor._components.items(): + resu._components[basis] = comp.copy() + return resu + else: + raise TypeError("cannot coerce the {} ".format(tensor) + + "to an element of {}".format(self)) + # standard construction + resu = self.element_class(self._fmodule, 1, name=name, latex_name=latex_name) + if comp: + resu.set_comp(basis)[:] = comp + return resu + + def _an_element_(self): + r""" + Construct some (unnamed) alternating form. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 4, name='M') + sage: e = M.basis('e') + sage: a = M.dual()._an_element_() ; a + Linear form on the 4-dimensional vector space M over the Rational + Field + sage: a.display() + 1/2 e^0 + + TESTS: + + When the base module has no default basis, a default + basis will be set for it:: + + sage: M2 = FiniteRankFreeModule(QQ, 4, name='M2') + sage: a = M2.dual()._an_element_(); a + Linear form on the 4-dimensional vector space M2 over the Rational Field + sage: a + a + Linear form on the 4-dimensional vector space M2 over the Rational Field + sage: M2.default_basis() + Basis (e_0,e_1,e_2,e_3) on the 4-dimensional vector space M2 over the Rational Field + + """ + resu = self.element_class(self._fmodule, 1) + # Make sure that the base module has a default basis + self._fmodule.an_element() + sindex = self._fmodule._sindex + ind = [sindex + i for i in range(resu._tensor_rank)] + resu.set_comp()[ind] = self._fmodule._ring.an_element() + return resu + + #### End of parent methods + + @cached_method + def zero(self): + r""" + Return the zero of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: A.zero() + Linear form zero on the Rank-3 free module M over the Integer Ring + sage: A(0) is A.zero() + True + + """ + resu = self._element_constructor_(name='zero', latex_name='0') + for basis in self._fmodule._known_bases: + resu._components[basis] = resu._new_comp(basis) + # (since new components are initialized to zero) + resu._is_zero = True # This element is certainly zero + resu.set_immutable() + return resu + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 5, name='M') + sage: M.dual_exterior_power(1)._repr_() + 'Dual of the Rank-5 free module M over the Integer Ring' + """ + return "Dual of the {}".format(self._fmodule) + + def base_module(self): + r""" + Return the free module on which ``self`` is constructed. + + OUTPUT: + + - instance of :class:`FiniteRankFreeModule` representing the free + module on which the dual is defined. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 5, name='M') + sage: A = M.dual() + sage: A.base_module() + Rank-5 free module M over the Integer Ring + sage: A.base_module() is M + True + + """ + return self._fmodule From 37e41b9b5f3968e32967128bd94df4f101b685bb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 13:56:26 -0700 Subject: [PATCH 334/454] FiniteRankDualFreeModule.tensor_type: New; add tensor_product doctests --- .../tensor/modules/finite_rank_free_module.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 654d6d3858b..f970d9e7f17 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -633,6 +633,10 @@ def tensor_product(self, *others): sage: M = FiniteRankFreeModule(QQ, 2) sage: M.tensor_product(M) Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.dual()) + Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field + sage: M.dual().tensor_product(M, M.dual()) + Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field sage: M.tensor_product(M.tensor_module(1,2)) Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field sage: M.tensor_module(1,2).tensor_product(M) @@ -3243,3 +3247,16 @@ def base_module(self): """ return self._fmodule + + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual().tensor_type() + (0, 1) + + """ + return (0, 1) From ccc1f853a0ff5c0b201cd73959f2a516e1aa574d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 16:48:11 -0700 Subject: [PATCH 335/454] src/sage/rings/ring.pyx: Remove methods duplicated from category --- src/sage/categories/rings.py | 50 ++++++++++++++++++++++++-- src/sage/rings/ring.pyx | 70 ------------------------------------ 2 files changed, 48 insertions(+), 72 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index fc1df0de372..512e799177b 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -866,6 +866,16 @@ def quo(self, I, names=None, **kwds): [0 1] ) + A test with a subclass of :class:`~sage.rings.ring.Ring`:: + + sage: R. = PolynomialRing(QQ,2) + sage: S. = R.quo((x^2, y)) + sage: S + Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) + sage: S.gens() + (a, 0) + sage: a == b + False """ return self.quotient(I,names=names,**kwds) @@ -875,7 +885,22 @@ def quotient_ring(self, I, names=None, **kwds): NOTE: - This is a synonyme for :meth:`quotient`. + This is a synonym for :meth:`quotient`. + + INPUT: + + - ``I`` -- an ideal of `R` + + - ``names`` -- (optional) names of the generators of the quotient. (If + there are multiple generators, you can specify a single character + string and the generators are named in sequence starting with 0.) + + - further named arguments that may be passed to the quotient ring + constructor. + + OUTPUT: + + - ``R/I`` -- the quotient ring of `R` by the ideal `I` EXAMPLES:: @@ -906,8 +931,24 @@ def quotient_ring(self, I, names=None, **kwds): [0 1] ) + A test with a subclass of :class:`~sage.rings.ring.Ring`:: + + sage: R. = PolynomialRing(ZZ) + sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) + sage: S = R.quotient_ring(I, 'a') + sage: S.gens() + (a,) + + sage: R. = PolynomialRing(QQ,2) + sage: S. = R.quotient_ring((x^2, y)) + sage: S + Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) + sage: S.gens() + (a, 0) + sage: a == b + False """ - return self.quotient(I,names=names, **kwds) + return self.quotient(I, names=names, **kwds) def __truediv__(self, I): """ @@ -923,6 +964,11 @@ def __truediv__(self, I): Traceback (most recent call last): ... TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. + + sage: QQ['x'] / ZZ + Traceback (most recent call last): + ... + TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. """ raise TypeError("Use self.quo(I) or self.quotient(I) to construct the quotient ring.") diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 1851af15fdb..433ba29bba4 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -650,76 +650,6 @@ cdef class Ring(ParentWithGens): import sage.rings.quotient_ring return sage.rings.quotient_ring.QuotientRing(self, I, names=names, **kwds) - def quo(self, I, names=None, **kwds): - """ - Create the quotient of `R` by the ideal `I`. This is a synonym for :meth:`.quotient` - - EXAMPLES:: - - sage: R. = PolynomialRing(QQ,2) - sage: S. = R.quo((x^2, y)) - sage: S - Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) - sage: S.gens() - (a, 0) - sage: a == b - False - """ - return self.quotient(I, names=names, **kwds) - - def __truediv__(self, I): - """ - Dividing one ring by another is not supported because there is no good - way to specify generator names. - - EXAMPLES:: - - sage: QQ['x'] / ZZ - Traceback (most recent call last): - ... - TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. - """ - raise TypeError("Use self.quo(I) or self.quotient(I) to construct the quotient ring.") - - def quotient_ring(self, I, names=None, **kwds): - """ - Return the quotient of self by the ideal `I` of ``self``. - (Synonym for ``self.quotient(I)``.) - - INPUT: - - - ``I`` -- an ideal of `R` - - - ``names`` -- (optional) names of the generators of the quotient. (If - there are multiple generators, you can specify a single character - string and the generators are named in sequence starting with 0.) - - - further named arguments that may be passed to the quotient ring - constructor. - - OUTPUT: - - - ``R/I`` -- the quotient ring of `R` by the ideal `I` - - EXAMPLES:: - - sage: R. = PolynomialRing(ZZ) - sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) - sage: S = R.quotient_ring(I, 'a') - sage: S.gens() - (a,) - - sage: R. = PolynomialRing(QQ,2) - sage: S. = R.quotient_ring((x^2, y)) - sage: S - Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) - sage: S.gens() - (a, 0) - sage: a == b - False - """ - return self.quotient(I, names, **kwds) - def zero(self): """ Return the zero element of this ring (cached). From ef266af585bc137b1c0e3a2944dc670777e53eba Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 17:00:53 -0700 Subject: [PATCH 336/454] src/sage/rings/ring.pyx: Remove another method duplicated from category --- src/sage/categories/rings.py | 25 ++++++++++++++++++++---- src/sage/rings/ring.pyx | 37 +----------------------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 512e799177b..88ce6ef5bc0 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -781,16 +781,16 @@ def _ideal_class_(self,n=0): ## # Quotient rings - # Again, this is defined in sage.rings.ring.pyx def quotient(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. INPUT: - - ``I``: A twosided ideal of this ring. - - ``names``: a list of strings to be used as names - for the variables in the quotient ring. + - ``I`` -- A twosided ideal of this ring. + - ``names`` -- (optional) names of the generators of the quotient (if + there are multiple generators, you can specify a single character + string and the generators are named in sequence starting with 0). - further named arguments that may be passed to the quotient ring constructor. @@ -823,6 +823,23 @@ def quotient(self, I, names=None, **kwds): xbar*ybar sage: Q.0*Q.1*Q.0 0 + + An example with polynomial rings:: + + sage: R. = PolynomialRing(ZZ) + sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) + sage: S = R.quotient(I, 'a') + sage: S.gens() + (a,) + + sage: R. = PolynomialRing(QQ,2) + sage: S. = R.quotient((x^2, y)) + sage: S + Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) + sage: S.gens() + (a, 0) + sage: a == b + False """ from sage.rings.quotient_ring import QuotientRing return QuotientRing(self, I, names=names, **kwds) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 433ba29bba4..75b0854d630 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -312,7 +312,7 @@ cdef class Ring(ParentWithGens): sage: F. = FreeAlgebra(ZZ, 3) sage: I = F*[x*y+y*z,x^2+x*y-y*x-y^2]*F - sage: Q = sage.rings.ring.Ring.quotient(F,I) + sage: Q = F.quotient(I) sage: Q.ideal_monoid() Monoid of ideals of Quotient of Free Algebra on 3 generators (x, y, z) over Integer Ring by the ideal (x*y + y*z, x^2 + x*y - y*x - y^2) sage: F. = FreeAlgebra(ZZ, implementation='letterplace') @@ -615,41 +615,6 @@ cdef class Ring(ParentWithGens): return I return self._zero_ideal - def quotient(self, I, names=None, **kwds): - """ - Create the quotient of this ring by a twosided ideal ``I``. - - INPUT: - - - ``I`` -- a twosided ideal of this ring, `R`. - - - ``names`` -- (optional) names of the generators of the quotient (if - there are multiple generators, you can specify a single character - string and the generators are named in sequence starting with 0). - - - further named arguments that may be passed to the quotient ring - constructor. - - EXAMPLES:: - - sage: R. = PolynomialRing(ZZ) - sage: I = R.ideal([4 + 3*x + x^2, 1 + x^2]) - sage: S = R.quotient(I, 'a') - sage: S.gens() - (a,) - - sage: R. = PolynomialRing(QQ,2) - sage: S. = R.quotient((x^2, y)) - sage: S - Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2, y) - sage: S.gens() - (a, 0) - sage: a == b - False - """ - import sage.rings.quotient_ring - return sage.rings.quotient_ring.QuotientRing(self, I, names=names, **kwds) - def zero(self): """ Return the zero element of this ring (cached). From c44607808c6b1212909292f3d9d0570546b641d1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 17:42:07 -0700 Subject: [PATCH 337/454] Modules.ParentMethods.quotient: New, delegates to .quotient_module; replaces alias Module_free_ambient.quotient --- src/sage/categories/modules.py | 17 +++++++++++++++++ src/sage/modules/free_module.py | 6 ++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index d37b4812209..755788eb4d3 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -675,6 +675,23 @@ def module_morphism(self, *, function, category=None, codomain, **keywords): category = Modules(self.base_ring()) return SetMorphism(Hom(self, codomain, category), function) + def quotient(self, submodule, check=True, **kwds): + r""" + Construct the quotient module ``self`` / ``submodule``. + + This method just delegates to :meth:`quotient_module`. + + INPUT: + + - ``submodule`` -- a submodule with basis of ``self``, or + something that can be turned into one via + ``self.submodule(submodule)`` + + - ``check``, other keyword arguments: passed on to + :meth:`quotient_module`. + """ + return self.quotient_module(submodule, check=check, **kwds) + class ElementMethods: pass diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index d508b22fcc7..c7685a6e27b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -1782,8 +1782,6 @@ def quotient_module(self, sub, check=True): from .quotient_module import QuotientModule_free_ambient return QuotientModule_free_ambient(self, sub) - quotient = quotient_module - def __truediv__(self, sub): """ Return the quotient of ``self`` by the given submodule sub. @@ -4180,7 +4178,7 @@ def vector_space_span_of_basis(self, basis, check=True): """ return FreeModule_submodule_with_basis_field(self.ambient_vector_space(), basis, check=check) - def quotient(self, sub, check=True, **kwds): + def quotient_module(self, sub, check=True, **kwds): """ Return the quotient of ``self`` by the given submodule sub. @@ -4994,7 +4992,7 @@ def __truediv__(self, sub): """ return self.quotient(sub, check=True) - def quotient(self, sub, check=True): + def quotient_module(self, sub, check=True): """ Return the quotient of ``self`` by the given subspace sub. From 4958d6d8ef223efce6cae23e7470f87be8b36d67 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 20:35:41 -0700 Subject: [PATCH 338/454] Modules.ParentMethods.quotient: Expand docstring --- src/sage/categories/modules.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index 755788eb4d3..b92453740c3 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -679,8 +679,6 @@ def quotient(self, submodule, check=True, **kwds): r""" Construct the quotient module ``self`` / ``submodule``. - This method just delegates to :meth:`quotient_module`. - INPUT: - ``submodule`` -- a submodule with basis of ``self``, or @@ -689,6 +687,22 @@ def quotient(self, submodule, check=True, **kwds): - ``check``, other keyword arguments: passed on to :meth:`quotient_module`. + + This method just delegates to :meth:`quotient_module`. + Classes implementing modules should override that method. + + Parents in categories with additional structure may override + :meth:`quotient`. For example, in algebras, :meth:`quotient` will + be the same as :meth:`quotient_ring`. + + EXAMPLES:: + + sage: C = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: TA = TensorAlgebra(C) + sage: TA.quotient + + """ return self.quotient_module(submodule, check=check, **kwds) From 0d4f3d5f06f0cb21bef759b5a3b8440bc90d644b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 07:30:35 -0700 Subject: [PATCH 339/454] build/pkgs/igraph: Update to 0.9.10 --- build/pkgs/igraph/checksums.ini | 6 +++--- build/pkgs/igraph/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/igraph/checksums.ini b/build/pkgs/igraph/checksums.ini index 1e09d661bd2..2d7a72630b0 100644 --- a/build/pkgs/igraph/checksums.ini +++ b/build/pkgs/igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=igraph-VERSION.tar.gz -sha1=66da9978e789e996e4b85cf970ab46e84dc971d7 -md5=fb0c24794bb88e88d4874802b78684bf -cksum=2298757908 +sha1=75aae4d9f1459d9e6f6bd03189e6dff4208c29ca +md5=351a276216a8f09e11a401d2313ea471 +cksum=1815245909 upstream_url=https://github.com/igraph/igraph/releases/download/VERSION/igraph-VERSION.tar.gz diff --git a/build/pkgs/igraph/package-version.txt b/build/pkgs/igraph/package-version.txt index c81aa44afbf..56f3151140c 100644 --- a/build/pkgs/igraph/package-version.txt +++ b/build/pkgs/igraph/package-version.txt @@ -1 +1 @@ -0.9.7 +0.9.10 From 76ffacf0fbcc1d2fd4782c1f9df35485765d142e Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sun, 4 Sep 2022 15:59:10 +0100 Subject: [PATCH 340/454] Run rimeann_surface.py through black to reformat --- .../riemann_surfaces/riemann_surface.py | 1047 ++++++++++------- 1 file changed, 653 insertions(+), 394 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index d5c25ddb81d..d0638bf030c 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -231,9 +231,9 @@ def bisect(L, t): if t < L[min][0] or t > L[max][0]: raise ValueError("value for t out of range") # Main loop. - while (min < max-1): + while min < max - 1: # Bisect. - mid = (max+min)//2 + mid = (max + min) // 2 # If it's equal, return the index we bisected to. if t == L[mid][0]: return mid @@ -357,9 +357,10 @@ def differential_basis_baker(f): if len(B.monomials()) > 1: return None from sage.geometry.polyhedron.constructor import Polyhedron + D = {(k[0], k[1]): v for k, v in f.dict().items()} P = Polyhedron(D) - kT = k['t'] + kT = k["t"] # here we check the additional genericity conditions: that the polynomials # along the edges of the Newton polygon are square-free. for e in P.bounded_edges(): @@ -367,8 +368,11 @@ def differential_basis_baker(f): if not h.is_squarefree(): return None x, y = f.parent().gens() - return [x**(a[0] - 1) * y**(a[1] - 1) for a in P.integral_points() - if P.interior_contains(a)] + return [ + x ** (a[0] - 1) * y ** (a[1] - 1) + for a in P.integral_points() + if P.interior_contains(a) + ] def find_closest_element(item, lst): @@ -396,7 +400,7 @@ def find_closest_element(item, lst): Note that this method does no checks on the input, but will fail for inputs where the absolute value or subtraction do not make sense. """ - dists = [(item-l).abs() for l in lst] + dists = [(item - l).abs() for l in lst] return dists.index(min(dists)) @@ -450,22 +454,22 @@ def reparameterize_differential_minpoly(minpoly, z0): rational function is reduced and then the numerator is taken. Over an inexact ring this is numerically unstable, and so it is advisable to only reparameterize about infinity over an exact ring. - """ + """ P = minpoly.parent() - F = PolynomialRing(P.base_ring(), [str(v)+"bar" for v in P.gens()]) + F = PolynomialRing(P.base_ring(), [str(v) + "bar" for v in P.gens()]) try: - Inf = bool(z0==z0.parent()(Infinity)) + Inf = bool(z0 == z0.parent()(Infinity)) except TypeError: Inf = False if Inf: F = F.fraction_field() - mt = F(minpoly(F.gen(0)**(-1),-F.gen(0)**(+2)*F.gen(1))) + mt = F(minpoly(F.gen(0) ** (-1), -F.gen(0) ** (+2) * F.gen(1))) mt.reduce() mt = mt.numerator() else: - mt = minpoly(F.gen(0)+z0,F.gen(1)) + mt = minpoly(F.gen(0) + z0, F.gen(1)) return mt @@ -619,7 +623,15 @@ class RiemannSurface(object): sage: tau.algdep(6).degree() == 2 True """ - def __init__(self, f, prec=53, certification=True, differentials=None, integration_method="rigorous"): + + def __init__( + self, + f, + prec=53, + certification=True, + differentials=None, + integration_method="rigorous", + ): r""" TESTS:: @@ -631,14 +643,14 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati # Initializations. self._prec = prec self._certification = certification - if not (integration_method=="heuristic" or integration_method=="rigorous"): + if not (integration_method == "heuristic" or integration_method == "rigorous"): raise ValueError("Invalid integration method") self._integration_method = integration_method self._R = f.parent() if len(self._R.gens()) != 2: - raise ValueError('only bivariate polynomials supported.') + raise ValueError("only bivariate polynomials supported.") if f.degree() <= 1: - raise ValueError('equation must be of degree at least 2.') + raise ValueError("equation must be of degree at least 2.") z, w = self._R.gen(0), self._R.gen(1) self._CC = ComplexField(self._prec) self._RR = RealField(self._prec) @@ -659,7 +671,9 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati self._differentials = None self.genus = self._R.ideal(self.f).genus() if self.genus < 0: - raise ValueError("Singular reports negative genus. Specify differentials manually.") + raise ValueError( + "Singular reports negative genus. Specify differentials manually." + ) self.degree = self.f.degree(w) self._dfdw = self.f.derivative(w) self._dfdz = self.f.derivative(z) @@ -667,17 +681,22 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati # Coefficients of the polynomial for use in homotopy continuation. self._a0 = self._CCz(self.f.coefficient({w: self.degree})(self._CCz.gen(), 0)) self._a0roots = self._a0.roots(multiplicities=False) - self._aks = [self._CCz(self.f.coefficient({w: self.degree - k - 1}) - (self._CCz.gen(), 0)) for k in range(self.degree)] + self._aks = [ + self._CCz(self.f.coefficient({w: self.degree - k - 1})(self._CCz.gen(), 0)) + for k in range(self.degree) + ] # Compute the branch locus. Takes the square-free part of the discriminant # because of numerical issues. self.branch_locus = [] existing_factors = [x[0] for x in self._discriminant.factor()] for fac in existing_factors: - self.branch_locus += self._CCz(fac(self._CCz.gen(), 0)).roots(multiplicities=False) + self.branch_locus += self._CCz(fac(self._CCz.gen(), 0)).roots( + multiplicities=False + ) self._f_branch_locus = self.branch_locus - self._cohomology_basis_bounding_data = self._bounding_data(self.cohomology_basis(), - exact=True) + self._cohomology_basis_bounding_data = self._bounding_data( + self.cohomology_basis(), exact=True + ) RBzg, bounding_data_list = self._cohomology_basis_bounding_data minpoly_list = [bd[2] for bd in bounding_data_list] # We now want to calculate the additional branchpoints associated to @@ -687,45 +706,48 @@ def __init__(self, f, prec=53, certification=True, differentials=None, integrati F = RBzg(minpoly) dF = F.derivative(RBzg.gen(1)) discriminants += [F.resultant(dF, RBzg.gen(1))] - combined_discriminant = lcm(discriminants)(*self._R.gens()) + combined_discriminant = lcm(discriminants)(*self._R.gens()) self._differentials_branch_locus = [] for x in combined_discriminant.factor(): if not x[0] in existing_factors: - self._differentials_branch_locus += self._CCz(x[0](self._CCz.gen(), - 0)).roots(multiplicities=False) + self._differentials_branch_locus += self._CCz( + x[0](self._CCz.gen(), 0) + ).roots(multiplicities=False) # We add these branchpoints to the existing. - #self.branch_locus = self.branch_locus+self._differentials_branch_locus + # self.branch_locus = self.branch_locus+self._differentials_branch_locus # We now want to also check whether Infinity is a branch point of any # of the differentials. # This will be useful when calculating the Abel-Jacobi map. - minpoly_list = [reparameterize_differential_minpoly(mp, Infinity) - for mp in minpoly_list] + minpoly_list = [ + reparameterize_differential_minpoly(mp, Infinity) for mp in minpoly_list + ] discriminants = [] for minpoly in minpoly_list: F = RBzg(minpoly) dF = F.derivative(RBzg.gen(1)) discriminants += [F.resultant(dF, RBzg.gen(1))] discriminant_about_infinity = RBzg(lcm(discriminants)) - if discriminant_about_infinity(0,0)==0: + if discriminant_about_infinity(0, 0) == 0: self._differentials_branch_locus.append(self._CC(Infinity)) # Voronoi diagram and the important points associated with it - self.voronoi_diagram = Voronoi(voronoi_ghost(self.branch_locus, - CC=self._CC)) - self._vertices = [self._CC(x0, y0) - for x0, y0 in self.voronoi_diagram.vertices] + self.voronoi_diagram = Voronoi(voronoi_ghost(self.branch_locus, CC=self._CC)) + self._vertices = [self._CC(x0, y0) for x0, y0 in self.voronoi_diagram.vertices] self._wvalues = [self.w_values(z0) for z0 in self._vertices] # We arbitrarily, but sensibly, set the basepoint to be the rightmost vertex - self._basepoint = (self._vertices.index(sorted(self._vertices, - key=lambda z:z.real())[-1]), 0) + self._basepoint = ( + self._vertices.index(sorted(self._vertices, key=lambda z: z.real())[-1]), + 0, + ) self._Sn = SymmetricGroup(range(self.degree)) self._L = {} self._integral_dict = {} self._fastcall_f = fast_callable(f, domain=self._CC) self._fastcall_dfdw = fast_callable(self._dfdw, domain=self._CC) self._fastcall_dfdz = fast_callable(self._dfdz, domain=self._CC) - self._fastcall_cohomology_basis = [fast_callable(h, domain = self._CC) - for h in self.cohomology_basis()] + self._fastcall_cohomology_basis = [ + fast_callable(h, domain=self._CC) for h in self.cohomology_basis() + ] def __repr__(self): r""" @@ -739,7 +761,10 @@ def __repr__(self): sage: RiemannSurface(f) Riemann surface defined by polynomial f = -z^4 + w^2 + 1 = 0, with 53 bits of precision """ - s = 'Riemann surface defined by polynomial f = %s = 0, with %s bits of precision' % (self.f, self._prec) + s = ( + "Riemann surface defined by polynomial f = %s = 0, with %s bits of precision" + % (self.f, self._prec) + ) return s def w_values(self, z0): @@ -772,7 +797,7 @@ def w_values(self, z0): sage: S.w_values(1) # abs tol 1e-14 [0.000000000000000] """ - return self.f(z0,self._CCw.gen(0)).roots(multiplicities=False) + return self.f(z0, self._CCw.gen(0)).roots(multiplicities=False) @cached_method def downstairs_edges(self): @@ -809,20 +834,23 @@ def downstairs_edges(self): # The regions of these points are all of the edges which don't go off # to infinity, which are exactly the ones we want. n = len(self.branch_locus) - desired_edges = [self.voronoi_diagram.regions[self.voronoi_diagram.point_region[i]] for i in range(n)] + desired_edges = [ + self.voronoi_diagram.regions[self.voronoi_diagram.point_region[i]] + for i in range(n) + ] # First construct the edges as a set because the regions will overlap # and we don't want to have two of the same edge. edges1 = set() for c in desired_edges: - for j in range(len(c)-1): - edges1.add(frozenset((c[j],c[j+1]))) - edges1.add(frozenset((c[0],c[-1]))) + for j in range(len(c) - 1): + edges1.add(frozenset((c[j], c[j + 1]))) + edges1.add(frozenset((c[0], c[-1]))) # Then make it into a list and sort it. # The sorting is important - it will make computing the monodromy group # MUCH easier. # We orient all the edges so that we go from lower to higher # numbered vertex for the continuation. - edges = [(i0,i1) if (i0 < i1) else (i1,i0) for (i0,i1) in edges1] + edges = [(i0, i1) if (i0 < i1) else (i1, i0) for (i0, i1) in edges1] edges.sort() return edges @@ -875,9 +903,18 @@ def upstairs_graph(self): True """ G = Graph(self.upstairs_edges(), immutable=True) - G.set_pos({(i,j): [self._vertices[i].real(), self._vertices[i].imag(), - self.w_values(self._vertices[i])[j].imag()] - for i in range(len(self._vertices)) for j in range(self.degree)}, dim=3) + G.set_pos( + { + (i, j): [ + self._vertices[i].real(), + self._vertices[i].imag(), + self.w_values(self._vertices[i])[j].imag(), + ] + for i in range(len(self._vertices)) + for j in range(self.degree) + }, + dim=3, + ) return G def _compute_delta(self, z1, epsilon, wvalues=None): @@ -935,22 +972,38 @@ def _compute_delta(self, z1, epsilon, wvalues=None): # For computation of rho. Need the branch locus + roots of a0. badpoints = self._f_branch_locus + self._a0roots rho = min(abs(z1 - z) for z in badpoints) / 2 - Y = max(abs(self._fastcall_dfdz(z1, wi)/self._fastcall_dfdw(z1, wi)) - for wi in wvalues) + Y = max( + abs(self._fastcall_dfdz(z1, wi) / self._fastcall_dfdw(z1, wi)) + for wi in wvalues + ) # compute M - upperbounds = [sum(ak[k] * (abs(z1) + rho)**k - for k in range(ak.degree())) - for ak in self._aks] + upperbounds = [ + sum(ak[k] * (abs(z1) + rho) ** k for k in range(ak.degree())) + for ak in self._aks + ] upperbounds.reverse() # If a0 is a constant polynomial, it is obviously bounded below. if not self._a0roots: lowerbound = self._CC(self._a0) / 2 else: - lowerbound = self._a0[self._a0.degree()]*prod(abs((zk - z1) - rho) for zk in self._a0roots) / 2 - M = 2 * max((upperbounds[k]/lowerbound).abs().nth_root(k+1) - for k in range(self.degree-1)) - return rho*(((rho*Y - epsilon)**2 + 4*epsilon*M).sqrt() - (rho*Y + epsilon))/(2*M - 2*rho*Y) + lowerbound = ( + self._a0[self._a0.degree()] + * prod(abs((zk - z1) - rho) for zk in self._a0roots) + / 2 + ) + M = 2 * max( + (upperbounds[k] / lowerbound).abs().nth_root(k + 1) + for k in range(self.degree - 1) + ) + return ( + rho + * ( + ((rho * Y - epsilon) ** 2 + 4 * epsilon * M).sqrt() + - (rho * Y + epsilon) + ) + / (2 * M - 2 * rho * Y) + ) else: # Instead, we just compute the minimum distance between branch # points and the point in question. @@ -1004,30 +1057,36 @@ def homotopy_continuation(self, edge): def path(t): return z_start * (1 - t) + z_end * t + # Primary procedure. T = ZERO currw = self.w_values(path(T)) n = len(currw) - epsilon = min([abs(currw[i] - currw[j]) for i in range(1,n) for j in range(i)])/3 - datastorage += [(T,currw,epsilon)] + epsilon = ( + min([abs(currw[i] - currw[j]) for i in range(1, n) for j in range(i)]) / 3 + ) + datastorage += [(T, currw, epsilon)] while T < ONE: - delta = self._compute_delta(path(T), epsilon, wvalues=currw)/path_length + delta = self._compute_delta(path(T), epsilon, wvalues=currw) / path_length # Move along the path by delta. T += delta # If T exceeds 1, just set it to 1 and compute. if T > ONE: - delta -= (T-ONE) + delta -= T - ONE T = ONE while True: try: - neww = self._determine_new_w(path(T),currw,epsilon) + neww = self._determine_new_w(path(T), currw, epsilon) except ConvergenceError: delta /= 2 T -= delta else: break currw = neww - epsilon = min([abs(currw[i] - currw[j]) for i in range(1,n) for j in range(i)])/3 + epsilon = ( + min([abs(currw[i] - currw[j]) for i in range(1, n) for j in range(i)]) + / 3 + ) datastorage += [(T, currw, epsilon)] return datastorage @@ -1119,8 +1178,11 @@ def _determine_new_w(self, z0, oldw, epsilon): Nnew_delta = new_delta.norm() # If we found the root exactly, or if delta only affects half the digits and # stops getting smaller, we decide that we have converged. - if (new_delta == 0) or (Nnew_delta >= Ndelta and - Ndelta.sign_mantissa_exponent()[2]+prec < wi.norm().sign_mantissa_exponent()[2]): + if (new_delta == 0) or ( + Nnew_delta >= Ndelta + and Ndelta.sign_mantissa_exponent()[2] + prec + < wi.norm().sign_mantissa_exponent()[2] + ): neww.append(wi) break delta = new_delta @@ -1128,7 +1190,9 @@ def _determine_new_w(self, z0, oldw, epsilon): wi -= delta # If we run 100 iterations without a result, terminate. else: - raise ConvergenceError("Newton iteration fails to converge after %s iterations" % j) + raise ConvergenceError( + "Newton iteration fails to converge after %s iterations" % j + ) return neww def _newton_iteration(self, z0, oldw, epsilon): @@ -1201,12 +1265,15 @@ def _newton_iteration(self, z0, oldw, epsilon): Nnew_delta = new_delta.norm() # If we found the root exactly, or if delta only affects half the digits and # stops getting smaller, we decide that we have converged. - if (new_delta == 0) or (Nnew_delta>=Ndelta and - Ndelta.sign_mantissa_exponent()[2]+prec < neww.norm().sign_mantissa_exponent()[2]): + if (new_delta == 0) or ( + Nnew_delta >= Ndelta + and Ndelta.sign_mantissa_exponent()[2] + prec + < neww.norm().sign_mantissa_exponent()[2] + ): return neww delta = new_delta Ndelta = Nnew_delta - neww-=delta + neww -= delta raise ConvergenceError("Newton iteration fails to converge") @cached_method @@ -1241,8 +1308,14 @@ def upstairs_edges(self): d_edge = (self._vertices[i0], self._vertices[i1]) # Epsilon for checking w-value later. val = self._wvalues[i1] - epsilon = min(abs(val[i] - val[n-j-1]) - for i in range(n) for j in range(n-i-1)) / 3 + epsilon = ( + min( + abs(val[i] - val[n - j - 1]) + for i in range(n) + for j in range(n - i - 1) + ) + / 3 + ) # Homotopy continuation along e. self._L[e] = self.homotopy_continuation(d_edge) homotopycont = self._L[e][-1][1] @@ -1288,15 +1361,18 @@ def _edge_permutation(self, edge): # find all upstairs edges that are lifts of the given # downstairs edge and store the corresponding indices at # start and end that label the branches upstairs. - L = [(j0, j1) for ((i0, j0), (i1, j1)) in self.upstairs_edges() - if edge == (i0, i1)] + L = [ + (j0, j1) + for ((i0, j0), (i1, j1)) in self.upstairs_edges() + if edge == (i0, i1) + ] # we should be finding exactly "degree" of these assert len(L) == self.degree # and as a corollary of how we construct them, the indices # at the start should be in order assert all(a == b[0] for a, b in enumerate(L)) return self._Sn([j1 for j0, j1 in L]) - raise ValueError('edge not in Voronoi diagram') + raise ValueError("edge not in Voronoi diagram") @cached_method def edge_permutations(self) -> dict: @@ -1340,7 +1416,7 @@ def edge_permutations(self) -> dict: """ D = {e: self._edge_permutation(e) for e in self.downstairs_edges()} for (a, b), p in list(D.items()): - D[(b, a)] = p**(-1) + D[(b, a)] = p ** (-1) return D @cached_method @@ -1392,15 +1468,19 @@ def monodromy_group(self): n = len(self.branch_locus) G = Graph(self.downstairs_edges()) # we get all the regions - loops = [self.voronoi_diagram.regions[i][:] - for i in self.voronoi_diagram.point_region] + loops = [ + self.voronoi_diagram.regions[i][:] + for i in self.voronoi_diagram.point_region + ] # and construct their Voronoi centers as complex numbers - centers = self.branch_locus + [self._CC(x, y) for x, y in self.voronoi_diagram.points[n:]] + centers = self.branch_locus + [ + self._CC(x, y) for x, y in self.voronoi_diagram.points[n:] + ] for center, loop in zip(centers, loops): if -1 in loop: # for loops involving infinity we take the finite part of the path i = loop.index(-1) - loop[:] = loop[i+1:]+loop[:i] + loop[:] = loop[i + 1 :] + loop[:i] else: # and for finite ones we close the paths loop.append(loop[0]) @@ -1415,7 +1495,7 @@ def monodromy_group(self): # infinity. There should be a unique way of doing so. inf_loops = loops[n:] inf_path = inf_loops.pop() - while (inf_loops): + while inf_loops: inf_path += (inf_loops.pop())[1:] assert inf_path[0] == inf_path[-1] @@ -1428,10 +1508,11 @@ def monodromy_group(self): SG = self._Sn for c in loops: to_loop = G.shortest_path(P0, c[0]) - to_loop_perm = SG.prod(edge_perms[(to_loop[i], to_loop[i + 1])] - for i in range(len(to_loop) - 1)) - c_perm = SG.prod(edge_perms[(c[i], c[i + 1])] - for i in range(len(c) - 1)) + to_loop_perm = SG.prod( + edge_perms[(to_loop[i], to_loop[i + 1])] + for i in range(len(to_loop) - 1) + ) + c_perm = SG.prod(edge_perms[(c[i], c[i + 1])] for i in range(len(c) - 1)) monodromy_gens.append(to_loop_perm * c_perm * ~to_loop_perm) return monodromy_gens @@ -1537,10 +1618,10 @@ def direction(center, neighbour): # score will be appropriately complemented at one of the # next vertices. - a_in = cycles[i][i0-1][0] - a_out = cycles[i][(i0+1) % len(cycles[i])][0] - b_in = cycles[j][i1-1][0] - b_out = cycles[j][(i1+1) % len(cycles[j])][0] + a_in = cycles[i][i0 - 1][0] + a_out = cycles[i][(i0 + 1) % len(cycles[i])][0] + b_in = cycles[j][i1 - 1][0] + b_out = cycles[j][(i1 + 1) % len(cycles[j])][0] # we can get the angles (and hence the rotation order) # by taking the arguments of the differences. @@ -1554,24 +1635,32 @@ def direction(center, neighbour): # problems occur with that. if (b_in != a_in) and (b_in != a_out): - if ((a_in_arg < b_in_arg < a_out_arg) + if ( + (a_in_arg < b_in_arg < a_out_arg) or (b_in_arg < a_out_arg < a_in_arg) - or (a_out_arg < a_in_arg < b_in_arg)): + or (a_out_arg < a_in_arg < b_in_arg) + ): intsum += 1 - elif ((a_out_arg < b_in_arg < a_in_arg) - or (b_in_arg < a_in_arg < a_out_arg) - or (a_in_arg < a_out_arg < b_in_arg)): + elif ( + (a_out_arg < b_in_arg < a_in_arg) + or (b_in_arg < a_in_arg < a_out_arg) + or (a_in_arg < a_out_arg < b_in_arg) + ): intsum -= 1 else: raise RuntimeError("impossible edge orientation") if (b_out != a_in) and (b_out != a_out): - if ((a_in_arg < b_out_arg < a_out_arg) + if ( + (a_in_arg < b_out_arg < a_out_arg) or (b_out_arg < a_out_arg < a_in_arg) - or (a_out_arg < a_in_arg < b_out_arg)): + or (a_out_arg < a_in_arg < b_out_arg) + ): intsum -= 1 - elif ((a_out_arg < b_out_arg < a_in_arg) - or (b_out_arg < a_in_arg < a_out_arg) - or (a_in_arg < a_out_arg < b_out_arg)): + elif ( + (a_out_arg < b_out_arg < a_in_arg) + or (b_out_arg < a_in_arg < a_out_arg) + or (a_in_arg < a_out_arg < b_out_arg) + ): intsum += 1 else: raise RuntimeError("impossible edge orientation") @@ -1598,7 +1687,9 @@ def direction(center, neighbour): if P[i][j] != 0: acycles[i] += [(P[i][j], [x for x in cycles[j]] + [cycles[j][0]])] if P[self.genus + i][j] != 0: - bcycles[i] += [(P[self.genus + i][j], [x for x in cycles[j]] + [cycles[j][0]])] + bcycles[i] += [ + (P[self.genus + i][j], [x for x in cycles[j]] + [cycles[j][0]]) + ] return acycles + bcycles def make_zw_interpolator(self, upstairs_edge, initial_continuation=None): @@ -1667,8 +1758,8 @@ def w_interpolate(t): w1 = w1[windex] t2, w2, _ = currL[i + 1] w2 = w2[windex] - z0 = (1-t)*z_start+t*z_end - w0 = self._CC(((t2-t)*w1+(t-t1)*w2)/(t2-t1)) + z0 = (1 - t) * z_start + t * z_end + w0 = self._CC(((t2 - t) * w1 + (t - t1) * w2) / (t2 - t1)) try: desired_result = self._newton_iteration(z0, w0, epsilon) except ConvergenceError: @@ -1679,7 +1770,7 @@ def w_interpolate(t): tnew = t while True: tnew = (t1 + tnew) / 2 - znew = (1-tnew)*z_start+tnew*z_end + znew = (1 - tnew) * z_start + tnew * z_end try: neww1 = self._determine_new_w(znew, currL[i][1], epsilon) except ConvergenceError: @@ -1690,6 +1781,7 @@ def w_interpolate(t): # once the loop has succeeded we insert our new value t1 = tnew currL.insert(i + 1, (t1, neww1, epsilon)) + return w_interpolate, (z_end - z_start) def simple_vector_line_integral(self, upstairs_edge, differentials): @@ -1739,8 +1831,10 @@ def simple_vector_line_integral(self, upstairs_edge, differentials): # for users. try: initial_continuation = self._L[d_edge] - upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), - (self._vertices[d_edge[1]], )) + upstairs_edge = ( + (self._vertices[d_edge[0]], upstairs_edge[0][1]), + (self._vertices[d_edge[1]],), + ) except KeyError: initial_continuation = self.homotopy_continuation(d_edge) w_of_t, Delta_z = self.make_zw_interpolator(upstairs_edge, initial_continuation) @@ -1782,7 +1876,7 @@ def cohomology_basis(self, option=1): """ if self.genus == 0: self._differentials = [] - return self._differentials #[0] + return self._differentials # [0] if self._differentials is None: # Computes differentials from the adjointIdeal using Singular # First we homogenize @@ -1795,26 +1889,31 @@ def cohomology_basis(self, option=1): # We load the relevant functionality into singularlib import sage.libs.singular.function_factory + sage.libs.singular.function_factory.lib("paraplanecurves.lib") adjointIdeal = sage.libs.singular.function.singular_function("adjointIdeal") libsing_options = sage.libs.singular.option.LibSingularVerboseOptions() # We compute the adjoint ideal (note we need to silence "redefine") - redef_save = libsing_options['redefine'] + redef_save = libsing_options["redefine"] try: - libsing_options['redefine'] = False + libsing_options["redefine"] = False J = adjointIdeal(fnew, option) finally: - libsing_options['redefine'] = redef_save + libsing_options["redefine"] = redef_save # We are interested in the (degree-3) subspace of the adjoint ideal. # We compute this by intersecting with (Z,W,U)^(degree-3). Then the # lowest degree generators are a basis of the relevant subspace. d = fnew.total_degree() - J2 = k.ideal(J).intersection(k.ideal([k.gen(0), k.gen(1), k.gen(2)])**(d - 3)) + J2 = k.ideal(J).intersection( + k.ideal([k.gen(0), k.gen(1), k.gen(2)]) ** (d - 3) + ) generators = [dehom(c) for c in J2.gens() if c.degree() == d - 3] if len(generators) != self.genus: - raise ValueError("computed regular differentials do not match stored genus") + raise ValueError( + "computed regular differentials do not match stored genus" + ) self._differentials = generators return self._differentials @@ -1892,29 +1991,33 @@ def _bounding_data(self, differentials, exact=False): # This copies previous work by NB, outputting the zipped list required # for a certified line integral. RB = self._R.base_ring() - P = PolynomialRing(RB, 'Z') + P = PolynomialRing(RB, "Z") k = P.fraction_field() - KP = PolynomialRing(k, 'W') # W->fraction field + KP = PolynomialRing(k, "W") # W->fraction field fZW = self.f(P.gen(0), KP.gen(0)) - L = k.extension(fZW, 'Wb') + L = k.extension(fZW, "Wb") dfdw_L = self._dfdw(P.gen(0), L.gen(0)) - integrand_list = [h/self._dfdw for h in differentials] + integrand_list = [h / self._dfdw for h in differentials] # minpoly_univ gives the minimal polynomial for h, in variable x, with # coefficients given by polynomials with coefficients in P (i.e. # rational polynomials in Z). - minpoly_univ = [(h(P.gen(0), L.gen(0))/dfdw_L).minpoly().numerator() - for h in differentials] - RBzg = PolynomialRing(RB, ['z', 'g']) + minpoly_univ = [ + (h(P.gen(0), L.gen(0)) / dfdw_L).minpoly().numerator() + for h in differentials + ] + RBzg = PolynomialRing(RB, ["z", "g"]) # The following line changes the variables in these minimal polynomials # as Z -> z, x -> G, then evaluates at G = QQzg.gens(1) ( = g ) - RBzgG = PolynomialRing(RBzg, 'G') - minpoly_list = [RBzgG([c(RBzg.gen(0)) for c in list(h)])(RBzg.gen(1)) - for h in minpoly_univ] + RBzgG = PolynomialRing(RBzg, "G") + minpoly_list = [ + RBzgG([c(RBzg.gen(0)) for c in list(h)])(RBzg.gen(1)) for h in minpoly_univ + ] # h(z,g)=0 --> dg/dz = - dhdz/dhdg - dgdz_list = [-h.derivative(RBzg.gen(0))/h.derivative(RBzg.gen(1)) - for h in minpoly_list] + dgdz_list = [ + -h.derivative(RBzg.gen(0)) / h.derivative(RBzg.gen(1)) for h in minpoly_list + ] - CCzg = PolynomialRing(self._CC, ['z','g']) + CCzg = PolynomialRing(self._CC, ["z", "g"]) CCminpoly_list = [CCzg(h) for h in minpoly_list] a0_list = [P(h.leading_coefficient()) for h in minpoly_univ] @@ -1922,10 +2025,18 @@ def _bounding_data(self, differentials, exact=False): # is embedded into CC, it has characteristic 0, and so we know the # irreducible factors are all separable, i.e. the roots have multiplicity # one. - a0_info = [(self._CC(a0.leading_coefficient()), - flatten([self._CCz(F).roots(multiplicities=False)*m - for F, m in a0.factor()])) - for a0 in a0_list] + a0_info = [ + ( + self._CC(a0.leading_coefficient()), + flatten( + [ + self._CCz(F).roots(multiplicities=False) * m + for F, m in a0.factor() + ] + ), + ) + for a0 in a0_list + ] if exact: return RBzg, list(zip(integrand_list, dgdz_list, minpoly_list, a0_info)) else: @@ -2024,22 +2135,25 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): # compatibility for users. try: initial_continuation = self._L[d_edge] - upstairs_edge = ((self._vertices[d_edge[0]], upstairs_edge[0][1]), - (self._vertices[d_edge[1]], )) + upstairs_edge = ( + (self._vertices[d_edge[0]], upstairs_edge[0][1]), + (self._vertices[d_edge[1]],), + ) except KeyError: initial_continuation = self.homotopy_continuation(d_edge) - zwt, z1_minus_z0 = self.make_zw_interpolator(upstairs_edge, - initial_continuation) + zwt, z1_minus_z0 = self.make_zw_interpolator( + upstairs_edge, initial_continuation + ) z0 = zwt(0)[0] z1 = zwt(1)[0] # list of (centre, radius) pairs that still need to be processed - ball_stack = [(self._RR(1/2), self._RR(1/2), 0)] - alpha = self._RR(912/1000) + ball_stack = [(self._RR(1 / 2), self._RR(1 / 2), 0)] + alpha = self._RR(912 / 1000) # alpha set manually for scaling purposes. Basic benchmarking shows # that ~0.9 is a sensible value. - E_global = self._RR(2)**(-self._prec+3) + E_global = self._RR(2) ** (-self._prec + 3) # Output will iteratively store the output of the integral. V = VectorSpace(self._CC, len(differentials)) @@ -2064,45 +2178,54 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): # possible to cover it entirely in a ball which encompasses an appropriate # ellipse. def local_N(ct, rt): - cz = (1-ct)*z0+ct*z1 # This is the central z-value of our ball. - distances = [(cz-b).abs() for b in self.branch_locus] + cz = (1 - ct) * z0 + ct * z1 # This is the central z-value of our ball. + distances = [(cz - b).abs() for b in self.branch_locus] rho_z = min(distances) - rho_t = rho_z/(z1_minus_z0).abs() - rho_t = alpha*rho_t+(1-alpha)*rt # sqrt(rho_t*rt) could also work - rho_z = rho_t*(z1-z0).abs() - delta_z = (alpha*rho_t+(1-alpha)*rt)*(z1_minus_z0).abs() - expr = rho_t/rt+((rho_t/rt)**2-1).sqrt() # Note this is really exp(arcosh(rho_t/rt)) + rho_t = rho_z / (z1_minus_z0).abs() + rho_t = alpha * rho_t + (1 - alpha) * rt # sqrt(rho_t*rt) could also work + rho_z = rho_t * (z1 - z0).abs() + delta_z = (alpha * rho_t + (1 - alpha) * rt) * (z1_minus_z0).abs() + expr = ( + rho_t / rt + ((rho_t / rt) ** 2 - 1).sqrt() + ) # Note this is really exp(arcosh(rho_t/rt)) Ni = 3 cw = zwt(ct)[1] - for g, dgdz, minpoly,(a0lc,a0roots) in bounding_data_list: - z_1 = a0lc.abs()*prod((cz-r).abs()-rho_z for r in a0roots) + for g, dgdz, minpoly, (a0lc, a0roots) in bounding_data_list: + z_1 = a0lc.abs() * prod((cz - r).abs() - rho_z for r in a0roots) n = minpoly.degree(CCzg.gen(1)) - ai_new = [(minpoly.coefficient({CCzg.gen(1):i}))(z=cz+self._CCz.gen(0)) - for i in range(n)] - ai_pos = [self._RRz([c.abs() for c in h.list()]) - for h in ai_new] - m = [a(rho_z)/z_1 for a in ai_pos] + ai_new = [ + (minpoly.coefficient({CCzg.gen(1): i}))(z=cz + self._CCz.gen(0)) + for i in range(n) + ] + ai_pos = [self._RRz([c.abs() for c in h.list()]) for h in ai_new] + m = [a(rho_z) / z_1 for a in ai_pos] l = len(m) - M_tilde = 2*max((m[i].abs())**(1/self._RR(l-i)) - for i in range(l)) - cg = g(cz,cw) - cdgdz = dgdz(cz,cg) - Delta = delta_z*cdgdz.abs() + (delta_z**2)*M_tilde/(rho_z*(rho_z-delta_z)) + M_tilde = 2 * max( + (m[i].abs()) ** (1 / self._RR(l - i)) for i in range(l) + ) + cg = g(cz, cw) + cdgdz = dgdz(cz, cg) + Delta = delta_z * cdgdz.abs() + (delta_z**2) * M_tilde / ( + rho_z * (rho_z - delta_z) + ) M = Delta - N_required = ((M*(self._RR.pi()+64/(15*(expr**2-1)))/E_global).log()/(2*expr.log())).ceil() + N_required = ( + (M * (self._RR.pi() + 64 / (15 * (expr**2 - 1))) / E_global).log() + / (2 * expr.log()) + ).ceil() Ni = max(Ni, N_required) return Ni while ball_stack: ct, rt, lN = ball_stack.pop() - ncts = [ct-rt/2, ct+rt/2] - nrt = rt/2 + ncts = [ct - rt / 2, ct + rt / 2] + nrt = rt / 2 if not lN: - cz = (1-ct)*z0+ct*z1 - distances = [(cz-b).abs() for b in self.branch_locus] + cz = (1 - ct) * z0 + ct * z1 + distances = [(cz - b).abs() for b in self.branch_locus] rho_z = min(distances) - rho_t = rho_z/(z1_minus_z0).abs() + rho_t = rho_z / (z1_minus_z0).abs() if rho_t <= rt: ball_stack.append((ncts[0], nrt, 0)) @@ -2118,20 +2241,20 @@ def local_N(ct, rt): ball_stack.append((ncts[1], nrt, nNs[1])) continue - if lN % 2 and not lN==3: - lN += 1 + if lN % 2 and not lN == 3: + lN += 1 - ct_minus_rt = ct-rt - two_rt = 2*rt + ct_minus_rt = ct - rt + two_rt = 2 * rt def integrand(t): zt, wt = zwt(ct_minus_rt + t * two_rt) dfdwt = self._fastcall_dfdw(zt, wt) return V([h(zt, wt) / dfdwt for h in differentials]) - output += two_rt*integrate_vector_N(integrand, self._prec, lN) + output += two_rt * integrate_vector_N(integrand, self._prec, lN) - return output*z1_minus_z0 + return output * z1_minus_z0 def matrix_of_integral_values(self, differentials, integration_method="heuristic"): r""" @@ -2191,9 +2314,9 @@ def normalize_pairs(L): else: R.append((L[i + 1], L[i])) return R + occurring_edges = set() - occurring_edges.update(*[normalize_pairs(p[1]) for h in cycles - for p in h]) + occurring_edges.update(*[normalize_pairs(p[1]) for h in cycles for p in h]) if differentials is self.cohomology_basis(): fcd = self._fastcall_cohomology_basis @@ -2293,8 +2416,11 @@ def riemann_matrix(self): True """ PeriodMatrix = self.period_matrix() - Am = PeriodMatrix[0:self.genus,0:self.genus] - RM = numerical_inverse(Am)*PeriodMatrix[0:self.genus,self.genus:2*self.genus] + Am = PeriodMatrix[0 : self.genus, 0 : self.genus] + RM = ( + numerical_inverse(Am) + * PeriodMatrix[0 : self.genus, self.genus : 2 * self.genus] + ) return RM def plot_paths(self): @@ -2316,6 +2442,7 @@ def plot_paths(self): Graphics object consisting of 2 graphics primitives """ from sage.plot.point import point2d + P = [] # trigger the computation of the homology basis, so that self._L is present @@ -2327,6 +2454,7 @@ def plot_paths(self): def path(t): return (1 - t) * z0 + t * z1 + T = self._L[e] P += [path(t[0]) for t in T] return point2d(P, size=1) + point2d(self.branch_locus, color="red") @@ -2352,6 +2480,7 @@ def plot_paths3d(self, thickness=0.01): """ from sage.plot.graphics import Graphics from sage.plot.plot3d.shapes2 import point3d, line3d + P = Graphics() # trigger the computation of the homology basis, so that @@ -2363,16 +2492,24 @@ def plot_paths3d(self, thickness=0.01): z1 = self._vertices[e[1]] def path(t): - z = (1-t)*z0+t*z1 - return (z.real_part(),z.imag_part()) + z = (1 - t) * z0 + t * z1 + return (z.real_part(), z.imag_part()) + T = self._L[e] color = "blue" for i in range(self.degree): - P += line3d([path(t[0])+(t[1][i].imag_part(),) for t in T],color=color,thickness=thickness) - for z,ws in zip(self._vertices,self._wvalues): + P += line3d( + [path(t[0]) + (t[1][i].imag_part(),) for t in T], + color=color, + thickness=thickness, + ) + for z, ws in zip(self._vertices, self._wvalues): for w in ws: - P += point3d([z.real_part(), z.imag_part(), w.imag_part()], - color="purple", size=20) + P += point3d( + [z.real_part(), z.imag_part(), w.imag_part()], + color="purple", + size=20, + ) return P def endomorphism_basis(self, b=None, r=None): @@ -2416,7 +2553,7 @@ def endomorphism_basis(self, b=None, r=None): """ M = self.riemann_matrix() - return integer_matrix_relations(M,M,b,r) + return integer_matrix_relations(M, M, b, r) def homomorphism_basis(self, other, b=None, r=None): r""" @@ -2455,7 +2592,7 @@ def homomorphism_basis(self, other, b=None, r=None): """ M1 = self.riemann_matrix() M2 = other.riemann_matrix() - return integer_matrix_relations(M2,M1,b,r) + return integer_matrix_relations(M2, M1, b, r) def tangent_representation_numerical(self, Rs, other=None): r""" @@ -2550,7 +2687,7 @@ def tangent_representation_algebraic(self, Rs, other=None, epscomp=None): True """ if not epscomp: - epscomp = 2**(-self._prec + 30) + epscomp = 2 ** (-self._prec + 30) QQalg = QQ.algebraic_closure() def polynomialize_element(alpha): @@ -2568,7 +2705,7 @@ def algebraize_element(alpha): rt = tup[0] if (alpha - CC(rt)).abs() < epscomp: return rt - raise AssertionError('No close root found while algebraizing') + raise AssertionError("No close root found while algebraizing") def algebraize_matrices(Ts): nr = Ts[0].nrows() @@ -2579,7 +2716,7 @@ def algebraize_matrices(Ts): L = eltsAlg[0].parent() TsAlgL = [] for i in range(len(Ts)): - TAlgL = [eltsAlg[j] for j in range(i*nr*nc, (i + 1)*nr*nc)] + TAlgL = [eltsAlg[j] for j in range(i * nr * nc, (i + 1) * nr * nc)] TsAlgL.append(Matrix(L, nr, nc, TAlgL)) return TsAlgL @@ -2610,13 +2747,17 @@ def rosati_involution(self, R): sage: S.rosati_involution(S.rosati_involution(Rs[1])) == Rs[1] True """ + def standard_symplectic_matrix(n): one = Matrix.identity(n) zero = Matrix.zero(n) return Matrix.block([[zero, -one], [one, zero]]) + g = self.genus - if not(R.nrows() == 2 * g == R.ncols()): - raise AssertionError("Matrix is not the homology representation of an endomorphism") + if not (R.nrows() == 2 * g == R.ncols()): + raise AssertionError( + "Matrix is not the homology representation of an endomorphism" + ) J = standard_symplectic_matrix(g) return -J * R.transpose() * J @@ -2674,7 +2815,7 @@ def symplectic_isomorphisms(self, other=None, hom_basis=None, b=None, r=None): Rs = self.homomorphism_basis(other=other, b=b, r=r) r = len(Rs) g = self.genus - A = PolynomialRing(QQ, r, 'x') + A = PolynomialRing(QQ, r, "x") gensA = A.gens() # Use that the trace is positive definite; we could also put this as an # extra condition when determining the endomorphism basis to speed up @@ -2682,10 +2823,14 @@ def symplectic_isomorphisms(self, other=None, hom_basis=None, b=None, r=None): R = sum(gensA[i] * Rs[i].change_ring(A) for i in range(r)) tr = (R * self.rosati_involution(R)).trace() # Condition tr = 2 g creates ellipsoid - M = Matrix(ZZ, r, r, [tr.derivative(gen1).derivative(gen2) - for gen1 in gensA for gen2 in gensA]) - vs = M.__pari__().qfminim(4*g)[2].sage().transpose() - vs = [v for v in vs if v * M * v == 4*g] + M = Matrix( + ZZ, + r, + r, + [tr.derivative(gen1).derivative(gen2) for gen1 in gensA for gen2 in gensA], + ) + vs = M.__pari__().qfminim(4 * g)[2].sage().transpose() + vs = [v for v in vs if v * M * v == 4 * g] vs += [-v for v in vs] RsIso = [] for v in vs: @@ -2749,7 +2894,9 @@ def __add__(self, other): """ return RiemannSurfaceSum([self, other]) - def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individually=False, raise_errors=True, prec=None): + def _integrate_differentials_iteratively( + self, upstairs_edge, cutoff_individually=False, raise_errors=True, prec=None + ): r""" Integrate the cohomology basis along a straight line edge. @@ -2822,110 +2969,126 @@ def _integrate_differentials_iteratively(self, upstairs_edge, cutoff_individuall z_start = self._CC(z_start) z_end = self._CC(z_end) - if z_end==self._CC(Infinity): + if z_end == self._CC(Infinity): raise NotImplementedError _, bounding_data_list = self._cohomology_basis_bounding_data mp_list = [bd[2] for bd in bounding_data_list] # Parameterise so zbar=0 corresponds to z=z_start - mp_list = [reparameterize_differential_minpoly(mp, z_start) - for mp in mp_list] + mp_list = [reparameterize_differential_minpoly(mp, z_start) for mp in mp_list] # Depending on whether we have reparameterized about infinity or not, # we initialise some values we will need in the calculation, inclduing # the function `initalize', which at a given value of zbar, calculates - # the starting value for the i-th differential so it can be iterated - # from via homotopy continuation. - if z_start==self._CC(Infinity): - CCzg = PolynomialRing(self._CC, ['zbar','gbar']) + # the starting value for the i-th differential so it can be iterated + # from via homotopy continuation. + if z_start == self._CC(Infinity): + CCzg = PolynomialRing(self._CC, ["zbar", "gbar"]) mp_list = [CCzg(mp) for mp in mp_list] - J = 1/z_end - endscale = -z_end**(-2) + J = 1 / z_end + endscale = -(z_end ** (-2)) def initialise(z, i): - DF = ComplexField(2*self._prec) - DFw = PolynomialRing(DF,'wbar') + DF = ComplexField(2 * self._prec) + DFw = PolynomialRing(DF, "wbar") z = DF(z) - R = DF(z**(-1)) + R = DF(z ** (-1)) wR = DFw(self.f(R, DFw.gen(0))).roots(multiplicities=False)[w_start] - newg = -R**2*self.cohomology_basis()[i](R, wR)/self._dfdw(R, wR) - err = mp_list[i](z,newg).abs() - if err>tau: + newg = -(R**2) * self.cohomology_basis()[i](R, wR) / self._dfdw(R, wR) + err = mp_list[i](z, newg).abs() + if err > tau: rs = mp_list[i](z, DFw.gen(0)).roots(multiplicities=False) sb = find_closest_element(newg, rs) newg = rs[sb] return newg + else: CCzg = mp_list[0].parent() - J = z_end-z_start + J = z_end - z_start endscale = 1 def initialise(z, i): - newg = self.cohomology_basis()[i](z_start, w_start)/self._dfdw(z_start, w_start) + newg = self.cohomology_basis()[i](z_start, w_start) / self._dfdw( + z_start, w_start + ) err = mp_list[i](z, newg).abs() - if err>tau: + if err > tau: rs = mp_list[i](z, self._CCw.gen(0)).roots(multiplicities=False) sb = find_closest_element(newg, rs) newg = rs[sb] return newg - # As multiple calls of the minimal polynomial and it's derivative will - # be required for the homotopy continuaiton, we create fast-callable - # versions of these. + # As multiple calls of the minimal polynomial and it's derivative will + # be required for the homotopy continuaiton, we create fast-callable + # versions of these. fc_mp_list = [fast_callable(mp, domain=self._CC) for mp in mp_list] - fc_dmp_list = [fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) - for mp in mp_list] + fc_dmp_list = [ + fast_callable(mp.derivative(CCzg.gen(1)), domain=self._CC) for mp in mp_list + ] if prec is None: prec = self._prec # tau here is playing the role of the desired error. - tau = self._RR(2)**(-prec+3) + tau = self._RR(2) ** (-prec + 3) one = self._RR(1) - la = self._RR.pi()/2 + la = self._RR.pi() / 2 - # Cutoffs are used to allow us to not have to integrate as close into - # a singularity as we might otherwise have to, by knowing that we can - # truncate the integration interval and only introduce a finite error + # Cutoffs are used to allow us to not have to integrate as close into + # a singularity as we might otherwise have to, by knowing that we can + # truncate the integration interval and only introduce a finite error # that can be bounded by knowledge of the asymptotics of the integrands, - # which we have from their minimal polynomials. This is really a + # which we have from their minimal polynomials. This is really a # precursor to what would be ideal to implement eventually, namely - # a method that uses Puiseux series to integrate into singularities. - # We allow for cutoffs to be tailored to each integrand, or we take a - # uniform value. + # a method that uses Puiseux series to integrate into singularities. + # We allow for cutoffs to be tailored to each integrand, or we take a + # uniform value. if cutoff_individually is None: cutoffs = [0] cutoff_individually = False else: cutoffs = [] - A = PolynomialRing(self._CC,'xyz') + A = PolynomialRing(self._CC, "xyz") aes = [] for mp in mp_list: d = mp.dict() - mp = sum([d[k]*CCzg.gen(0)**k[0]*CCzg.gen(1)**k[1] - for k in d.keys() if d[k].abs()>tau]) - cst = min([iz for (iz, ig) in d.keys() if ig==0]) - a = QQ(max([(cst-iz)/ig for (iz,ig) in d.keys() if ig>0])) - sum_coeffs = sum([d[k]*A.gen(0)**k[1] for k in d.keys() - if ((k[1]==0 and k[0]==cst) or k[1]*a+k[0]-cst==0)]) + mp = sum( + [ + d[k] * CCzg.gen(0) ** k[0] * CCzg.gen(1) ** k[1] + for k in d.keys() + if d[k].abs() > tau + ] + ) + cst = min([iz for (iz, ig) in d.keys() if ig == 0]) + a = QQ(max([(cst - iz) / ig for (iz, ig) in d.keys() if ig > 0])) + sum_coeffs = sum( + [ + d[k] * A.gen(0) ** k[1] + for k in d.keys() + if ((k[1] == 0 and k[0] == cst) or k[1] * a + k[0] - cst == 0) + ] + ) G = max([r.abs() for r in sum_coeffs.roots(multiplicities=False)]) - cutoffs.append(((a+1)*tau/G)**(1/self._CC(a+1))/J.abs()) + cutoffs.append(((a + 1) * tau / G) ** (1 / self._CC(a + 1)) / J.abs()) aes.append(a) - cutoff_individually = bool(not all(ai<=0 for ai in aes) and cutoff_individually) + cutoff_individually = bool( + not all(ai <= 0 for ai in aes) and cutoff_individually + ) - # The `raise_errors' variable toggles what we do in the event that - # newton iteration hasn't converged to the desired precision in a - # fixed number of steps, here set to 100. + # The `raise_errors' variable toggles what we do in the event that + # newton iteration hasn't converged to the desired precision in a + # fixed number of steps, here set to 100. # If the default value of True is taken, then the failure to converge - # raises an error. If the value of False is taken, this failure to - # converge happens silently, thus allowing the user to get *an* - # answer out of the integration, but numerical imprecision is to be - # expected. + # raises an error. If the value of False is taken, this failure to + # converge happens silently, thus allowing the user to get *an* + # answer out of the integration, but numerical imprecision is to be + # expected. if raise_errors: - n_steps = self._prec-1 + n_steps = self._prec - 1 def error_handle(out): raise ConvergenceError("Newton iteration fails to converge") + else: n_steps = 15 @@ -2934,28 +3097,28 @@ def error_handle(out): V = VectorSpace(self._CC, self.genus) h = one - Nh = (-lambert_w(-1,-tau/2)/la).log().ceil() - h0 = Nh*h + Nh = (-lambert_w(-1, -tau / 2) / la).log().ceil() + h0 = Nh * h # Depending on how the cutoffs were defined, we now create the function # which calculates the integrand we want to integrate via double- - # exponential methods. This will get the value at the next node by + # exponential methods. This will get the value at the next node by # homotopy-continuing from the last node value. There is also a slight - # technical condition which implements the cutoffs. + # technical condition which implements the cutoffs. if cutoff_individually: z_fc_list = list(zip(fc_mp_list, fc_dmp_list)) def fv(hj, previous_estimate_and_validity): - u2 = la*hj.sinh() - t = 1/(2*u2.exp()*u2.cosh()) - z0 = J*t + u2 = la * hj.sinh() + t = 1 / (2 * u2.exp() * u2.cosh()) + z0 = J * t outg = [] - valid = self.genus*[True] + valid = self.genus * [True] previous_estimate, validity = previous_estimate_and_validity for i in range(self.genus): co = cutoffs[i] pv = validity[i] - if t= Ndelta and - (Ndelta.sign_mantissa_exponent()[2] - +self._prec) < newg.norm().sign_mantissa_exponent()[2]): + if (new_delta == 0) or ( + Nnew_delta >= Ndelta + and (Ndelta.sign_mantissa_exponent()[2] + self._prec) + < newg.norm().sign_mantissa_exponent()[2] + ): outg.append(newg) break delta = new_delta Ndelta = Nnew_delta - newg-=delta - if j==99: + newg -= delta + if j == 99: outg.append(error_handle(newg)) fj = V(outg) - u1 = la*hj.cosh() - w = u1/(2*u2.cosh()**2) - return (fj, valid), w*fj + u1 = la * hj.cosh() + w = u1 / (2 * u2.cosh() ** 2) + return (fj, valid), w * fj - f0, v0 = fv(h0, (self.genus*[0], self.genus*[False])) + f0, v0 = fv(h0, (self.genus * [0], self.genus * [False])) else: cutoffs.append(1) cutoff = min(cutoffs) - cutoff_z = J*cutoff + cutoff_z = J * cutoff J -= cutoff_z def fv(hj, previous_estimate): - u2 = la*hj.sinh() - t = 1/(2*u2.exp()*u2.cosh()) - z0 = cutoff_z+J*t + u2 = la * hj.sinh() + t = 1 / (2 * u2.exp() * u2.cosh()) + z0 = cutoff_z + J * t outg = [] for F, dF, oldg in zip(fc_mp_list, fc_dmp_list, previous_estimate): delta = F(z0, oldg) / dF(z0, oldg) @@ -3003,43 +3168,45 @@ def fv(hj, previous_estimate): for j in range(100): new_delta = F(z0, newg) / dF(z0, newg) Nnew_delta = new_delta.norm() - if (new_delta == 0) or (Nnew_delta >= Ndelta and - (Ndelta.sign_mantissa_exponent()[2] - +self._prec) < newg.norm().sign_mantissa_exponent()[2]): + if (new_delta == 0) or ( + Nnew_delta >= Ndelta + and (Ndelta.sign_mantissa_exponent()[2] + self._prec) + < newg.norm().sign_mantissa_exponent()[2] + ): outg.append(newg) break delta = new_delta Ndelta = Nnew_delta - newg-=delta - if j==99: + newg -= delta + if j == 99: outg.append(error_handle(newg)) fj = V(outg) - u1 = la*hj.cosh() - w = u1/(2*u2.cosh()**2) - return fj, w*fj + u1 = la * hj.cosh() + w = u1 / (2 * u2.cosh() ** 2) + return fj, w * fj - u1, u2 = (la*h0.cosh(),la*h0.sinh()) - y, w = (1/(2*u2.exp()*u2.cosh()), u1/(2*u2.cosh()**2)) - z0 = cutoff_z+J*y + u1, u2 = (la * h0.cosh(), la * h0.sinh()) + y, w = (1 / (2 * u2.exp() * u2.cosh()), u1 / (2 * u2.cosh() ** 2)) + z0 = cutoff_z + J * y f0 = [initialise(z0, i) for i in range(self.genus)] f0 = V(f0) - v0 = w*f0 + v0 = w * f0 D3_over_tau = v0.norm(Infinity) D4 = D3_over_tau results = [] # we now calculate the integral via double-exponential methods - # repeatedly halving the step size and then using a heuristic + # repeatedly halving the step size and then using a heuristic # convergence check. We again use the error_handle function to deal - # with the case where we exceed the maximum number of steps allowed, + # with the case where we exceed the maximum number of steps allowed, # currently set by to make sure the step size does not fall below the - # resolution set by the binary precision used. + # resolution set by the binary precision used. for k in range(n_steps): hj = h0 val = v0 fj = f0 - for j in range(2*Nh): + for j in range(2 * Nh): hj -= h try: fj, v = fv(hj, fj) @@ -3047,24 +3214,33 @@ def fv(hj, previous_estimate): break D3_over_tau = max(v.norm(Infinity), D3_over_tau) val += v - if j==2*Nh-1: - results.append(h*val) + if j == 2 * Nh - 1: + results.append(h * val) D4 = max(D4, v.norm(Infinity)) - if len(results)>2: + if len(results) > 2: if results[-1] == results[-2] or results[2] == results[-3]: D = tau else: - D1 = (results[-1]-results[-2]).norm(Infinity) - D2 = (results[-1]-results[-3]).norm(Infinity) - D = min(one, max(D1**(D1.log()/D2.log()),D2**2,tau*D3_over_tau,D4,tau)) + D1 = (results[-1] - results[-2]).norm(Infinity) + D2 = (results[-1] - results[-3]).norm(Infinity) + D = min( + one, + max( + D1 ** (D1.log() / D2.log()), + D2**2, + tau * D3_over_tau, + D4, + tau, + ), + ) if D <= tau: if cutoff_individually: fj = fj[0] - return J*results[-1], endscale*fj + return J * results[-1], endscale * fj h /= 2 Nh *= 2 - return error_handle((J*results[-1], endscale*fj)) + return error_handle((J * results[-1], endscale * fj)) def _aj_based(self, P): r""" @@ -3115,7 +3291,7 @@ def _aj_based(self, P): ##### fcd = self._fastcall_cohomology_basis - if self._integration_method=="heuristic": + if self._integration_method == "heuristic": line_int = lambda edge: self.simple_vector_line_integral(edge, fcd) else: bd = self._cohomology_basis_bounding_data @@ -3125,64 +3301,88 @@ def _aj_based(self, P): zP, wP = P try: - Inf = bool(zP==zP.parent()(Infinity)) + Inf = bool(zP == zP.parent()(Infinity)) except TypeError: Inf = False if Inf: zV = self._vertices[B[0]] - if zV==0: + if zV == 0: zV += 1 upstairs_edge = (P, zV) ci = bool(self._CC(Infinity) in self._differentials_branch_locus) - AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, - cutoff_individually=ci) + AJ, endgs = self._integrate_differentials_iteratively( + upstairs_edge, cutoff_individually=ci + ) AJ = -AJ g0e = endgs[0] ws = self.w_values(zV) - g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] + g0s = [self.cohomology_basis()[0](zV, wi) / self._dfdw(zV, wi) for wi in ws] W_index = find_closest_element(g0e, g0s) - if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") + if ( + g0e + - self.cohomology_basis()[0](zV, ws[W_index]) + / self._dfdw(zV, ws[W_index]) + ).abs() > 1e-10: + raise ConvergenceError( + "Integrand continuation failed to get representative values, higher precision required." + ) V_index = B[0] else: zP = self._CC(zP) wP = self._CC(wP) V_index = find_closest_element(zP, self._vertices) - if zP==self._vertices[V_index]: + if zP == self._vertices[V_index]: W_index = find_closest_element(wP, self._wvalues[V_index]) AJ = 0 else: b_index = find_closest_element(zP, self.branch_locus) b = self.branch_locus[b_index] - #bl = self.branch_locus+self._differentials_branch_locus - #b_index = find_closest_element(zP, bl) - #b = bl[b_index] + # bl = self.branch_locus+self._differentials_branch_locus + # b_index = find_closest_element(zP, bl) + # b = bl[b_index] scale = max(b.abs() for b in self.branch_locus) - d1 = self._CC(1e-2)*scale + d1 = self._CC(1e-2) * scale # We choose the first vertex we want to go to. # If the closest vertex is closer than the nearest branch point, just take that vertex # otherwise we need something smarter. - delta = self._RR(2)**(-self._prec+1) - if not ((zP-self._vertices[V_index]).abs() < (zP-b).abs() or (zP-b).abs()<=delta): - region = self.voronoi_diagram.regions[self.voronoi_diagram.point_region[b_index]] - args = [(self._vertices[i]-zP).argument() - (b-zP).argument() for i in region] - suitable_vertex_indices = [region[i] - for i in range(len(region)) if args[i].abs()-self._RR.pi()/2>=-self._RR(1e-15)] - suitable_vertices = [self._vertices[i] for i in suitable_vertex_indices] - if suitable_vertices==[]: - raise ValueError("There is no satisfactory choice of V for zP={}".format(zP)) - V_index = suitable_vertex_indices[find_closest_element(zP, suitable_vertices)] + delta = self._RR(2) ** (-self._prec + 1) + if not ( + (zP - self._vertices[V_index]).abs() < (zP - b).abs() + or (zP - b).abs() <= delta + ): + region = self.voronoi_diagram.regions[ + self.voronoi_diagram.point_region[b_index] + ] + args = [ + (self._vertices[i] - zP).argument() - (b - zP).argument() + for i in region + ] + suitable_vertex_indices = [ + region[i] + for i in range(len(region)) + if args[i].abs() - self._RR.pi() / 2 >= -self._RR(1e-15) + ] + suitable_vertices = [ + self._vertices[i] for i in suitable_vertex_indices + ] + if suitable_vertices == []: + raise ValueError( + "There is no satisfactory choice of V for zP={}".format(zP) + ) + V_index = suitable_vertex_indices[ + find_closest_element(zP, suitable_vertices) + ] ##### zV = self._vertices[V_index] - if (zP-b).abs() >= d1 or b in self._differentials_branch_locus: + if (zP - b).abs() >= d1 or b in self._differentials_branch_locus: wP_index = find_closest_element(wP, self.w_values(zP)) d_edge = (zP, zV) - u_edge = ((zP, wP_index), (zV, )) + u_edge = ((zP, wP_index), (zV,)) initial_continuation = self.homotopy_continuation(d_edge) AJ = -line_int(u_edge) @@ -3195,55 +3395,81 @@ def _aj_based(self, P): ##### # Here we need a block of code to change the vertex if the path # from zP to zV would go through a ramification point of the integrands - fl = [c for c in self._differentials_branch_locus if not c==self._CC(Infinity)] - ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 - for c in fl] - ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() - for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] - while (len(ds)>=1 and min(ds)= 0 and ts[i] <= 1) + ] + while len(ds) >= 1 and min(ds) < delta: V_index = suitable_vertex_indices.pop() zV = self._vertices[V_index] - ts = [((c-zP)*(zV-zP).conjugate()).real()/(zP-zV).norm()**2 - for c in fl] - ds = [(fl[i]-zP-ts[i]*(zV-zP)).abs() - for i in range(len(ts)) if (ts[i]>=0 and ts[i]<=1)] + ts = [ + ((c - zP) * (zV - zP).conjugate()).real() + / (zP - zV).norm() ** 2 + for c in fl + ] + ds = [ + (fl[i] - zP - ts[i] * (zV - zP)).abs() + for i in range(len(ts)) + if (ts[i] >= 0 and ts[i] <= 1) + ] ##### - while self._dfdw(zs, ws).abs()==0: - zs = zs+delta*(zV-zs)/(zV-zs).abs()/2 + while self._dfdw(zs, ws).abs() == 0: + zs = zs + delta * (zV - zs) / (zV - zs).abs() / 2 ws_list = self.w_values(zs) wP_index = find_closest_element(ws, ws_list) ws = ws_list[wP_index] upstairs_edge = ((zs, ws), zV) - AJ, endgs = self._integrate_differentials_iteratively(upstairs_edge, - cutoff_individually=False) + AJ, endgs = self._integrate_differentials_iteratively( + upstairs_edge, cutoff_individually=False + ) AJ = -AJ g0e = endgs[0] ws = self.w_values(zV) - g0s = [self.cohomology_basis()[0](zV, wi)/self._dfdw(zV, wi) for wi in ws] + g0s = [ + self.cohomology_basis()[0](zV, wi) / self._dfdw(zV, wi) + for wi in ws + ] W_index = find_closest_element(g0e, g0s) - if (g0e - self.cohomology_basis()[0](zV, ws[W_index])/self._dfdw(zV, ws[W_index])).abs()>1e-10: - raise ConvergenceError("Integrand continuation failed to get representative values, higher precision required.") + if ( + g0e + - self.cohomology_basis()[0](zV, ws[W_index]) + / self._dfdw(zV, ws[W_index]) + ).abs() > 1e-10: + raise ConvergenceError( + "Integrand continuation failed to get representative values, higher precision required." + ) uV_index = (V_index, W_index) ##### G = self.upstairs_graph() path = G.shortest_path(B, uV_index) - edges = [(path[i],path[i+1]) for i in range(len(path)-1)] + edges = [(path[i], path[i + 1]) for i in range(len(path) - 1)] ##### for e in edges: - if e[1][0]>e[0][0]: + if e[1][0] > e[0][0]: s = 1 else: s = -1 e = tuple(reversed(e)) try: - AJ += s*self._integral_dict[e] + AJ += s * self._integral_dict[e] except KeyError: Ie = line_int(e) self._integral_dict[e] = Ie - AJ += s*Ie + AJ += s * Ie return AJ def abel_jacobi(self, divisor, verbose=False): @@ -3290,12 +3516,16 @@ def abel_jacobi(self, divisor, verbose=False): v, p = divisor[i] if verbose: print("starting computation for p = {}".format(p)) - ans += v*self._aj_based(p) + ans += v * self._aj_based(p) if verbose: - print("Done, {}% complete".format(numerical_approx(100*(i+1)/n, 11))) + print( + "Done, {}% complete".format(numerical_approx(100 * (i + 1) / n, 11)) + ) return ans - def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normalised=False): + def reduce_over_period_lattice( + self, vector, method="ip", b=None, r=None, normalised=False + ): r""" Reduce a vector over the period lattice. @@ -3360,54 +3590,61 @@ def reduce_over_period_lattice(self, vector, method="ip", b=None, r=None, normal True True """ - if not len(vector)==self.genus: + if not len(vector) == self.genus: raise ValueError("Input vector needs to be of length {}".format(self.genus)) - VR = VectorSpace(self._RR, 2*self.genus) + VR = VectorSpace(self._RR, 2 * self.genus) VC = VectorSpace(self._CC, self.genus) I = self._CC(0, 1) PM = self.period_matrix() if normalised: - AM = PM[:, 0:self.genus] + AM = PM[:, 0 : self.genus] AInv = numerical_inverse(AM) - PM = AInv*PM + PM = AInv * PM - if method=="svp": - H = max(max(z.real_part().abs() for z in vector), - max(z.imag_part().abs() for z in vector)) + if method == "svp": + H = max( + max(z.real_part().abs() for z in vector), + max(z.imag_part().abs() for z in vector), + ) if b is None: - b = self._prec-5-H.log2().floor() + b = self._prec - 5 - H.log2().floor() if r is None: - r = b//4 + r = b // 4 S = 2**b - if H*S > 2**(self._prec-4): + if H * S > 2 ** (self._prec - 4): raise ValueError("insufficient precision for b=%s" % b) def C2Z(v): - vR = [(S*z.real_part()).round() for z in v] - vR += [(S*z.imag_part()).round() for z in v] + vR = [(S * z.real_part()).round() for z in v] + vR += [(S * z.imag_part()).round() for z in v] return vR - M = Matrix(ZZ, 2*self.genus, 2*self.genus, - [C2Z(c) for c in PM.columns()]) + M = Matrix( + ZZ, 2 * self.genus, 2 * self.genus, [C2Z(c) for c in PM.columns()] + ) u = C2Z(vector) L = IntegerLattice(M) - u = VR(u)-VR(L.closest_vector(u)) - reduced = VC([self._CC(u[i]+I*u[i+self.genus])/S for i in range(self.genus)]) + u = VR(u) - VR(L.closest_vector(u)) + reduced = VC( + [self._CC(u[i] + I * u[i + self.genus]) / S for i in range(self.genus)] + ) - elif method=="ip": + elif method == "ip": def C2R(v): - return VR([z.real_part() for z in v]+[z.imag_part() for z in v]) + return VR([z.real_part() for z in v] + [z.imag_part() for z in v]) u = C2R(vector) basis_vecs = [C2R(c) for c in PM.columns()] M = Matrix([[ei.dot_product(ej) for ei in basis_vecs] for ej in basis_vecs]) v_dot_e = VR([u.dot_product(e) for e in basis_vecs]) coeffs = M.solve_right(v_dot_e) - u -= sum([t.round()*e for t, e in zip(coeffs, basis_vecs)]) - reduced = VC([self._CC(u[i]+I*u[i+self.genus]) for i in range(self.genus)]) + u -= sum([t.round() * e for t, e in zip(coeffs, basis_vecs)]) + reduced = VC( + [self._CC(u[i] + I * u[i + self.genus]) for i in range(self.genus)] + ) else: raise ValueError("Must give a valid method.") @@ -3524,7 +3761,7 @@ def strong_approximation(self, divisor, S): # To avoid current implementation issues with going between divisors # and divisor lists, we implement a method that handles only divisors K = self._R.base_ring() - if not K==QQ: + if not K == QQ: raise NotImplementedError C = self.curve() KC = C.function_field() @@ -3536,7 +3773,7 @@ def strong_approximation(self, divisor, S): rr = self._vertices[self._basepoint[0]].real() rr = rr.ceil() - Fac = g0-K(rr) + Fac = g0 - K(rr) p0 = MO.ideal(Fac).place() q0 = KC.places_above(p0)[0] @@ -3547,12 +3784,12 @@ def strong_approximation(self, divisor, S): v = divisor.valuation(p) i = S.index(p) Q = S[i] - D = D_base+Q + D = D_base + Q if not D: ios = self.genus else: ios = len(D.basis_differential_space()) - while ios>0: + while ios > 0: D += q0 ios = len(D.basis_differential_space()) LD = D.function_space() @@ -3560,15 +3797,15 @@ def strong_approximation(self, divisor, S): a = LD[1] b = 0 for s in S: - LDps = (D+s).function_space() + LDps = (D + s).function_space() Vps = LDps[0] ebd = [LDps[2](a(g)) for g in V.gens()] U = Vps.span(ebd) Quot = Vps.quotient(U) bs = LDps[1](Quot.lift(Quot.basis()[0])) b += bs - B.append((v,b)) - new_divisor += v*b.divisor() + B.append((v, b)) + new_divisor += v * b.divisor() return new_divisor, B def divisor_to_divisor_list(self, divisor, eps=None): @@ -3582,7 +3819,7 @@ def divisor_to_divisor_list(self, divisor, eps=None): INPUT: - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` - - ``eps`` -- real number (optional); tolerance used to determine whether a complex + - ``eps`` -- real number (optional); tolerance used to determine whether a complex number is close enough to a root of a polynomial. OUTPUT: @@ -3609,21 +3846,25 @@ def divisor_to_divisor_list(self, divisor, eps=None): # If this error bound is too restrictive, this method might fail and # not return. One might want to change the way this error is handled. if not eps: - eps = self._RR(2)**(-self._prec+3) + eps = self._RR(2) ** (-self._prec + 3) dl = [] - PZ = PolynomialRing(self._R.base(), 'z').fraction_field() - RF = PolynomialRing(PZ, 'w') + PZ = PolynomialRing(self._R.base(), "z").fraction_field() + RF = PolynomialRing(PZ, "w") for d in divisor.support(): if d.is_infinite_place(): - raise NotImplementedError("Conversion of infinite places not implemented yet.") + raise NotImplementedError( + "Conversion of infinite places not implemented yet." + ) v = divisor.valuation(d) gs = d._prime.gens() g0 = self._R(gs[0]) - gis = [sum([PZ(gi.list()[i])*RF.gen()**i - for i in range(len(gi.list()))]) for gi in gs[1:]] + gis = [ + sum([PZ(gi.list()[i]) * RF.gen() ** i for i in range(len(gi.list()))]) + for gi in gs[1:] + ] rs = self._CCz(g0).roots() rys = [] @@ -3637,23 +3878,27 @@ def divisor_to_divisor_list(self, divisor, eps=None): else: ers = 1 - if not ers<=eps: + if not ers <= eps: poly = self._CCw(gi(self._CCw.gen(0), r)) - if poly==0: + if poly == 0: nys = [] else: nys = poly.roots() ys.extend(ny[0] for ny in nys) - rys.extend((v*m*n, (r, y)) for y, n in nys) + rys.extend((v * m * n, (r, y)) for y, n in nys) if rys: dl.extend(rys) else: for r, m in rs: ys = self._CCw(self.f(r, self._CCw.gen(0))).roots() - dl.extend([(v*m*n, (r, y)) for y, n in ys]) - if not sum([v[0] for v in dl])==divisor.degree(): - raise ValueError("numerical instability, list of wrong degree, returning list {}".format(dl)) + dl.extend([(v * m * n, (r, y)) for y, n in ys]) + if not sum([v[0] for v in dl]) == divisor.degree(): + raise ValueError( + "numerical instability, list of wrong degree, returning list {}".format( + dl + ) + ) return dl @@ -3701,44 +3946,57 @@ def integer_matrix_relations(M1, M2, b=None, r=None): sage: [((m[:,:2]^(-1)*m)[:,2:]-M2).norm() < 1e-13 for m in M1t] [True, True] """ - if not(M1.is_square() and M2.is_square()): + if not (M1.is_square() and M2.is_square()): raise ValueError("matrices need to be square") - prec = min(M1.base_ring().precision(),M2.base_ring().precision()) - H = max(max(abs(m.real_part()) for m in M1.list() + M2.list()), - max(abs(m.imag_part()) for m in M1.list() + M2.list())) + prec = min(M1.base_ring().precision(), M2.base_ring().precision()) + H = max( + max(abs(m.real_part()) for m in M1.list() + M2.list()), + max(abs(m.imag_part()) for m in M1.list() + M2.list()), + ) if b is None: - b = prec-5-H.log2().floor() + b = prec - 5 - H.log2().floor() if r is None: - r = b//4 + r = b // 4 S = 2**b - if H*S > 2**(prec-4): + if H * S > 2 ** (prec - 4): raise ValueError("insufficient precision for b=%s" % b) g1 = M1.ncols() g2 = M2.ncols() - CC = M1.base_ring() if (M1.base_ring().precision() <= M2.base_ring().precision()) else M2.base_ring() - V = ["%s%s" % (n, i) for n in ["a","b","c","d"] for i in range(1,1+g1*g2)] + CC = ( + M1.base_ring() + if (M1.base_ring().precision() <= M2.base_ring().precision()) + else M2.base_ring() + ) + V = ["%s%s" % (n, i) for n in ["a", "b", "c", "d"] for i in range(1, 1 + g1 * g2)] R = PolynomialRing(CC, V) vars = R.gens() - A = Matrix(R, g1, g2, vars[:g1*g2]) - B = Matrix(R, g1, g2, vars[g1*g2:2*g1*g2]) - C = Matrix(R, g1, g2, vars[2*g1*g2:3*g1*g2]) - D = Matrix(R, g1, g2, vars[3*g1*g2:4*g1*g2]) - W = ((M1*A+B) - (M1*C+D)*M2).list() + A = Matrix(R, g1, g2, vars[: g1 * g2]) + B = Matrix(R, g1, g2, vars[g1 * g2 : 2 * g1 * g2]) + C = Matrix(R, g1, g2, vars[2 * g1 * g2 : 3 * g1 * g2]) + D = Matrix(R, g1, g2, vars[3 * g1 * g2 : 4 * g1 * g2]) + W = ((M1 * A + B) - (M1 * C + D) * M2).list() vars = R.gens() - mt = Matrix(ZZ,[[1 if i == j else 0 for j in range(4*g1*g2)] + - [(S*w.monomial_coefficient(vars[i]).real_part()).round() for w in W] + - [(S*w.monomial_coefficient(vars[i]).imag_part()).round() for w in W] for i in range(len(vars))]) + mt = Matrix( + ZZ, + [ + [1 if i == j else 0 for j in range(4 * g1 * g2)] + + [(S * w.monomial_coefficient(vars[i]).real_part()).round() for w in W] + + [(S * w.monomial_coefficient(vars[i]).imag_part()).round() for w in W] + for i in range(len(vars)) + ], + ) # we compute an LLL-reduced basis of this lattice: mtL = mt.LLL() def vectomat(v): - A = Matrix(g1,g2,v[:g1*g2].list()) - B = Matrix(g1,g2,v[g1*g2:2*g1*g2].list()) - C = Matrix(g1,g2,v[2*g1*g2:3*g1*g2].list()) - D = Matrix(g1,g2,v[3*g1*g2:4*g1*g2].list()) + A = Matrix(g1, g2, v[: g1 * g2].list()) + B = Matrix(g1, g2, v[g1 * g2 : 2 * g1 * g2].list()) + C = Matrix(g1, g2, v[2 * g1 * g2 : 3 * g1 * g2].list()) + D = Matrix(g1, g2, v[3 * g1 * g2 : 4 * g1 * g2].list()) return D.augment(B).stack(C.augment(A)) + c = 2**r - return [vectomat(v) for v in mtL if all(a.abs() <= c for a in v[g1*g2:])] + return [vectomat(v) for v in mtL if all(a.abs() <= c for a in v[g1 * g2 :])] class RiemannSurfaceSum(RiemannSurface): @@ -3763,6 +4021,7 @@ class RiemannSurfaceSum(RiemannSurface): sage: len(SC.homomorphism_basis(S1+S2)) 2 """ + def __init__(self, L): r""" TESTS:: @@ -3785,13 +4044,13 @@ def __init__(self, L): g = s.genus PM = s.period_matrix() PM1 = PM[:g, :g] - PM2 = PM[:g, g:2*g] + PM2 = PM[:g, g : 2 * g] tau = s.riemann_matrix() for s in it: g = s.genus PM = s.period_matrix() PM1 = PM1.block_sum(PM[:g, :g]) - PM2 = PM2.block_sum(PM[:g, g:2*g]) + PM2 = PM2.block_sum(PM[:g, g : 2 * g]) tau = tau.block_sum(s.riemann_matrix()) self.PM = block_matrix([[PM1, PM2]], subdivide=False) self.tau = tau From 019f9a2a19489238af61ecb80250c7fe866ccf9d Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sun, 4 Sep 2022 16:05:21 +0100 Subject: [PATCH 341/454] Added more clarifying comments about the usage of error_handle --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index d0638bf030c..0c5a182d453 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3240,6 +3240,12 @@ def fv(hj, previous_estimate): return J * results[-1], endscale * fj h /= 2 Nh *= 2 + # Note that throughout this loop there is a return statement, intended + # to be activated when the sequence of integral approximations is + # deemed to have converged by the heuristic error. If this has no + # happened by the time we have gone through the process n_steps times, + # we have one final error handle. Again, this will throw an error if + # the raise_errors flag is true, but will just return the answer otherwise. return error_handle((J * results[-1], endscale * fj)) def _aj_based(self, P): From 1c2533f67ebccd1651557fe76811f5da0b9840e8 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Sun, 4 Sep 2022 16:22:05 +0100 Subject: [PATCH 342/454] use for/else to remove checks --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 0c5a182d453..597ade10214 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3142,8 +3142,8 @@ def fv(hj, previous_estimate_and_validity): delta = new_delta Ndelta = Nnew_delta newg -= delta - if j == 99: - outg.append(error_handle(newg)) + else: + outg.append(error_handle(newg)) fj = V(outg) u1 = la * hj.cosh() w = u1 / (2 * u2.cosh() ** 2) @@ -3178,8 +3178,8 @@ def fv(hj, previous_estimate): delta = new_delta Ndelta = Nnew_delta newg -= delta - if j == 99: - outg.append(error_handle(newg)) + else: + outg.append(error_handle(newg)) fj = V(outg) u1 = la * hj.cosh() w = u1 / (2 * u2.cosh() ** 2) From 50440246008572472fcaac446fc1f98e44e8e8a3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 11:21:15 -0700 Subject: [PATCH 343/454] FiniteRankDualFreeModule: Doc fixes --- src/sage/tensor/modules/finite_rank_free_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index f970d9e7f17..1bd5d13b8b6 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -2977,7 +2977,7 @@ class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): Dual of a free module of finite rank over a commutative ring. Given a free module `M` of finite rank over a commutative ring `R`, - the *dual of* `M` is the set `M^*` of all linear forms `p` on `M`, + the *dual of* `M` is the set `M^*` of all linear forms on `M`, i.e., linear maps .. MATH:: @@ -2991,10 +2991,10 @@ class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): - ``fmodule`` -- free module `M` of finite rank, as an instance of :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule` - - ``name`` -- (default: ``None``) string; name given to `\Lambda^p(M^*)` + - ``name`` -- (default: ``None``) string; name given to `M^*` - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote `M^*` - EXAMPLES: + EXAMPLES:: sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: e = M.basis('e') From fc66ad14915c1b2d02d72db4114fbab48e7b601f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 11:45:44 -0700 Subject: [PATCH 344/454] FiniteRankFreeModule.dual_symmetric_power: Update for changes in #34474 --- src/sage/tensor/modules/finite_rank_free_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 414e48ee665..e84c57443df 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1413,8 +1413,8 @@ def dual_symmetric_power(self, p): sage: e = M.basis('e') sage: M.dual_symmetric_power(0) Free module of type-(0,0) tensors on the Rank-3 free module M over the Integer Ring - sage: M.dual_symmetric_power(1) # return the module itself - Free module of type-(0,1) tensors on the Rank-3 free module M over the Integer Ring + sage: M.dual_symmetric_power(1) # return the dual module + Dual of the Rank-3 free module M over the Integer Ring sage: M.dual_symmetric_power(2) Free module of fully symmetric type-(0,2) tensors on the Rank-3 free module M over the Integer Ring From 84d7b5eabff42af064f875ab7a0dca0354892d3a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 11:51:37 -0700 Subject: [PATCH 345/454] ExtPower[Dual]FreeModule, FiniteRankDualFreeModule: Add docstring to construction method --- src/sage/tensor/modules/ext_pow_free_module.py | 8 ++++++++ src/sage/tensor/modules/finite_rank_free_module.py | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index 92483ffe422..620f49909e7 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -253,6 +253,10 @@ def __init__(self, fmodule, degree, name=None, latex_name=None): def construction(self): r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerFreeModule @@ -634,6 +638,10 @@ def __init__(self, fmodule, degree, name=None, latex_name=None): def construction(self): r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 1bd5d13b8b6..232c403ce39 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -3101,6 +3101,10 @@ def __init__(self, fmodule, name=None, latex_name=None): def construction(self): r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + TESTS:: sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule From edec50edd9a1704578baaccc21d143da342c551a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 16:20:38 -0700 Subject: [PATCH 346/454] src/sage/modules/free_module.py, src/sage/modules/fg_pid/fgp_morphism.py: Use ModulesWithBasis instead of FreeModules, which is only an alias defined in sage.categories.all --- src/sage/modules/fg_pid/fgp_morphism.py | 4 ++-- src/sage/modules/free_module.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index 8eaf32ed7bd..5f61c221d93 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -507,8 +507,8 @@ def __init__(self, X, Y, category=None): if category is None: from sage.modules.free_module import is_FreeModule if is_FreeModule(X) and is_FreeModule(Y): - from sage.categories.all import FreeModules - category = FreeModules(X.base_ring()) + from sage.categories.modules_with_basis import ModulesWithBasis + category = ModulesWithBasis(X.base_ring()) else: from sage.categories.all import Modules category = Modules(X.base_ring()) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index f069cf552a3..957e2f5dee7 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -870,8 +870,8 @@ def __init__(self, base_ring, degree, sparse=False, category=None): raise ValueError("degree (=%s) must be nonnegative" % degree) if category is None: - from sage.categories.all import FreeModules - category = FreeModules(base_ring.category()).FiniteDimensional() + from sage.categories.modules_with_basis import ModulesWithBasis + category = ModulesWithBasis(base_ring.category()).FiniteDimensional() try: if base_ring.is_finite() or degree == 0: category = category.Enumerated().Finite() @@ -6409,8 +6409,8 @@ def __init__(self, ambient, basis, check=True, # Adapted from Module_free_ambient.__init__ if category is None: - from sage.categories.all import FreeModules - category = FreeModules(R.category()).FiniteDimensional() + from sage.categories.modules_with_basis import ModulesWithBasis + category = ModulesWithBasis(R.category()).FiniteDimensional() try: if base_ring.is_finite() or len(basis) == 0: category = category.Enumerated().Finite() From 823073652e1a1e9f734589e6d5c74d0744de8f32 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 16:25:39 -0700 Subject: [PATCH 347/454] FreeModule_submodule_with_basis_pid: Fix up category --- src/sage/modules/free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 957e2f5dee7..e2250f643e8 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -6412,7 +6412,7 @@ def __init__(self, ambient, basis, check=True, from sage.categories.modules_with_basis import ModulesWithBasis category = ModulesWithBasis(R.category()).FiniteDimensional() try: - if base_ring.is_finite() or len(basis) == 0: + if R.is_finite() or len(basis) == 0: category = category.Enumerated().Finite() except Exception: pass From 80e2c380981e194309c4bcd3f6c26e1dac07439a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 17:22:48 -0700 Subject: [PATCH 348/454] src/doc/en/thematic_tutorials/coercion_and_categories.rst: Update doctest output --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index bfb8247841e..7ecd47f09b3 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -160,9 +160,6 @@ This base class provides a lot more methods than a general parent:: 'order', 'prime_subfield', 'principal_ideal', - 'quo', - 'quotient', - 'quotient_ring', 'random_element', 'unit_ideal', 'zero', From cb82460b9a4a029a0516929024f98272ff3fd225 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 19:03:08 -0700 Subject: [PATCH 349/454] src/doc/en/thematic_tutorials/coercion_and_categories.rst: Update doctest output (again) --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 7ecd47f09b3..4efe68a2617 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -109,9 +109,7 @@ This base class provides a lot more methods than a general parent:: '__ideal_monoid', '__iter__', '__len__', - '__rtruediv__', '__rxor__', - '__truediv__', '__xor__', '_an_element_impl', '_coerce_', From 2929a9e799ab5a913f753bc4e46b78aaae4180be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 08:20:59 -0700 Subject: [PATCH 350/454] src/sage/modules/free_module.py: Make 'except Exception' more specific --- src/sage/modules/free_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index e2250f643e8..1595858a6dd 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -875,7 +875,7 @@ def __init__(self, base_ring, degree, sparse=False, category=None): try: if base_ring.is_finite() or degree == 0: category = category.Enumerated().Finite() - except Exception: + except (ValueError, TypeError, AttributeError, NotImplementedError): pass if not hasattr(self, 'Element'): @@ -6414,7 +6414,7 @@ def __init__(self, ambient, basis, check=True, try: if R.is_finite() or len(basis) == 0: category = category.Enumerated().Finite() - except Exception: + except (ValueError, TypeError, AttributeError, NotImplementedError): pass category = category.Subobjects() From eba6657e37e00e183dbd270d261b16bb72673dfc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 10:48:08 -0700 Subject: [PATCH 351/454] src/sage/categories/pushout.py (EquivariantSubobjectConstructionFunctor): New --- src/sage/categories/pushout.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index ea4e3c22dcc..893984f24be 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3679,6 +3679,33 @@ def merge(self, other): new_domain) +class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): + r""" + Constructor for a subobject invariant or equivariant under a given semigroup action. + + Let `S` be a semigroup that + - acts on a parent `X` as `s \cdot x` (``action``, ``side='left'``) or + - acts on `X` as `x \cdot s` (``action``, ``side='right'``), + and (possibly trivially) + - acts on `X` as `s * x` (``other_action``, ``other_side='left'``) or + - acts on `X` as `x * s` (``other_action``, ``other_side='right'``). + + The `S`-equivariant subobject is the subobject + + .. MATH:: + + X^S := \{x \in X : s \cdot x = s * x,\, \forall s \in S \} + + when ``side = other_side = 'left'`` and mutatis mutandis for the other values + of ``side`` and ``other_side``. + + When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject. + """ + def __init__(self, S, action=operator.mul, side='left', + other_action=None, other_side='left'): + raise NotImplementedError + + class BlackBoxConstructionFunctor(ConstructionFunctor): """ Construction functor obtained from any callable object. From 1ee54705b353289e20cc9a30ccfb9483cc1cba69 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 11:28:23 -0700 Subject: [PATCH 352/454] FiniteDimensionalInvariantModule.construction: New --- src/sage/categories/pushout.py | 34 ++++++++++++++++++++++-- src/sage/modules/with_basis/invariant.py | 22 ++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 893984f24be..746db12ad7b 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2,6 +2,8 @@ Coercion via construction functors """ +import operator + from sage.misc.lazy_import import lazy_import from sage.structure.coerce_exceptions import CoercionException from .functor import Functor, IdentityFunctor_generic @@ -3681,7 +3683,7 @@ def merge(self, other): class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): r""" - Constructor for a subobject invariant or equivariant under a given semigroup action. + Constructor for subobjects invariant or equivariant under given semigroup actions. Let `S` be a semigroup that - acts on a parent `X` as `s \cdot x` (``action``, ``side='left'``) or @@ -3700,10 +3702,38 @@ class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): of ``side`` and ``other_side``. When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject. + + EXAMPLES:: + + sage: G = SymmetricGroup(3); G.rename('S3') + sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M') + sage: action = lambda g, x: M.term(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action); I + (S3)-invariant submodule of M + sage: from sage.categories.pushout import pushout + sage: pushout(I, QQ) + Traceback (most recent call last): + ... + sage.structure.coerce_exceptions.CoercionException: No common base ("join") found for EquivariantSubobjectConstructionFunctor(Representation of S3 indexed by {1, 2, 3} over Integer Ring) and FractionField(Integer Ring). """ def __init__(self, S, action=operator.mul, side='left', other_action=None, other_side='left'): - raise NotImplementedError + from sage.categories.sets_cat import Sets + super().__init__(Sets(), Sets()) + self.S = S + self.action = action + self.side = side + self.other_action = other_action + self.other_side = other_side + + def _apply_functor(self, X): + """ + Apply the functor to an object of ``self``'s domain. + """ + if self.other_action is not None: + raise NotImplementedError(f'non-trivial {other_action=} is not implemented') + # Currently only implemented for FiniteDimensionalModulesWithBasis + return X.invariant_module(self.S, action=self.action, side=self.side) class BlackBoxConstructionFunctor(ConstructionFunctor): diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index f180f442c19..dccee08a840 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -262,9 +262,29 @@ def _invariant_map(g, x): category=category, *args, **kwargs) + def construction(self): + r""" + Return the functorial construction of ``self``. + + EXAMPLES:: + + sage: G = CyclicPermutationGroup(3) + sage: R = G.regular_representation(); R + Left Regular Representation of Cyclic group of order 3 as a permutation group over Integer Ring + sage: I = R.invariant_module() + sage: I.construction() + (EquivariantSubobjectConstructionFunctor, + Left Regular Representation of Cyclic group of order 3 as a permutation group over Integer Ring) + """ + from sage.categories.pushout import EquivariantSubobjectConstructionFunctor + return (EquivariantSubobjectConstructionFunctor(self._semigroup, + self._action, + self._side), + self.ambient()) + def _repr_(self): r""" - Return a string representaion of ``self``. + Return a string representation of ``self``. EXAMPLES:: From e7f2f0588c61233cc9222e4619f086f5f8ee6af4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 14:54:36 -0700 Subject: [PATCH 353/454] src/sage/categories/pushout.py: Add example --- src/sage/categories/pushout.py | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 746db12ad7b..1215c3417cf 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3715,6 +3715,63 @@ class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): Traceback (most recent call last): ... sage.structure.coerce_exceptions.CoercionException: No common base ("join") found for EquivariantSubobjectConstructionFunctor(Representation of S3 indexed by {1, 2, 3} over Integer Ring) and FractionField(Integer Ring). + + Monoterm symmetries of a tensor, here only for matrices: row (index 0), + column (index 1); the order of the extra element 2 in a permutation determines + whether it is a symmetry or an antisymmetry:: + + sage: GSym01 = PermutationGroup([[(0,1),(2,),(3,)]]); GSym01 + Permutation Group with generators [(0,1)] + sage: GASym01 = PermutationGroup([[(0,1),(2,3)]]); GASym01 + Permutation Group with generators [(0,1)(2,3)] + sage: from sage.categories.action import Action + sage: from sage.structure.element import Matrix + sage: class TensorIndexAction(Action): + ....: def _act_(self, g, x): + ....: if isinstance(x, Matrix): + ....: if g(0) == 1: + ....: if g(2) == 2: + ....: return x.transpose() + ....: else: + ....: return -x.transpose() + ....: else: + ....: return x + ....: raise NotImplementedError + sage: M = matrix([[1, 2], [3, 4]]); M + [1 2] + [3 4] + sage: GSym01_action = TensorIndexAction(GSym01, M.parent()) + sage: GASym01_action = TensorIndexAction(GASym01, M.parent()) + sage: GSym01_action.act(GSym01.0, M) + [1 3] + [2 4] + sage: GASym01_action.act(GASym01.0, M) + [-1 -3] + [-2 -4] + sage: Sym01 = M.parent().invariant_module(GSym01, action=GSym01_action); Sym01 + (Permutation Group with generators [(0,1)])-invariant submodule + of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + sage: list(Sym01.basis()) + [B[0], B[1], B[2]] + sage: list(Sym01.basis().map(Sym01.lift)) + [ + [1 0] [0 1] [0 0] + [0 0], [1 0], [0 1] + ] + sage: ASym01 = M.parent().invariant_module(GASym01, action=GASym01_action); ASym01 + (Permutation Group with generators [(0,1)(2,3)])-invariant submodule + of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + sage: list(ASym01.basis()) + [B[0]] + sage: list(ASym01.basis().map(ASym01.lift)) + [ + [ 0 1] + [-1 0] + ] + sage: from sage.categories.pushout import pushout + sage: pushout(Sym01, QQ) + (Permutation Group with generators [(0,1)])-invariant submodule + of Full MatrixSpace of 2 by 2 dense matrices over Rational Field """ def __init__(self, S, action=operator.mul, side='left', other_action=None, other_side='left'): From 9e09c2f9acb727a59dc4955ceb2864c2308f6b26 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 14:59:39 -0700 Subject: [PATCH 354/454] src/sage/categories/pushout.py: Remove unfinished example --- src/sage/categories/pushout.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 1215c3417cf..c090df3b4f5 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3703,18 +3703,7 @@ class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject. - EXAMPLES:: - - sage: G = SymmetricGroup(3); G.rename('S3') - sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M') - sage: action = lambda g, x: M.term(g(x)) - sage: I = M.invariant_module(G, action_on_basis=action); I - (S3)-invariant submodule of M - sage: from sage.categories.pushout import pushout - sage: pushout(I, QQ) - Traceback (most recent call last): - ... - sage.structure.coerce_exceptions.CoercionException: No common base ("join") found for EquivariantSubobjectConstructionFunctor(Representation of S3 indexed by {1, 2, 3} over Integer Ring) and FractionField(Integer Ring). + EXAMPLES: Monoterm symmetries of a tensor, here only for matrices: row (index 0), column (index 1); the order of the extra element 2 in a permutation determines From 86d9602e95f650e7b247780126a40aedd0f92769 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 15:13:03 -0700 Subject: [PATCH 355/454] src/sage/categories/pushout.py: Add missing doctests --- src/sage/categories/pushout.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index c090df3b4f5..cb37b2cd20e 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3764,6 +3764,18 @@ class EquivariantSubobjectConstructionFunctor(ConstructionFunctor): """ def __init__(self, S, action=operator.mul, side='left', other_action=None, other_side='left'): + """ + EXAMPLES:: + + sage: G = SymmetricGroup(3); G.rename('S3') + sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M') + sage: action = lambda g, x: M.term(g(x)) + sage: I = M.invariant_module(G, action_on_basis=action); I + (S3)-invariant submodule of M + sage: I.construction() + (EquivariantSubobjectConstructionFunctor, + Representation of S3 indexed by {1, 2, 3} over Integer Ring) + """ from sage.categories.sets_cat import Sets super().__init__(Sets(), Sets()) self.S = S @@ -3775,8 +3787,23 @@ def __init__(self, S, action=operator.mul, side='left', def _apply_functor(self, X): """ Apply the functor to an object of ``self``'s domain. + + TESTS:: + + sage: from sage.categories.pushout import EquivariantSubobjectConstructionFunctor + sage: M2 = MatrixSpace(QQ, 2); M2 + Full MatrixSpace of 2 by 2 dense matrices over Rational Field + sage: F = EquivariantSubobjectConstructionFunctor(M2, + ....: operator.mul, 'left', + ....: operator.mul, 'right'); F + EquivariantSubobjectConstructionFunctor + sage: F(M2) + Traceback (most recent call last): + ... + NotImplementedError: non-trivial other_action= is not implemented """ - if self.other_action is not None: + other_action = self.other_action + if other_action is not None: raise NotImplementedError(f'non-trivial {other_action=} is not implemented') # Currently only implemented for FiniteDimensionalModulesWithBasis return X.invariant_module(self.S, action=self.action, side=self.side) From 8ddcaa15ac898e60a094f4de22e84682f8699e19 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 15:27:41 -0700 Subject: [PATCH 356/454] src/sage/categories/pushout.py: Add copyright according to 'git blame -w --date=format:%Y src/sage/categories/pushout.py | sort -k2' --- src/sage/categories/pushout.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index cb37b2cd20e..3136160f39d 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -2,6 +2,30 @@ Coercion via construction functors """ +# **************************************************************************** +# Copyright (C) 2007-2014 Robert Bradshaw +# 2007-2018 David Roe +# 2009-2013 Simon King +# 2010 John Cremona +# 2010-2011 Mike Hansen +# 2012 Julian Rueth +# 2013-2016 Peter Bruin +# 2014 Wilfried Luebbe +# 2015 Benjamin Hackl +# 2015 Daniel Krenn +# 2016-2020 Frédéric Chapoton +# 2017 Jori Mäntysalo +# 2018 Vincent Delecroix +# 2020 Marc Mezzarobba +# 2020-2022 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/ +# **************************************************************************** + import operator from sage.misc.lazy_import import lazy_import From bfd6eee17d7f6105e8f13d4c3796ff74edfe8a51 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 15:29:26 -0700 Subject: [PATCH 357/454] src/sage/modules/with_basis/invariant.py: Update copyright --- src/sage/modules/with_basis/invariant.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/with_basis/invariant.py b/src/sage/modules/with_basis/invariant.py index dccee08a840..33af946e8a0 100644 --- a/src/sage/modules/with_basis/invariant.py +++ b/src/sage/modules/with_basis/invariant.py @@ -4,7 +4,8 @@ # **************************************************************************** # Copyright (C) 2021 Trevor K. Karn -# Travis Scrimshaw +# 2021 Travis Scrimshaw +# 2022 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 7fe5763fb8b82c578cb3c49014361304c027568a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 19:19:06 -0700 Subject: [PATCH 358/454] src/sage/algebras/orlik_{solomon,terao}.py: No construction for subclasses of FiniteDimensionalInvariantModule --- src/sage/algebras/orlik_solomon.py | 20 ++++++++++++++++++-- src/sage/algebras/orlik_terao.py | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index ffbbbb6a5af..4f256fe7777 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -576,7 +576,7 @@ class OrlikSolomonInvariantAlgebra(FiniteDimensionalInvariantModule): .. NOTE:: The algebra structure only exists when the action on the - groundset yeilds an equivariant matroid, in the sense that + groundset yields an equivariant matroid, in the sense that `g \cdot I \in \mathcal{I}` for every `g \in G` and for every `I \in \mathcal{I}`. """ @@ -638,9 +638,25 @@ def action(g, m): *args, **kwargs) # To subclass FiniteDimensionalInvariant module, we also need a - # self._semigroup method. + # self._semigroup attribute. self._semigroup = G + def construction(self): + r""" + Return the functorial construction of ``self``. + + This implementation of the method only returns ``None``. + + TESTS:: + + sage: M = matroids.Wheel(3) + sage: from sage.algebras.orlik_solomon import OrlikSolomonAlgebra + sage: OS1 = OrlikSolomonAlgebra(QQ, M) + sage: OS1.construction() is None + True + """ + return None + def _basis_action(self, g, f): r""" Return the action of the group element ``g`` on the n.b.c. set ``f`` diff --git a/src/sage/algebras/orlik_terao.py b/src/sage/algebras/orlik_terao.py index 7bd25496745..55fd2bdb641 100644 --- a/src/sage/algebras/orlik_terao.py +++ b/src/sage/algebras/orlik_terao.py @@ -660,6 +660,25 @@ def action(g, m): self._semigroup = G + def construction(self): + r""" + Return the functorial construction of ``self``. + + This implementation of the method only returns ``None``. + + TESTS:: + + sage: A = matrix([[1,1,0],[-1,0,1],[0,-1,-1]]) + sage: M = Matroid(A) + sage: G = SymmetricGroup(3) + sage: def on_groundset(g,x): + ....: return g(x+1)-1 + sage: OTG = M.orlik_terao_algebra(QQ, invariant=(G,on_groundset)) + sage: OTG.construction() is None + True + """ + return None + def _basis_action(self, g, f): r""" Let ``f`` be an n.b.c. set so that it indexes a basis From 62f28a334262b6b6712c0a6d98e07291abcba5e2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 5 Sep 2022 21:33:20 -0700 Subject: [PATCH 359/454] src/sage/modules/free_module.py: Compute category more carefully --- src/sage/modules/free_module.py | 53 ++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 1595858a6dd..2699006e891 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -869,14 +869,14 @@ def __init__(self, base_ring, degree, sparse=False, category=None): if degree < 0: raise ValueError("degree (=%s) must be nonnegative" % degree) - if category is None: - from sage.categories.modules_with_basis import ModulesWithBasis - category = ModulesWithBasis(base_ring.category()).FiniteDimensional() - try: - if base_ring.is_finite() or degree == 0: - category = category.Enumerated().Finite() - except (ValueError, TypeError, AttributeError, NotImplementedError): - pass + from sage.categories.modules_with_basis import ModulesWithBasis + modules_category = ModulesWithBasis(base_ring.category()).FiniteDimensional() + try: + if base_ring.is_finite() or degree == 0: + modules_category = modules_category.Enumerated().Finite() + except (ValueError, TypeError, AttributeError, NotImplementedError): + pass + category = modules_category.or_subcategory(category, join=True) if not hasattr(self, 'Element'): self.Element = element_class(base_ring, sparse) @@ -6384,6 +6384,25 @@ def __init__(self, ambient, basis, check=True, sage: w = sqrt(2) * V([1,1]) sage: 3 * w (3*sqrt(2), 3*sqrt(2)) + + TESTS: + + Test that the category is determined as intended:: + + sage: from sage.modules.free_module import FreeModule_ambient_pid, FreeModule_submodule_with_basis_pid + sage: V = FreeModule_ambient_pid(QQ, 3, category=Algebras(QQ)) + sage: V.category() + Category of finite dimensional algebras with basis over Rational Field + sage: W = FreeModule_submodule_with_basis_pid(V, [[1,2,3]]) + sage: W.category() + Join of + Category of finite dimensional vector spaces with basis over (number fields and quotient fields and metric spaces) and + Category of subobjects of sets + sage: W = FreeModule_submodule_with_basis_pid(V, [[1,2,3]], category=Algebras(QQ)) + sage: W.category() + Join of + Category of finite dimensional algebras with basis over Rational Field and + Category of subobjects of sets """ if not isinstance(ambient, FreeModule_ambient_pid): raise TypeError("ambient (=%s) must be ambient." % ambient) @@ -6408,15 +6427,15 @@ def __init__(self, ambient, basis, check=True, basis = self._echelonized_basis(ambient, basis) # Adapted from Module_free_ambient.__init__ - if category is None: - from sage.categories.modules_with_basis import ModulesWithBasis - category = ModulesWithBasis(R.category()).FiniteDimensional() - try: - if R.is_finite() or len(basis) == 0: - category = category.Enumerated().Finite() - except (ValueError, TypeError, AttributeError, NotImplementedError): - pass - category = category.Subobjects() + from sage.categories.modules_with_basis import ModulesWithBasis + modules_category = ModulesWithBasis(R.category()).FiniteDimensional() + try: + if R.is_finite() or len(basis) == 0: + modules_category = modules_category.Enumerated().Finite() + except (ValueError, TypeError, AttributeError, NotImplementedError): + pass + modules_category = modules_category.Subobjects() + category = modules_category.or_subcategory(category, join=True) FreeModule_generic_pid.__init__(self, base_ring=R, coordinate_ring=R_coord, rank=len(basis), degree=ambient.degree(), From a77ff0c9c90b94bd83799351326f4dca6237c2f2 Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Tue, 6 Sep 2022 08:46:38 +0200 Subject: [PATCH 360/454] 34282: pyflakes and missing import in doctest --- src/sage/features/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index 501c82c0539..02bcb57a0ca 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -13,7 +13,6 @@ # **************************************************************************** from . import StaticFile, Executable, FeatureTestResult, FeatureNotPresentError -from sage.features.join_feature import JoinFeature latex_url = 'https://www.latex-project.org/' latex_spkg = 'texlive' @@ -179,6 +178,7 @@ def __init__(self, name, filename, **kwds): TESTS:: + sage: from sage.features.latex import TeXFile sage: TeXFile('nonexisting', 'xxxxxx-nonexisting-file.tex').is_present() # optional - latex FeatureTestResult('nonexisting', False) """ From d51e167dec946680487e98d5675855e0d520088e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Sep 2022 08:12:37 -0700 Subject: [PATCH 361/454] build/pkgs/igraph/spkg-configure.m4: Reject igraph >= 0.10 --- build/pkgs/igraph/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/igraph/spkg-configure.m4 b/build/pkgs/igraph/spkg-configure.m4 index 441c926cdf6..b3ab621474a 100644 --- a/build/pkgs/igraph/spkg-configure.m4 +++ b/build/pkgs/igraph/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([igraph], [ SAGE_SPKG_DEPCHECK([glpk openblas gmp], [ dnl check for igraph with pkg-config - PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.9.5], [], [ + PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.9.5 igraph < 0.10], [], [ sage_spkg_install_igraph=yes]) ]) ]) From 0d338ed2608c3886b334dd70d503317f9139e28a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Sep 2022 08:16:21 -0700 Subject: [PATCH 362/454] build/pkgs/igraph: Update to 0.10.0 --- build/pkgs/igraph/checksums.ini | 6 +++--- build/pkgs/igraph/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/igraph/checksums.ini b/build/pkgs/igraph/checksums.ini index 2d7a72630b0..c38a75f5b7f 100644 --- a/build/pkgs/igraph/checksums.ini +++ b/build/pkgs/igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=igraph-VERSION.tar.gz -sha1=75aae4d9f1459d9e6f6bd03189e6dff4208c29ca -md5=351a276216a8f09e11a401d2313ea471 -cksum=1815245909 +sha1=e4097fc00c3cb9b64a251003623a104c2ac4ae1f +md5=fbca766c3e76b2098c4555a13689fec6 +cksum=627334466 upstream_url=https://github.com/igraph/igraph/releases/download/VERSION/igraph-VERSION.tar.gz diff --git a/build/pkgs/igraph/package-version.txt b/build/pkgs/igraph/package-version.txt index 56f3151140c..78bc1abd14f 100644 --- a/build/pkgs/igraph/package-version.txt +++ b/build/pkgs/igraph/package-version.txt @@ -1 +1 @@ -0.9.10 +0.10.0 From a5f5420dcadcce388fe4ecc19855c46807793d06 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Sep 2022 08:16:57 -0700 Subject: [PATCH 363/454] build/pkgs/igraph/spkg-configure.m4: Update version constraints --- build/pkgs/igraph/spkg-configure.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/igraph/spkg-configure.m4 b/build/pkgs/igraph/spkg-configure.m4 index b3ab621474a..30b5fcf9c53 100644 --- a/build/pkgs/igraph/spkg-configure.m4 +++ b/build/pkgs/igraph/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([igraph], [ SAGE_SPKG_DEPCHECK([glpk openblas gmp], [ dnl check for igraph with pkg-config - PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.9.5 igraph < 0.10], [], [ + PKG_CHECK_MODULES([IGRAPH], [igraph >= 0.10 igraph < 0.11], [], [ sage_spkg_install_igraph=yes]) ]) ]) From c498cacf70fee0b64dd9f2fece7438514647da35 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Sep 2022 08:17:26 -0700 Subject: [PATCH 364/454] build/pkgs/python_igraph: Update to 0.10.0 --- build/pkgs/python_igraph/checksums.ini | 6 +++--- build/pkgs/python_igraph/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/python_igraph/checksums.ini b/build/pkgs/python_igraph/checksums.ini index 5fbaf1821bc..8e53fc46eca 100644 --- a/build/pkgs/python_igraph/checksums.ini +++ b/build/pkgs/python_igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=python-igraph-VERSION.tar.gz -sha1=b54b4f1a0c7ce8a80ddb35d35b4e5d1552592793 -md5=9ce0139a294d1c738abc4eb75aab84cd -cksum=4113725847 +sha1=86a35162babd0d4af520b2ee0f8d9bb555a744cc +md5=d9c1ec7ddad66f927490f1de893fe42f +cksum=4022167909 upstream_url=https://pypi.io/packages/source/i/igraph/igraph-VERSION.tar.gz diff --git a/build/pkgs/python_igraph/package-version.txt b/build/pkgs/python_igraph/package-version.txt index 8225a4ba4fd..78bc1abd14f 100644 --- a/build/pkgs/python_igraph/package-version.txt +++ b/build/pkgs/python_igraph/package-version.txt @@ -1 +1 @@ -0.9.11 +0.10.0 From 7aaa9d0a09c77429c98dc9aef858fac48b7209aa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Sep 2022 08:55:40 -0700 Subject: [PATCH 365/454] build/pkgs/igraph/dependencies: Remove suitesparse, per https://sagemath.zulipchat.com/\#narrow/stream/271086-feedback/topic/hello/near/297431320 --- build/pkgs/igraph/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/igraph/dependencies b/build/pkgs/igraph/dependencies index 267354a48f6..a36c43a6a02 100644 --- a/build/pkgs/igraph/dependencies +++ b/build/pkgs/igraph/dependencies @@ -1,4 +1,4 @@ -$(MP_LIBRARY) glpk $(BLAS) suitesparse | cmake +$(MP_LIBRARY) glpk $(BLAS) | cmake ---------- All lines of this file are ignored except the first. From 38a1c26680b667521a7bfb5d2a3eb8a3a7802cc9 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 7 Sep 2022 08:57:00 +0100 Subject: [PATCH 366/454] Altered how the raise_errors flag is used in _integrate_differentials_iteratively --- .../riemann_surfaces/riemann_surface.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 665eaaf0cd9..dee7059f539 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3083,19 +3083,13 @@ def initialise(z, i): # raises an error. If the value of False is taken, this failure to # converge happens silently, thus allowing the user to get *an* # answer out of the integration, but numerical imprecision is to be - # expected. + # expected. As such, we set the maximum number of steps in the sequence + # of DE integrations to be lower in the latter case. if raise_errors: n_steps = self._prec - 1 - - def error_handle(out): - raise ConvergenceError("Newton iteration fails to converge") - else: n_steps = 15 - def error_handle(out): - return out - V = VectorSpace(self._CC, self.genus) h = one Nh = (-lambert_w(-1, -tau / 2) / la).log().ceil() @@ -3144,7 +3138,10 @@ def fv(hj, previous_estimate_and_validity): Ndelta = Nnew_delta newg -= delta else: - outg.append(error_handle(newg)) + if raise_errors: + ConvergenceError("Newton iteration fails to converge") + else: + outg.append(newg) fj = V(outg) u1 = la * hj.cosh() w = u1 / (2 * u2.cosh() ** 2) @@ -3180,7 +3177,10 @@ def fv(hj, previous_estimate): Ndelta = Nnew_delta newg -= delta else: - outg.append(error_handle(newg)) + if raise_errors: + ConvergenceError("Newton iteration fails to converge") + else: + outg.append(newg) fj = V(outg) u1 = la * hj.cosh() w = u1 / (2 * u2.cosh() ** 2) @@ -3199,9 +3199,8 @@ def fv(hj, previous_estimate): # we now calculate the integral via double-exponential methods # repeatedly halving the step size and then using a heuristic - # convergence check. We again use the error_handle function to deal - # with the case where we exceed the maximum number of steps allowed, - # currently set by to make sure the step size does not fall below the + # convergence check. The maximum number of steps allowed is + # currently set to make sure the step size does not fall below the # resolution set by the binary precision used. for k in range(n_steps): hj = h0 @@ -3246,8 +3245,11 @@ def fv(hj, previous_estimate): # deemed to have converged by the heuristic error. If this has no # happened by the time we have gone through the process n_steps times, # we have one final error handle. Again, this will throw an error if - # the raise_errors flag is true, but will just return the answer otherwise. - return error_handle((J * results[-1], endscale * fj)) + # the raise_errors flag is true, but will just return the answer otherwise. + if raise_errors: + ConvergenceError("Newton iteration fails to converge") + + return (J * results[-1], endscale * fj) def _aj_based(self, P): r""" From de510a5732c108d8f916857d9e7af3f8b50754a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Sep 2022 10:09:51 +0200 Subject: [PATCH 367/454] trying to fix things in the categories --- src/sage/categories/groups.py | 21 ----------------- src/sage/categories/magmas.py | 4 +--- src/sage/categories/monoids.py | 42 ++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index b921570a63c..ef681dbb07a 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -640,27 +640,6 @@ def order(self): from sage.misc.misc_c import prod return prod(c.cardinality() for c in self.cartesian_factors()) - class ElementMethods: - def multiplicative_order(self): - r""" - Return the multiplicative order of this element. - - EXAMPLES:: - - sage: G1 = SymmetricGroup(3) - sage: G2 = SL(2,3) - sage: G = cartesian_product([G1,G2]) - sage: G((G1.gen(0), G2.gen(1))).multiplicative_order() - 12 - """ - from sage.rings.infinity import Infinity - orders = [x.multiplicative_order() for x in self.cartesian_factors()] - if any(o is Infinity for o in orders): - return Infinity - else: - from sage.arith.functions import LCM_list - return LCM_list(orders) - class Topological(TopologicalSpacesCategory): """ Category of topological groups. diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index c4a35fe6f1a..c1f255a6eb9 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -671,9 +671,7 @@ def __invert__(self): ZeroDivisionError: rational division by zero sage: ~C([2,2,2,2]) - Traceback (most recent call last): - ... - TypeError: no conversion of this rational to integer + (1/2, 1/2, 0.500000000000000, 3) """ # variant without coercion: # return self.parent()._cartesian_product_of_elements( diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index e8dd994ee3e..debf640d30d 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -252,18 +252,18 @@ def _div_(left, right): sage: c1._div_(c2) (x0*x1^-1, x1*x0^-1) - With this implementation, division will fail as soon - as ``right`` is not invertible, even if ``right`` + With this default implementation, division will fail as + soon as ``right`` is not invertible, even if ``right`` actually divides ``left``:: - sage: x = cartesian_product([2, 1]) + sage: x = cartesian_product([2, 0]) sage: y = cartesian_product([1, 1]) sage: x / y - (2, 1) - sage: x / x + (2, 0) + sage: y / x Traceback (most recent call last): ... - TypeError: no conversion of this rational to integer + ZeroDivisionError: rational division by zero TESTS:: @@ -367,7 +367,7 @@ def __invert__(self): [ 1 0] [-1 1] """ - return self.parent().one()._div_(self) + raise NotImplementedError("please implement __invert__") def inverse(self): """ @@ -669,3 +669,31 @@ def lift(i, gen): lambda g: (i, g)) for i, M in enumerate(F)]) return Family(gens_prod, lift, name="gen") + + class ElementMethods: + def multiplicative_order(self): + r""" + Return the multiplicative order of this element. + + EXAMPLES:: + + sage: G1 = SymmetricGroup(3) + sage: G2 = SL(2,3) + sage: G = cartesian_product([G1,G2]) + sage: G((G1.gen(0), G2.gen(1))).multiplicative_order() + 12 + """ + from sage.rings.infinity import Infinity + orders = [x.multiplicative_order() for x in self.cartesian_factors()] + if any(o is Infinity for o in orders): + return Infinity + else: + from sage.arith.functions import LCM_list + return LCM_list(orders) + + def __invert__(self): + """ + Return the inverse. + """ + build = self.parent()._cartesian_product_of_elements + return build([x.__invert__() for x in self.cartesian_factors()]) From 01ed9103d0b7d71a13f0ab0372aa0f479cea26ae Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Sep 2022 19:03:29 +0900 Subject: [PATCH 368/454] Using the free_resolution() hook to let each class decide what to build. --- src/sage/homology/free_resolution.py | 96 ++++++++++++++------------ src/sage/homology/graded_resolution.py | 8 +-- src/sage/rings/ideal.py | 2 + src/sage/rings/polynomial/ideal.py | 1 - 4 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index f6bc238ff4d..59059ea1d40 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -84,11 +84,22 @@ class FreeResolution(SageObject, metaclass=ClasscallMetaclass): - """ - Abstract base class for free resolutions. + r""" + A free resolution. + + Let `R` be a commutative ring. A *free resolution* of an `R`-module `M` + is a (possibly infinite) chain complex of free `R`-modules + + .. MATH:: + + 0 \xleftarrow{d_0} R^{n_1} \xleftarrow{d_1} R^{n_1} \xleftarrow{d_2} + \cdots \xleftarrow{d_k} R^{n_k} \xleftarrow{d_{k+1}} \cdots + + that is exact (all homology groups are zero) such that the image + of `d_1` is `M`. """ @staticmethod - def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', graded=False, **kwds): + def __classcall_private__(cls, module, *args, graded=False, degrees=None, shifts=None, **kwds): """ Dispatch to the correct constructor. @@ -127,23 +138,13 @@ def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', grad False sage: xb, yb = Q.gens() sage: FreeResolution(Q.ideal([xb])) # has torsion - Traceback (most recent call last): - ... - NotImplementedError: the module must be a free module or have the base ring be a polynomial ring using Singular + NotImplementedError: the ring must be a polynomial ring using Singular """ - # The module might still be free even if is_free_module is False. - # This is just to handle the cases when we trivially know it is. - is_free_module = False - if isinstance(module, Ideal_generic): - S = module.ring() - if len(module.gens()) == 1 and S in IntegralDomains(): - is_free_module = True - elif isinstance(module, Module_free_ambient): - S = module.base_ring() - if (S in PrincipalIdealDomains() - or isinstance(module, FreeModule_generic)): - is_free_module = True - elif isinstance(module, Matrix): + if degrees is not None or shifts is not None: + graded = True + + if isinstance(module, Matrix): + is_free_module = False S = module.base_ring() if S in PrincipalIdealDomains(): module = module.echelon_form() @@ -155,33 +156,36 @@ def __classcall_private__(cls, module, degrees=None, shifts=None, name='S', grad # We need to make an immutable copy of the matrix module = copy(module) module.set_immutable() - else: - raise TypeError('no module, matrix, or ideal') + if is_free_module: + if graded: + from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module + return GradedFiniteFreeResolution_free_module(module, + *args, + degrees=degrees, + shifts=shifts, + **kwds) + return FiniteFreeResolution_free_module(module, *args, **kwds) - if not is_free_module: from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular if not isinstance(S, MPolynomialRing_libsingular): - raise NotImplementedError("the module must be a free module or have the base ring be a polynomial ring using Singular") + raise NotImplementedError("the matrix must be over a PID or a " + " polynomial ring that is using Singular") - if graded or degrees is not None or shifts is not None: + if graded: # We are computing a graded resolution from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular - return GradedFiniteFreeResolution_singular(module, degrees=degrees, shifts=shifts, name=name, **kwds) - - return FiniteFreeResolution_singular(module, name=name, **kwds) + return GradedFiniteFreeResolution_singular(module, *args, degrees=degrees, + shifts=shifts, **kwds) - # Otherwise we know it is a free module + return FiniteFreeResolution_singular(module, **kwds) - if graded or degrees is not None or shifts is not None: - # We are computing a graded resolution - from sage.homology.graded_resolution import GradedFiniteFreeResolution_free_module - return GradedFiniteFreeResolution_free_module(module, degrees=degrees, shifts=shifts, name=name, **kwds) - - return FiniteFreeResolution_free_module(module, name=name, **kwds) + if graded: + return module.graded_free_resolution(*args, **kwds) + return module.free_resolution(*args, **kwds) def __init__(self, module, name='S', **kwds): """ - Initialize. + Initialize ``self``. INPUT: @@ -303,9 +307,6 @@ class FiniteFreeResolution(FreeResolution): r""" Finite free resolutions. - A subclass must provide a ``_maps`` attribute that contains a list of the - maps defining the resolution. - The matrix at index `i` in the list defines the differential map from `(i + 1)`-th free module to the `i`-th free module over the base ring by multiplication on the left. The number of matrices in the list is the @@ -315,6 +316,9 @@ class FiniteFreeResolution(FreeResolution): Note that the first matrix in the list defines the differential map at homological index `1`. + A subclass must provide a ``_maps`` attribute that contains a list of the + maps defining the resolution. + A subclass can define ``_initial_differential`` attribute that contains the `0`-th differential map whose codomain is the target of the free resolution. @@ -576,7 +580,7 @@ def _initial_differential(self): S = module.ring() M = FreeModule(S, 1) N = M.submodule([vector([g]) for g in module.gens()]) - elif isinstance(ideal, Module_free_ambient): + elif isinstance(module, Module_free_ambient): S = module.base_ring() M = module.ambient_module() N = module @@ -600,7 +604,8 @@ def _m(self): sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) sage: r = FreeResolution(I) sage: r._m() - Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of Multivariate Polynomial Ring in x, y, z, w over Rational Field + Ideal (-z^2 + y*w, y*z - x*w, -y^2 + x*z) of + Multivariate Polynomial Ring in x, y, z, w over Rational Field sage: m = matrix(S, 1, [z^2 - y*w, y*z - x*w, y^2 - x*z]).transpose() sage: r = FreeResolution(m, name='S') @@ -638,7 +643,8 @@ class FiniteFreeResolution_free_module(FiniteFreeResolution): sage: w = M([0, x, 2*x]) sage: S = M.submodule([v, w]) sage: S - Free module of degree 3 and rank 2 over Univariate Polynomial Ring in x over Rational Field + Free module of degree 3 and rank 2 over + Univariate Polynomial Ring in x over Rational Field Echelon basis matrix: [ x^2 2*x^2 3*x^2] [ 0 x 2*x] @@ -737,9 +743,7 @@ class FiniteFreeResolution_singular(FiniteFreeResolution): - ``module`` -- a submodule of a free module `M` of rank `n` over `S` or an ideal of a multi-variate polynomial ring - - ``name`` -- string (optional); name of the base ring - - ``algorithm`` -- (default: ``'heuristic'``) Singular algorithm to compute a resolution of ``ideal`` @@ -748,8 +752,8 @@ class FiniteFreeResolution_singular(FiniteFreeResolution): If ``module`` is an ideal of `S`, it is considered as a submodule of a free module of rank `1` over `S`. - The available algorithms and the corresponding Singular commands are shown - below: + The available algorithms and the corresponding Singular commands + are shown below: ============= ============================ algorithm Singular commands @@ -816,7 +820,7 @@ class FiniteFreeResolution_singular(FiniteFreeResolution): """ def __init__(self, module, name='S', algorithm='heuristic', **kwds): r""" - Initialize. + Initialize ``self``. TESTS:: diff --git a/src/sage/homology/graded_resolution.py b/src/sage/homology/graded_resolution.py index 1728193dfba..97a3cb9624b 100644 --- a/src/sage/homology/graded_resolution.py +++ b/src/sage/homology/graded_resolution.py @@ -87,7 +87,6 @@ FiniteFreeResolution_free_module, FiniteFreeResolution_singular) - class GradedFiniteFreeResolution(FiniteFreeResolution): r""" Graded finite free resolutions. @@ -96,14 +95,11 @@ class GradedFiniteFreeResolution(FiniteFreeResolution): - ``module`` -- a homogeneous submodule of a free module `M` of rank `n` over `S` or a homogeneous ideal of a multivariate polynomial ring `S` - - ``degrees`` -- (default: a list with all entries `1`) a list of integers or integer vectors giving degrees of variables of `S` - - ``shifts`` -- a list of integers or integer vectors giving shifts of degrees of `n` summands of the free module `M`; this is a list of zero degrees of length `n` by default - - ``name`` -- a string; name of the base ring .. WARNING:: @@ -111,8 +107,8 @@ class GradedFiniteFreeResolution(FiniteFreeResolution): This does not check that the module is homogeneous. """ def __init__(self, module, degrees=None, shifts=None, name='S', **kwds): - """ - Initialize. + r""" + Initialize ``self``. TESTS:: diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index b1537ee3651..311b9c45a10 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1217,6 +1217,8 @@ def free_resolution(self, *args, **kwds): sage: I.free_resolution() S^1 <-- S^1 <-- 0 """ + if not self.is_principal(): + raise NotImplementedError("the ideal must be a prinical ideal") from sage.homology.free_resolution import FiniteFreeResolution_free_module return FiniteFreeResolution_free_module(self, *args, **kwds) diff --git a/src/sage/rings/polynomial/ideal.py b/src/sage/rings/polynomial/ideal.py index b768ed1a96b..857718c9a10 100644 --- a/src/sage/rings/polynomial/ideal.py +++ b/src/sage/rings/polynomial/ideal.py @@ -84,4 +84,3 @@ def groebner_basis(self, algorithm=None): gb = self.gens_reduced() from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence_generic return PolynomialSequence_generic([gb], self.ring(), immutable=True) - From 35a97798ab87f572f5fa6ca1d865359d0a039c06 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Sep 2022 19:08:28 +0900 Subject: [PATCH 369/454] Using the hook in projective morphism. --- src/sage/schemes/projective/projective_morphism.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index e3347c409bc..fb3c7d4781b 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -88,7 +88,6 @@ from sage.categories.number_fields import NumberFields from sage.categories.homset import Hom, End from sage.categories.fields import Fields -from sage.homology.graded_resolution import GradedFiniteFreeResolution_singular _NumberFields = NumberFields() _FiniteFields = FiniteFields() @@ -2691,7 +2690,7 @@ def projective_degrees(self): I = G.defining_ideal() # a bihomogeneous ideal degrees = xn*[vector([1,0])] + yn*[vector([0,1])] - res = GradedFiniteFreeResolution_singular(I, degrees, algorithm='shreyer') + res = I.graded_free_resolution(degrees=degrees, algorithm='shreyer') kpoly = res.K_polynomial() L = kpoly.parent() From 0e19786c4715fa582ec87121592595626c4a56ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Sep 2022 12:22:03 +0200 Subject: [PATCH 370/454] add doctest --- src/sage/categories/monoids.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index debf640d30d..c7119e0275f 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -694,6 +694,15 @@ def multiplicative_order(self): def __invert__(self): """ Return the inverse. + + EXAMPLES:: + + sage: a1 = Permutation((4,2,1,3)) + sage: a2 = SL(2,3)([2,1,1,1]) + sage: h = cartesian_product([a1,a2]) + sage: ~h + ([2, 4, 1, 3], [1 2] + [2 2]) """ build = self.parent()._cartesian_product_of_elements return build([x.__invert__() for x in self.cartesian_factors()]) From 5c35ae5b0a0b6893f1a060319757aa5cb64dffd6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 7 Sep 2022 19:43:11 +0900 Subject: [PATCH 371/454] Making singular.pyx full coverage. --- src/sage/libs/singular/singular.pyx | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index bb729cf2db2..d8ea7b07f3c 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -653,6 +653,24 @@ cpdef list si2sa_resolution(Resolution res): - ``res`` -- Singular resolution The procedure is destructive and ``res`` is not usable afterward. + + EXAMPLES:: + + sage: from sage.libs.singular.singular import si2sa_resolution + sage: from sage.libs.singular.function import singular_function + sage: module = singular_function("module") + sage: mres = singular_function('mres') + + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: mod = module(I) + sage: r = mres(mod, 0) + sage: si2sa_resolution(r) + [ + [ y x] + [-z -y] + [z^2 - y*w y*z - x*w y^2 - x*z], [ w z] + ] """ cdef ring *singular_ring cdef syStrategy singular_res @@ -754,6 +772,27 @@ cpdef tuple si2sa_resolution_graded(Resolution res, tuple degrees): - ``degrees`` -- list of integers or integer vectors The procedure is destructive, and ``res`` is not usable afterward. + + EXAMPLES:: + + sage: from sage.libs.singular.singular import si2sa_resolution_graded + sage: from sage.libs.singular.function import singular_function + sage: module = singular_function("module") + sage: mres = singular_function('mres') + + sage: S. = PolynomialRing(QQ) + sage: I = S.ideal([y*w - z^2, -x*w + y*z, x*z - y^2]) + sage: mod = module(I) + sage: r = mres(mod, 0) + sage: res_mats, res_degs = si2sa_resolution_graded(r, (1, 1, 1, 1)) + sage: res_mats + [ + [ y x] + [-z -y] + [z^2 - y*w y*z - x*w y^2 - x*z], [ w z] + ] + sage: res_degs + [[[2], [2], [2]], [[1, 1, 1], [1, 1, 1]]] """ cdef ring *singular_ring cdef syStrategy singular_res From 7b832f26298289eec02f369c66b06faa14847587 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 7 Sep 2022 11:50:23 +0100 Subject: [PATCH 372/454] Added to author list. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index dee7059f539..24feb919881 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -25,6 +25,7 @@ - Alexandre Zotine, Nils Bruin (2017-06-10): initial version - Nils Bruin, Jeroen Sijsling (2018-01-05): algebraization, isomorphisms - Linden Disney-Hogg, Nils Bruin (2021-06-23): efficient integration +- Linden Disney-Hogg, Nils Bruin (2022-09-07): Abel-Jacobi map EXAMPLES: From 250c5cb139df7c52eadb6608437df8cb4d1cdc24 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 7 Sep 2022 20:26:37 +0900 Subject: [PATCH 373/454] Some edits --- src/sage/homology/free_resolution.py | 4 ++++ src/sage/rings/ideal.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 59059ea1d40..7bdd8583ca0 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -138,6 +138,8 @@ def __classcall_private__(cls, module, *args, graded=False, degrees=None, shifts False sage: xb, yb = Q.gens() sage: FreeResolution(Q.ideal([xb])) # has torsion + Traceback (most recent call last): + ... NotImplementedError: the ring must be a polynomial ring using Singular """ if degrees is not None or shifts is not None: @@ -743,7 +745,9 @@ class FiniteFreeResolution_singular(FiniteFreeResolution): - ``module`` -- a submodule of a free module `M` of rank `n` over `S` or an ideal of a multi-variate polynomial ring + - ``name`` -- string (optional); name of the base ring + - ``algorithm`` -- (default: ``'heuristic'``) Singular algorithm to compute a resolution of ``ideal`` diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 311b9c45a10..8e584ee0141 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -1218,7 +1218,7 @@ def free_resolution(self, *args, **kwds): S^1 <-- S^1 <-- 0 """ if not self.is_principal(): - raise NotImplementedError("the ideal must be a prinical ideal") + raise NotImplementedError("the ideal must be a principal ideal") from sage.homology.free_resolution import FiniteFreeResolution_free_module return FiniteFreeResolution_free_module(self, *args, **kwds) From 24b88a5991a41d2bf9cf4fe9c2281b05f9fefcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Sep 2022 14:03:14 +0200 Subject: [PATCH 374/454] fix doc --- src/sage/categories/monoids.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index c7119e0275f..2daeab2ac71 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -358,7 +358,8 @@ def __invert__(self): r""" Return the inverse of ``self``. - The default implementation is to divide ``self.one()``. + There is no default implementation, to avoid conflict + with the default implementation of ``_div_``. EXAMPLES:: From 6dba5e5ca91ec27e175aa73c3526a2bc15f39121 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 8 Sep 2022 01:17:39 +0900 Subject: [PATCH 375/454] Hot fixes --- src/sage/homology/free_resolution.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/free_resolution.py b/src/sage/homology/free_resolution.py index 7bdd8583ca0..7d3ea29057e 100644 --- a/src/sage/homology/free_resolution.py +++ b/src/sage/homology/free_resolution.py @@ -6,7 +6,7 @@ .. MATH:: - 0 \xleftarrow{d_0} R^{n_1} \xleftarrow{d_1} R^{n_1} \xleftarrow{d_2} + R^{n_1} \xleftarrow{d_1} R^{n_1} \xleftarrow{d_2} \cdots \xleftarrow{d_k} R^{n_k} \xleftarrow{d_{k+1}} 0 terminating with a zero module at the end that is exact (all homology groups @@ -92,7 +92,7 @@ class FreeResolution(SageObject, metaclass=ClasscallMetaclass): .. MATH:: - 0 \xleftarrow{d_0} R^{n_1} \xleftarrow{d_1} R^{n_1} \xleftarrow{d_2} + R^{n_1} \xleftarrow{d_1} R^{n_1} \xleftarrow{d_2} \cdots \xleftarrow{d_k} R^{n_k} \xleftarrow{d_{k+1}} \cdots that is exact (all homology groups are zero) such that the image @@ -285,7 +285,10 @@ def differential(self, i): def target(self): r""" - Return the codomain of the ``0``-th differential map. + Return the codomain of the `0`-th differential map. + + The codomain of the `0`-th differential map is the cokernel of + the first differential map. EXAMPLES:: From b6a5773fc8c6b861150dc9e03d241ddf41741c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 7 Sep 2022 18:44:31 +0200 Subject: [PATCH 376/454] fix typo --- src/sage/categories/monoids.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 2daeab2ac71..33229d27390 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -374,7 +374,7 @@ def inverse(self): """ Return the inverse of ``self``. - This an alias for inversion, defined in ``__invert__``. + This is an alias for inversion, defined in ``__invert__``. Element classes should implement ``__invert__`` only. From e08cd7a4e9fb5d6492e8a611040dd562bdddf6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Sep 2022 09:17:10 +0200 Subject: [PATCH 377/454] tweak doc --- src/sage/categories/monoids.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 33229d27390..541e867074f 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -374,15 +374,15 @@ def inverse(self): """ Return the inverse of ``self``. - This is an alias for inversion, defined in ``__invert__``. - - Element classes should implement ``__invert__`` only. + This is an alias for inversion, which can also be invoked + by ``~x`` for an element ``x``. EXAMPLES:: sage: AA(sqrt(~2)).inverse() 1.414213562373095? """ + # Nota Bene: Element classes should implement ``__invert__`` only. return self.__invert__() class Commutative(CategoryWithAxiom): From d0fbe93d4de3b38b40034b4c1743399f37393a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Sep 2022 11:28:56 +0200 Subject: [PATCH 378/454] tweak doc again --- src/sage/categories/monoids.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/monoids.py b/src/sage/categories/monoids.py index 541e867074f..93638b04078 100644 --- a/src/sage/categories/monoids.py +++ b/src/sage/categories/monoids.py @@ -356,7 +356,7 @@ def powers(self, n): def __invert__(self): r""" - Return the inverse of ``self``. + Return the multiplicative inverse of ``self``. There is no default implementation, to avoid conflict with the default implementation of ``_div_``. @@ -372,7 +372,7 @@ def __invert__(self): def inverse(self): """ - Return the inverse of ``self``. + Return the multiplicative inverse of ``self``. This is an alias for inversion, which can also be invoked by ``~x`` for an element ``x``. From 4676b51a0f360a33b0b1bdddf644d6f499f18e64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Sep 2022 11:45:47 +0200 Subject: [PATCH 379/454] using items in indexed_element.pyx --- .../modules/with_basis/indexed_element.pyx | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index cd1f17112f0..ecfcdf14a04 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -8,7 +8,7 @@ AUTHORS: - Travis Scrimshaw (29-08-2022): Implemented ``copy`` as the identity map. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2017, 2022 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ AUTHORS: # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from sage.structure.element cimport parent from sage.structure.richcmp cimport richcmp, rich_to_bool @@ -30,12 +30,14 @@ from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art from sage.categories.all import Category, Sets, ModulesWithBasis from sage.data_structures.blas_dict cimport add, negate, scal, axpy + cdef class IndexedFreeModuleElement(ModuleElement): def __init__(self, M, x): """ - Create a combinatorial module element. This should never be - called directly, but only through the parent combinatorial - free module's :meth:`__call__` method. + Create a combinatorial module element. + + This should never be called directly, but only through the + parent combinatorial free module's :meth:`__call__` method. TESTS:: @@ -60,19 +62,17 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: [i for i in sorted(f)] [('a', 1), ('c', 3)] - :: - sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1]) + s([3]) sage: [i for i in sorted(a)] [([2, 1], 1), ([3], 1)] """ - return iter(self._monomial_coefficients.iteritems()) + return iter(self._monomial_coefficients.items()) def __contains__(self, x): """ - Returns whether or not a combinatorial object x indexing a basis - element is in the support of self. + Return whether or not a combinatorial object ``x`` indexing a basis + element is in the support of ``self``. EXAMPLES:: @@ -84,8 +84,6 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: 'b' in f False - :: - sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1]) + s([3]) sage: Partition([2,1]) in a @@ -129,7 +127,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): hash value of the parent. See :trac:`15959`. """ if not self._hash_set: - self._hash = hash(frozenset(self._monomial_coefficients.iteritems())) + self._hash = hash(frozenset(self._monomial_coefficients.items())) self._hash_set = True return self._hash @@ -148,7 +146,8 @@ cdef class IndexedFreeModuleElement(ModuleElement): def __setstate__(self, state): r""" For unpickling old ``CombinatorialFreeModuleElement`` classes. - See :trac:`22632` and register_unpickle_override below. + + See :trac:`22632` and ``register_unpickle_override`` below. EXAMPLES:: @@ -178,7 +177,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): 2*B['x'] + 2*B['y'] """ self._set_parent(state[0]) - for k, v in state[1].iteritems(): + for k, v in state[1].items(): setattr(self, k, v) def __copy__(self): @@ -257,7 +256,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): def _sorted_items_for_printing(self): """ - Returns the items (i.e terms) of ``self``, sorted for printing + Return the items (i.e. terms) of ``self``, sorted for printing. EXAMPLES:: @@ -274,7 +273,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): .. SEEALSO:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` """ print_options = self._parent.print_options() - v = list(self._monomial_coefficients.iteritems()) + v = list(self._monomial_coefficients.items()) try: v.sort(key=lambda monomial_coeff: print_options['sorting_key'](monomial_coeff[0]), @@ -361,7 +360,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): except AttributeError: one_basis = None - for (monomial,c) in terms: + for monomial, c in terms: b = repr_monomial(monomial) # PCR if c != 0: break_points = [] @@ -620,8 +619,8 @@ cdef class IndexedFreeModuleElement(ModuleElement): if op == Py_NE: return True - v = sorted(self._monomial_coefficients.iteritems()) - w = sorted(elt._monomial_coefficients.iteritems()) + v = sorted(self._monomial_coefficients.items()) + w = sorted(elt._monomial_coefficients.items()) return richcmp(v, w, op) cpdef _add_(self, other): @@ -938,11 +937,12 @@ cdef class IndexedFreeModuleElement(ModuleElement): D = self._monomial_coefficients if not B.is_field(): return type(self)(F, {k: c._divide_if_possible(x) - for k, c in D.iteritems()}) + for k, c in D.items()}) x_inv = B(x) ** -1 return type(self)(F, scal(x_inv, D)) + def _unpickle_element(C, d): """ Unpickle an element in ``C`` given by ``d``. From 312a91d4f327802ca1e6d48975cd1d221128833e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 8 Sep 2022 13:50:17 +0200 Subject: [PATCH 380/454] re-introduce default inversion --- src/sage/structure/element.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index ff18599617e..b5d83ef71b6 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -2613,6 +2613,15 @@ cdef class MultiplicativeGroupElement(MonoidElement): """ return self * ~right + def __invert__(self): + r""" + Return the multiplicative inverse of ``self``. + + This may cause infinite recursion because of the default definition + of division using inversion in ``_div_``. + """ + return self._parent.one() / self + def is_RingElement(x): """ From 54a7e3cc9bfe46eecd463826850507254e35d103 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 8 Sep 2022 13:22:43 +0100 Subject: [PATCH 381/454] Edit documentation to improve readability. Based on comment 69 by TS, made some edits to the documentation to make it easier to parse. Edits to the code itself for the sake of formatting, aside from occurences of ** as exponentiation, was not done to avoid making the run through black redundant. --- .../riemann_surfaces/riemann_surface.py | 119 +++++++++--------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 24feb919881..d3faed36a0c 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -85,7 +85,7 @@ Note we could check using exact techniques that `2D = K`:: sage: Z = K - 2*D - sage: (Z.degree()==0, len(Z.basis_differential_space())==S.genus, len(Z.basis_function_space())==1) + sage: (Z.degree() == 0, len(Z.basis_differential_space()) == S.genus, len(Z.basis_function_space()) == 1) (True, True, True) We can also check this using our Abel-Jacobi functions:: @@ -325,10 +325,10 @@ def differential_basis_baker(f): sage: from sage.schemes.riemann_surfaces.riemann_surface import differential_basis_baker sage: R. = QQ[] - sage: f = x^3+y^3+x^5*y^5 + sage: f = x^3 + y^3 + x^5*y^5 sage: differential_basis_baker(f) [y^2, x*y, x*y^2, x^2, x^2*y, x^2*y^2, x^2*y^3, x^3*y^2, x^3*y^3] - sage: f = y^2-(x-3)^2*x + sage: f = y^2 - (x-3)^2*x sage: differential_basis_baker(f) is None True sage: differential_basis_baker(x^2+y^2-1) @@ -370,7 +370,7 @@ def differential_basis_baker(f): return None x, y = f.parent().gens() return [ - x ** (a[0] - 1) * y ** (a[1] - 1) + x**(a[0] - 1) * y**(a[1] - 1) for a in P.integral_points() if P.interior_contains(a) ] @@ -427,13 +427,17 @@ def reparameterize_differential_minpoly(minpoly, z0): EXAMPLES: - On the curve given by `w^2-z^3+1=0`, we have differential + On the curve given by `w^2 - z^3 + 1 = 0`, we have differential `\frac{dz}{2w} = \frac{dz}{2\sqrt{z^3-1}}` - with minimal polynomial `g^2(z^3-1)-1/4=0`. We can make the substitution - `\bar{z}=z^{-1}` to parameterise the differential about `z=\Infty` as - `\frac{-\bar{z}^{-2} d\bar{z}}{2\sqrt{\bar{z}^{-3}-1}} = \frac{-d\bar{z}}{2\sqrt{\bar{z}(1-\bar{z}^3)}}`. + with minimal polynomial `g^2(z^3-1) - 1/4=0`. We can make the substitution + `\bar{z}=z^{-1}` to parameterise the differential about `z=\infty` as + + .. MATH:: + + `\frac{-\bar{z}^{-2} d\bar{z}}{2\sqrt{\bar{z}^{-3}-1}} = \frac{-d\bar{z}}{2\sqrt{\bar{z}(1-\bar{z}^3)}}`. + Hence the transformed differential should have minimal polynomial - `\bar{g}^2\bar{z}(1-\bar{z}^3)-1/4=0`, and we can check this:: + `\bar{g}^2 \bar{z} (1 - \bar{z}^3) - 1/4 = 0`, and we can check this:: sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface, reparameterize_differential_minpoly sage: R. = QQ[] @@ -446,7 +450,7 @@ def reparameterize_differential_minpoly(minpoly, z0): We can further check that reparameterising about `0` is the identity operation:: - sage: reparameterize_differential_minpoly(minpoly, 0)(*minpoly.parent().gens())==minpoly + sage: reparameterize_differential_minpoly(minpoly, 0)(*minpoly.parent().gens()) == minpoly True .. NOTE:: @@ -466,7 +470,7 @@ def reparameterize_differential_minpoly(minpoly, z0): if Inf: F = F.fraction_field() - mt = F(minpoly(F.gen(0) ** (-1), -F.gen(0) ** (+2) * F.gen(1))) + mt = F(minpoly(F.gen(0)**(-1), -F.gen(0)**2 * F.gen(1))) mt.reduce() mt = mt.numerator() else: @@ -531,7 +535,7 @@ class RiemannSurface(object): sage: Qt. = QQ[] sage: K. = NumberField(t^2-t+3,embedding=CC(0.5+1.6*I)) sage: R. = K[] - sage: f = y^2+y-(x^3+(1-a)*x^2-(2+a)*x-2) + sage: f = y^2 + y - (x^3 + (1-a)*x^2 - (2+a)*x - 2) sage: S = RiemannSurface(f, prec=100, differentials=[1]) sage: A = S.endomorphism_basis() sage: len(A) @@ -562,7 +566,7 @@ class RiemannSurface(object): rigorous method, but for slower computations the rigorous method can be much faster:: - sage: f = z*w^3+z^3+w + sage: f = z*w^3 + z^3 + w sage: p = 53 sage: Sh = RiemannSurface(f, prec=p, integration_method='heuristic') sage: Sr = RiemannSurface(f, prec=p, integration_method='rigorous') @@ -585,21 +589,15 @@ class RiemannSurface(object): being significantly slower than the rigorous method. For a worse conditioned curve, this effect is more pronounced:: - sage: q = 1/10 - sage: f = y^2-(x^2-2*x+1+q^2)*(x^2+2*x+1+q^2) + sage: q = 1 / 10 + sage: f = y^2 - (x^2 - 2*x + 1 + q^2) * (x^2 + 2*x + 1 + q^2) sage: p = 500 sage: Sh = RiemannSurface(f, prec=p, integration_method='heuristic') sage: Sr = RiemannSurface(f, prec=p, integration_method='rigorous') sage: nodes.cache.clear() - sage: ct = time.time() sage: Rh = Sh.riemann_matrix() # long time (8 seconds) - sage: ct1 = time.time()-ct sage: nodes.cache.clear() - sage: ct = time.time() sage: Rr = Sr.riemann_matrix() # long time (1 seconds) - sage: ct2 = time.time()-ct - sage: ct2/ct1 # random - 0.19453288941244148 This disparity in timings can get increasingly worse, and testing has shown that even for random quadrics the heuristic method can be as bad as 30 times @@ -632,7 +630,7 @@ def __init__( prec=53, certification=True, differentials=None, - integration_method="rigorous", + integration_method="rigorous" ): r""" TESTS:: @@ -646,13 +644,13 @@ def __init__( self._prec = prec self._certification = certification if not (integration_method == "heuristic" or integration_method == "rigorous"): - raise ValueError("Invalid integration method") + raise ValueError("invalid integration method") self._integration_method = integration_method self._R = f.parent() if len(self._R.gens()) != 2: - raise ValueError("only bivariate polynomials supported.") + raise ValueError('only bivariate polynomials supported') if f.degree() <= 1: - raise ValueError("equation must be of degree at least 2.") + raise ValueError('equation must be of degree at least 2') z, w = self._R.gen(0), self._R.gen(1) self._CC = ComplexField(self._prec) self._RR = RealField(self._prec) @@ -981,7 +979,7 @@ def _compute_delta(self, z1, epsilon, wvalues=None): # compute M upperbounds = [ - sum(ak[k] * (abs(z1) + rho) ** k for k in range(ak.degree())) + sum(ak[k] * (abs(z1) + rho)**k for k in range(ak.degree())) for ak in self._aks ] upperbounds.reverse() @@ -1001,7 +999,7 @@ def _compute_delta(self, z1, epsilon, wvalues=None): return ( rho * ( - ((rho * Y - epsilon) ** 2 + 4 * epsilon * M).sqrt() + ((rho * Y - epsilon)**2 + 4 * epsilon * M).sqrt() - (rho * Y + epsilon) ) / (2 * M - 2 * rho * Y) @@ -1019,7 +1017,7 @@ def homotopy_continuation(self, edge): INPUT: - ``edge`` -- a tuple ``(z_start, z_end)`` indicating the straight line - over which to perform the homotopy continutation. + over which to perform the homotopy continutation OUTPUT: @@ -1418,7 +1416,7 @@ def edge_permutations(self) -> dict: """ D = {e: self._edge_permutation(e) for e in self.downstairs_edges()} for (a, b), p in list(D.items()): - D[(b, a)] = p ** (-1) + D[(b, a)] = p**(-1) return D @cached_method @@ -1909,7 +1907,7 @@ def cohomology_basis(self, option=1): # lowest degree generators are a basis of the relevant subspace. d = fnew.total_degree() J2 = k.ideal(J).intersection( - k.ideal([k.gen(0), k.gen(1), k.gen(2)]) ** (d - 3) + k.ideal([k.gen(0), k.gen(1), k.gen(2)])**(d - 3) ) generators = [dehom(c) for c in J2.gens() if c.degree() == d - 3] if len(generators) != self.genus: @@ -1928,13 +1926,12 @@ def _bounding_data(self, differentials, exact=False): INPUT: - - ``differentials`` -- list. A list of polynomials in ``self._R`` giving + - ``differentials`` -- list of polynomials in ``self._R`` giving the numerators of the differentials, as per the output of - :meth:`cohomology_basis`. + :meth:`cohomology_basis` - - ``exact`` -- logical (default: False). Whether to return the minimal - polynomials over the exact base ring, or whether to return them over - ``self._CC``. + - ``exact`` -- boolean (default: ``False``); whether to return the minimal + polynomials over the exact base ring, or over ``self._CC`` OUTPUT: @@ -1961,7 +1958,7 @@ def _bounding_data(self, differentials, exact=False): sage: from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface sage: R. = QQ[] - sage: f = y^2-x^3+1 + sage: f = y^2 - x^3 + 1 sage: S = RiemannSurface(f) sage: differentials = S.cohomology_basis(); differentials [1] @@ -2155,7 +2152,7 @@ def rigorous_line_integral(self, upstairs_edge, differentials, bounding_data): alpha = self._RR(912 / 1000) # alpha set manually for scaling purposes. Basic benchmarking shows # that ~0.9 is a sensible value. - E_global = self._RR(2) ** (-self._prec + 3) + E_global = self._RR(2)**(-self._prec + 3) # Output will iteratively store the output of the integral. V = VectorSpace(self._CC, len(differentials)) @@ -2188,7 +2185,7 @@ def local_N(ct, rt): rho_z = rho_t * (z1 - z0).abs() delta_z = (alpha * rho_t + (1 - alpha) * rt) * (z1_minus_z0).abs() expr = ( - rho_t / rt + ((rho_t / rt) ** 2 - 1).sqrt() + rho_t / rt + ((rho_t / rt)**2 - 1).sqrt() ) # Note this is really exp(arcosh(rho_t/rt)) Ni = 3 cw = zwt(ct)[1] @@ -2203,7 +2200,7 @@ def local_N(ct, rt): m = [a(rho_z) / z_1 for a in ai_pos] l = len(m) M_tilde = 2 * max( - (m[i].abs()) ** (1 / self._RR(l - i)) for i in range(l) + (m[i].abs())**(1 / self._RR(l - i)) for i in range(l) ) cg = g(cz, cw) cdgdz = dgdz(cz, cg) @@ -2689,7 +2686,7 @@ def tangent_representation_algebraic(self, Rs, other=None, epscomp=None): True """ if not epscomp: - epscomp = 2 ** (-self._prec + 30) + epscomp = 2**(-self._prec + 30) QQalg = QQ.algebraic_closure() def polynomialize_element(alpha): @@ -2989,13 +2986,13 @@ def _integrate_differentials_iteratively( CCzg = PolynomialRing(self._CC, ["zbar", "gbar"]) mp_list = [CCzg(mp) for mp in mp_list] J = 1 / z_end - endscale = -(z_end ** (-2)) + endscale = -(z_end**(-2)) def initialise(z, i): DF = ComplexField(2 * self._prec) DFw = PolynomialRing(DF, "wbar") z = DF(z) - R = DF(z ** (-1)) + R = DF(z**(-1)) wR = DFw(self.f(R, DFw.gen(0))).roots(multiplicities=False)[w_start] newg = -(R**2) * self.cohomology_basis()[i](R, wR) / self._dfdw(R, wR) err = mp_list[i](z, newg).abs() @@ -3032,7 +3029,7 @@ def initialise(z, i): if prec is None: prec = self._prec # tau here is playing the role of the desired error. - tau = self._RR(2) ** (-prec + 3) + tau = self._RR(2)**(-prec + 3) one = self._RR(1) la = self._RR.pi() / 2 @@ -3056,7 +3053,7 @@ def initialise(z, i): d = mp.dict() mp = sum( [ - d[k] * CCzg.gen(0) ** k[0] * CCzg.gen(1) ** k[1] + d[k] * CCzg.gen(0)**k[0] * CCzg.gen(1)**k[1] for k in d.keys() if d[k].abs() > tau ] @@ -3065,13 +3062,13 @@ def initialise(z, i): a = QQ(max([(cst - iz) / ig for (iz, ig) in d.keys() if ig > 0])) sum_coeffs = sum( [ - d[k] * A.gen(0) ** k[1] + d[k] * A.gen(0)**k[1] for k in d.keys() if ((k[1] == 0 and k[0] == cst) or k[1] * a + k[0] - cst == 0) ] ) G = max([r.abs() for r in sum_coeffs.roots(multiplicities=False)]) - cutoffs.append(((a + 1) * tau / G) ** (1 / self._CC(a + 1)) / J.abs()) + cutoffs.append(((a + 1) * tau / G)**(1 / self._CC(a + 1)) / J.abs()) aes.append(a) cutoff_individually = bool( not all(ai <= 0 for ai in aes) and cutoff_individually @@ -3145,7 +3142,7 @@ def fv(hj, previous_estimate_and_validity): outg.append(newg) fj = V(outg) u1 = la * hj.cosh() - w = u1 / (2 * u2.cosh() ** 2) + w = u1 / (2 * u2.cosh()**2) return (fj, valid), w * fj f0, v0 = fv(h0, (self.genus * [0], self.genus * [False])) @@ -3184,7 +3181,7 @@ def fv(hj, previous_estimate): outg.append(newg) fj = V(outg) u1 = la * hj.cosh() - w = u1 / (2 * u2.cosh() ** 2) + w = u1 / (2 * u2.cosh()**2) return fj, w * fj u1, u2 = (la * h0.cosh(), la * h0.sinh()) @@ -3227,7 +3224,7 @@ def fv(hj, previous_estimate): D = min( one, max( - D1 ** (D1.log() / D2.log()), + D1**(D1.log() / D2.log()), D2**2, tau * D3_over_tau, D4, @@ -3359,7 +3356,7 @@ def _aj_based(self, P): # We choose the first vertex we want to go to. # If the closest vertex is closer than the nearest branch point, just take that vertex # otherwise we need something smarter. - delta = self._RR(2) ** (-self._prec + 1) + delta = self._RR(2)**(-self._prec + 1) if not ( (zP - self._vertices[V_index]).abs() < (zP - b).abs() or (zP - b).abs() <= delta @@ -3412,7 +3409,7 @@ def _aj_based(self, P): ] ts = [ ((c - zP) * (zV - zP).conjugate()).real() - / (zP - zV).norm() ** 2 + / (zP - zV).norm()**2 for c in fl ] ds = [ @@ -3425,7 +3422,7 @@ def _aj_based(self, P): zV = self._vertices[V_index] ts = [ ((c - zP) * (zV - zP).conjugate()).real() - / (zP - zV).norm() ** 2 + / (zP - zV).norm()**2 for c in fl ] ds = [ @@ -3623,7 +3620,7 @@ def reduce_over_period_lattice( if r is None: r = b // 4 S = 2**b - if H * S > 2 ** (self._prec - 4): + if H * S > 2**(self._prec - 4): raise ValueError("insufficient precision for b=%s" % b) def C2Z(v): @@ -3662,7 +3659,7 @@ def C2R(v): def curve(self): r""" - Return the curve from which this Riemann surface is obtained + Return the curve from which this Riemann surface is obtained. Riemann surfaces explicitly obtained from a curve return that same object. For others, the curve is constructed and cached, so that an identical curve is @@ -3691,7 +3688,7 @@ def places_at_branch_locus(self): Return a list of the of places above the branch locus. This must be done over the base ring, and so the places are given in terms of the factors of the discriminant. Currently, this method only works when - ``self._R.base_ring()==QQ`` as for other rings, the function field + ``self._R.base_ring() == QQ`` as for other rings, the function field for ``Curve(self.f)`` is not implemented. To go from these divisors to a divisor list, see :meth:`divisor_to_divisor_list`. @@ -3734,14 +3731,14 @@ def strong_approximation(self, divisor, S): As described in [Neu2018]_, apply the method of strong approximation to ``divisor`` with list of places to avoid ``S``. Currently, this method - only works when ``self._R.base_ring()==QQ`` as for other rings, the function + only works when ``self._R.base_ring() == QQ`` as for other rings, the function field for ``Curve(self.f)`` is not implemented. INPUT: - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` - - ``S`` -- list. A list of places to avoid. + - ``S`` -- list of places to avoid OUTPUT: @@ -3830,7 +3827,7 @@ def divisor_to_divisor_list(self, divisor, eps=None): - ``divisor`` -- an element of ``Curve(self.f).function_field().divisor_group()`` - ``eps`` -- real number (optional); tolerance used to determine whether a complex - number is close enough to a root of a polynomial. + number is close enough to a root of a polynomial OUTPUT: @@ -3856,7 +3853,7 @@ def divisor_to_divisor_list(self, divisor, eps=None): # If this error bound is too restrictive, this method might fail and # not return. One might want to change the way this error is handled. if not eps: - eps = self._RR(2) ** (-self._prec + 3) + eps = self._RR(2)**(-self._prec + 3) dl = [] PZ = PolynomialRing(self._R.base(), "z").fraction_field() @@ -3872,7 +3869,7 @@ def divisor_to_divisor_list(self, divisor, eps=None): g0 = self._R(gs[0]) gis = [ - sum([PZ(gi.list()[i]) * RF.gen() ** i for i in range(len(gi.list()))]) + sum([PZ(gi.list()[i]) * RF.gen()**i for i in range(len(gi.list()))]) for gi in gs[1:] ] @@ -3968,7 +3965,7 @@ def integer_matrix_relations(M1, M2, b=None, r=None): if r is None: r = b // 4 S = 2**b - if H * S > 2 ** (prec - 4): + if H * S > 2**(prec - 4): raise ValueError("insufficient precision for b=%s" % b) g1 = M1.ncols() g2 = M2.ncols() From da3d534332540f3b55cfd70729593b35629e3645 Mon Sep 17 00:00:00 2001 From: Yuan Zhou Date: Thu, 8 Sep 2022 19:01:38 -0400 Subject: [PATCH 382/454] assert isinstance(polymake_base_field, PolymakeElement) to muffle pyflakes and keep polymake tests --- src/sage/geometry/polyhedron/parent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 10b541bd4ad..ba1987b1fe2 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -174,9 +174,9 @@ def Polyhedra(ambient_space_or_base_ring=None, ambient_dim=None, backend=None, * elif backend == 'polymake': base_field = base_ring.fraction_field() try: - from sage.interfaces.polymake import polymake + from sage.interfaces.polymake import polymake, PolymakeElement polymake_base_field = polymake(base_field) - assert polymake_base_field # to muffle pyflakes + assert isinstance(polymake_base_field, PolymakeElement) # to muffle pyflakes except TypeError: raise ValueError(f"the 'polymake' backend for polyhedron cannot be used with {base_field}") return Polyhedra_polymake(base_field, ambient_dim, backend) From c8dff33ab174483247969e59eec03ffb5de4b27c Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Fri, 9 Sep 2022 11:16:10 +0100 Subject: [PATCH 383/454] Fixed issue with failure to raise error. --- src/sage/schemes/riemann_surfaces/riemann_surface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index d3faed36a0c..f85a7295dbc 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -3137,7 +3137,7 @@ def fv(hj, previous_estimate_and_validity): newg -= delta else: if raise_errors: - ConvergenceError("Newton iteration fails to converge") + raise ConvergenceError("Newton iteration fails to converge") else: outg.append(newg) fj = V(outg) @@ -3176,7 +3176,7 @@ def fv(hj, previous_estimate): newg -= delta else: if raise_errors: - ConvergenceError("Newton iteration fails to converge") + raise ConvergenceError("Newton iteration fails to converge") else: outg.append(newg) fj = V(outg) @@ -3245,7 +3245,7 @@ def fv(hj, previous_estimate): # we have one final error handle. Again, this will throw an error if # the raise_errors flag is true, but will just return the answer otherwise. if raise_errors: - ConvergenceError("Newton iteration fails to converge") + raise ConvergenceError("Newton iteration fails to converge") return (J * results[-1], endscale * fj) From 7fc5a445834cfc67fa84185fc87338dbbdd8f73c Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 9 Sep 2022 12:02:41 -0400 Subject: [PATCH 384/454] Undo Python's limit on string to int and int to string conversions --- src/sage/all.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/all.py b/src/sage/all.py index 584c0614181..5600674ddf4 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -285,6 +285,12 @@ def quit_sage(verbose=True): sage.misc.lazy_import.finish_startup() +### Python broke large ints; see trac #34506 + +if hasattr(sys, "set_int_max_str_digits"): + sys.set_int_max_str_digits(0) + + def sage_globals(): r""" Return the Sage namespace. From 20f06e6eb4bdd093fe952911bb568f7ebf9141f1 Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 9 Sep 2022 12:37:38 -0400 Subject: [PATCH 385/454] Add test --- src/sage/all.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/all.py b/src/sage/all.py index 5600674ddf4..965230969f6 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -38,6 +38,10 @@ sage: interacts + +Check that :trac:`34506` is resolved:: + + sage: x = int('1'*4301) """ # **************************************************************************** # Copyright (C) 2005-2012 William Stein From e538c5dfac10a7855521c874cdcf46a96257d32a Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 9 Sep 2022 12:39:06 -0400 Subject: [PATCH 386/454] Fix bug in decomposition_type --- src/sage/rings/number_field/number_field.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 78e0eb50e92..4ffa62f66cf 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -5998,12 +5998,21 @@ def decomposition_type(self, p): 2 sage: M.decomposition_type(Q1) [(2, 5, 1)] + + Check that :trac:`34514` is fixed:: + + sage: K. = NumberField(x^4 + 18*x^2 - 1) + sage: R. = K[] + sage: L. = K.extension(y^2 + 9*a^3 - 2*a^2 + 162*a - 38) + sage: [L.decomposition_type(i) for i in K.primes_above(3)] + [[(1, 1, 2)], [(1, 1, 2)], [(1, 2, 1)]] """ v0 = self.base_ring().valuation(p) e0 = v0.value_group().gen().denominator() - f0 = v0.residue_field().degree() + # Ideally we would compute f using the degree, but residue fields of relative extensions are currently implemented using polynomial quotient rings (this will hopefully be improved after #28485). + C0 = v0.residue_field().cardinality() valuations = v0.extensions(self) - ef = [(v.value_group().gen().denominator() // e0, v.residue_field().degree() // f0) for v in valuations] + ef = [(v.value_group().gen().denominator() // e0, v.residue_field().cardinality().exact_log(C0)) for v in valuations] return sorted([(e, f, g) for ((e, f), g) in Counter(ef).items()]) def gen(self, n=0): From 35b8b6ec6dcbd3dcf25e54937aec668322e5ad1c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:33:02 -0700 Subject: [PATCH 387/454] ModulesWithBasis.ElementMethod.support: Return a SupportView --- src/sage/categories/modules_with_basis.py | 8 ++--- src/sage/structure/support_view.py | 44 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/sage/structure/support_view.py diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 22f5a6d8f07..0664ae33b7d 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -1534,7 +1534,7 @@ def length(self): def support(self): """ - Return a list of the objects indexing the basis of + Return an iterable of the objects indexing the basis of ``self.parent()`` whose corresponding coefficients of ``self`` are non-zero. @@ -1555,9 +1555,9 @@ def support(self): sage: sorted(z.support()) [[1], [1, 1, 1], [2, 1], [4]] """ - zero = self.parent().base_ring().zero() - return [key for key, coeff in self.monomial_coefficients(copy=False).items() - if coeff != zero] + from sage.structure.support_view import SupportView + mc = self.monomial_coefficients(copy=False) + return SupportView(mc) def monomials(self): """ diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py new file mode 100644 index 00000000000..13631e3d89e --- /dev/null +++ b/src/sage/structure/support_view.py @@ -0,0 +1,44 @@ +r""" +Iterable of the keys of a Mapping associated with nonzero values +""" + +from collections.abc import MappingView, Sequence, Set + + +class SupportView(MappingView, Sequence, Set): + + def __init__(self, mapping, *, zero=None): + self._mapping = mapping + self._zero = zero + + def __len__(self): + length = 0 + for key in self: + length += 1 + return length + + def __getitem__(self, index): + for i, key in enumerate(self): + if i == index: + return key + raise IndexError + + def __iter__(self): + zero = self._zero + if zero is None: + for key, value in self._mapping.items(): + if value: + yield key + else: + for key, value in self._mapping.items(): + if value != zero: + yield key + + def __contains__(self, key): + try: + value = self._mapping[key] + except KeyError: + return False + if zero is None: + return bool(value) + return value != zero From 8ae8f09538745e3388340d9307e2b661eae0148a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:34:08 -0700 Subject: [PATCH 388/454] src/sage/combinat/crystals, src/sage/categories/*crystals*: Use collections.abc for isinstance --- src/sage/categories/crystals.py | 15 ++++++++------- src/sage/combinat/crystals/subcrystal.py | 4 +++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index ec7a5865398..1cfbf9175ab 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -19,6 +19,7 @@ # https://www.gnu.org/licenses/ #***************************************************************************** +import collections.abc from sage.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method @@ -779,10 +780,10 @@ def crystal_morphism(self, on_gens, codomain=None, if codomain is None: if hasattr(on_gens, 'codomain'): codomain = on_gens.codomain() - elif isinstance(on_gens, (list, tuple)): + elif isinstance(on_gens, collections.abc.Sequence): if on_gens: codomain = on_gens[0].parent() - elif isinstance(on_gens, dict): + elif isinstance(on_gens, collections.abc.Mapping): if on_gens: codomain = next(iter(on_gens.values())).parent() else: @@ -1845,7 +1846,7 @@ def __init__(self, parent, cartan_type=None, scaling_factors = {i: 1 for i in index_set} if virtualization is None: virtualization = {i: (i,) for i in index_set} - elif not isinstance(virtualization, dict): + elif not isinstance(virtualization, collections.abc.Mapping): try: virtualization = dict(virtualization) except (TypeError, ValueError): @@ -2057,16 +2058,16 @@ def __init__(self, parent, on_gens, cartan_type=None, virtualization, scaling_factors) if gens is None: - if isinstance(on_gens, dict): + if isinstance(on_gens, collections.abc.Mapping): gens = on_gens.keys() else: gens = parent.domain().module_generators self._gens = tuple(gens) # Make sure on_gens is a function - if isinstance(on_gens, dict): + if isinstance(on_gens, collections.abc.Mapping): f = lambda x: on_gens[x] - elif isinstance(on_gens, (list, tuple)): + elif isinstance(on_gens, collections.abc.Sequence): if len(self._gens) != len(on_gens): raise ValueError("invalid generator images") d = {x: y for x, y in zip(self._gens, on_gens)} @@ -2579,7 +2580,7 @@ def __call__(self, on_gens, cartan_type=None, index_set=None, generators=None, if automorphism is not None: if virtualization is not None: raise ValueError("the automorphism and virtualization cannot both be specified") - if not isinstance(automorphism, dict): + if not isinstance(automorphism, collections.abc.Mapping): try: automorphism = dict(automorphism) virtualization = {i: (automorphism[i],) for i in automorphism} diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index 9881a021e27..c62815af4be 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -23,6 +23,8 @@ # http://www.gnu.org/licenses/ #**************************************************************************** +import collections.abc + from sage.misc.lazy_attribute import lazy_attribute from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -126,7 +128,7 @@ def __classcall_private__(cls, ambient, contained=None, generators=None, sage: S1 is S2 True """ - if isinstance(contained, (list, tuple, set, frozenset)): + if isinstance(contained, (collections.abc.Sequence, collections.abc.Set)): contained = frozenset(contained) #elif contained in Sets(): From c5857f82c435f706ddce6ff496949cb3fb9fd242 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:35:08 -0700 Subject: [PATCH 389/454] Convert result of support() to list or tuple in two places --- src/sage/categories/loop_crystals.py | 2 +- src/sage/combinat/ncsf_qsym/tutorial.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index aa96f2b5594..e065b7ef4f6 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -456,7 +456,7 @@ def b_sharp(self): bsharp = None for b in self: phi = b.Phi() - if phi.support() == [0] and phi[0] < ell: + if list(phi.support()) == [0] and phi[0] < ell: bsharp = b ell = phi[0] return bsharp diff --git a/src/sage/combinat/ncsf_qsym/tutorial.py b/src/sage/combinat/ncsf_qsym/tutorial.py index 5efc8d43728..78978507cb3 100644 --- a/src/sage/combinat/ncsf_qsym/tutorial.py +++ b/src/sage/combinat/ncsf_qsym/tutorial.py @@ -111,7 +111,7 @@ sage: sorted(z.coefficients()) [1, 2, 3] - sage: sorted(z.monomials(), key=lambda x: x.support()) + sage: sorted(z.monomials(), key=lambda x: tuple(x.support())) [M[1, 2], M[3, 3], M[6]] sage: z.monomial_coefficients() From 12da4ed7451883da43371a0296f0f29a3b9af347 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:44:09 -0700 Subject: [PATCH 390/454] ModulesWithBasis.ElementMethod.len: Use support --- src/sage/categories/modules_with_basis.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 0664ae33b7d..79d98764dd8 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -1506,9 +1506,7 @@ def __len__(self): sage: len(z) 4 """ - zero = self.parent().base_ring().zero() - return len([key for key, coeff in self.monomial_coefficients(copy=False).items() - if coeff != zero]) + return len(self.support()) def length(self): """ @@ -1556,8 +1554,9 @@ def support(self): [[1], [1, 1, 1], [2, 1], [4]] """ from sage.structure.support_view import SupportView + zero = self.parent().base_ring().zero() mc = self.monomial_coefficients(copy=False) - return SupportView(mc) + return SupportView(mc, zero=zero) def monomials(self): """ From 1ddb29bcae0394d86153231bf2c4e6090bccd757 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:55:27 -0700 Subject: [PATCH 391/454] src/sage/structure/support_view.py: Fix up --- src/sage/structure/support_view.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py index 13631e3d89e..1c7babee506 100644 --- a/src/sage/structure/support_view.py +++ b/src/sage/structure/support_view.py @@ -39,6 +39,7 @@ def __contains__(self, key): value = self._mapping[key] except KeyError: return False + zero = self._zero if zero is None: return bool(value) return value != zero From 31559b26eacad4c2d1591bd8212d3b4e4f3fcead Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 15:55:45 -0700 Subject: [PATCH 392/454] IndexedFreeModuleElement.__contains__: Deprecate --- src/sage/modules/with_basis/indexed_element.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index cd1f17112f0..c9b92640d78 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -25,6 +25,7 @@ from cpython.object cimport Py_NE, Py_EQ from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.superseded import deprecation from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art, unicode_art from sage.categories.all import Category, Sets, ModulesWithBasis @@ -93,7 +94,8 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: Partition([1,1,1]) in a False """ - return x in self._monomial_coefficients and self._monomial_coefficients[x] != 0 + deprecation(34509, "using 'index in vector' is deprecated; use 'index in vector.support()' instead") + return x in self.support() def __hash__(self): """ From eb2c92d58022493a6b005e4fcca6e982e41f95ad Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 16:09:06 -0700 Subject: [PATCH 393/454] ModulesWithBasis.support: Try to cache the SupportView object --- src/sage/categories/modules_with_basis.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 79d98764dd8..26b51ef4b87 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -1553,10 +1553,19 @@ def support(self): sage: sorted(z.support()) [[1], [1, 1, 1], [2, 1], [4]] """ - from sage.structure.support_view import SupportView - zero = self.parent().base_ring().zero() - mc = self.monomial_coefficients(copy=False) - return SupportView(mc, zero=zero) + try: + return self._support_view + except AttributeError: + from sage.structure.support_view import SupportView + zero = self.parent().base_ring().zero() + mc = self.monomial_coefficients(copy=False) + support_view = SupportView(mc, zero=zero) + try: + # Try to cache it for next time, but this may fail for Cython classes + self._support_view = support_view + except AttributeError: + pass + return support_view def monomials(self): """ From bf91ea48ff7409cc2de2fecd236808bb1abd1a06 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 16:09:32 -0700 Subject: [PATCH 394/454] src/sage/combinat/ncsf_qsym/generic_basis_code.py: Replace deprecated 'index in vector' --- src/sage/combinat/ncsf_qsym/generic_basis_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/ncsf_qsym/generic_basis_code.py b/src/sage/combinat/ncsf_qsym/generic_basis_code.py index 98ebd37a0de..87364ac04c6 100644 --- a/src/sage/combinat/ncsf_qsym/generic_basis_code.py +++ b/src/sage/combinat/ncsf_qsym/generic_basis_code.py @@ -472,7 +472,7 @@ def skew(self, x, y, side='left'): y = self.dual()(y) v = 1 if side == 'left' else 0 return self.sum(coeff * y[IJ[1-v]] * self[IJ[v]] \ - for (IJ, coeff) in x.coproduct() if IJ[1-v] in y) + for (IJ, coeff) in x.coproduct() if IJ[1-v] in y.support()) else: return self._skew_by_coercion(x, y, side=side) From b3abc6c4094959e0ab6b75a67c06c8ce1acb004d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 16:14:52 -0700 Subject: [PATCH 395/454] src/sage/combinat/sf/sfa.py: Replace deprecated 'index in vector' --- src/sage/combinat/sf/sfa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index afeaf70a3e0..7eac828fa4b 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -2601,7 +2601,7 @@ def _inner_plethysm_pk_g(self, k, g, cache): for d in degrees: for mu in Partitions_n(d): mu_k = mu.power(k) - if mu_k in g: + if mu_k in g.support(): res += g.coefficient(mu_k)*mu_k.centralizer_size()/mu.centralizer_size()*p(mu) cache[(k,g)] = res From 82d8b62091f58f3ff68a8287be7e30515000f61f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 16:41:35 -0700 Subject: [PATCH 396/454] src/sage/structure/support_view.py: Add tests --- src/sage/structure/support_view.py | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py index 1c7babee506..ea7d38cf096 100644 --- a/src/sage/structure/support_view.py +++ b/src/sage/structure/support_view.py @@ -6,8 +6,56 @@ class SupportView(MappingView, Sequence, Set): + r""" + Dynamic view of the set of keys of a dictionary that are associated with nonzero values + + It behaves like the objects returned by the :meth:`keys`, :meth:`values`, + :meth:`items` of a dictionary (or other :class:`collections.abc.Mapping` + classes). + + INPUT: + + - ``mapping`` -- a :class:`dict` or another :class:`collections.abc.Mapping`. + + - ``zero`` -- (optional) test for zeroness by comparing with this value. + + EXAMPLES:: + + sage: d = {'a': 47, 'b': 0, 'c': 11} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({'a': 47, 'b': 0, 'c': 11}) + sage: 'a' in supp, 'b' in supp, 'z' in supp + (True, False, False) + sage: len(supp) + 2 + sage: list(supp) + ['a', 'c'] + sage: supp[0], supp[1] + ('a', 'c') + sage: supp[-1] + 'c' + sage: supp[:] + ('a', 'c') + sage: supp[2] + Traceback (most recent call last): + ... + IndexError + + sage: d['b'] = 815 + sage: len(supp) + 3 + """ def __init__(self, mapping, *, zero=None): + r""" + TESTS:: + + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView({'a': 'b', 'c': ''}, zero='') + sage: len(supp) + 1 + """ self._mapping = mapping self._zero = zero @@ -18,6 +66,10 @@ def __len__(self): return length def __getitem__(self, index): + if isinstance(index, slice): + return tuple(self)[index] + if index < 0: + return tuple(self)[index] for i, key in enumerate(self): if i == index: return key From 1f48aa0a04db254a63ad3eb953d64f167a2295cc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 16:57:32 -0700 Subject: [PATCH 397/454] src/sage/structure/support_view.py: Add more tests --- src/sage/structure/support_view.py | 48 +++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py index ea7d38cf096..49fc788868d 100644 --- a/src/sage/structure/support_view.py +++ b/src/sage/structure/support_view.py @@ -37,10 +37,8 @@ class SupportView(MappingView, Sequence, Set): 'c' sage: supp[:] ('a', 'c') - sage: supp[2] - Traceback (most recent call last): - ... - IndexError + + It reflects changes to the underlying dictionary:: sage: d['b'] = 815 sage: len(supp) @@ -60,12 +58,34 @@ def __init__(self, mapping, *, zero=None): self._zero = zero def __len__(self): + r""" + TESTS:: + + sage: d = {'a': 47, 'b': 0, 'c': 11} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({'a': 47, 'b': 0, 'c': 11}) + sage: len(supp) + 2 + """ length = 0 for key in self: length += 1 return length def __getitem__(self, index): + r""" + TESTS:: + + sage: d = {'a': 47, 'b': 0, 'c': 11} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({'a': 47, 'b': 0, 'c': 11}) + sage: supp[2] + Traceback (most recent call last): + ... + IndexError + """ if isinstance(index, slice): return tuple(self)[index] if index < 0: @@ -76,6 +96,16 @@ def __getitem__(self, index): raise IndexError def __iter__(self): + r""" + TESTS:: + + sage: d = {'a': 47, 'b': 0, 'c': 11} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({'a': 47, 'b': 0, 'c': 11}) + sage: iter(supp) + + """ zero = self._zero if zero is None: for key, value in self._mapping.items(): @@ -87,6 +117,16 @@ def __iter__(self): yield key def __contains__(self, key): + r""" + TESTS:: + + sage: d = {'a': 47, 'b': 0, 'c': 11} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({'a': 47, 'b': 0, 'c': 11}) + sage: 'a' in supp, 'b' in supp, 'z' in supp + (True, False, False) + """ try: value = self._mapping[key] except KeyError: From 2583a37c57bf2ea04c48eec6cc65a45b02c4938d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 17:05:30 -0700 Subject: [PATCH 398/454] IndexedFreeModuleElement: Add doc --- .../modules/with_basis/indexed_element.pyx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index c9b92640d78..22309d2d364 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -32,6 +32,23 @@ from sage.categories.all import Category, Sets, ModulesWithBasis from sage.data_structures.blas_dict cimport add, negate, scal, axpy cdef class IndexedFreeModuleElement(ModuleElement): + r""" + Element class for :class:`~sage.combinat.free_module.CombinatorialFreeModule` + + TESTS:: + + sage: import collections.abc + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c']; f + B['a'] + 3*B['c'] + sage: isinstance(f, collections.abc.Sized) + True + sage: isinstance(f, collections.abc.Iterable) + True + sage: isinstance(f, collections.abc.Collection) # known bug - will be fixed by removing __contains__ + False + """ def __init__(self, M, x): """ Create a combinatorial module element. This should never be @@ -81,6 +98,9 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: 'a' in f + doctest:warning... + DeprecationWarning: using 'index in vector' is deprecated; use 'index in vector.support()' instead + See https://trac.sagemath.org/34509 for details. True sage: 'b' in f False From 88fe278795354296ad74dcc41b61b2aeb301d900 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 17:12:19 -0700 Subject: [PATCH 399/454] src/sage/structure/support_view.py: Fix doctest output --- src/sage/structure/support_view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py index 49fc788868d..6af9408bd29 100644 --- a/src/sage/structure/support_view.py +++ b/src/sage/structure/support_view.py @@ -104,7 +104,7 @@ def __iter__(self): sage: supp = SupportView(d); supp SupportView({'a': 47, 'b': 0, 'c': 11}) sage: iter(supp) - + """ zero = self._zero if zero is None: From 026d8a5061f8d7de5e6366679f7047e4a83f541e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 17:54:09 -0700 Subject: [PATCH 400/454] src/sage/algebras/steenrod/steenrod_algebra.py: Use key=...tuple(....support()) for sorting by lex support --- src/sage/algebras/steenrod/steenrod_algebra.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 9e9ad450358..8feec0d6d98 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -402,13 +402,13 @@ (1, (2, 1)) sage: c.monomial_coefficients() == {(2, 1): 1, (5,): 1} True - sage: sorted(c.monomials(), key=lambda x: x.support()) + sage: sorted(c.monomials(), key=lambda x: tuple(x.support())) [Sq(2,1), Sq(5)] sage: sorted(c.support()) [(2, 1), (5,)] sage: Adem = SteenrodAlgebra(basis='adem') sage: elt = Adem.Sq(10) + Adem.Sq(9) * Adem.Sq(1) - sage: sorted(elt.monomials(), key=lambda x: x.support()) + sage: sorted(elt.monomials(), key=lambda x: tuple(x.support())) [Sq^9 Sq^1, Sq^10] sage: A7 = SteenrodAlgebra(p=7) @@ -3100,7 +3100,7 @@ class Element(CombinatorialFreeModule.Element): (1, (2, 1)) sage: c.monomial_coefficients() == {(2, 1): 1, (5,): 1} True - sage: sorted(c.monomials(), key=lambda x: x.support()) + sage: sorted(c.monomials(), key=lambda x: tuple(x.support())) [Sq(2,1), Sq(5)] sage: sorted(c.support()) [(2, 1), (5,)] @@ -3458,7 +3458,7 @@ def excess(self): sage: (Sq(0,0,1) + Sq(4,1) + Sq(7)).excess() 1 sage: elt = Sq(0,0,1) + Sq(4,1) + Sq(7) - sage: M = sorted(elt.monomials(), key=lambda x: x.support()) + sage: M = sorted(elt.monomials(), key=lambda x: tuple(x.support())) sage: [m.excess() for m in M] [1, 5, 7] sage: [m for m in M] From a4174115ef0899344ecfb301c42f032de5b1950e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 18:06:12 -0700 Subject: [PATCH 401/454] SupportView.__eq__, __ne__: New --- src/sage/structure/support_view.py | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/sage/structure/support_view.py b/src/sage/structure/support_view.py index 6af9408bd29..0212afb5414 100644 --- a/src/sage/structure/support_view.py +++ b/src/sage/structure/support_view.py @@ -4,6 +4,8 @@ from collections.abc import MappingView, Sequence, Set +from sage.misc.superseded import deprecation + class SupportView(MappingView, Sequence, Set): r""" @@ -135,3 +137,41 @@ def __contains__(self, key): if zero is None: return bool(value) return value != zero + + def __eq__(self, other): + r""" + TESTS:: + + sage: d = {1: 17, 2: 0} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({1: 17, 2: 0}) + sage: supp == [1] + doctest:warning... + DeprecationWarning: comparing a SupportView with a list is deprecated + See https://trac.sagemath.org/34509 for details. + True + """ + if isinstance(other, list): + deprecation(34509, 'comparing a SupportView with a list is deprecated') + return list(self) == other + return NotImplemented + + def __ne__(self, other): + r""" + TESTS:: + + sage: d = {1: 17, 2: 0} + sage: from sage.structure.support_view import SupportView + sage: supp = SupportView(d); supp + SupportView({1: 17, 2: 0}) + sage: supp != [1] + doctest:warning... + DeprecationWarning: comparing a SupportView with a list is deprecated + See https://trac.sagemath.org/34509 for details. + False + """ + if isinstance(other, list): + deprecation(34509, 'comparing a SupportView with a list is deprecated') + return list(self) != other + return NotImplemented From b80989ff00667e5578f04a28a3d7ec6ff679c498 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 18:08:31 -0700 Subject: [PATCH 402/454] src/sage/combinat/sf/character.py: Use list(....support()) --- src/sage/combinat/sf/character.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/character.py b/src/sage/combinat/sf/character.py index e55f356f5d1..e5752c307eb 100644 --- a/src/sage/combinat/sf/character.py +++ b/src/sage/combinat/sf/character.py @@ -104,7 +104,7 @@ def _other_to_self(self, sexpr): """ if sexpr == 0: return self(0) - if sexpr.support() == [[]]: + if list(sexpr.support()) == [[]]: return self._from_dict({self.one_basis(): sexpr.coefficient([])}, remove_zeros=False) out = self.zero() From 57273ea3262a27501953d1af04f6d8f35110b0da Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 21:13:33 -0700 Subject: [PATCH 403/454] src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst: Use key=...tuple(....support()) for sorting by lex support --- src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst b/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst index a7052507284..ddc33a325a8 100644 --- a/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst +++ b/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst @@ -164,7 +164,7 @@ coefficients) through the usual free module accessors:: [((0, 0, 0), 1), ((1, 0, 0), 1), ((1, 1, 0), 1), ((1, 1, 1), 1)] sage: pprint(dict(chi)) {(0, 0, 0): 1, (1, 0, 0): 1, (1, 1, 0): 1, (1, 1, 1): 1} - sage: M = sorted(chi.monomials(), key=lambda x: x.support()); M + sage: M = sorted(chi.monomials(), key=lambda x: tuple(x.support())); M [B3(0,0,0), B3(1,0,0), B3(1,1,0), B3(1,1,1)] sage: sorted(chi.support()) [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)] @@ -485,7 +485,7 @@ itself, that is, the integral of `|tr(g)|^{10}`:: sage: tr^5 5*A2(2,2,1) + 6*A2(3,1,1) + 5*A2(3,2,0) + 4*A2(4,1,0) + A2(5,0,0) - sage: sorted((tr^5).monomials(), key=lambda x: x.support()) + sage: sorted((tr^5).monomials(), key=lambda x: tuple(x.support())) [A2(2,2,1), A2(3,1,1), A2(3,2,0), A2(4,1,0), A2(5,0,0)] sage: sorted((tr^5).coefficients()) [1, 4, 5, 5, 6] From 6ce669c687776a781f01c86439fc8739a25eb4cc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 21:14:26 -0700 Subject: [PATCH 404/454] src/sage/modules/tutorial_free_modules.py: Fix doctest output --- src/sage/modules/tutorial_free_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/tutorial_free_modules.py b/src/sage/modules/tutorial_free_modules.py index 88810b2e42b..d58acd4ae9f 100644 --- a/src/sage/modules/tutorial_free_modules.py +++ b/src/sage/modules/tutorial_free_modules.py @@ -162,7 +162,7 @@ (2, 3) sage: f.support() - [0, 1, 2] + SupportView({0: 2, 1: 2, 2: 3}) sage: f.monomials() [a[0], a[1], a[2]] sage: f.coefficients() From 2fe3b981c7ebf07dfe8992d5f591b3bb73841ede Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Sep 2022 21:17:48 -0700 Subject: [PATCH 405/454] src/sage/algebras/quantum_groups/fock_space.py: Use sorted() with support, not sort --- src/sage/algebras/quantum_groups/fock_space.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sage/algebras/quantum_groups/fock_space.py b/src/sage/algebras/quantum_groups/fock_space.py index dea28fd1760..3352f143a7c 100644 --- a/src/sage/algebras/quantum_groups/fock_space.py +++ b/src/sage/algebras/quantum_groups/fock_space.py @@ -1354,9 +1354,8 @@ def _G_to_fock_basis(self, la): return fock.sum_of_terms((fock._indices([[]]*k + list(pt)), c) for pt,c in cur) cur = R.A()._A_to_fock_basis(la) - s = cur.support() - s.sort() # Sort lex, which respects dominance order - s.pop() # Remove the largest + s = sorted(cur.support()) # Sort lex, which respects dominance order + s.pop() # Remove the largest q = R._q while s: @@ -2189,9 +2188,8 @@ def add_cols(nu): # Perform the triangular reduction cur = self.realization_of().A(algorithm)._A_to_fock_basis(la) - s = cur.support() - s.sort() # Sort lex, which respects dominance order - s.pop() # Remove the largest + s = sorted(cur.support()) # Sort lex, which respects dominance order + s.pop() # Remove the largest q = self.realization_of()._q while s: From 367b86236c861a41d7684a4228e26075207910fb Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 11 Sep 2022 19:32:47 +0200 Subject: [PATCH 406/454] #34519 fix msolve interface msolve's output in "parameterization" mode has changed with v0.3.0(?), and, as far as I understand, should now be stable. --- src/sage/rings/polynomial/msolve.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index b32f9f8505a..d05d103b159 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -158,7 +158,10 @@ def _variety(ideal, ring, proof): # Interpret output - data = sage_eval(msolve_out.stdout[:-2]) + try: + data = sage_eval(msolve_out.stdout[:-2]) + except SyntaxError: + raise NotImplementedError(f"unsupported msolve output format: {data}") dim = data[0] if dim == -1: @@ -176,10 +179,11 @@ def to_poly(p, upol=PolynomialRing(base, 't')): assert len(p[1]) == p[0] + 1 return upol(p[1]) - if len(data) != 3: + try: + [dim1, nvars, _, vars, _, [one, [elim, den, param]]] = data[1] + except (IndexError, ValueError): raise NotImplementedError( f"unsupported msolve output format: {data}") - [dim1, nvars, _, vars, _, [one, elim, den, param]] = data[1] assert dim1.is_zero() assert one.is_one() assert len(vars) == nvars From ea64d4a71b92f5ea94b2431afb8c3819a07a6683 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 12 Sep 2022 08:15:37 +0200 Subject: [PATCH 407/454] =?UTF-8?q?#34519=20msolve:=20real=20solving=20wit?= =?UTF-8?q?h=20=E2=84=93=20>=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt to support the case ℓ > 1 described in §4 of the msolve tutorial when computing zero-dimensional varieties over the reals. Last I heard this is not yet actually used in msolve but will be in the future. --- src/sage/rings/polynomial/msolve.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index d05d103b159..88f1c6889bb 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -205,10 +205,9 @@ def to_poly(p, upol=PolynomialRing(base, 't')): else: - if len(data) != 2 or data[1][0] != 1: + if len(data[1]) < 2 or len(data[1]) != data[1][0] + 1: raise NotImplementedError( f"unsupported msolve output format: {data}") - _, [_, variety] = data if isinstance(ring, (RealIntervalField_class, RealBallField)): to_out_ring = ring else: @@ -217,7 +216,8 @@ def to_poly(p, upol=PolynomialRing(base, 't')): to_out_ring = lambda iv: ring.coerce(myRIF(iv).center()) vars = out_ring.gens() variety = [[to_out_ring(iv) for iv in point] - for point in variety] + for l in data[1][1:] + for point in l] return [KeyConvertingDict(out_ring, zip(vars, point)) for point in variety] From d252ea46cf7ede4cd737cfdc586c92c44a2ecbcb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Sep 2022 10:19:57 -0700 Subject: [PATCH 408/454] build/pkgs/pynormaliz/distros: Add arch, conda --- build/pkgs/pynormaliz/distros/arch.txt | 1 + build/pkgs/pynormaliz/distros/conda.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 build/pkgs/pynormaliz/distros/arch.txt create mode 100644 build/pkgs/pynormaliz/distros/conda.txt diff --git a/build/pkgs/pynormaliz/distros/arch.txt b/build/pkgs/pynormaliz/distros/arch.txt new file mode 100644 index 00000000000..140dd0508e9 --- /dev/null +++ b/build/pkgs/pynormaliz/distros/arch.txt @@ -0,0 +1 @@ +python-pynormaliz diff --git a/build/pkgs/pynormaliz/distros/conda.txt b/build/pkgs/pynormaliz/distros/conda.txt new file mode 100644 index 00000000000..276703b325b --- /dev/null +++ b/build/pkgs/pynormaliz/distros/conda.txt @@ -0,0 +1 @@ +pynormaliz From d90b64c6b7957b6c2294a1f0efb0447fcce138fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Sep 2022 11:27:17 -0700 Subject: [PATCH 409/454] Revert "build/pkgs/git_trac_command: New" This reverts commit ac2c48db84c1a59e333552e5abfcdc3229fb9084. --- build/pkgs/git_trac_command/SPKG.rst | 14 -------------- build/pkgs/git_trac_command/requirements.txt | 1 - build/pkgs/git_trac_command/trees.txt | 1 - build/pkgs/git_trac_command/type | 1 - 4 files changed, 17 deletions(-) delete mode 100644 build/pkgs/git_trac_command/SPKG.rst delete mode 100644 build/pkgs/git_trac_command/requirements.txt delete mode 100644 build/pkgs/git_trac_command/trees.txt delete mode 100644 build/pkgs/git_trac_command/type diff --git a/build/pkgs/git_trac_command/SPKG.rst b/build/pkgs/git_trac_command/SPKG.rst deleted file mode 100644 index d8da01bd818..00000000000 --- a/build/pkgs/git_trac_command/SPKG.rst +++ /dev/null @@ -1,14 +0,0 @@ -git_trac_command: Provides the subcommand "git trac" -==================================================== - -Description ------------ - -This module implements a subcommand ``git trac``. -See https://doc.sagemath.org/html/en/developer/git_trac.html - - -Upstream Contact ----------------- - -https://github.com/sagemath/git-trac-command diff --git a/build/pkgs/git_trac_command/requirements.txt b/build/pkgs/git_trac_command/requirements.txt deleted file mode 100644 index 4f36b5eae53..00000000000 --- a/build/pkgs/git_trac_command/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -git+https://github.com/sagemath/git-trac-command diff --git a/build/pkgs/git_trac_command/trees.txt b/build/pkgs/git_trac_command/trees.txt deleted file mode 100644 index b268580307d..00000000000 --- a/build/pkgs/git_trac_command/trees.txt +++ /dev/null @@ -1 +0,0 @@ -# Users should install this manually in their environment. It should not be installed in SAGE_VENV diff --git a/build/pkgs/git_trac_command/type b/build/pkgs/git_trac_command/type deleted file mode 100644 index 134d9bc32d5..00000000000 --- a/build/pkgs/git_trac_command/type +++ /dev/null @@ -1 +0,0 @@ -optional From 9a03d7fe7beb1bae71415938f7cb3ef9417c9fb1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Sep 2022 17:26:45 -0700 Subject: [PATCH 410/454] build/sage_bootstrap/installcheck.py: Make error message more specific --- build/sage_bootstrap/installcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index 83f28c07c6d..a02db3c74fd 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -153,7 +153,7 @@ def spkg_type(pkg): if not pth.isdir(pkgbase): raise argparse.ArgumentTypeError( - "'{0}' is not a known spkg".format(pkg)) + "'{0}' is not an spkg listed in '{1}'".format(pkg, PKGS)) return pkg From 70ba7b581c14aff406bec3d453199dfef47cc1fa Mon Sep 17 00:00:00 2001 From: Dennis Jahn Date: Tue, 13 Sep 2022 09:48:00 +0200 Subject: [PATCH 411/454] added Dy1994 and JS2021 to master reference file --- src/doc/en/reference/references/index.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 46e46557a02..564a89f42f6 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2129,6 +2129,9 @@ REFERENCES: .. [Dy1993] \M. J. Dyer. *Hecke algebras and shellings of Bruhat intervals*. Compositio Mathematica, 1993, 89(1): 91-115. +.. [Dy1994] \M. J. Dyer. *Bruhat intervals, polyhedral cones and + Kazhdan-Lusztig-Stanley polynomials*. Math.Z., 215(2):223-236, 1994. + .. _ref-E: **E** @@ -3297,6 +3300,10 @@ REFERENCES: J. Algebra. **324** (2010). 2512-2542. :doi:`10.1016/j.bbr.2011.03.031`, :arxiv:`0909.2442`. +.. [JS2021] \D. Jahn, C. Stump. + *Bruhat intervals, subword complexes and brick polyhedra for + finite Coxeter groups*, 2021, :arxiv:`2103.03715`. + .. [JV2000] \J. Justin, L. Vuillon, *Return words in Sturmian and episturmian words*, Theor. Inform. Appl. 34 (2000) 343--356. From 648e6340df54ecdf456a4019a84dae2853661660 Mon Sep 17 00:00:00 2001 From: Dennis Jahn Date: Tue, 13 Sep 2022 09:49:43 +0200 Subject: [PATCH 412/454] moved references and deleted empty lines --- .../combinat/root_system/reflection_group_real.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py index 38bfee60855..9e5c2e2c1b0 100644 --- a/src/sage/combinat/root_system/reflection_group_real.py +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -27,11 +27,6 @@ - Christian Stump (initial version 2011--2015) -REFERENCES: - -.. [Dye] Dyer. *Bruhat intervals, polyhedral cones and Kazhdan-Lusztig-Stanley polynomials*. Math.Z., 215(2):223-236, 1994. -.. [JahStu] Jahn and Stump. *Bruhat intervals, subword complexes and brick polyhedra for finite Coxeter groups*. Preprint, available at :arxiv:`2103.03715`, 2021. - .. WARNING:: Uses the GAP3 package *Chevie* which is available as an @@ -727,9 +722,7 @@ def bruhat_cone(self, x, y, side='upper', backend='cdd'): INPUT: - ``x`` - an element in the group `W` - - ``y`` - an element in the group `W` - - ``side`` (default: ``'upper'``) -- must be one of the following: * ``'upper'`` - return the upper Bruhat cone of the interval [``x``, ``y``] @@ -763,8 +756,8 @@ def bruhat_cone(self, x, y, side='upper', backend='cdd'): REFERENCES: - - [Dye]_ - - [JahStu]_ + - [Dy1994]_ + - [JS2021]_ """ if side == 'upper': roots = [self.reflection_to_positive_root(x * r * x.inverse()) From 4ddf379b64475385ab662e78edf45f81c4b0717d Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 8 Sep 2022 17:43:18 -0500 Subject: [PATCH 413/454] Add tests, remove buggy check, add to check --- src/sage/combinat/integer_vector.py | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index ee73284a59c..7295ba97571 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -453,9 +453,29 @@ def check(self): sage: IV = IntegerVectors() sage: elt = IV([1,2,1]) sage: elt.check() + + Check :trac:`34510`:: + + sage: IV3 = IntegerVectors(n=3) + sage: IV3([2,2]) + Traceback (most recent call last): + ... + ValueError: [2, 2] doesn't satisfy correct constraints + sage: IVk3 = IntegerVectors(k=3) + sage: IVk3([2,2]) + Traceback (most recent call last): + ... + ValueError: [2, 2] doesn't satisfy correct constraints + sage: IV33 = IntegerVectors(n=3, k=3) + sage: IV33([2,2]) + Traceback (most recent call last): + ... + ValueError: [2, 2] doesn't satisfy correct constraints """ if any(x < 0 for x in self): raise ValueError("all entries must be non-negative") + if self not in self.parent(): + raise ValueError(f"{self} doesn't satisfy correct constraints") class IntegerVectors(Parent, metaclass=ClasscallMetaclass): @@ -1015,10 +1035,15 @@ def __contains__(self, x): False sage: [3,2,2,1] in IntegerVectors(8, 4) True - """ - if isinstance(x, IntegerVector) and x.parent() is self: - return True + Check :trac:`34510`:: + + sage: IV33 = IntegerVectors(n=3, k=3) + sage: IV33([0]) + Traceback (most recent call last): + ... + ValueError: [0] doesn't satisfy correct constraints + """ if not IntegerVectors.__contains__(self, x): return False From 43e68135a00c4a1707545061bef649d2506070f3 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 13 Sep 2022 11:16:21 -0500 Subject: [PATCH 414/454] List/tuple -> sequence --- src/sage/combinat/integer_vector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 7295ba97571..e0cd57a3eec 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -30,6 +30,7 @@ from sage.combinat.integer_lists import IntegerListsLex from itertools import product +from collections.abc import Sequence import numbers from sage.structure.parent import Parent @@ -696,7 +697,7 @@ def __contains__(self, x): if isinstance(x, IntegerVector): return True - if not isinstance(x, (list, tuple)): + if not isinstance(x, Sequence): return False for i in x: From 1463b3ed10ef383a6fb447e405d04c1db1024094 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 13 Sep 2022 13:48:33 -0500 Subject: [PATCH 415/454] Register Composition as a sequence --- src/sage/combinat/composition.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index cc709bc5091..ad401487471 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -20,6 +20,7 @@ - Mike Hansen, Nicolas M. Thiéry - MuPAD-Combinat developers (algorithms and design inspiration) - Travis Scrimshaw (2013-02-03): Removed ``CombinatorialClass`` +- Trevor K. Karn (2022-09-13): Make ``Composition`` a ``collections.abc.Sequence`` """ # **************************************************************************** # Copyright (C) 2007 Mike Hansen @@ -30,6 +31,7 @@ # **************************************************************************** from __future__ import annotations from itertools import accumulate +from collections.abc import Sequence from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -109,6 +111,12 @@ class Composition(CombinatorialElement): sage: Composition(descents=({0,1,3},5)) [1, 1, 2, 1] + Check :trac:`34527`:: + + sage: from collections.abc import Sequence + sage: isinstance(Composition([3,2,3]), Sequence) + True + EXAMPLES:: sage: C = Composition([3,1,2]) @@ -1350,6 +1358,7 @@ def wll_gt(self, co2) -> bool: return False return False +Sequence.register(Composition) ############################################################## From 67c914c6079204194708ff1188871724ca81adb9 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 14 Sep 2022 13:25:59 -0700 Subject: [PATCH 416/454] trac 34533: silence warning during doctesting about "chained fixups" --- src/sage/doctest/parsing.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 064414befbc..1752caa851a 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -43,6 +43,8 @@ ld_warning_regex = re.compile(r'^.*dylib.*was built for newer macOS version.*than being linked.*') # :trac:`30845` -- suppress warning on conda about ld ld_pie_warning_regex = re.compile(r'ld: warning: -pie being ignored. It is only used when linking a main executable') +# :trac:`34533` -- suppress warning on OS X 12.6 about chained fixups +chained_fixup_warning_regex = re.compile(r'ld: warning: -undefined dynamic_lookup may not work with chained fixups') sympow_cache_warning_regex = re.compile(r'\*\*WARNING\*\* /var/cache/sympow/datafiles/le64 yields insufficient permissions') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) @@ -117,6 +119,9 @@ def fake_RIFtol(*args): (lambda g, w: "Long-step" in g, lambda g, w: (glpk_simplex_warning_regex.sub('', g), w)), + (lambda g, w: "chained fixups" in g, + lambda g, w: (chained_fixup_warning_regex.sub('', g), w)), + (lambda g, w: "insufficient permissions" in g, lambda g, w: (sympow_cache_warning_regex.sub('', g), w)), From 6ceb44edbd932b7c772d3844c74103e2e3d6732c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Sep 2022 15:15:25 -0700 Subject: [PATCH 417/454] build/pkgs/auditwheel_or_delocate/SPKG.rst: Explain that we use delocate also on Linux --- build/pkgs/auditwheel_or_delocate/SPKG.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build/pkgs/auditwheel_or_delocate/SPKG.rst b/build/pkgs/auditwheel_or_delocate/SPKG.rst index 845a8da1c24..710ea0224e0 100644 --- a/build/pkgs/auditwheel_or_delocate/SPKG.rst +++ b/build/pkgs/auditwheel_or_delocate/SPKG.rst @@ -4,8 +4,11 @@ auditwheel_or_delocate: Repair wheels on Linux or macOS Description ----------- -This package represents auditwheel on Linux -and delocate on macOS. +This package represents ``auditwheel`` on Linux and ``delocate`` on macOS. + +(Actually, we install ``delocate`` also on Linux because our script +``make -j list-broken-packages`` uses a small subroutine of ``delocate`` +even on Linux.) License ------- From 3c70a38483a15327118ccfcaeabf154e5d9f2af1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 14 Sep 2022 15:24:01 -0700 Subject: [PATCH 418/454] build/sage_bootstrap/installcheck.py: Update comment on stamp files --- build/sage_bootstrap/installcheck.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/sage_bootstrap/installcheck.py b/build/sage_bootstrap/installcheck.py index a02db3c74fd..1448ccdcbc9 100644 --- a/build/sage_bootstrap/installcheck.py +++ b/build/sage_bootstrap/installcheck.py @@ -51,8 +51,7 @@ def installcheck(spkg_name, sage_local, verbose=False): spkg_inst = pth.join(sage_local, 'var', 'lib', 'sage', 'installed') # Find all stamp files for the package; there should be only one, but if - # there is somehow more than one we'll work with the most recent and delete - # the rest + # there is somehow more than one we'll work with the most recent one. pattern = pth.join(spkg_inst, '{0}-*'.format(spkg_name)) stamp_files = sorted(glob.glob(pattern), key=pth.getmtime) From e74e0b2662607ded26b38b7f64960952ddf139d0 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 14 Sep 2022 22:22:45 -0500 Subject: [PATCH 419/454] Update doctest --- src/sage/combinat/composition.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index ad401487471..2373f3947d5 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -20,7 +20,6 @@ - Mike Hansen, Nicolas M. Thiéry - MuPAD-Combinat developers (algorithms and design inspiration) - Travis Scrimshaw (2013-02-03): Removed ``CombinatorialClass`` -- Trevor K. Karn (2022-09-13): Make ``Composition`` a ``collections.abc.Sequence`` """ # **************************************************************************** # Copyright (C) 2007 Mike Hansen @@ -113,8 +112,8 @@ class Composition(CombinatorialElement): Check :trac:`34527`:: - sage: from collections.abc import Sequence - sage: isinstance(Composition([3,2,3]), Sequence) + sage: import collections.abc + sage: isinstance(Composition([3,2,3]), collections.abc.Sequence) True EXAMPLES:: From 1f3e2853f1cd98272220cd6f384c58fc7dfadacb Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 14 Sep 2022 23:51:15 -0500 Subject: [PATCH 420/454] Add _floordiv_ --- .../polynomial/infinite_polynomial_element.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 659e903ce75..40d1521cf7e 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -349,6 +349,21 @@ def __call__(self, *args, **kwargs): except Exception: return res + def _floordiv_(self, right): + """ + Implement a floor division by passing to the finite polynomial ring. + + EXAMPLES:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: (z[10]+z[0]) // (z[10] + z[0]) + 1 + sage: z[10] // (z[10] + z[0]) + 0 + """ + R = self.parent()._P + return self.parent()(R(self)._floordiv_(R(right))) + def _getAttributeNames(self): """ This method implements tab completion, see :trac:`6854`. From 27b986acad2252c66ec110b06b7ddd911749892f Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 15 Sep 2022 00:22:50 -0500 Subject: [PATCH 421/454] More updates to doctest --- src/sage/combinat/composition.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 2373f3947d5..72df25b263c 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -113,8 +113,18 @@ class Composition(CombinatorialElement): Check :trac:`34527`:: sage: import collections.abc - sage: isinstance(Composition([3,2,3]), collections.abc.Sequence) + sage: C = Composition([3,2,3]) + sage: isinstance(C, collections.abc.Sequence) True + sage: issubclass(C.__class__, collections.abc.Sequence) + True + sage: C.count + Traceback (most recent call last): + ... + AttributeError: 'Compositions_all_with_category.element_class' object + has no attribute 'count' + sage: type(reversed(C)) + EXAMPLES:: From c71dc3f2672ec4ab7d0ae7050776afef3a93e9bd Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 1 Sep 2022 14:49:39 +0800 Subject: [PATCH 422/454] =?UTF-8?q?increase=20minimum=20degree=20for=20?= =?UTF-8?q?=E2=88=9A=C3=A9lu=20formulas=20to=209?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is mainly done to exclude curves over GF(3) with 7 rational points, which cause trouble for √élu as they don't have any points defined only in a quadratic extension of the base field. Hence, the resulting isogeny won't be defined over GF(3). Since √élu is still much slower than Vélu for degrees this small, noone will miss the functionality thus excluded. --- src/sage/schemes/elliptic_curves/hom_velusqrt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 86ca9e51de0..5b8838514ba 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -756,7 +756,7 @@ class EllipticCurveHom_velusqrt(EllipticCurveHom): INPUT: - ``E`` -- an elliptic curve over a finite field - - ``P`` -- a point on `E` of odd order `\geq 5` + - ``P`` -- a point on `E` of odd order `\geq 9` - ``codomain`` -- codomain elliptic curve (optional) - ``model`` -- string (optional); input to :meth:`~sage.schemes.elliptic_curves.ell_field.compute_model` @@ -884,8 +884,8 @@ def __init__(self, E, P, *, codomain=None, model=None, Q=None): self._P = self._pre_iso(P) self._degree = self._P.order() - if self._degree % 2 != 1 or self._degree < 5: - raise NotImplementedError('only implemented for odd degrees >= 5') + if self._degree % 2 != 1 or self._degree < 9: + raise NotImplementedError('only implemented for odd degrees >= 9') if Q is not None: self._Q = E(Q) @@ -1196,10 +1196,10 @@ def _random_example_for_testing(): True """ from sage.all import prime_range, choice, randrange, GF, gcd - p = choice(prime_range(3, 100)) - e = randrange(1,5) - F,t = GF((p,e),'t').objgen() while True: + p = choice(prime_range(2, 100)) + e = randrange(1,5) + F,t = GF((p,e),'t').objgen() try: E = EllipticCurve([F.random_element() for _ in range(5)]) except ArithmeticError: @@ -1208,11 +1208,11 @@ def _random_example_for_testing(): E.short_weierstrass_model() except ValueError: continue - if E.cardinality() < 5: + if E.cardinality() < 9: continue A = E.abelian_group() ds = max(A.invariants()).prime_to_m_part(2).divisors() - ds = [d for d in ds if 5 <= d < 1000] + ds = [d for d in ds if 9 <= d < 1000] if ds: deg = choice(ds) break From 467eda55e5ff8ea839cb5cdf06915eb0fc482b9a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 15 Sep 2022 13:57:43 +0800 Subject: [PATCH 423/454] add failing example --- .../schemes/elliptic_curves/hom_velusqrt.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 5b8838514ba..56d8603fbb6 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -864,6 +864,26 @@ def __init__(self, E, P, *, codomain=None, model=None, Q=None): Elliptic-curve isogeny (using √élu) of degree 105: From: Elliptic Curve defined by y^2 = x^3 + x over Finite Field of size 419 To: Elliptic Curve defined by y^2 = x^3 + 6*x^2 + x over Finite Field of size 419 + + Note that the implementation in fact also works in almost all + cases when the degree is `5` or `7`. The reason we restrict to + degrees `\geq 9` is that (only!) when trying to compute a + `7`-isogeny from a rational point on an elliptic curve defined + over `\GF{3}`, the point `Q` required in the formulas has to be + defined over a cubic extension rather than an at most quadratic + extension, which can result in the constructed isogeny being + irrational. See :trac:`34467`. The assertion in the following + example currently fails if the minimum degree is lowered:: + + sage: E = EllipticCurve(GF(3), [2,1]) + sage: P, = E.gens() + sage: P.order() + 7 + sage: psi = E.isogeny(P) + sage: phi = E.isogeny(P, algorithm='velusqrt') # not tested + sage: phi._Q.base_ring() # not tested + Finite Field in z3 of size 3^3 + sage: assert phi.codomain().is_isomorphic(psi.codomain()) # not tested """ if not isinstance(E, EllipticCurve_finite_field): raise NotImplementedError('only implemented for elliptic curves over finite fields') From 1f0a3d0e3b92773526e526aa0257c73f98694b37 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 15 Sep 2022 13:19:20 -0500 Subject: [PATCH 424/454] Update floordiv --- .../polynomial/infinite_polynomial_element.py | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 40d1521cf7e..3a88f14b727 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -349,21 +349,6 @@ def __call__(self, *args, **kwargs): except Exception: return res - def _floordiv_(self, right): - """ - Implement a floor division by passing to the finite polynomial ring. - - EXAMPLES:: - - sage: R. = InfinitePolynomialRing(QQ) - sage: (z[10]+z[0]) // (z[10] + z[0]) - 1 - sage: z[10] // (z[10] + z[0]) - 0 - """ - R = self.parent()._P - return self.parent()(R(self)._floordiv_(R(right))) - def _getAttributeNames(self): """ This method implements tab completion, see :trac:`6854`. @@ -573,6 +558,11 @@ def _add_(self, x): sage: x[1] + x[2] # indirect doctest x_2 + x_1 + Check adding from a different parent:: + + sage: Y. = PolynomialRing(QQ) + sage: x[0] - x_0 + 0 """ # One may need a new parent for self._p and x._p try: @@ -705,6 +695,29 @@ def _div_(self, x): # there remains a problem in reduction return FractionFieldElement(field, self, x, reduce=False) + def _floordiv_(self, x): + """ + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(ZZ) + sage: x[2]//x[2] # indirect doctest + 1 + """ + try: + return InfinitePolynomial_sparse(self.parent(),self._p//x._p) + except Exception: + pass + ## We can now assume that self._p and x._p actually are polynomials, + ## hence, their parent is not just the underlying ring. + VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) + VarList.sort(key=self.parent().varname_key,reverse=True) + if VarList: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self._p.base_ring(),VarList,order=self.parent()._order) + else: + R = self._p.base_ring() + return InfinitePolynomial_sparse(self.parent(),R(self._p) // R(x._p)) + def _sub_(self, x): """ EXAMPLES:: From cf9b1e69faa1db451c19e47ae8160f641575f395 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 15 Sep 2022 20:56:06 -0700 Subject: [PATCH 425/454] trac 33093: fixes for octave interface - fix failing doctests - do not create file 'octave-workspace' in the current directory --- src/sage/interfaces/octave.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index 4923aec71da..ae1b87c55cb 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -146,6 +146,7 @@ import pexpect from sage.misc.verbose import verbose from sage.misc.instancedoc import instancedoc +from sage.misc.temporary_file import tmp_filename from sage.cpython.string import bytes_to_str @@ -156,13 +157,13 @@ class Octave(Expect): EXAMPLES:: sage: octave.eval("a = [ 1, 1, 2; 3, 5, 8; 13, 21, 33 ]") # optional - octave - 'a =\n\n 1 1 2\n 3 5 8\n 13 21 33\n\n' + 'a =\n\n 1 1 2\n 3 5 8\n 13 21 33\n' sage: octave.eval("b = [ 1; 3; 13]") # optional - octave - 'b =\n\n 1\n 3\n 13\n\n' - sage: octave.eval("c=a \\ b") # solves linear equation: a*c = b # optional - octave; random output - 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n\n' + 'b =\n\n 1\n 3\n 13\n' + sage: octave.eval(r"c=a \ b") # solves linear equation: a*c = b # optional - octave; random output + 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n' sage: octave.eval("c") # optional - octave; random output - 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n\n' + 'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n' TESTS: @@ -186,12 +187,14 @@ def __init__(self, maxread=None, script_subdirectory=None, logfile=None, command = os.getenv('SAGE_OCTAVE_COMMAND') or 'octave-cli' if server is None: server = os.getenv('SAGE_OCTAVE_SERVER') or None + # Use a temporary workspace file. + workspace_file = tmp_filename() Expect.__init__(self, name='octave', # We want the prompt sequence to be unique to avoid confusion with syntax error messages containing >>> prompt=r'octave\:\d+> ', # We don't want any pagination of output - command=command + " --no-line-editing --silent --eval 'PS2(PS1());more off' --persist", + command=command + f" --no-line-editing --silent --eval 'PS2(PS1());more off; octave_core_file_name (\"{workspace_file}\")' --persist", maxread=maxread, server=server, server_tmpdir=server_tmpdir, @@ -510,9 +513,9 @@ def solve_linear_system(self, A, b): sb = self.sage2octave_matrix_string(b) self.eval("a = " + sA ) self.eval("b = " + sb ) - soln = octave.eval("c = a \\ b") + soln = octave.eval(r"c = a \ b") soln = soln.replace("\n\n ","[") - soln = soln.replace("\n\n","]") + soln = soln.rstrip() + "]" soln = soln.replace("\n",",") sol = soln[3:] return eval(sol) From d5565cf9042197e380bcc4b8ac3fca73750b385b Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Tue, 13 Sep 2022 19:00:53 +0200 Subject: [PATCH 426/454] #34519 msolve: varieties over finite fields --- src/sage/rings/polynomial/msolve.py | 54 ++++++++++++++----- .../polynomial/multi_polynomial_ideal.py | 30 +++++++++-- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 88f1c6889bb..22b66385369 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -28,6 +28,7 @@ from sage.misc.converting_dict import KeyConvertingDict from sage.misc.sage_eval import sage_eval from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.rational_field import QQ from sage.rings.real_arb import RealBallField from sage.rings.real_double import RealDoubleField_class @@ -44,7 +45,27 @@ def _variety(ideal, ring, proof): TESTS:: - sage: K. = PolynomialRing(QQ, 2, order='lex') + sage: p = 536870909 + sage: R. = PolynomialRing(GF(p), 2, order='lex') + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + + sage: sorted(I.variety(algorithm="msolve", proof=False), key=str) # optional - msolve + [{x: 1, y: 1}, {x: 267525699, y: 473946006}] + + sage: K. = GF(p^2) + sage: sorted(I.variety(K, algorithm="msolve", proof=False), key=str) # optional - msolve + [{x: 1, y: 1}, + {x: 118750849*a + 194048031, y: 510295713*a + 18174854}, + {x: 267525699, y: 473946006}, + {x: 418120060*a + 75297182, y: 26575196*a + 44750050}] + + sage: R. = PolynomialRing(GF(2147483659), 2, order='lex') + sage: ideal([x, y]).variety(algorithm="msolve", proof=False) + Traceback (most recent call last): + ... + NotImplementedError: unsupported base field: Finite Field of size 2147483659 + + sage: R. = PolynomialRing(QQ, 2, order='lex') sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) sage: I.variety(algorithm='msolve', proof=False) # optional - msolve @@ -98,13 +119,13 @@ def _variety(ideal, ring, proof): ... ValueError: positive-dimensional ideal - sage: K. = PolynomialRing(RR, 2, order='lex') + sage: R. = PolynomialRing(RR, 2, order='lex') sage: Ideal(x, y).variety(algorithm='msolve', proof=False) Traceback (most recent call last): ... NotImplementedError: unsupported base field: Real Field with 53 bits of precision - sage: K. = PolynomialRing(QQ, 2, order='lex') + sage: R. = PolynomialRing(QQ, 2, order='lex') sage: Ideal(x, y).variety(ZZ, algorithm='msolve', proof=False) Traceback (most recent call last): ... @@ -119,11 +140,8 @@ def _variety(ideal, ring, proof): proof = sage.structure.proof.proof.get_flag(proof, "polynomial") if proof: raise ValueError("msolve relies on heuristics; please use proof=False") - # As of msolve 0.2.4, prime fields seem to be supported, by I cannot - # make sense of msolve's output in the positive characteristic case. - # if not (base is QQ or isinstance(base, FiniteField) and - # base.is_prime_field() and base.characteristic() < 2**31): - if base is not QQ: + if not (base is QQ or isinstance(base, FiniteField) and + base.is_prime_field() and base.characteristic() < 2**31): raise NotImplementedError(f"unsupported base field: {base}") if not ring.has_coerce_map_from(base): raise ValueError( @@ -175,24 +193,32 @@ def _variety(ideal, ring, proof): if parameterization: - def to_poly(p, upol=PolynomialRing(base, 't')): - assert len(p[1]) == p[0] + 1 - return upol(p[1]) + def to_poly(p, d=1, *, upol=PolynomialRing(base, 't')): + assert len(p[1]) == p[0] + 1 or p == [-1, [0]] + return upol(p[1])/d try: - [dim1, nvars, _, vars, _, [one, [elim, den, param]]] = data[1] + [char, nvars, deg, vars, _, [one, [elim, den, param]]] = data[1] except (IndexError, ValueError): raise NotImplementedError( f"unsupported msolve output format: {data}") - assert dim1.is_zero() + assert char == base.characteristic() assert one.is_one() assert len(vars) == nvars ringvars = out_ring.variable_names() assert sorted(vars[:len(ringvars)]) == sorted(ringvars) vars = [out_ring(name) for name in vars[:len(ringvars)]] elim = to_poly(elim) + # Criterion suggested by Mohab Safey El Din to avoid cases where there + # is no rational parameterization or where the one returned by msolve + # has a significant probability of being incorrect. + if deg >= char > 0 or 0 < char <= 2**17 and deg != elim.degree(): + raise NotImplementedError(f"characteristic {char} too small") den = to_poly(den) - param = [to_poly(f)/d for [f, d] in param] + # As of msolve 0.4.4, param is of the form [pol, denom] in char 0, but + # [pol] in char p > 0. My understanding is that both cases will + # eventually use the same format, so let's not be too picky. + param = [to_poly(*f) for f in param] elim_roots = elim.roots(ring, multiplicities=False) variety = [] for rt in elim_roots: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 10c0db501af..64cb6d9ea5a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2373,9 +2373,6 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: I.variety(ring=AA) [{y: 1, x: 1}, {y: 0.3611030805286474?, x: 2.769292354238632?}] - sage: I.variety(RBF, algorithm='msolve', proof=False) # optional - msolve - [{x: [2.76929235423863 +/- 2.08e-15], y: [0.361103080528647 +/- 4.53e-16]}, - {x: 1.000000000000000, y: 1.000000000000000}] and a total of four intersections:: @@ -2394,6 +2391,13 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True {y: 0.3611030805286474?, x: 2.769292354238632?}, {y: 1, x: 1}] + We can also use the external program msolve to compute the variety. + See :mod:`~sage.rings.polynomial.msolve` for more information. :: + + sage: I.variety(RBF, algorithm='msolve', proof=False) # optional - msolve + [{x: [2.76929235423863 +/- 2.08e-15], y: [0.361103080528647 +/- 4.53e-16]}, + {x: 1.000000000000000, y: 1.000000000000000}] + Computation over floating point numbers may compute only a partial solution, or even none at all. Notice that x values are missing from the following variety:: @@ -2437,6 +2441,26 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: v["y"] -7.464101615137755? + msolve also works over finite fields:: + + sage: R. = PolynomialRing(GF(536870909), 2, order='lex') + sage: I = Ideal([ x^2 - 1, y^2 - 1 ]) + sage: sorted(I.variety(algorithm='msolve', proof=False), key=str) # optional - msolve + [{x: 1, y: 1}, + {x: 1, y: 536870908}, + {x: 536870908, y: 1}, + {x: 536870908, y: 536870908}] + + but may fail in small characteristic, especially with ideals of high + degree with respect to the characteristic:: + + sage: R. = PolynomialRing(GF(3), 2, order='lex') + sage: I = Ideal([ x^2 - 1, y^2 - 1 ]) + sage: I.variety(algorithm='msolve', proof=False) # optional - msolve + Traceback (most recent call last): + ... + NotImplementedError: characteristic 3 too small + ALGORITHM: - With ``algorithm`` = ``"triangular_decomposition"`` (default), From 7b09d5d5aebdc741a6b6575283b5ec03590ce584 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 15 Sep 2022 13:48:30 +0200 Subject: [PATCH 427/454] #34519 msolve._variety() -> msolve.variety() --- src/sage/rings/polynomial/msolve.py | 2 +- src/sage/rings/polynomial/multi_polynomial_ideal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 22b66385369..1ae069572d0 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -36,7 +36,7 @@ from sage.rings.real_mpfi import RealIntervalField_class, RealIntervalField -def _variety(ideal, ring, proof): +def variety(ideal, ring, *, proof=True): r""" Compute the variety of a zero-dimensional ideal using msolve. diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 64cb6d9ea5a..3a49865fd80 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2477,7 +2477,7 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True return self._variety_triangular_decomposition(ring) elif algorithm == "msolve": from . import msolve - return msolve._variety(self, ring, proof) + return msolve.variety(self, ring, proof=proof) else: raise ValueError(f"unknown algorithm {algorithm!r}") From 7db567860e57be5a4bb1ba51eb5fa92dd7e4d07a Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 15 Sep 2022 19:00:47 +0200 Subject: [PATCH 428/454] #34519 _run_msolve() extract reusable code from msolve.variety() --- src/sage/rings/polynomial/msolve.py | 73 +++++++++++++++++------------ 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 1ae069572d0..3ed1dd6e29e 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -35,6 +35,40 @@ from sage.rings.real_mpfr import RealField_class from sage.rings.real_mpfi import RealIntervalField_class, RealIntervalField +def _run_msolve(ideal, options): + r""" + Internal utility function + """ + + base = ideal.base_ring() + if not (base is QQ or isinstance(base, FiniteField) and + base.is_prime_field() and base.characteristic() < 2**31): + raise NotImplementedError(f"unsupported base field: {base}") + + # Run msolve + + msolve().require() + + drlpolring = ideal.ring().change_ring(order='degrevlex') + polys = ideal.change_ring(drlpolring).gens() + msolve_in = tempfile.NamedTemporaryFile(mode='w', + encoding='ascii', delete=False) + command = ["msolve", "-f", msolve_in.name] + options + try: + print(",".join(drlpolring.variable_names()), file=msolve_in) + print(base.characteristic(), file=msolve_in) + print(*(pol._repr_().replace(" ", "") for pol in polys), + sep=',\n', file=msolve_in) + msolve_in.close() + msolve_out = subprocess.run(command, capture_output=True, text=True) + finally: + os.unlink(msolve_in.name) + msolve_out.check_returncode() + + return msolve_out.stdout + +def groebner_basis_degrevlex(ideal, *, proof=True): + pass def variety(ideal, ring, *, proof=True): r""" @@ -132,52 +166,31 @@ def variety(ideal, ring, *, proof=True): ValueError: no coercion from base field Rational Field to output ring Integer Ring """ - # Normalize and check input + proof = sage.structure.proof.proof.get_flag(proof, "polynomial") + if proof: + raise ValueError("msolve relies on heuristics; please use proof=False") base = ideal.base_ring() if ring is None: ring = base - proof = sage.structure.proof.proof.get_flag(proof, "polynomial") - if proof: - raise ValueError("msolve relies on heuristics; please use proof=False") - if not (base is QQ or isinstance(base, FiniteField) and - base.is_prime_field() and base.characteristic() < 2**31): - raise NotImplementedError(f"unsupported base field: {base}") if not ring.has_coerce_map_from(base): raise ValueError( f"no coercion from base field {base} to output ring {ring}") - # Run msolve - - msolve().require() - - drlpolring = ideal.ring().change_ring(order='degrevlex') - polys = ideal.change_ring(drlpolring).gens() - msolve_in = tempfile.NamedTemporaryFile(mode='w', - encoding='ascii', delete=False) - command = ["msolve", "-f", msolve_in.name] if isinstance(ring, (RealIntervalField_class, RealBallField, RealField_class, RealDoubleField_class)): parameterization = False - command += ["-p", str(ring.precision())] + options = ["-p", str(ring.precision())] else: parameterization = True - command += ["-P", "1"] - try: - print(",".join(drlpolring.variable_names()), file=msolve_in) - print(base.characteristic(), file=msolve_in) - print(*(pol._repr_().replace(" ", "") for pol in polys), - sep=',\n', file=msolve_in) - msolve_in.close() - msolve_out = subprocess.run(command, capture_output=True, text=True) - finally: - os.unlink(msolve_in.name) - msolve_out.check_returncode() + options = ["-P", "1"] + + msolve_out = _run_msolve(ideal, options) # Interpret output try: - data = sage_eval(msolve_out.stdout[:-2]) + data = sage_eval(msolve_out[:-2]) except SyntaxError: raise NotImplementedError(f"unsupported msolve output format: {data}") @@ -202,7 +215,7 @@ def to_poly(p, d=1, *, upol=PolynomialRing(base, 't')): except (IndexError, ValueError): raise NotImplementedError( f"unsupported msolve output format: {data}") - assert char == base.characteristic() + assert char == ideal.base_ring().characteristic() assert one.is_one() assert len(vars) == nvars ringvars = out_ring.variable_names() From e43bc6ec7c5c9875411abaa054fba0aa6d4c6d1e Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Thu, 15 Sep 2022 19:08:44 +0200 Subject: [PATCH 429/454] =?UTF-8?q?#34519=20Gr=C3=B6bner=20bases=20using?= =?UTF-8?q?=20msolve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/rings/polynomial/msolve.py | 48 ++++++++++++++++++- .../polynomial/multi_polynomial_ideal.py | 31 +++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 3ed1dd6e29e..2dafacbba66 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -34,6 +34,7 @@ from sage.rings.real_double import RealDoubleField_class from sage.rings.real_mpfr import RealField_class from sage.rings.real_mpfi import RealIntervalField_class, RealIntervalField +from sage.structure.sequence import Sequence def _run_msolve(ideal, options): r""" @@ -67,8 +68,51 @@ def _run_msolve(ideal, options): return msolve_out.stdout -def groebner_basis_degrevlex(ideal, *, proof=True): - pass +def groebner_basis_degrevlex(ideal): + r""" + Compute a degrevlex Gröbner basis using msolve + + EXAMPLES:: + + sage: from sage.rings.polynomial.msolve import groebner_basis_degrevlex + + sage: R. = PolynomialRing(GF(101), 3, order='lex') + sage: I = sage.rings.ideal.Katsura(R,3) + sage: gb = groebner_basis_degrevlex(I); gb # optional - msolve + [a + 2*b + 2*c - 1, b*c - 19*c^2 + 10*b + 40*c, + b^2 - 41*c^2 + 20*b - 20*c, c^3 + 28*c^2 - 37*b + 13*c] + sage: gb.universe() is R # optional - msolve + False + sage: gb.universe().term_order() # optional - msolve + Degree reverse lexicographic term order + sage: ideal(gb).transformed_basis(other_ring=R) # optional - msolve + [c^4 + 38*c^3 - 6*c^2 - 6*c, 30*c^3 + 32*c^2 + b - 14*c, + a + 2*b + 2*c - 1] + + TESTS:: + + sage: R. = PolynomialRing(GF(536870909), 2) + sage: I = Ideal([ foo^2 - 1, bar^2 - 1 ]) + sage: I.groebner_basis(algorithm='msolve') # optional - msolve + [bar^2 - 1, foo^2 - 1] + + sage: R. = PolynomialRing(QQ, 2) + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: I.groebner_basis(algorithm='msolve') # optional - msolve + Traceback (most recent call last): + ... + NotImplementedError: unsupported base field: Rational Field + """ + + base = ideal.base_ring() + if not (isinstance(base, FiniteField) and base.is_prime_field() and + base.characteristic() < 2**31): + raise NotImplementedError(f"unsupported base field: {base}") + + drlpolring = ideal.ring().change_ring(order='degrevlex') + msolve_out = _run_msolve(ideal, ["-g", "2"]) + gbasis = sage_eval(msolve_out[:-2], locals=drlpolring.gens_dict()) + return Sequence(gbasis) def variety(ideal, ring, *, proof=True): r""" diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 3a49865fd80..caa74226fd3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -4012,6 +4012,10 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal 'macaulay2:mgb' Macaulay2's ``GroebnerBasis`` command with the strategy "MGB" (if available) + 'msolve' + `msolve `_ (if available, degrevlex order, + prime fields) + 'magma:GroebnerBasis' Magma's ``Groebnerbasis`` command (if available) @@ -4135,6 +4139,15 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] + Over prime fields of small characteristic, we can also use + `msolve `_ (if available in the system program + search path):: + + sage: R. = PolynomialRing(GF(101), 3) + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching + sage: I.groebner_basis('msolve') # optional - msolve + [a + 2*b + 2*c - 1, b*c - 19*c^2 + 10*b + 40*c, b^2 - 41*c^2 + 20*b - 20*c, c^3 + 28*c^2 - 37*b + 13*c] + :: sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching @@ -4353,6 +4366,15 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] + + msolve currently supports the degrevlex order only:: + + sage: R. = PolynomialRing(GF(101), 3, order='lex') + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching + sage: I.groebner_basis('msolve') # optional - msolve + Traceback (most recent call last): + ... + NotImplementedError: msolve only supports the degrevlex order (use transformed_basis()) """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -4438,7 +4460,14 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal elif algorithm == 'giac:gbasis': from sage.libs.giac import groebner_basis as groebner_basis_libgiac gb = groebner_basis_libgiac(self, prot=prot, *args, **kwds) - + elif algorithm == 'msolve': + if self.ring().term_order() != 'degrevlex': + raise NotImplementedError("msolve only supports the degrevlex order " + "(use transformed_basis())") + if not (deg_bound is mult_bound is None) or prot: + raise NotImplementedError("unsupported options for msolve") + from . import msolve + return msolve.groebner_basis_degrevlex(self, *args, **kwds) else: raise NameError("Algorithm '%s' unknown."%algorithm) From 774a3de6e00dd71461bb0500c9effa26bb1d7a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 16 Sep 2022 11:25:16 +0200 Subject: [PATCH 430/454] enhance detail in free dendriform algebras --- src/sage/combinat/free_dendriform_algebra.py | 27 ++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index 04b8cf1208e..1bdcd1dbe31 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -12,7 +12,7 @@ # 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.categories.hopf_algebras import HopfAlgebras @@ -30,6 +30,7 @@ from sage.misc.cachefunc import cached_method from sage.sets.family import Family from sage.structure.coerce_exceptions import CoercionException +from sage.rings.infinity import Infinity class FreeDendriformAlgebra(CombinatorialFreeModule): @@ -115,6 +116,16 @@ class FreeDendriformAlgebra(CombinatorialFreeModule): sage: w * w * w B[[., [., [., .]]]] + B[[., [[., .], .]]] + B[[[., .], [., .]]] + B[[[., [., .]], .]] + B[[[[., .], .], .]] + The set `E` can be infinite:: + + sage: F = algebras.FreeDendriform(QQ, ZZ) + sage: w = F.gen(1); w + B[1[., .]] + sage: x = F.gen(2); x + B[-1[., .]] + sage: w*x + B[-1[1[., .], .]] + B[1[., -1[., .]]] + REFERENCES: - [LR1998]_ @@ -193,14 +204,20 @@ def _repr_(self): Free Dendriform algebra on one generator ['@'] over Rational Field """ n = self.algebra_generators().cardinality() - if n == 1: + finite = bool(n < Infinity) + if not finite: + gen = "generators indexed by" + elif n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Free Dendriform algebra on {} {} over {}" - try: - return s.format(gen, self._alphabet.list(), self.base_ring()) - except NotImplementedError: + if finite: + try: + return s.format(gen, self._alphabet.list(), self.base_ring()) + except NotImplementedError: + return s.format(gen, self._alphabet, self.base_ring()) + else: return s.format(gen, self._alphabet, self.base_ring()) def gen(self, i): From b5adcc93998366100ad6e9273fe8203b8dbb20d4 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 16 Sep 2022 11:50:48 +0200 Subject: [PATCH 431/454] #34519 update doc on msolve --- .../polynomial_rings_multivar.rst | 2 ++ src/sage/features/msolve.py | 8 ++++--- src/sage/rings/polynomial/msolve.py | 22 ++++++++++++++----- .../polynomial/multi_polynomial_ideal.py | 4 ++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst index 46388f58f25..6147929ccf2 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst @@ -36,6 +36,8 @@ are implemented using the PolyBoRi library (cf. :mod:`sage.rings.polynomial.pbor sage/rings/polynomial/multi_polynomial_libsingular sage/rings/polynomial/multi_polynomial_ideal_libsingular + sage/rings/polynomial/msolve + sage/rings/polynomial/polydict sage/rings/polynomial/hilbert diff --git a/src/sage/features/msolve.py b/src/sage/features/msolve.py index fa6679c2014..a7c7d5441b7 100644 --- a/src/sage/features/msolve.py +++ b/src/sage/features/msolve.py @@ -2,9 +2,11 @@ r""" Feature for testing the presence of msolve -`msolve `_ is a multivariate polynomial system solver -developed mainly by Jérémy Berthomieu (Sorbonne University), Christian Eder -(TU Kaiserslautern), and Mohab Safey El Din (Sorbonne University). +`msolve `_ is a multivariate polynomial system solver. + +.. SEEALSO:: + + - :mod:`sage.rings.polynomial.msolve` """ import subprocess diff --git a/src/sage/rings/polynomial/msolve.py b/src/sage/rings/polynomial/msolve.py index 2dafacbba66..65ae859f751 100644 --- a/src/sage/rings/polynomial/msolve.py +++ b/src/sage/rings/polynomial/msolve.py @@ -3,19 +3,17 @@ Solution of polynomial systems using msolve `msolve `_ is a multivariate polynomial system solver -developed mainly by Jérémy Berthomieu (Sorbonne University), Christian Eder -(TU Kaiserslautern), and Mohab Safey El Din (Sorbonne University). +based on Gröbner bases. This module provide implementations of some operations on polynomial ideals -based on msolve. Currently the only supported operation is the computation of -the variety of zero-dimensional ideal over the rationals. +based on msolve. Note that msolve must be installed separately. .. SEEALSO:: -- :mod:`sage.features.msolve` -- :mod:`sage.rings.polynomial.multi_polynomial_ideal` + - :mod:`sage.features.msolve` + - :mod:`sage.rings.polynomial.multi_polynomial_ideal` """ import os @@ -121,6 +119,18 @@ def variety(ideal, ring, *, proof=True): Part of the initial implementation was loosely based on the example interfaces available as part of msolve, with the authors' permission. + EXAMPLES:: + + sage: from sage.rings.polynomial.msolve import variety + sage: p = 536870909 + sage: R. = PolynomialRing(GF(p), 2, order='lex') + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: sorted(variety(I, GF(p^2), proof=False), key=str) # optional - msolve + [{x: 1, y: 1}, + {x: 254228855*z2 + 114981228, y: 232449571*z2 + 402714189}, + {x: 267525699, y: 473946006}, + {x: 282642054*z2 + 154363985, y: 304421338*z2 + 197081624}] + TESTS:: sage: p = 536870909 diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index caa74226fd3..243bb9e557b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -4285,8 +4285,8 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal ALGORITHM: - Uses Singular, Magma (if available), Macaulay2 (if available), - Giac (if available), or a toy implementation. + Uses Singular, one of the other systems listed above (if available), + or a toy implementation. TESTS: From 17ea8dd39a33b231c88b7ed59dc7b259b28baf34 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Fri, 16 Sep 2022 20:06:46 +0200 Subject: [PATCH 432/454] #34519 update doctest --- src/sage/misc/cachefunc.pyx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index c0691cff9c5..9fa967ce737 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -849,8 +849,6 @@ cdef class CachedFunction(): sage: print(sage_getdoc(I.groebner_basis)) # indirect doctest Return the reduced Groebner basis of this ideal. ... - ALGORITHM: Uses Singular, Magma (if available), Macaulay2 (if - available), Giac (if available), or a toy implementation. Test that :trac:`15184` is fixed:: From 59ac11a9375feaada4486b63ed9900651e0a6fdb Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sat, 17 Sep 2022 11:36:29 +0200 Subject: [PATCH 433/454] Updated SageMath version to 9.7.rc2 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 24 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index d7b2439a788..49b22d68dc7 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.7.rc1", - "version": "9.7.rc1", + "title": "sagemath/sage: 9.7.rc2", + "version": "9.7.rc2", "upload_type": "software", - "publication_date": "2022-09-07", + "publication_date": "2022-09-17", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.7.rc1", + "identifier": "https://github.com/sagemath/sage/tree/9.7.rc2", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index affcafdf359..b63eff86bef 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.7.rc1, Release Date: 2022-09-07 +SageMath version 9.7.rc2, Release Date: 2022-09-17 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ec99cd206cc..48189ef845c 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=fae7f3b6b614f4317d549df685c71f3d73266f53 -md5=b5f77a97a8ef2f292a0413af8fce6b4b -cksum=1358596786 +sha1=70b1e96e1bd71efd90b1d1345b9c8d4d12de432b +md5=bf9529bff5e1c938c51668f0687ae5a0 +cksum=1982975454 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index c1a4e286897..37da2e9a6db 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -d049cd8b3d2b86dc91acfca862daee4d105099c7 +4f225f4cc66a7d515ea6598ebe9b2ff48e114a15 diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 806746abbe4..496914816ad 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.7rc1 +sage-conf ~= 9.7rc2 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 9a762283dae..56ba1015d98 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.7rc1 +sage-docbuild ~= 9.7rc2 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 72ddc44f5ef..60e2ed2c881 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.7rc1 +sage-setup ~= 9.7rc2 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 0c11aa3cd0d..47283b61993 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.7rc1 +sage-sws2rst ~= 9.7rc2 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 17a9b7fbd9e..d009d8b4834 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.7rc1 +sagelib ~= 9.7rc2 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index faa6593b049..984fcd77cb0 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.7rc1 +sagemath-categories ~= 9.7rc2 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 9dd9d149e08..6de5c339724 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.7rc1 +sagemath-environment ~= 9.7rc2 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 7d5feaa7bb8..614677a4a19 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.7rc1 +sagemath-objects ~= 9.7rc2 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/src/VERSION.txt b/src/VERSION.txt index 37d25ab1334..13ac8fa02e9 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.7.rc1 +9.7.rc2 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index a18cc712d08..6f4e41fabfc 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.7.rc1' -SAGE_RELEASE_DATE='2022-09-07' -SAGE_VERSION_BANNER='SageMath version 9.7.rc1, Release Date: 2022-09-07' +SAGE_VERSION='9.7.rc2' +SAGE_RELEASE_DATE='2022-09-17' +SAGE_VERSION_BANNER='SageMath version 9.7.rc2, Release Date: 2022-09-17' diff --git a/src/sage/version.py b/src/sage/version.py index 4862a5603a6..8e72819281d 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.7.rc1' -date = '2022-09-07' -banner = 'SageMath version 9.7.rc1, Release Date: 2022-09-07' +version = '9.7.rc2' +date = '2022-09-17' +banner = 'SageMath version 9.7.rc2, Release Date: 2022-09-17' From debca31b188013b645644f70905144863c5c56c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 29 Jul 2022 11:36:53 +0530 Subject: [PATCH 434/454] Update docker build The docker images are using Ubuntu impish. We should instead build against the latest LTS version jammy. Also, the README on Docker Hub is quite outdated. Finally, we drop the CircleCI setup here. To my knowledge nobody is using it and I it's likely broken by now anyway. --- .circleci/before-script.sh | 26 ----------- .circleci/config.yml | 89 -------------------------------------- docker/Dockerfile | 32 +++++++++++++- docker/README.md | 12 ++--- 4 files changed, 36 insertions(+), 123 deletions(-) delete mode 100644 .circleci/before-script.sh delete mode 100644 .circleci/config.yml diff --git a/.circleci/before-script.sh b/.circleci/before-script.sh deleted file mode 100644 index 3b9c7949cd4..00000000000 --- a/.circleci/before-script.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# Source this script during a CI run to set environment variables and print -# some informational messages about the system we are running on. - -# **************************************************************************** -# Copyright (C) 2018 Julian Rüth -# -# 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/ -# **************************************************************************** - -# CircleCI has no mechanism to hide secret variables. -# Therefore we roll our own to protect $SECRET_* variables. -. .ci/protect-secrets.sh -# Collect debug infos about the system we are running on -.ci/describe-system.sh -# Set MAKEFLAGS and SAGE_NUM_THREADS -. .ci/setup-make-parallelity.sh - -# Set DOCKER_TAG according to the current branch/tag -export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} -. .ci/update-env.sh diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 2b425783ce6..00000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,89 +0,0 @@ -# This file configures automatic builds of Sage on [CircleCI](https://circleci.com). -# To make the build time not too excessive, we seed the build cache with -# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much, -# as the full build will often exceed CircleCI's limits for open source -# projcets (five hours on 2 vCPUs as of early 2018.) -# You might want to try to build locally or with GitLab CI, see -# `.gitlab-ci.yml` for more options. - -# As of early 2018, a run on CircleCI takes usually about 25 minutes. Most of -# the time is spent pulling/pushing from/to Docker Hub and copying files -# locally during the docker build. We could probably save five minutes by not -# building and testing the sagemath-dev image for most branches. - -version: 2 -jobs: - build-test-release: &build-test-release - docker: - - image: docker:latest - environment: - DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop - steps: - - run: apk --update add git openssh - - checkout - - setup_remote_docker: - version: 18.05.0-ce - - run: &build - # The docker commands sometimes take a while to produce output - no_output_timeout: 30m - name: build - command: | - . .circleci/before-script.sh - .ci/build-docker.sh - - run: &test-dev - name: test-dev - command: | - . .circleci/before-script.sh - .ci/test-dev.sh $DOCKER_IMAGE_DEV - - run: &test-cli - name: test-cli - command: | - . .circleci/before-script.sh - .ci/test-cli.sh $DOCKER_IMAGE_CLI - - run: &test-jupyter - name: test-jupyter - command: | - . .circleci/before-script.sh - .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost - - run: &release - # The docker commands sometimes take a while to produce output - no_output_timeout: 30m - name: release - command: | - . .circleci/before-script.sh - # Push docker images to dockerhub if a dockerhub user has been configured - .ci/push-dockerhub.sh sagemath-dev - .ci/push-dockerhub.sh sagemath - build-from-latest-test-release: - <<: *build-test-release - build-from-clean-test-release: - <<: *build-test-release - environment: - ARTIFACT_BASE: source-clean - -workflows: - version: 2 - build-branch-from-clean: - jobs: - - build-from-clean-test-release: - filters: - branches: - only: - - master - - develop - build-tag-from-clean: - jobs: - - build-from-clean-test-release: - filters: - branches: - ignore: /.*/ - tags: - only: /.*/ - build-branch-from-latest: - jobs: - - build-from-latest-test-release: - filters: - branches: - ignore: - - master - - develop diff --git a/docker/Dockerfile b/docker/Dockerfile index 269b4667ed9..bf14547cd38 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,12 +41,40 @@ # local branch for this to work. # ################################################################################ +################################################################################ +# HOWTO use this file to manually create new images for Docker Hub # +################################################################################ +# Make sure you have the tag checked out that you are trying to build. Then # +# from the root directory of the sage repository run: # +# # +# bash # open a subshell so 'set -e' does not close your shell on error # +# export ARTIFACT_BASE=source-clean # +# export DOCKER_TAG=9.6 # replace with the version you are building for # +# export CI_COMMIT_SHA=`git rev-parse --short HEAD` # +# export CPUTHREADS=8 # +# export RAMTHREADS=8 # +# export RAMTHREADS_DOCBUILD=8 # +# . .ci/update-env.sh # +# . .ci/setup-make-parallelity.sh # +# .ci/build-docker.sh # +# # +# Now you can push the relevant images to the Docker Hub: # +# # +# docker push sagemath/sagemath:$DOCKER_TAG # +# docker tag sagemath/sagemath:$DOCKER_TAG sagemath/sagemath:latest # +# docker push sagemath/sagemath:latest # +# docker push sagemath/sagemath-dev:$DOCKER_TAG # +# docker tag sagemath/sagemath-dev:$DOCKER_TAG sagemath/sagemath-dev:latest # +# docker push sagemath/sagemath-dev:latest # +################################################################################ + + ARG ARTIFACT_BASE=source-clean ################################################################################ # Image containing the run-time dependencies for Sage # ################################################################################ -FROM ubuntu:impish as run-time-dependencies +FROM ubuntu:jammy as run-time-dependencies LABEL maintainer="Erik M. Bray , Julian Rüth " # Set sane defaults for common environment variables. ENV LC_ALL C.UTF-8 @@ -82,7 +110,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ - && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python3 libssl-dev git rdfind \ && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* diff --git a/docker/README.md b/docker/README.md index e77008fc886..b1a674e2482 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,8 +4,8 @@ * `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) * `x.x` — all stable releases of Sage are tagged with their version number. -* `x.x.{beta,rc}x` - betas and release candidates of Sage as [tagged in our git repository](https://github.com/sagemath/sage/tags). -* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/develop.svg)](https://github.com/sagemath/sage/commits/develop) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) +* `x.x.{beta,rc}x` - betas and release candidates of Sage as [tagged in our git repository](https://github.com/sagemath/sage/tags); since docker images are built and uploaded manually at the moment, there are often no recent betas here anymore. +* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/develop.svg)](https://github.com/sagemath/sage/commits/develop) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop); again, there is often no recent develop built here anymore. * `-py3` - until Sage 9.1, we provided Python 2 builds (with no suffix) and Python 3 builds (with the `-py3` suffix). From Sage 9.2.beta0 on, all images we provide are based on Python 3 and the `-py3` suffix survives only for historical reasons: with or without it, you get Python 3. # What is SageMath @@ -26,7 +26,7 @@ There are several flavours of this image. ``` docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter ``` -* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly (currently, this is broken, see [#34241](https://trac.sagemath.org/ticket/34241#comment).) This version is probably only relevant for Sage developers. Run this image with: ``` docker run -it sagemath/sagemath-dev:develop ``` @@ -41,11 +41,11 @@ Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=sagemath/sagema # How these images get updated -Every push to our [github repository](https://github.com/sagemath/sage) triggers a "build" on our [Docker Hub](https://hub.docker.com) repositories. This build is mostly disabled by the `hooks/` and only updates the `README.md`. +Currently, these images are updated manually after every release. Instructions for this are at the top of `docker/Dockerfile`. -Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) triggers a pipeline in GitLab CI which pushes the actual images to Docker Hub. +Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) used to trigger a pipeline in GitLab CI which pushed the actual images to Docker Hub. These pipelines are not running anymore since nobody is maintaining them or providing the CI resources for it. -Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup CircleCI or GitLab CI for your own fork of the SageMath repository. +Have a look at `.gitlab-ci.yml` if you want to setup CircleCI or GitLab CI for your own fork of the SageMath repository. # Report bugs and issues From 417ca3104a5d3c729dc6ba223c06356b88745308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 29 Jul 2022 11:42:52 +0530 Subject: [PATCH 435/454] Fix grammar --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index b1a674e2482..05aa89ebae0 100644 --- a/docker/README.md +++ b/docker/README.md @@ -5,7 +5,7 @@ * `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) * `x.x` — all stable releases of Sage are tagged with their version number. * `x.x.{beta,rc}x` - betas and release candidates of Sage as [tagged in our git repository](https://github.com/sagemath/sage/tags); since docker images are built and uploaded manually at the moment, there are often no recent betas here anymore. -* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/develop.svg)](https://github.com/sagemath/sage/commits/develop) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop); again, there is often no recent develop built here anymore. +* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/develop.svg)](https://github.com/sagemath/sage/commits/develop) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop); again, there is often no recent develop build here anymore. * `-py3` - until Sage 9.1, we provided Python 2 builds (with no suffix) and Python 3 builds (with the `-py3` suffix). From Sage 9.2.beta0 on, all images we provide are based on Python 3 and the `-py3` suffix survives only for historical reasons: with or without it, you get Python 3. # What is SageMath From 27910f11cd88e65a355919039cb61d1af8dd3b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 29 Jul 2022 11:43:34 +0530 Subject: [PATCH 436/454] Link to trac ticket correctly in README --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index 05aa89ebae0..c3145f8e90e 100644 --- a/docker/README.md +++ b/docker/README.md @@ -26,7 +26,7 @@ There are several flavours of this image. ``` docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter ``` -* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly (currently, this is broken, see [#34241](https://trac.sagemath.org/ticket/34241#comment).) This version is probably only relevant for Sage developers. Run this image with: +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly (currently, this is broken, see [#34241](https://trac.sagemath.org/ticket/34241).) This version is probably only relevant for Sage developers. Run this image with: ``` docker run -it sagemath/sagemath-dev:develop ``` From f1f5a0f8020ee446f76927cf568eb35655edfb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 29 Jul 2022 11:45:18 +0530 Subject: [PATCH 437/454] Let volunteers know that they are welcome to help with docker --- docker/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/README.md b/docker/README.md index c3145f8e90e..eb0f1ba344d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -43,9 +43,9 @@ Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=sagemath/sagema Currently, these images are updated manually after every release. Instructions for this are at the top of `docker/Dockerfile`. -Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) used to trigger a pipeline in GitLab CI which pushed the actual images to Docker Hub. These pipelines are not running anymore since nobody is maintaining them or providing the CI resources for it. +Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) used to trigger a pipeline in GitLab CI which pushed the actual images to Docker Hub. These pipelines are not running anymore since nobody is maintaining them or providing the CI resources for it. (Please reach out to [sage-devel](https://groups.google.com/forum/#!forum/sage-devel) if you want to help!) -Have a look at `.gitlab-ci.yml` if you want to setup CircleCI or GitLab CI for your own fork of the SageMath repository. +Have a look at `.gitlab-ci.yml` if you want to setup GitLab CI for your own fork of the SageMath repository. # Report bugs and issues From 77cbf42a5eafaadd08caa449155f6b429e3078c0 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 17 Sep 2022 08:48:35 -0500 Subject: [PATCH 438/454] PEP8 compliance --- .../polynomial/infinite_polynomial_element.py | 249 +++++++++--------- 1 file changed, 128 insertions(+), 121 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 3a88f14b727..ac21b71553f 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -165,7 +165,7 @@ def InfinitePolynomial(A, p): """ from sage.structure.element import parent - if hasattr(A,'_P'): + if hasattr(A, '_P'): if parent(p) is A._P or (A._P.base_ring().has_coerce_map_from(parent(p))): return InfinitePolynomial_dense(A, p) # MPolynomialRing_polydict is crab. So, in that case, use sage_eval @@ -173,14 +173,14 @@ def InfinitePolynomial(A, p): if isinstance(A._P, MPolynomialRing_polydict): from sage.rings.polynomial.infinite_polynomial_ring import GenDictWithBasering from sage.misc.sage_eval import sage_eval - p = sage_eval(repr(p), GenDictWithBasering(A._P,A._P.gens_dict())) + p = sage_eval(repr(p), GenDictWithBasering(A._P, A._P.gens_dict())) return InfinitePolynomial_dense(A, p) else: # Now there remains to fight the oddities and bugs of libsingular. PP = p.parent() if A._P.has_coerce_map_from(PP): - if A._P.ngens() == PP.ngens(): # coercion is sometimes by position! - f = PP.hom(PP.variable_names(),A._P) + if A._P.ngens() == PP.ngens(): # coercion is sometimes by position! + f = PP.hom(PP.variable_names(), A._P) try: return InfinitePolynomial_dense(A, f(p)) except (ValueError, TypeError): @@ -205,6 +205,7 @@ def InfinitePolynomial(A, p): return InfinitePolynomial_dense(A, sage_eval(repr(p), GenDictWithBasering(A._P, A._P.gens_dict()))) return InfinitePolynomial_sparse(A, p) + class InfinitePolynomial_sparse(RingElement): """ Element of a sparse Polynomial Ring with a Countably Infinite Number of Variables. @@ -317,8 +318,8 @@ def __call__(self, *args, **kwargs): x_100 + x_0 """ - #Replace any InfinitePolynomials by their underlying polynomials - if hasattr(self._p,'variables'): + # Replace any InfinitePolynomials by their underlying polynomials + if hasattr(self._p, 'variables'): V = [str(x) for x in self._p.variables()] else: V = [] @@ -327,19 +328,19 @@ def __call__(self, *args, **kwargs): if isinstance(value, InfinitePolynomial_sparse): kwargs[kw] = value._p V.append(kw) - if hasattr(value._p,'variables'): + if hasattr(value._p, 'variables'): V.extend([str(x) for x in value._p.variables()]) args = list(args) for i, arg in enumerate(args): if isinstance(arg, InfinitePolynomial_sparse): args[i] = arg._p - if hasattr(arg._p,'variables'): + if hasattr(arg._p, 'variables'): V.extend([str(x) for x in arg._p.variables()]) - V=list(set(V)) + V = list(set(V)) V.sort(key=self.parent().varname_key, reverse=True) if V: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(),V,order=self.parent()._order) + R = PolynomialRing(self._p.base_ring(), V, order=self.parent()._order) else: return self res = R(self._p)(*args, **kwargs) @@ -422,14 +423,15 @@ def __getattr__(self, s): True """ - if s=='__members__': + if s == '__members__': return dir(self._p) - if s=='__methods__': - return [X for X in dir(self._p) if hasattr(self._p,X) and ('method' in str(type(getattr(self._p,X))))] + if s == '__methods__': + return [X for X in dir(self._p) if hasattr(self._p, X) + and ('method' in str(type(getattr(self._p, X))))] try: - return getattr(self._p,s) + return getattr(self._p, s) except AttributeError: - raise AttributeError('%s has no attribute %s'%(self.__class__, s)) + raise AttributeError('%s has no attribute %s' % (self.__class__, s)) def ring(self): """ @@ -566,19 +568,19 @@ def _add_(self, x): """ # One may need a new parent for self._p and x._p try: - return InfinitePolynomial_sparse(self.parent(),self._p+x._p) + return InfinitePolynomial_sparse(self.parent(), self._p + x._p) except Exception: pass - ## We can now assume that self._p and x._p actually are polynomials, - ## hence, their parent is not simply the underlying ring. + # We can now assume that self._p and x._p actually are polynomials, + # hence, their parent is not simply the underlying ring. VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) VarList.sort(key=self.parent().varname_key, reverse=True) if VarList: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(),VarList,order=self.parent()._order) + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) else: R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(),R(self._p) + R(x._p)) + return InfinitePolynomial_sparse(self.parent(), R(self._p) + R(x._p)) def _mul_(self, x): """ @@ -590,19 +592,19 @@ def _mul_(self, x): """ try: - return InfinitePolynomial_sparse(self.parent(),self._p*x._p) + return InfinitePolynomial_sparse(self.parent(), self._p * x._p) except Exception: pass - ## We can now assume that self._p and x._p actually are polynomials, - ## hence, their parent is not just the underlying ring. + # We can now assume that self._p and x._p actually are polynomials, + # hence, their parent is not just the underlying ring. VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key,reverse=True) + VarList.sort(key=self.parent().varname_key, reverse=True) if VarList: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(),VarList,order=self.parent()._order) + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) else: R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(),R(self._p) * R(x._p)) + return InfinitePolynomial_sparse(self.parent(), R(self._p) * R(x._p)) def gcd(self, x): """ @@ -620,7 +622,7 @@ def gcd(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_sparse(self.parent(),self._p.gcd(x._p)) + return InfinitePolynomial_sparse(self.parent(), self._p.gcd(x._p)) def _rmul_(self, left): """ @@ -631,7 +633,7 @@ def _rmul_(self, left): 4 """ - return InfinitePolynomial_sparse(self.parent(),left*self._p) + return InfinitePolynomial_sparse(self.parent(), left * self._p) def _lmul_(self, right): """ @@ -642,7 +644,7 @@ def _lmul_(self, right): 4*alpha_3 """ - return InfinitePolynomial_sparse(self.parent(),self._p*right) + return InfinitePolynomial_sparse(self.parent(), self._p * right) def _div_(self, x): r""" @@ -686,9 +688,9 @@ def _div_(self, x): """ if not x.variables(): p = self.base_ring()(x._p) - divisor = self.base_ring().one()/p # use induction + divisor = self.base_ring().one() / p # use induction OUTP = self.parent().tensor_with_ring(divisor.base_ring()) - return OUTP(self)*OUTP(divisor) + return OUTP(self) * OUTP(divisor) else: from sage.rings.fraction_field_element import FractionFieldElement field = self.parent().fraction_field() @@ -704,19 +706,19 @@ def _floordiv_(self, x): 1 """ try: - return InfinitePolynomial_sparse(self.parent(),self._p//x._p) + return InfinitePolynomial_sparse(self.parent(), self._p // x._p) except Exception: pass - ## We can now assume that self._p and x._p actually are polynomials, - ## hence, their parent is not just the underlying ring. + # We can now assume that self._p and x._p actually are polynomials, + # hence, their parent is not just the underlying ring. VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key,reverse=True) + VarList.sort(key=self.parent().varname_key, reverse=True) if VarList: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(),VarList,order=self.parent()._order) + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) else: R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(),R(self._p) // R(x._p)) + return InfinitePolynomial_sparse(self.parent(), R(self._p) // R(x._p)) def _sub_(self, x): """ @@ -728,19 +730,19 @@ def _sub_(self, x): """ try: - return InfinitePolynomial_sparse(self.parent(),self._p-x._p) + return InfinitePolynomial_sparse(self.parent(), self._p - x._p) except Exception: pass - ## We can now assume that self._p and x._p actually are polynomials, - ## hence, their parent is not just the underlying ring. + # We can now assume that self._p and x._p actually are polynomials, + # hence, their parent is not just the underlying ring. VarList = list(set(self._p.parent().variable_names()).union(x._p.parent().variable_names())) - VarList.sort(key=self.parent().varname_key,reverse=True) + VarList.sort(key=self.parent().varname_key, reverse=True) if VarList: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(),VarList, order=self.parent()._order) + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) else: R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(),R(self._p) - R(x._p)) + return InfinitePolynomial_sparse(self.parent(), R(self._p) - R(x._p)) def __pow__(self, n): """ @@ -766,15 +768,18 @@ def __pow__(self, n): if callable(n): if (self._p.parent() == self._p.base_ring()): return self - if not (hasattr(self._p,'variables') and self._p.variables()): + if not (hasattr(self._p, 'variables') and self._p.variables()): return self - if hasattr(n,'to_cycles') and hasattr(n,'__len__'): # duck typing Permutation + if hasattr(n, 'to_cycles') and hasattr(n, '__len__'): # duck typing Permutation # auxiliary function, necessary since n(m) raises an error if m>len(n) l = len(n) - p = lambda m: n(m) if 0=i: + if Lbig[j] >= i: ExpoBigSave = [e for e in Fbig[Lbig[j]]] ExpoBig = Fbig[Lbig[j]] found = True for k in gens: - if ExpoBig[k]len(n) l = len(n) - p = lambda m: n(m) if 0 Date: Sat, 17 Sep 2022 09:06:39 -0500 Subject: [PATCH 439/454] Add to documentation and remove reversed type-check --- src/sage/combinat/composition.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 72df25b263c..47808008981 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -110,7 +110,11 @@ class Composition(CombinatorialElement): sage: Composition(descents=({0,1,3},5)) [1, 1, 2, 1] - Check :trac:`34527`:: + An integer composition may be regarded as a sequence. Thus it is an + instance of the Python abstract base class ``Sequence`` allows us to check if objects + behave "like" sequences based on implemented methods. Note that + ``collections.abc.Sequence`` is not the same as + :class:`sage.structure.sequence.Sequence`:: sage: import collections.abc sage: C = Composition([3,2,3]) @@ -118,13 +122,15 @@ class Composition(CombinatorialElement): True sage: issubclass(C.__class__, collections.abc.Sequence) True + + Typically, instances of ``collections.abc.Sequence`` have a ``.count`` method. + This is *not* the case for ``Composition``s:: + sage: C.count Traceback (most recent call last): ... AttributeError: 'Compositions_all_with_category.element_class' object has no attribute 'count' - sage: type(reversed(C)) - EXAMPLES:: From b0a87c62f2bdb6e7982390331725d6f3654b1881 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 17 Sep 2022 20:47:45 -0500 Subject: [PATCH 440/454] Fix documentation --- 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 47808008981..7123d9e35cb 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -124,7 +124,7 @@ class Composition(CombinatorialElement): True Typically, instances of ``collections.abc.Sequence`` have a ``.count`` method. - This is *not* the case for ``Composition``s:: + This is *not* the case for a ``Composition``:: sage: C.count Traceback (most recent call last): From 22385d98106c739db2f5cbee71e264e3831a5039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 18 Sep 2022 11:49:20 +0200 Subject: [PATCH 441/454] adding one doctest for integral --- src/sage/symbolic/integration/integral.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index a7eec1b72a5..f9914e438a1 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -321,7 +321,7 @@ def _tderivative_(self, f, x, a, b, diff_param=None): def _print_latex_(self, f, x, a, b): r""" - Convert this integral to LaTeX notation + Convert this integral to LaTeX notation. EXAMPLES:: @@ -345,7 +345,7 @@ def _print_latex_(self, f, x, a, b): def _sympy_(self, f, x, a, b): """ - Convert this integral to the equivalent SymPy object + Convert this integral to the equivalent SymPy object. The resulting SymPy integral can be evaluated using ``doit()``. @@ -1038,6 +1038,12 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): sage: x,m = SR.var('x,m', domain='real') # long time sage: integrate(elliptic_e(x,m).diff(x), x) # long time elliptic_e(x, m) + + Check that :trac:`20467` is fixed:: + + sage: k = var('k') + sage: integral(sin(k*x)/x*erf(x^2), x, 0, oo, algorithm='maxima') + integrate(erf(x^2)*sin(k*x)/x, x, 0, +Infinity) """ expression, v, a, b = _normalize_integral_input(expression, v, a, b) if algorithm is not None: From 25768de9c84a378af1c28eb7a3353e2795a4be98 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Sep 2022 21:23:09 -0700 Subject: [PATCH 442/454] build/pkgs/igraph: Update to 0.10.1 --- build/pkgs/igraph/checksums.ini | 6 +++--- build/pkgs/igraph/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/igraph/checksums.ini b/build/pkgs/igraph/checksums.ini index c38a75f5b7f..9da572238e5 100644 --- a/build/pkgs/igraph/checksums.ini +++ b/build/pkgs/igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=igraph-VERSION.tar.gz -sha1=e4097fc00c3cb9b64a251003623a104c2ac4ae1f -md5=fbca766c3e76b2098c4555a13689fec6 -cksum=627334466 +sha1=74abe82bdebdefc295a4f04b54170880dcb265e8 +md5=4e61e5e86ebe4fe478df415b7407e87e +cksum=2574937983 upstream_url=https://github.com/igraph/igraph/releases/download/VERSION/igraph-VERSION.tar.gz diff --git a/build/pkgs/igraph/package-version.txt b/build/pkgs/igraph/package-version.txt index 78bc1abd14f..571215736a6 100644 --- a/build/pkgs/igraph/package-version.txt +++ b/build/pkgs/igraph/package-version.txt @@ -1 +1 @@ -0.10.0 +0.10.1 From 4ab41e64da92a48691dff2027fa2b6d8cea309f9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 18 Sep 2022 21:23:16 -0700 Subject: [PATCH 443/454] build/pkgs/python_igraph: Update to 0.10.1 --- build/pkgs/python_igraph/checksums.ini | 6 +++--- build/pkgs/python_igraph/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/python_igraph/checksums.ini b/build/pkgs/python_igraph/checksums.ini index 8e53fc46eca..9ddab211f0d 100644 --- a/build/pkgs/python_igraph/checksums.ini +++ b/build/pkgs/python_igraph/checksums.ini @@ -1,5 +1,5 @@ tarball=python-igraph-VERSION.tar.gz -sha1=86a35162babd0d4af520b2ee0f8d9bb555a744cc -md5=d9c1ec7ddad66f927490f1de893fe42f -cksum=4022167909 +sha1=75eae8ca6de2daa8c5ca43f99487cf20b5cdf432 +md5=18a54cd2fe507f5602e6c10584f665ee +cksum=594785782 upstream_url=https://pypi.io/packages/source/i/igraph/igraph-VERSION.tar.gz diff --git a/build/pkgs/python_igraph/package-version.txt b/build/pkgs/python_igraph/package-version.txt index 78bc1abd14f..571215736a6 100644 --- a/build/pkgs/python_igraph/package-version.txt +++ b/build/pkgs/python_igraph/package-version.txt @@ -1 +1 @@ -0.10.0 +0.10.1 From bac7eb44e680379ce9f19547dd58d90fd6744de2 Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 19 Sep 2022 08:46:41 +0200 Subject: [PATCH 444/454] Trac #32921: Show full output in example for shifted_inhomogeneities The input consists of two inhomogeneities, but the output of the example in the doctests only shows one entry (this is legal due to the `...` at the end). I think that this is confusing, so I add the full output. --- src/sage/combinat/k_regular_sequence.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index e35ff0bfb12..6afb313151c 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2555,7 +2555,8 @@ def shifted_inhomogeneities(self, recurrence_rules): sage: recurrence_rules = RR(M=3, m=0, ll=-14, uu=14, ....: inhomogeneities={0: S, 1: S}) sage: RP.shifted_inhomogeneities(recurrence_rules) - {0: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ...} + {0: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ..., + 1: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ...} TESTS:: From e2d8f1d413375197e95038a6b12a891fe35d0fcc Mon Sep 17 00:00:00 2001 From: Clemens Heuberger Date: Mon, 19 Sep 2022 09:41:25 +0200 Subject: [PATCH 445/454] Trac #32921: `shifted_inhomogeneities`: doctest demonstrating blocks I think that the sentence in the description of the output is hard to understand, but I have no idea for improvement. Therefore I propose to explain it via a doctest. --- src/sage/combinat/k_regular_sequence.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 6afb313151c..9a1fdb83025 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -2554,10 +2554,28 @@ def shifted_inhomogeneities(self, recurrence_rules): ....: ['M', 'm', 'll', 'uu', 'inhomogeneities']) sage: recurrence_rules = RR(M=3, m=0, ll=-14, uu=14, ....: inhomogeneities={0: S, 1: S}) - sage: RP.shifted_inhomogeneities(recurrence_rules) + sage: SI = RP.shifted_inhomogeneities(recurrence_rules) + sage: SI {0: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ..., 1: 2-regular sequence 4, 5, 7, 9, 11, 11, 11, 12, 13, 13, ...} + The first blocks of the corresponding vector-valued sequence correspond + to the corresponding shifts of the inhomogeneity. In this particular + case, there are no other blocks:: + + sage: lower = -2 + sage: upper = 3 + sage: SI[0].dimension() == S.dimension() * (upper - lower + 1) + True + sage: all( + ....: Seq2( + ....: SI[0].mu, + ....: vector((i - lower)*[0, 0] + list(S.left) + (upper - i)*[0, 0]), + ....: SI[0].right) + ....: == S.subsequence(1, i) + ....: for i in range(lower, upper+1)) + True + TESTS:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) From 78f3a9218b8a51f643855917280b4e97b46db0c2 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 19 Sep 2022 17:36:55 -0500 Subject: [PATCH 446/454] Add .count --- src/sage/combinat/composition.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 7123d9e35cb..ee711e52f9f 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -124,13 +124,10 @@ class Composition(CombinatorialElement): True Typically, instances of ``collections.abc.Sequence`` have a ``.count`` method. - This is *not* the case for a ``Composition``:: + ``Composition.count`` counts the number of parts of a specified size:: - sage: C.count - Traceback (most recent call last): - ... - AttributeError: 'Compositions_all_with_category.element_class' object - has no attribute 'count' + sage: C.count(3) + 2 EXAMPLES:: @@ -1373,6 +1370,22 @@ def wll_gt(self, co2) -> bool: return False return False + def count(self, n): + r""" + Return the number of parts of size ``n``. + + EXAMPLES:: + + sage: C = Composition([3,2,3]) + sage: C.count(3) + 2 + sage: C.count(2) + 1 + sage: C.count(1) + 0 + """ + return sum(i == n for i in self) + Sequence.register(Composition) ############################################################## From 627b2bdfe922e2f9eed9d190c19df778674bdcfb Mon Sep 17 00:00:00 2001 From: Release Manager Date: Tue, 20 Sep 2022 00:38:18 +0200 Subject: [PATCH 447/454] Updated SageMath version to 9.7 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 24 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 49b22d68dc7..91b74285b39 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.7.rc2", - "version": "9.7.rc2", + "title": "sagemath/sage: 9.7", + "version": "9.7", "upload_type": "software", - "publication_date": "2022-09-17", + "publication_date": "2022-09-19", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.7.rc2", + "identifier": "https://github.com/sagemath/sage/tree/9.7", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index b63eff86bef..6e6a77791e1 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.7.rc2, Release Date: 2022-09-17 +SageMath version 9.7, Release Date: 2022-09-19 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 48189ef845c..60fd713965e 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=70b1e96e1bd71efd90b1d1345b9c8d4d12de432b -md5=bf9529bff5e1c938c51668f0687ae5a0 -cksum=1982975454 +sha1=db02cdbab3867dce3649cd49823a54dac6a3835b +md5=3580c14fc2c2671103073cb9cb7f8176 +cksum=2388907413 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 37da2e9a6db..efaab3b5685 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -4f225f4cc66a7d515ea6598ebe9b2ff48e114a15 +59ac11a9375feaada4486b63ed9900651e0a6fdb diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 496914816ad..a0356056891 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.7rc2 +sage-conf ~= 9.7 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 56ba1015d98..202df450687 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.7rc2 +sage-docbuild ~= 9.7 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 60e2ed2c881..1b631d955d2 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.7rc2 +sage-setup ~= 9.7 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 47283b61993..344cda711c9 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.7rc2 +sage-sws2rst ~= 9.7 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index d009d8b4834..edd78431534 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.7rc2 +sagelib ~= 9.7 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 984fcd77cb0..848ad97d3e1 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.7rc2 +sagemath-categories ~= 9.7 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 6de5c339724..92b35788acb 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.7rc2 +sagemath-environment ~= 9.7 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 614677a4a19..17764575b14 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.7rc2 +sagemath-objects ~= 9.7 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/src/VERSION.txt b/src/VERSION.txt index 13ac8fa02e9..9d5e716c055 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.7.rc2 +9.7 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 6f4e41fabfc..7fc8e994958 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.7.rc2' -SAGE_RELEASE_DATE='2022-09-17' -SAGE_VERSION_BANNER='SageMath version 9.7.rc2, Release Date: 2022-09-17' +SAGE_VERSION='9.7' +SAGE_RELEASE_DATE='2022-09-19' +SAGE_VERSION_BANNER='SageMath version 9.7, Release Date: 2022-09-19' diff --git a/src/sage/version.py b/src/sage/version.py index 8e72819281d..139e6f07404 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.7.rc2' -date = '2022-09-17' -banner = 'SageMath version 9.7.rc2, Release Date: 2022-09-17' +version = '9.7' +date = '2022-09-19' +banner = 'SageMath version 9.7, Release Date: 2022-09-19' From 9548409c9cd69ec52a3c5bca95331a5595d64727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 21 Sep 2022 15:42:44 +0200 Subject: [PATCH 448/454] change name of example in simplicial complexes --- src/doc/en/reference/references/index.rst | 4 ++++ src/sage/homology/examples.py | 2 +- src/sage/topology/simplicial_complex_catalog.py | 4 ++-- src/sage/topology/simplicial_complex_examples.py | 14 ++++++-------- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 1dbd6f036e7..dd3c36e2540 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2662,6 +2662,10 @@ REFERENCES: .. [Gop1981] \V. D. Goppa, “Codes on algebraic curves,” Sov. Math. Dokl., vol. 24, no. 1, pp. 170–172, 1981. +.. [Gor2016] \D. Gorodkov, *A 15-vertex triangulation of the quaternionic + projective plane*, Discrete & Computational Geometry, vol. 62, + pp. 348-373 (2019). :doi:`10.1007/s00454-018-00055-w` + .. [Gos1972] Bill Gosper, "Continued Fraction Arithmetic" https://perl.plover.com/classes/cftalk/INFO/gosper.txt diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py index 704b1219189..c469f9d6c54 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/homology/examples.py @@ -21,7 +21,7 @@ 'SurfaceOfGenus', 'MooreSpace', 'ComplexProjectivePlane', - 'PseudoQuaternionicProjectivePlane', + 'QuaternionicProjectivePlane', 'PoincareHomologyThreeSphere', 'RealProjectiveSpace', 'K3Surface', diff --git a/src/sage/topology/simplicial_complex_catalog.py b/src/sage/topology/simplicial_complex_catalog.py index cbb84e0892e..07a0de43b4a 100644 --- a/src/sage/topology/simplicial_complex_catalog.py +++ b/src/sage/topology/simplicial_complex_catalog.py @@ -34,7 +34,7 @@ - :meth:`~sage.topology.examples.MooreSpace` - :meth:`~sage.topology.examples.NotIConnectedGraphs` - :meth:`~sage.topology.examples.PoincareHomologyThreeSphere` -- :meth:`~sage.topology.examples.PseudoQuaternionicProjectivePlane` +- :meth:`~sage.topology.examples.QuaternionicProjectivePlane` - :meth:`~sage.topology.examples.RandomComplex` - :meth:`~sage.topology.examples.RandomTwoSphere` - :meth:`~sage.topology.examples.RealProjectivePlane` @@ -69,7 +69,7 @@ ProjectivePlane, RealProjectivePlane, KleinBottle, FareyMap, GenusSix, SurfaceOfGenus, MooreSpace, - ComplexProjectivePlane, PseudoQuaternionicProjectivePlane, + ComplexProjectivePlane, QuaternionicProjectivePlane, PoincareHomologyThreeSphere, RealProjectiveSpace, K3Surface, BarnetteSphere, BrucknerGrunbaumSphere, NotIConnectedGraphs, MatchingComplex, ChessboardComplex, RandomComplex, SumComplex, diff --git a/src/sage/topology/simplicial_complex_examples.py b/src/sage/topology/simplicial_complex_examples.py index 27d74458a65..971b7d75d83 100644 --- a/src/sage/topology/simplicial_complex_examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -34,7 +34,7 @@ - :func:`MooreSpace` - :func:`NotIConnectedGraphs` - :func:`PoincareHomologyThreeSphere` -- :func:`PseudoQuaternionicProjectivePlane` +- :func:`QuaternionicProjectivePlane` - :func:`RandomComplex` - :func:`RandomTwoSphere` - :func:`RealProjectivePlane` @@ -544,15 +544,14 @@ def ComplexProjectivePlane(): name='Minimal triangulation of the complex projective plane') -def PseudoQuaternionicProjectivePlane(): +def QuaternionicProjectivePlane(): r""" Return a pure simplicial complex of dimension 8 with 490 facets. .. WARNING:: - This is expected to be a triangulation of the projective plane - `HP^2` over the ring of quaternions, but this has not been - proved yet. + This was proven to be a triangulation of the projective plane + `HP^2` over the ring of quaternions by Gorodkov in 2016 [Gor2016]_. This simplicial complex has the same homology as `HP^2`. Its automorphism group is isomorphic to the alternating group `A_5` @@ -560,12 +559,11 @@ def PseudoQuaternionicProjectivePlane(): This is defined here using the description in [BK1992]_. This article deals with three different triangulations. This procedure - returns the only one which has a transitive group of - automorphisms. + returns the only one which has a transitive group of automorphisms. EXAMPLES:: - sage: HP2 = simplicial_complexes.PseudoQuaternionicProjectivePlane() ; HP2 + sage: HP2 = simplicial_complexes.QuaternionicProjectivePlane() ; HP2 Simplicial complex with 15 vertices and 490 facets sage: HP2.f_vector() [1, 15, 105, 455, 1365, 3003, 4515, 4230, 2205, 490] From 7b55ef58e4f2c274165b5a8e277149f83705b52c Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 15 Aug 2022 11:07:03 +0900 Subject: [PATCH 449/454] Speeding up powers of lazy series by using a different algorithm. --- src/sage/rings/lazy_series.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 9f4cd1b0f39..57e19c1bc0a 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -117,9 +117,11 @@ from sage.structure.element import Element, parent from sage.structure.richcmp import op_EQ, op_NE from sage.functions.other import factorial +from sage.misc.misc_c import prod from sage.arith.power import generic_power from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.data_structures.stream import ( @@ -2014,7 +2016,12 @@ def __pow__(self, n): from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) - exp = P(lambda k: 1/factorial(ZZ(k)), valuation=0) + + if n in QQ or n in self.base_ring(): + f = P(lambda k: prod(n - i for i in range(k)) / ZZ(k).factorial(), valuation=0) + return f(self - 1) + + exp = P(lambda k: 1 / ZZ(k).factorial(), valuation=0) return exp(self.log() * n) def sqrt(self): @@ -2045,7 +2052,7 @@ def sqrt(self): sage: f*f - Z O(1/(8^s)) """ - return self ** (1/ZZ(2)) + return self ** QQ((1, 2)) # == 1/2 class LazyCauchyProductSeries(LazyModuleElement): From d4450fb31c4d7331cafbad27bf45c4c3697df679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 21 Sep 2022 16:42:14 +0200 Subject: [PATCH 450/454] Update lazy_series' __pow__ docstring It used to only document the integer powers of a series whereas it can do much more. --- src/sage/rings/lazy_series.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 57e19c1bc0a..8aafe4fd4c6 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1995,7 +1995,9 @@ def __pow__(self, n): INPUT: - - ``n`` -- integer; the power to which to raise the series + - ``n`` -- the power to which to raise the series. This may be an + integer, a rational number, an element of the base ring, or an other + series. EXAMPLES:: @@ -2010,6 +2012,20 @@ def __pow__(self, n): 1 + 2/3/2^s + 2/3/3^s + 5/9/4^s + 2/3/5^s + 4/9/6^s + 2/3/7^s + O(1/(8^s)) sage: f^3 - Z O(1/(8^s)) + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + sage: f ^ (1 / 2) + 1 + 1/2*z - 1/8*z^2 + 1/16*z^3 - 5/128*z^4 + 7/256*z^5 - 21/1024*z^6 + O(z^7) + + sage: f^f + 1 + z + z^2 + 1/2*z^3 + 1/3*z^4 + 1/12*z^5 + 3/40*z^6 + O(z^7) + + sage: q = ZZ['q'].fraction_field().gen() + sage: L. = LazyLaurentSeriesRing(q.parent()) + sage: f = (1 - z)^q + sage: f + 1 - q*z + ((q^2 - q)/2)*z^2 + ((-q^3 + 3*q^2 - 2*q)/6)*z^3 + ((q^4 - 6*q^3 + 11*q^2 - 6*q)/24)*z^4 + ((-q^5 + 10*q^4 - 35*q^3 + 50*q^2 - 24*q)/120)*z^5 + ((q^6 - 15*q^5 + 85*q^4 - 225*q^3 + 274*q^2 - 120*q)/720)*z^6 + O(z^7) """ if n in ZZ: return generic_power(self, n) From ae5676b4fdf9db1482c01e902a1e32a33eedacc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 22 Sep 2022 08:31:44 +0200 Subject: [PATCH 451/454] adding a deprecation --- src/sage/topology/simplicial_complex_examples.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/topology/simplicial_complex_examples.py b/src/sage/topology/simplicial_complex_examples.py index 971b7d75d83..a21391beab3 100644 --- a/src/sage/topology/simplicial_complex_examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -63,6 +63,14 @@ {0: 0, 1: C4, 2: 0} sage: simplicial_complexes.MatchingComplex(6).homology() {0: 0, 1: Z^16, 2: 0} + +TESTS:: + + sage: from sage.topology.simplicial_complex_examples import PseudoQuaternionicProjectivePlane + sage: H = PseudoQuaternionicProjectivePlane() + doctest:warning...: + DeprecationWarning: PseudoQuaternionicProjectivePlane is deprecated. Please use sage.topology.simplicial_complex_examples.QuaternionicProjectivePlane instead. + See https://trac.sagemath.org/34568 for details. """ from .simplicial_complex import SimplicialComplex @@ -75,6 +83,7 @@ from sage.misc.functional import is_even from sage.combinat.subset import Subsets import sage.misc.prandom as random +from sage.misc.superseded import deprecated_function_alias # Miscellaneous utility functions. @@ -596,6 +605,10 @@ def QuaternionicProjectivePlane(): for tuple in start_list for g in PermutationGroup([P, S])]) + +PseudoQuaternionicProjectivePlane = deprecated_function_alias(34568, QuaternionicProjectivePlane) + + def PoincareHomologyThreeSphere(): """ A triangulation of the Poincaré homology 3-sphere. From b23792105a2fbb3d8e7d529774f91eaa3831ac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Thu, 22 Sep 2022 09:33:13 +0200 Subject: [PATCH 452/454] Formatting issues / docstring conventions --- src/sage/rings/lazy_series.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 8aafe4fd4c6..6bfb8730c39 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1995,9 +1995,8 @@ def __pow__(self, n): INPUT: - - ``n`` -- the power to which to raise the series. This may be an - integer, a rational number, an element of the base ring, or an other - series. + - ``n`` -- the power to which to raise the series; this may be a + rational number, an element of the base ring, or an other series EXAMPLES:: @@ -2015,7 +2014,7 @@ def __pow__(self, n): sage: L. = LazyLaurentSeriesRing(QQ) sage: f = 1 + z - sage: f ^ (1 / 2) + sage: f^(1 / 2) 1 + 1/2*z - 1/8*z^2 + 1/16*z^3 - 5/128*z^4 + 7/256*z^5 - 21/1024*z^6 + O(z^7) sage: f^f @@ -2025,7 +2024,11 @@ def __pow__(self, n): sage: L. = LazyLaurentSeriesRing(q.parent()) sage: f = (1 - z)^q sage: f - 1 - q*z + ((q^2 - q)/2)*z^2 + ((-q^3 + 3*q^2 - 2*q)/6)*z^3 + ((q^4 - 6*q^3 + 11*q^2 - 6*q)/24)*z^4 + ((-q^5 + 10*q^4 - 35*q^3 + 50*q^2 - 24*q)/120)*z^5 + ((q^6 - 15*q^5 + 85*q^4 - 225*q^3 + 274*q^2 - 120*q)/720)*z^6 + O(z^7) + 1 - q*z + ((q^2 - q)/2)*z^2 + ((-q^3 + 3*q^2 - 2*q)/6)*z^3 + + ((q^4 - 6*q^3 + 11*q^2 - 6*q)/24)*z^4 + + ((-q^5 + 10*q^4 - 35*q^3 + 50*q^2 - 24*q)/120)*z^5 + + ((q^6 - 15*q^5 + 85*q^4 - 225*q^3 + 274*q^2 - 120*q)/720)*z^6 + + O(z^7) """ if n in ZZ: return generic_power(self, n) @@ -2068,7 +2071,7 @@ def sqrt(self): sage: f*f - Z O(1/(8^s)) """ - return self ** QQ((1, 2)) # == 1/2 + return self ** QQ((1, 2)) # == 1/2 class LazyCauchyProductSeries(LazyModuleElement): From 2de6d46f0363a6a3f8b45ad37fe150bc472abf86 Mon Sep 17 00:00:00 2001 From: Jan Groenewald Date: Thu, 22 Sep 2022 09:45:08 -0700 Subject: [PATCH 453/454] build/pkgs/_develop/distros/debian.txt: Remove nonexistent package openssh --- build/pkgs/_develop/distros/debian.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/build/pkgs/_develop/distros/debian.txt b/build/pkgs/_develop/distros/debian.txt index 4598e83f3f5..5c3c17488e8 100644 --- a/build/pkgs/_develop/distros/debian.txt +++ b/build/pkgs/_develop/distros/debian.txt @@ -1,4 +1,3 @@ # Needed for devcontainer support in VS code gpgconf openssh-client -openssh From 4541564cf151a5138810e9c2144349f59469d769 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sun, 25 Sep 2022 20:47:57 +0200 Subject: [PATCH 454/454] Updated SageMath version to 9.8.beta0 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 24 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 91b74285b39..06bf86e4065 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.7", - "version": "9.7", + "title": "sagemath/sage: 9.8.beta0", + "version": "9.8.beta0", "upload_type": "software", - "publication_date": "2022-09-19", + "publication_date": "2022-09-25", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.7", + "identifier": "https://github.com/sagemath/sage/tree/9.8.beta0", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 6e6a77791e1..58778909bd8 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.7, Release Date: 2022-09-19 +SageMath version 9.8.beta0, Release Date: 2022-09-25 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 1506e268bde..d7e7fc0fcc1 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=5a1689117566cc63ea05f0aa9a2f125da7ba7dc6 -md5=37178d28e02f691476b4ba5b9e915026 -cksum=4153085574 +sha1=c2e1c4db6762c4d97d5127f5f056e46fe3d5a94d +md5=70dcc35964e4234443c4e77beb2245d7 +cksum=2271434645 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index dc10cfa4f97..abdbdc62467 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -1cee2c8b179b9fb913facf54b48dd710178bbffc +04fbc829e9850eedf555acc683666c55cb7052d7 diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index a0356056891..35a8993af20 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.7 +sage-conf ~= 9.8b0 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 202df450687..002c6b79c65 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.7 +sage-docbuild ~= 9.8b0 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 1b631d955d2..5720bc8f2c5 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.7 +sage-setup ~= 9.8b0 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 344cda711c9..70be4d731ee 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.7 +sage-sws2rst ~= 9.8b0 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index edd78431534..359dff35670 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.7 +sagelib ~= 9.8b0 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 848ad97d3e1..070f8caa2ea 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.7 +sagemath-categories ~= 9.8b0 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 92b35788acb..e9976c7146f 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.7 +sagemath-environment ~= 9.8b0 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 17764575b14..0fb621f26f2 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.7 +sagemath-objects ~= 9.8b0 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/src/VERSION.txt b/src/VERSION.txt index 9d5e716c055..aadd65deb15 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.7 +9.8.beta0 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 7fc8e994958..802ca805250 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.7' -SAGE_RELEASE_DATE='2022-09-19' -SAGE_VERSION_BANNER='SageMath version 9.7, Release Date: 2022-09-19' +SAGE_VERSION='9.8.beta0' +SAGE_RELEASE_DATE='2022-09-25' +SAGE_VERSION_BANNER='SageMath version 9.8.beta0, Release Date: 2022-09-25' diff --git a/src/sage/version.py b/src/sage/version.py index 139e6f07404..94cf8afef07 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.7' -date = '2022-09-19' -banner = 'SageMath version 9.7, Release Date: 2022-09-19' +version = '9.8.beta0' +date = '2022-09-25' +banner = 'SageMath version 9.8.beta0, Release Date: 2022-09-25'