From 08cea167f4643251105113c169384db64c07f9a4 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 16 Apr 2024 22:25:49 -0400 Subject: [PATCH 001/193] src/sage/modular/modform/ring.py: fix the category of ModularFormsRing --- src/sage/modular/modform/ring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 365f9f2a504..637e4931ffa 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -243,7 +243,8 @@ def __init__(self, group, base_ring=QQ): self.__cached_gens = [] self.__cached_cusp_maxweight = ZZ(-1) self.__cached_cusp_gens = [] - Parent.__init__(self, base=base_ring, category=GradedAlgebras(base_ring)) + cat = GradedAlgebras(base_ring).Commutative() + Parent.__init__(self, base=base_ring, category=cat) def change_ring(self, base_ring): r""" From 996dcc81fd6a39dec8ebe569a24e54a694eeb2e0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 16 Apr 2024 23:24:48 -0400 Subject: [PATCH 002/193] src/sage/modular/modform/ring.py: documentation cleanup in ModularFormsRing --- src/sage/modular/modform/ring.py | 122 ++++++++++++++++++------------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 637e4931ffa..dcf6f3abcb8 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -248,13 +248,13 @@ def __init__(self, group, base_ring=QQ): def change_ring(self, base_ring): r""" - Return the ring of modular forms over the given base ring and the same - group as ``self``. + Return a ring of modular forms over a new base ring of the same + congruence subgroup. INPUT: - - ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or the - integers mod `p` for some prime `p`. + - ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or + the integers mod `p` for some prime `p`. EXAMPLES:: @@ -269,7 +269,7 @@ def change_ring(self, base_ring): def some_elements(self): r""" - Return a list of generators of ``self``. + Return some elements of this ring. EXAMPLES:: @@ -281,7 +281,7 @@ def some_elements(self): def group(self): r""" - Return the congruence subgroup for which this is the ring of modular forms. + Return the congruence subgroup of this ring of modular forms. EXAMPLES:: @@ -293,14 +293,15 @@ def group(self): def gen(self, i): r""" - Return the `i`-th generator of ``self``. + Return the `i`-th generator of this ring. INPUT: - - ``i`` (Integer) -- correspond to the `i`-th modular form generating - the ring of modular forms. + - ``i`` (Integer) -- correspond to the `i`-th modular form + generating the ring of modular forms. - OUTPUT: A ``GradedModularFormElement`` + OUTPUT: A generator this ring, which is an instance of + :class:`sage.modular.modform.GradedModularFormElement` EXAMPLES:: @@ -316,7 +317,7 @@ def gen(self, i): def ngens(self): r""" - Return the number of generators of ``self`` + Return the number of generators of this ring. EXAMPLES:: @@ -336,17 +337,22 @@ def ngens(self): def polynomial_ring(self, names, gens=None): r""" - Return a polynomial ring of which ``self`` is a quotient. + Return a polynomial ring of which this ring of modular forms is + a quotient. INPUT: - - ``names`` -- a list or tuple of names (strings), or a comma separated string - - ``gens`` (default: None) -- (list) a list of generator of ``self``. If ``gens`` is - ``None`` then the generators returned by :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` + - ``names`` -- a list or tuple of names (strings), or a comma + separated string + - ``gens`` (default: None) -- (list) a list of generator of + ``self``. If ``gens`` is ``None`` then the generators returned by + :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` is used instead. - OUTPUT: A multivariate polynomial ring in the variable ``names``. Each variable of the - polynomial ring correspond to a generator given in gens (following the ordering of the list). + OUTPUT: A multivariate polynomial ring in the variable + ``names``. Each variable of the polynomial ring correspond to a + generator given in the list ``gens`` (following the ordering of + the list). EXAMPLES:: @@ -359,7 +365,8 @@ def polynomial_ring(self, names, gens=None): sage: M.polynomial_ring('g', gens) Multivariate Polynomial Ring in g0, g1, g2 over Rational Field - The degrees of the variables are the weights of the corresponding forms:: + The degrees of the variables are the weights of the + corresponding forms:: sage: M = ModularFormsRing(1) sage: P. = M.polynomial_ring() @@ -373,12 +380,13 @@ def polynomial_ring(self, names, gens=None): if gens is None: gens = self.gen_forms() degs = [f.weight() for f in gens] - return PolynomialRing(self.base_ring(), len(gens), names, order=TermOrder('wdeglex', degs)) # Should we remove the deg lexicographic ordering here? + return PolynomialRing(self.base_ring(), len(gens), names, + order=TermOrder('wdeglex', degs)) def _generators_variables_dictionnary(self, poly_parent, gens): r""" - Utility function that returns a dictionary giving an association between - polynomial ring generators and generators of modular forms ring. + Return a dictionary giving an association between polynomial + ring generators and generators of modular forms ring. INPUT: @@ -404,18 +412,19 @@ def _generators_variables_dictionnary(self, poly_parent, gens): def from_polynomial(self, polynomial, gens=None): r""" - Convert the given polynomial to a graded form living in ``self``. If - ``gens`` is ``None`` then the list of generators given by the method - :meth:`gen_forms` will be used. Otherwise, ``gens`` should be a list of - generators. + Return a graded modular form constructed by evaluating a given + multivariate polynomial at a set of generators. INPUT: - - ``polynomial`` -- A multivariate polynomial. The variables names of - the polynomial should be different from ``'q'``. The number of - variable of this polynomial should equal the number of generators - - ``gens`` -- list (default: ``None``) of generators of the modular - forms ring + - ``polynomial`` -- A multivariate polynomial. The variables + names of the polynomial should be different from ``'q'``. The + number of variable of this polynomial should equal the number + of given generators. + - ``gens`` (default: ``None``) -- a list of generators of this + ring. If this parameter is ``None``, then the generators given + by the method :meth:`gen_forms` will be used. Note that we do + not check if the list is indeed a generating set. OUTPUT: A ``GradedModularFormElement`` given by the polynomial relation ``polynomial``. @@ -438,7 +447,8 @@ def from_polynomial(self, polynomial, gens=None): sage: M.from_polynomial(P(1/2)) 1/2 - Note that the number of variables must be equal to the number of generators:: + Note that the number of variables must be equal to the number of + generators:: sage: x, y = polygens(QQ, 'x, y') sage: M(x + y) @@ -471,7 +481,7 @@ def from_polynomial(self, polynomial, gens=None): def _element_constructor_(self, forms_datum): r""" - The call method of self. + Return the graded modular form corresponding to the given data. INPUT: @@ -578,7 +588,8 @@ def one(self): def _coerce_map_from_(self, M): r""" - Code to make ModularFormRing work well with coercion framework. + Return ``True`` if there is a coercion map from ``M`` to this + ring. TESTS:: @@ -624,7 +635,7 @@ def __richcmp__(self, other, op): def _repr_(self): r""" - String representation of self. + Return the string representation of self. EXAMPLES:: @@ -637,7 +648,8 @@ def _repr_(self): def modular_forms_of_weight(self, weight): """ - Return the space of modular forms on this group of the given weight. + Return the space of modular forms of the given weight and the + same congruence subgroup. EXAMPLES:: @@ -651,9 +663,15 @@ def modular_forms_of_weight(self, weight): def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): r""" - If `R` is the base ring of self, then this function calculates a set of - modular forms which generate the `R`-algebra of all modular forms of - weight up to ``maxweight`` with coefficients in `R`. + Return a list of generator of this ring as a list of pairs + `(k, f)` where `k` is an integer and `f` is a univariate power + series in `q` corresponding to the `q`-expansion of a modular + form of weight `k`. + + More precisely, if `R` is the base ring of self, then this + function calculates a set of modular forms which generate the + `R`-algebra of all modular forms of weight up to ``maxweight`` + with coefficients in `R`. INPUT: @@ -667,7 +685,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): triples `(k, f, F)`, where: - `k` is an integer, - - `f` is the `q`-expansion of a modular form of weight `k`, as a power series over the base ring of self, + - `f` is the `q`-expansion of a modular form of weight `k`, + as a power series over the base ring of self, - `F` (if provided) is a modular form object corresponding to F. If this list is nonempty, we find a minimal generating set containing @@ -681,8 +700,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): OUTPUT: - a list of pairs (k, f), where f is the q-expansion to precision - ``prec`` of a modular form of weight k. + a list of pairs `(k, f)`, where `f` is the `q`-expansion to precision + ``prec`` of a modular form of weight `k`. .. SEEALSO:: @@ -788,7 +807,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): for x in start_gens: if len(x) == 2: if x[1].prec() < prec: - raise ValueError("Requested precision cannot be higher than precision of approximate starting generators!") + raise ValueError("Requested precision cannot be higher" + " than precision of approximate starting generators!") sgs.append((x[0], x[1], None)) else: sgs.append(x) @@ -808,8 +828,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): r""" - Return a list of modular forms generating this ring (as an algebra over - the appropriate base ring). + Return a list of modular forms generating this ring (as an algebra + over the appropriate base ring). This method differs from :meth:`generators` only in that it returns graded modular form objects, rather than bare `q`-expansions. @@ -852,11 +872,13 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): def _find_generators(self, maxweight, start_gens, start_weight): r""" - For internal use. This function is called by :meth:`generators` and - :meth:`gen_forms`: it returns a list of triples `(k, f, F)` where `F` + Returns a list of triples `(k, f, F)` where `F` is a modular form of weight `k` and `f` is its `q`-expansion coerced into the base ring of self. + For internal use. This function is called by :meth:`generators` and + :meth:`gen_forms`. + INPUT: - ``maxweight`` -- maximum weight to try @@ -977,7 +999,7 @@ def _find_generators(self, maxweight, start_gens, start_weight): @cached_method def q_expansion_basis(self, weight, prec=None, use_random=True): r""" - Calculate a basis of q-expansions for the space of modular forms of the + Return a basis of q-expansions for the space of modular forms of the given weight for this group, calculated using the ring generators given by ``find_generators``. @@ -1045,8 +1067,8 @@ def q_expansion_basis(self, weight, prec=None, use_random=True): def cuspidal_ideal_generators(self, maxweight=8, prec=None): r""" - Calculate generators for the ideal of cuspidal forms in this ring, as a - module over the whole ring. + Return a set of generators for the ideal of cuspidal forms in + this ring, as a module over the whole ring. EXAMPLES:: @@ -1127,7 +1149,7 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None): def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): r""" - Calculate a basis of `q`-expansions for the space of cusp forms of + Return a basis of `q`-expansions for the space of cusp forms of weight ``weight`` for this group. INPUT: From 09258bf07a364a52d6294e1b0e487f3a7b552ac9 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 16 Apr 2024 23:36:21 -0400 Subject: [PATCH 003/193] src/sage/modular/modform/ring.py: some code style cleanup --- src/sage/modular/modform/ring.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index dcf6f3abcb8..ee5d3304515 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -407,7 +407,9 @@ def _generators_variables_dictionnary(self, poly_parent, gens): nb_var = poly_parent.ngens() nb_gens = self.ngens() if nb_var != nb_gens: - raise ValueError('the number of variables (%s) must be equal to the number of generators of the modular forms ring (%s)' % (nb_var, self.ngens())) + raise ValueError('the number of variables (%s) must be equal to' + ' the number of generators of the modular forms' + ' ring (%s)' % (nb_var, self.ngens())) return {poly_parent.gen(i): self(gens[i]) for i in range(0, nb_var)} def from_polynomial(self, polynomial, gens=None): @@ -535,7 +537,7 @@ def _element_constructor_(self, forms_datum): else: raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s' % (forms_datum.group(), forms_datum.base_ring(), self)) elif forms_datum in self.base_ring(): - forms_dictionary = {0:forms_datum} + forms_dictionary = {0: forms_datum} elif isinstance(forms_datum, MPolynomial): return self.from_polynomial(forms_datum) elif isinstance(forms_datum, PowerSeries_poly): @@ -864,9 +866,9 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field """ - sgs = tuple( (F.weight(), None, F) for F in start_gens ) + sgs = tuple((F.weight(), None, F) for F in start_gens) G = self._find_generators(maxweight, sgs, start_weight) - return [F for k,f,F in G] + return [F for k, f, F in G] gens = gen_forms @@ -1089,7 +1091,7 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None): for j,f,F in self.__cached_cusp_gens: if f.prec() >= working_prec: f = F.qexp(working_prec).change_ring(self.base_ring()) - G.append( (j,f,F) ) + G.append((j, f, F)) else: k = 2 G = [] @@ -1140,10 +1142,10 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None): if prec is None: return G elif prec <= working_prec: - return [ (k, f.truncate_powerseries(prec), F) for k,f,F in G] + return [(k, f.truncate_powerseries(prec), F) for k,f,F in G] else: # user wants increased precision, so we may as well cache that - Gnew = [ (k, F.qexp(prec).change_ring(self.base_ring()), F) for k,f,F in G] + Gnew = [(k, F.qexp(prec).change_ring(self.base_ring()), F) for k, f, F in G] self.__cached_cusp_gens = Gnew return Gnew From 0f3a4838c3b96240e88b0a9bf787e58481df6187 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 17 Apr 2024 10:44:43 -0400 Subject: [PATCH 004/193] src/sage/modular/modform/ring.py: fix introduced typo --- src/sage/modular/modform/ring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index ee5d3304515..64c8d44f2c4 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -300,8 +300,8 @@ def gen(self, i): - ``i`` (Integer) -- correspond to the `i`-th modular form generating the ring of modular forms. - OUTPUT: A generator this ring, which is an instance of - :class:`sage.modular.modform.GradedModularFormElement` + OUTPUT: A generator of this ring, which is an instance of + :class:`~sage.modular.modform.GradedModularFormElement` EXAMPLES:: From cd722395fcb6eac3320439278127197f882698c5 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 31 May 2024 21:47:44 -0400 Subject: [PATCH 005/193] src/sage/modular/modform/ring.py: apply reviewer comments (+ extras) --- src/sage/modular/modform/ring.py | 88 +++++++++++++++++--------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 1a292c28e99..625e48b4ae9 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -201,7 +201,7 @@ def __init__(self, group, base_ring=QQ): - ``group`` -- a congruence subgroup of `\SL_2(\ZZ)`, or a positive integer `N` (interpreted as `\Gamma_0(N)`) - - ``base_ring`` (ring, default: `\QQ`) -- a base ring, which should be + - ``base_ring`` -- (default: `\QQ`) a base ring, which should be `\QQ`, `\ZZ`, or the integers mod `p` for some prime `p` TESTS: @@ -296,8 +296,8 @@ def gen(self, i): INPUT: - - ``i`` (Integer) -- correspond to the `i`-th modular form - generating the ring of modular forms. + - ``i`` -- integer corresponding to the `i`-th generator of this + ring. OUTPUT: A generator of this ring, which is an instance of :class:`~sage.modular.modform.GradedModularFormElement` @@ -342,11 +342,14 @@ def polynomial_ring(self, names, gens=None): INPUT: - ``names`` -- a list or tuple of names (strings), or a comma - separated string - - ``gens`` (default: None) -- (list) a list of generator of - ``self``. If ``gens`` is ``None`` then the generators returned by + separated string; consists in the names of the polynomial + ring variables + - ``gens`` -- list of modular forms generating this ring + (default: None); if ``gens`` is ``None`` then the list of + generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` - is used instead. + is used instead. Note that we do not check if the list is + indeed a generating set. OUTPUT: A multivariate polynomial ring in the variable ``names``. Each variable of the polynomial ring correspond to a @@ -422,10 +425,12 @@ def from_polynomial(self, polynomial, gens=None): names of the polynomial should be different from ``'q'``. The number of variable of this polynomial should equal the number of given generators. - - ``gens`` (default: ``None``) -- a list of generators of this - ring. If this parameter is ``None``, then the generators given - by the method :meth:`gen_forms` will be used. Note that we do - not check if the list is indeed a generating set. + - ``gens`` -- list of modular forms generating this ring + (default: None); if ``gens`` is ``None`` then the list of + generators returned by the method + :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` + is used instead. Note that we do not check if the list is + indeed a generating set. OUTPUT: A ``GradedModularFormElement`` given by the polynomial relation ``polynomial``. @@ -676,14 +681,14 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): INPUT: - - ``maxweight`` (integer, default: 8) -- check up to this weight for - generators + - ``maxweight`` -- integer (default: 8); check up to this weight + for generators - - ``prec`` (integer, default: 10) -- return `q`-expansions to this - precision + - ``prec`` -- integer (default: 10); return `q`-expansions to + this precision - - ``start_gens`` (list, default: ``[]``) -- list of pairs `(k, f)`, or - triples `(k, f, F)`, where: + - ``start_gens`` -- list (default: empty list); list of pairs + `(k, f)`, or triples `(k, f, F)`, where: - `k` is an integer, - `f` is the `q`-expansion of a modular form of weight `k`, @@ -696,7 +701,7 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2): the case); otherwise, more terms will be calculated from the modular form object `F`. - - ``start_weight`` (integer, default: 2) -- calculate the graded + - ``start_weight`` -- integer (default: 2); calculate the graded subalgebra of forms of weight at least ``start_weight``. OUTPUT: @@ -837,14 +842,14 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): INPUT: - - ``maxweight`` (integer, default: 8) -- calculate forms generating all - forms up to this weight + - ``maxweight`` -- integer (default: 8); calculate forms + generating all forms up to this weight - - ``start_gens`` (list, default: ``[]``) -- a list of modular forms. If - this list is nonempty, we find a minimal generating set containing - these forms + - ``start_gens`` -- list (default: empty list); a list of + modular forms. If this list is nonempty, we find a minimal + generating set containing these forms - - ``start_weight`` (integer, default: 2) -- calculate the graded + - ``start_weight`` -- integer (default: 2); calculate the graded subalgebra of forms of weight at least ``start_weight`` .. NOTE:: @@ -873,12 +878,12 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): def _find_generators(self, maxweight, start_gens, start_weight): r""" - Returns a list of triples `(k, f, F)` where `F` - is a modular form of weight `k` and `f` is its `q`-expansion coerced - into the base ring of self. + Returns a list of triples `(k, f, F)` where `F` is a modular + form of weight `k` and `f` is its `q`-expansion coerced into the + base ring of self. - For internal use. This function is called by :meth:`generators` and - :meth:`gen_forms`. + For internal use. This function is called by :meth:`generators` + and :meth:`gen_forms`. INPUT: @@ -1000,19 +1005,19 @@ def _find_generators(self, maxweight, start_gens, start_weight): @cached_method def q_expansion_basis(self, weight, prec=None, use_random=True): r""" - Return a basis of q-expansions for the space of modular forms of the - given weight for this group, calculated using the ring generators given - by ``find_generators``. + Return a basis of `q`-expansions for the space of modular forms + of the given weight for this group, calculated using the ring + generators given by ``find_generators``. INPUT: - - ``weight`` (integer) -- the weight - - ``prec`` (integer or ``None``, default: ``None``) -- power series - precision. If ``None``, the precision defaults to the Sturm bound for - the requested level and weight. - - ``use_random`` (boolean, default: True) -- whether or not to use a - randomized algorithm when building up the space of forms at the given - weight from known generators of small weight. + - ``weight`` -- the weight + - ``prec`` -- integer (default: ``None``); power series + precision. If ``None``, the precision defaults to the Sturm + bound for the requested level and weight. + - ``use_random`` -- boolean (default: True); whether or not to + use a randomized algorithm when building up the space of forms + at the given weight from known generators of small weight. EXAMPLES:: @@ -1155,8 +1160,9 @@ def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): INPUT: - - ``weight`` (integer) -- the weight - - ``prec`` (integer or None) -- precision of `q`-expansions to return + - ``weight`` -- the weight + - ``prec`` -- integer (default: ``None``) precision of + `q`-expansions to return ALGORITHM: Uses the method :meth:`cuspidal_ideal_generators` to calculate generators of the ideal of cusp forms inside this ring. Then From 943554778cb50bcda4b0da7b01ce42bdb49e1581 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 1 Jun 2024 08:08:51 -0400 Subject: [PATCH 006/193] src/sage/modular/modform/ring.py: add missing backticks --- src/sage/modular/modform/ring.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 625e48b4ae9..8bae20d0c9e 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -345,7 +345,7 @@ def polynomial_ring(self, names, gens=None): separated string; consists in the names of the polynomial ring variables - ``gens`` -- list of modular forms generating this ring - (default: None); if ``gens`` is ``None`` then the list of + (default: ``None``); if ``gens`` is ``None`` then the list of generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` is used instead. Note that we do not check if the list is @@ -426,7 +426,7 @@ def from_polynomial(self, polynomial, gens=None): number of variable of this polynomial should equal the number of given generators. - ``gens`` -- list of modular forms generating this ring - (default: None); if ``gens`` is ``None`` then the list of + (default: ``None``); if ``gens`` is ``None`` then the list of generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` is used instead. Note that we do not check if the list is @@ -1015,7 +1015,7 @@ def q_expansion_basis(self, weight, prec=None, use_random=True): - ``prec`` -- integer (default: ``None``); power series precision. If ``None``, the precision defaults to the Sturm bound for the requested level and weight. - - ``use_random`` -- boolean (default: True); whether or not to + - ``use_random`` -- boolean (default: ``True``); whether or not to use a randomized algorithm when building up the space of forms at the given weight from known generators of small weight. From 7c8f8d29194a5243dbc64199e7ab3675cfab7b8d Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Fri, 28 Jun 2024 09:23:48 +0200 Subject: [PATCH 007/193] graphs: add implementation of slice decomposition via extended LexBFS --- src/doc/en/reference/graphs/index.rst | 1 + src/doc/en/reference/references/index.rst | 2 +- src/sage/graphs/graph.py | 1 + .../slice_decomposition.pxd | 17 + .../slice_decomposition.pyx | 966 ++++++++++++++++++ src/sage/graphs/traversals.pyx | 883 ++++++---------- 6 files changed, 1296 insertions(+), 574 deletions(-) create mode 100644 src/sage/graphs/graph_decompositions/slice_decomposition.pxd create mode 100644 src/sage/graphs/graph_decompositions/slice_decomposition.pyx diff --git a/src/doc/en/reference/graphs/index.rst b/src/doc/en/reference/graphs/index.rst index f681c083a08..c549876fe31 100644 --- a/src/doc/en/reference/graphs/index.rst +++ b/src/doc/en/reference/graphs/index.rst @@ -98,6 +98,7 @@ Libraries of algorithms sage/graphs/graph_decompositions/bandwidth sage/graphs/graph_decompositions/cutwidth sage/graphs/graph_decompositions/graph_products + sage/graphs/graph_decompositions/slice_decomposition sage/graphs/graph_decompositions/modular_decomposition sage/graphs/graph_decompositions/clique_separators sage/graphs/convexity_properties diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index b7725c7848b..22a70f78bc0 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -6358,7 +6358,7 @@ REFERENCES: .. [TCHP2008] Marc Tedder, Derek Corneil, Michel Habib and Christophe Paul, *Simple, linear-time modular decomposition*, 2008. - :arxiv:`0710.3901`. + :arxiv:`0710.3901v3`. .. [Tee1997] Tee, Garry J. "Continuous branches of inverses of the 12 Jacobi elliptic functions for real diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 3eda130f4d8..307047e3e01 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -10227,6 +10227,7 @@ def bipartite_double(self, extended=False): from sage.graphs.graph_decompositions.clique_separators import atoms_and_clique_separators from sage.graphs.graph_decompositions.bandwidth import bandwidth from sage.graphs.graph_decompositions.cutwidth import cutwidth + from sage.graphs.graph_decompositions.slice_decomposition import slice_decomposition matching_polynomial = LazyImport('sage.graphs.matchpoly', 'matching_polynomial', at_startup=True) from sage.graphs.cliquer import all_max_clique as cliques_maximum from sage.graphs.cliquer import all_cliques diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pxd b/src/sage/graphs/graph_decompositions/slice_decomposition.pxd new file mode 100644 index 00000000000..fcd14a25300 --- /dev/null +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pxd @@ -0,0 +1,17 @@ +from libcpp.vector cimport vector + +from sage.structure.sage_object cimport SageObject +from sage.graphs.base.c_graph cimport CGraph + +cdef void extended_lex_BFS( + CGraph cg, vector[int] &sigma, vector[int] *sigma_inv, + int initial_v_int, vector[int] *pred, vector[size_t] *xslice_len, + vector[vector[int]] *lex_label) except * + +cdef class SliceDecomposition(SageObject): + cdef tuple sigma + cdef dict sigma_inv + cdef vector[size_t] xslice_len + cdef dict lex_label + cdef object _graph_class + cdef object _underlying_graph diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx new file mode 100644 index 00000000000..08b2e712fc3 --- /dev/null +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -0,0 +1,966 @@ +# cython: binding=True +# distutils: language = c++ +# distutils: extra_compile_args = -std=c++11 +r""" +Slice decomposition + +This module implements an extended lexBFS algorithm for computing the slice +decomposition of undirected graphs and the class :class:`~SliceDecomposition` to +represent such decompositions. + +AUTHORS: + +- Cyril Bouvier (2024-06-25): initial version +""" +# **************************************************************************** +# Copyright (C) 2024 Cyril Bouvier +# +# 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 libcpp.algorithm cimport swap +from cython.operator cimport dereference as deref + +from sage.graphs.base.c_graph cimport CGraphBackend +from sage.data_structures.bitset_base cimport bitset_in + +cdef void extended_lex_BFS( + CGraph cg, vector[int] &sigma, vector[int] *sigma_inv, + int initial_v_int, vector[int] *pred, vector[size_t] *xslice_len, + vector[vector[int]] *lex_label) except *: + r""" + Perform a extended lexicographic breadth first search (LexBFS) on the + undirected graph `G`. In addition to computing a LexBFS ordering, the + extended LexBFS algorithm can be used to compute the slice decomposition of + the graph. + + This function implements the `O(n+m)` time algorithm proposed in [HMPV2000]_ + and [TCHP2008]_. + + INPUT: + + - ``cg`` -- a ``CGraph``. This function ignores loops and multiple edges and + assumes that the graph is undirected. + + - ``sigma`` -- vector of ``int`` to store the ordering of the vertices + resulting from the LexBFS traversal. At the end, the vector will have size + `n` (the number of vertices of the graph). + + - ``sigma_inv`` -- a pointer to a vector to store the inverse of the + permutation ``sigma``. ``sigma_inv`` can be ``NULL`` if the caller does + not need it (but, note that, the inverse of ``sigma`` is still needed by + the algorithm, so it does not save time nor memory to have ``sigma_inv`` + equal to ``NULL``). At the end, if ``sigma_inv`` is not NULL, the vector + pointer by it will have size `n` (the number of vertices of the graph) + and will satisfy: + * sigma[deref(sigma_inv)[v_int]] = v_int + * deref(sigma_inv)[sigma[i]] = i + + - ``initial_v_int`` -- the first vertex to consider, can be -1. + + - ``pred`` -- a pointer to a vector of int to store the predecessor of a + vertex in the LexBFS traversal. ``pred`` can be ``NULL`` if the caller + does not need it (and the information will not be computed by the + algorithm). At the end, if ``pred`` is not NULL, the vector pointer by it + will have size `n` (the number of vertices of the graph) and pred[i] will + be either -1 (if sigma[i] as no predecessor) or a positive value less than + n such that the predecessor of sigma[i] is sigma[pred[i]]. + + - ``xslice_len`` -- a pointer to a vector of size_t to store the length of + the x-slices associated with the lexBFS traversal. ``xslice_len`` can be + ``NULL`` if the caller does not need it (and the information will not be + computed by the algorithm). At the end, if ``xslice_len`` is not NULL, the + vector pointer by it will have size `n` (the number of vertices of the + graph) and the length of the x-slice starting at sigma[i] will be + xslice_len[i]. + + - ``lex_label`` -- a pointer to a vector of vector[int] to store the + lexicographic labels associated with the lexBFS traversal. ``lex_label`` + can be ``NULL`` if the caller does not need it (and the information will + not be computed by the algorithm). At the end, if ``lex_label`` is not + NULL, the vector pointer by it will have size `n` (the number of + vertices of the graph) and the lexicographic label of sigma[i] + will given by lex_label[i]. + + ALGORITHM: + + This algorithm uses the notion of *partition refinement* to determine the + exact position of the vertices in the ordering. + + Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define + `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset + of neighbors of `v` appearing before the `i`-th vertex in the ordering + `\sigma`. Now, a part of an ordering `\sigma` is a set of consecutive + vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in + S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < + \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a part is the + first position of its vertices. + + The algorithm starts with a single part containing all vertices. Then, when + the position of the `i`-th vertex `v` is fixed, it explores the neighbors of + `v` that have not yet been ordered. Consider a part `S` such that `N(x)\cap + S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices + in `S` so that the first vertices are the neighbors of `v`. The subpart + containing the neighbors of `v` is assigned a new name, and the head of `S` + is set to the position of the first vertex of `S \setminus N(v)` in the + ordering `\sigma`. + + Observe that each arc of the graph can induce the subdivision of a part. + Hence, the algorithm can use up to `m + 1` different parts. + + The time complexity of this algorithm is in `O(n + m)`, and our + implementation follows that complexity ``SparseGraph``. For ``DenseGraph``, + the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more + details. + + This implementation of extended LexBFS offers some guarantee on the order in + which the vertices appear in the computed ordering: in case of a tie between + lexicographic labels during the computation, this function will "choose" the + vertices in the order in which they appear during the enumeration of the + neighbors of their last common neighbor. + For example, if `(u_0, ..., u_k)` is the beginning of the ordering being + computed and the vertices `v` and `w` currently have the same lexicographic + label (it means that they have the same neighbors in `(u_0, ..., u_k)`). + Let call `u_j` their last neighbor in the current ordering (*i.e.*, for all + `i > j`, `u_i` is not a neighbor of `v` and `w`). This implementation + will choose `v` for the next vertex of the ordering if and only if `v` + appeared before `w` when the neighbors of `u_j` where enumerated. + + One possible use of this guarantee is that the caller can reorder the + adjacency list of vertices (by using, for example, a static sparse graph) + to force the computed LexBFS order to respect a previous one. + + EXAMPLE: + + To see how it can be used, see the code of the lex_BFS method (in + traversals.pyx) or of the class SliceDecomposition in this module. + + """ + cdef int n = cg.num_verts + # Variables for the partition refinement algorithm + cdef size_t max_nparts = cg.num_arcs // 2 + 1 + cdef bint need_to_delete_sigma_inv = sigma_inv == NULL + if sigma_inv == NULL: + sigma_inv = new vector[int]() + cdef vector[size_t] part_of = vector[size_t](n, 0) + cdef vector[size_t] part_len # only used if xslice_len != NULL (see below) + cdef vector[size_t] part_head = vector[size_t](max_nparts) + cdef vector[size_t] subpart = vector[size_t](max_nparts) + cdef size_t p, part_of_i, nparts, old_nparts + # Temporary variables + cdef int max_degree = 0 + cdef int i, j, k, l, u_int, v_int, t_int + + # Resize vectors + sigma.resize(n) + deref(sigma_inv).resize(cg.active_vertices.size) + if pred != NULL: + deref(pred).clear() + deref(pred).resize(n, -1) # initialize pred[i] to -1 for 0 <= i < n + if xslice_len != NULL: + deref(xslice_len).resize(n) + part_len.resize(max_nparts) + if lex_label != NULL: + deref(lex_label).resize(n) + + # Initialize the position of vertices in sigma (and compute max_degree) + if initial_v_int >= 0: + sigma[0] = initial_v_int + deref(sigma_inv)[initial_v_int] = 0 + i = 1 + else: + i = 0 + for v_int in range( cg.active_vertices.size): + if bitset_in(cg.active_vertices, v_int): + if v_int != initial_v_int: + sigma[i] = v_int + deref(sigma_inv)[v_int] = i + i = i + 1 + max_degree = max(max_degree, cg.out_degrees[v_int]) + + # Variables needed to iterate over neighbors of a vertex + cdef int nneighbors + cdef vector[int] neighbors = vector[int](max_degree) + + # Initialize partition: one part containing all the vertices + nparts = 1 + # all element of part_of are already initialized to 0 + part_head[0] = 0 + subpart[0] = 0 + if xslice_len != NULL: + part_len[0] = n + + # Main loop + for i in range(n): + old_nparts = nparts + + part_of_i = part_of[i] + + # put i out of its part (updating part_len if needed) + part_head[part_of_i] += 1 + if xslice_len != NULL: + deref(xslice_len)[i] = part_len[part_of_i] + part_len[part_of_i] -= 1 + + v_int = sigma[i] + + # Iterate over the neighbors of v + nneighbors = cg.out_neighbors_unsafe (v_int, neighbors.data(), max_degree) + for k in range(nneighbors): + u_int = neighbors[k] + j = deref(sigma_inv)[u_int] # get the position of u + if j <= i: + continue # already taken care of + + if lex_label != NULL: + deref(lex_label)[j].push_back (v_int) + + p = part_of[j] # get the part of u + l = part_head[p] # get the beginning of the part containing u + + # if not last and next elem belongs in the same part (ie #part >= 2) + if l < n - 1 and part_of[l + 1] == p: + if l != j: # not already first elem of the part + # Place u at the position of the head of the part + t_int = sigma[l] + deref(sigma_inv)[t_int], deref(sigma_inv)[u_int] = j, l + sigma[j], sigma[l] = t_int, u_int + if lex_label != NULL: + swap[vector[int]](deref(lex_label)[j], + deref(lex_label)[l]) + j = l + part_head[p] += 1 # move the head of the part to next elem + + # if part p was not already cut in two during this iteration, we + # create a new part using subpart + if subpart[p] < old_nparts: + subpart[p] = nparts + part_head[nparts] = j + if xslice_len != NULL: + part_len[nparts] = 0 + subpart[nparts] = 0 + nparts += 1 + + # Finally, we update the name of the part for position j and set v + # as predecessor of u + part_of[j] = subpart[p] + if xslice_len != NULL: + part_len[p] -= 1 + part_len[subpart[p]] += 1 + if pred != NULL: + deref(pred)[j] = i + + if need_to_delete_sigma_inv: + del sigma_inv + +def slice_decomposition(G, initial_vertex=None): + r""" + Compute a slice decomposition of the simple undirected graph + + INPUT: + + - ``G`` -- a Sage graph. + + - ``initial_vertex`` -- (default: ``None``); the first vertex to consider. + + OUTPUT: + + An object of type :class:`~sage.graphs.graph_decompositions.slice_decomposition.SliceDecomposition` + that represents a slice decomposition of ``G`` + + .. NOTE:: + + Loops and multiple edges are ignored during the computation of the slice + decomposition. + + ALGORITHM: + + The method use the algorithm based on "partition refinement" described in + [HMPV2000]_ and [TCHP2008]_. + The time complexity of this algorithm is in `O(n + m)`, and our + implementation follows that complexity for ``SparseGraph``. For + ``DenseGraph``, the complexity is `O(n^2)`. + + EXAMPLES: + + Slice decomposition of the Petersen Graph:: + + sage: G = graphs.PetersenGraph() + sage: SD = G.slice_decomposition(); SD + [0[1[4[5]]][2[6]][3][9][7][8]] + + The graph can have loops or multiple edges but they are ignored:: + + sage: H = Graph(G,loops=True,multiedges=True) + sage: H.add_edges([(4, 4), (2, 2), (1, 6)]) + sage: SD2 = H.slice_decomposition() + sage: SD2 == SD + True + sage: SD2.underlying_graph() == G.to_simple(immutable=True) + True + + The tree corresponding to the slice decomposition can be displayed using + ``view``:: + + sage: view(G) # not tested + sage: latex(G) # to obtain the corresponding LaTeX code + \begin{tikzpicture} + ... + \end{tikzpicture} + + Slice decompositions are only defined for undirected graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import slice_decomposition + sage: slice_decomposition(DiGraph()) + Traceback (most recent call last): + ... + ValueError: Graph must be undirected + + """ + return SliceDecomposition(G, initial_vertex=initial_vertex) + + +cdef class SliceDecomposition(SageObject): + + def __init__(self, G, initial_vertex=None): + r""" + Represents a slice decomposition of a simple directed graph. + + INPUT: + + - ``G`` -- a Sage graph. + + - ``initial_vertex`` -- (default: ``None``); the first vertex to + consider. + + See :meth:`~slice_decomposition` for more details. + + .. automethod:: __getitem__ + """ + if G.is_directed(): + raise ValueError("Graph must be undirected") + + if initial_vertex is not None and initial_vertex not in G: + raise LookupError(f"vertex ({initial_vertex}) is not a vertex of the graph") + + cdef CGraphBackend Gbackend = G._backend + cdef CGraph cg = Gbackend.cg() + + self._graph_class = type(G) + + cdef int initial_v_int + if initial_vertex is not None: + # we already checked that initial_vertex is in G + initial_v_int = Gbackend.get_vertex(initial_vertex) + else: + initial_v_int = -1 + + cdef vector[int] sigma + cdef vector[vector[int]] lex_label + + # Compute the slice decomposition using the extended lexBFS algorithm + extended_lex_BFS(cg, sigma, NULL, initial_v_int, NULL, + &(self.xslice_len), &lex_label) + + # Translate the results with the actual vertices of the graph + self.sigma = tuple(Gbackend.vertex_label(v_int) for v_int in sigma) + self.sigma_inv = {v: i for i, v in enumerate(self.sigma)} + self.lex_label = {i: tuple(Gbackend.vertex_label(v_int) for v_int in lli) + for i, lli in enumerate(lex_label)} + + def __eq__(self, other): + """ + TESTS: + + sage: G = graphs.PetersenGraph() + sage: SD = G.slice_decomposition() + sage: SD == SD + True + sage: SD == G.slice_decomposition() + True + + sage: P3 = graphs.PathGraph(3) + sage: SD1 = P3.slice_decomposition(initial_vertex=0) + sage: SD2 = P3.slice_decomposition(initial_vertex=2) + sage: SD1 == SD2 + False + sage: SD3 = graphs.CompleteGraph(3).slice_decomposition() + sage: SD1 == SD3 # same lexBFS but different slice for 1 + False + sage: SD4 = Graph([(0,1), (0,2)]).slice_decomposition() + sage: SD3 == SD4 # same lexBFS and slices but different active edges + False + """ + if not isinstance(other, type(self)): + return False + + cdef SliceDecomposition sd = other + + return self.sigma_inv == sd.sigma_inv \ + and self.lex_label == sd.lex_label \ + and self.xslice_len == sd.xslice_len + + def __hash__(self): + return hash((tuple(self.sigma_inv.items()), + tuple(self.lex_label.items()), + tuple(self.xslice_len))) + + def __getitem__(self, v): + r""" + Return the data about the x-slice of the vertex `v`. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A dictionnary with the keys: + + * ``"pivot"`` -- the vertex `v` given as parameter + + * ``"slice"`` -- the slice of `v` (see :meth:`~slice`) + + * ``"active_edges"`` -- the actives edges of `v` (see + :meth:`~active_edges`) + + * ``"lexicographic_label"`` -- the lexicographic label of `v` (see + :meth:`~lexicographic_label`) + + * ``"sequence"`` -- the x-slice sequence of `v` (see + :meth:`~xslice_sequence`) + + + This method can also be called via :meth:`xslice_data`. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_data('a') + {'active_edges': [('a', 'b'), + ('a', 'c'), + ('a', 'd'), + ('a', 'e'), + ('a', 'f'), + ('c', 'g'), + ('d', 'g'), + ('f', 'g')], + 'lexicographic_label': ['x'], + 'pivot': 'a', + 'sequence': [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']], + 'slice': ['a', 'b', 'c', 'd', 'e', 'f', 'g']} + sage: SD.xslice_data('u') + {'active_edges': [], + 'lexicographic_label': ['a', 'b', 'c', 'd', 'e', 'f', 'g'], + 'pivot': 'u', + 'sequence': [['u'], ['y', 'z']], + 'slice': ['u', 'y', 'z']} + + Some values of the returned dictionnary can be obtained via other + methods (:meth:`~slice`, :meth:`~xslice_sequence`, + :meth:`~active_edges`, :meth:`~lexicographic_label`):: + + sage: SD.slice('a') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + sage: SD.xslice_data('a')['slice'] + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + sage: SD.xslice_data('a')['sequence'] + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + + sage: SD.active_edges('b') == SD.xslice_data('b')['active_edges'] + True + + sage: SD.lexicographic_label('u') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + sage: SD.xslice_data('u')['lexicographic_label'] + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + TESTS: + + sage: G = graphs.RandomGNP(15, 0.3) + sage: SD = G.slice_decomposition() + sage: all(SD[v]['slice'] == SD.slice(v) for v in G) + True + sage: all(SD[v]['sequence'] == SD.xslice_sequence(v) for v in G) + True + sage: all(SD[v]['active_edges'] == SD.active_edges(v) for v in G) + True + sage: all(SD[v]['lexicographic_label'] == SD.lexicographic_label(v) for v in G) + True + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD['John'] + Traceback (most recent call last): + ... + LookupError: vertex (John) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice "\ + "decomposition") + i = self.sigma_inv[v] + return {'pivot': v, + 'slice': self._slice(i), + 'sequence': self._xslice_sequence(i), + 'lexicographic_label': self._xslice_lex_label(i), + 'active_edges': self._xslice_active_edges(i), + } + + def lexBFS_order(self): + r""" + Return the lexBFS order corresponding to the slice decomposition. + + EXAMPLES: + + :: + + sage: from sage.graphs.traversals import _is_valid_lex_BFS_order + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: SD.lexBFS_order() + [0, 1, 4, 5, 2, 6, 3, 9, 7, 8] + sage: _is_valid_lex_BFS_order(G, SD.lexBFS_order()) + True + + TESTS: + + sage: from sage.graphs.traversals import _is_valid_lex_BFS_order + sage: for _ in range(5): + ....: G = graphs.RandomGNP(15, 0.3) + ....: SD = G.slice_decomposition() + ....: _is_valid_lex_BFS_order(G, SD.lexBFS_order()) + True + True + True + True + True + + """ + return list(self.sigma) + + def xslice_data(self, v): + r""" + Return the data about the x-slice of the vertex `v`. + + This method is a wrapper around :meth:`SliceDecomposition.__getitem__` + + TESTS: + + sage: G = graphs.RandomGNP(15, 0.3) + sage: SD = G.slice_decomposition() + sage: all(SD[v] == SD.xslice_data(v) for v in G) + True + + """ + return self[v] + + def slice (self, v): + r""" + Return the slice of the vertex `v`. + + The slice of `v` is the list of vertices `u` such that the neighbors of + `u` that are before `v` in the lexBFS order are that same that the + neighbors of `v` that are before `v` in the lexBFS order (*i.e.*, the + lexicographic label of `v`). It can be shown that it is a factor of the + lexBFS order. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of vertices + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.slice('a') + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + The vertices of the slice have the same neighborhood "on the left":: + + sage: pos = lambda v: SD.lexBFS_order().index(v) + sage: lla = set(SD.lexicographic_label('a')) + sage: all(lla == {u for u in G.neighbors(v) if pos(u) < pos('a')} \ + ....: for v in SD.slice('a')) + True + + The slice is a factor of the lexBFS order:: + + sage: ''.join(SD.slice('a')) in ''.join(SD.lexBFS_order()) + True + + The slice of the initial vertex is the whole graph:: + + sage: SD.slice('x') == SD.lexBFS_order() + True + + TESTS: + + sage: SD.slice('Michael') + Traceback (most recent call last): + ... + LookupError: vertex (Michael) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice "\ + "decomposition") + i = self.sigma_inv[v] + return self._slice(i) + + def xslice_sequence(self, v): + r""" + Return the x-slice sequence of the vertex `v`. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of list corresponding to the x-slice sequence of ``v``. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_sequence('x') + [['x'], ['a', 'b', 'c', 'd', 'e', 'f', 'g'], ['u', 'y', 'z'], ['v', 'w']] + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + + The flatten x-slice sequence of a vertex corresponds to the slice of the + same vertex:: + + sage: from itertools import chain + sage: all(list(chain(*SD.xslice_sequence(v))) == SD.slice(v) \ + ....: for v in G) + True + + The first list of the sequence is always a singleton containing the + input vertex:: + + sage: all(SD.xslice_sequence(v)[0] == [v] for v in G) + True + + If the length of the slice if more than 1, the second list of the + sequence is either, all the remaining vertices of the slice of `v`, if + `v` is isolated in the subgraph induced by the slice of `v`, or the + neighbors of `v` in the subgraph induced by the slice of `v`:: + + sage: all(SD.xslice_sequence(v)[1] == SD.slice(v)[1:] for v in G \ + ....: if G.subgraph(SD.slice(v)).degree(v) == 0 \ + ....: and len(SD.slice(v)) > 1) + True + sage: for v in G: + ....: if len(SD.slice(v)) > 1: + ....: xslice_seq = SD.xslice_sequence(v) + ....: S = G.subgraph(SD.slice(v)) + ....: if S.degree(v) > 0: + ....: set(xslice_seq[1]) == set(S.neighbor_iterator(v)) + True + True + True + True + + TESTS: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.xslice_sequence('Terry') + Traceback (most recent call last): + ... + LookupError: vertex (Terry) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice "\ + "decomposition") + i = self.sigma_inv[v] + return self._xslice_sequence(i) + + def lexicographic_label(self, v): + r""" + Return the lexicographic label of the vertex `v`. + + The lexicographic label of a vertex `v` is the list of all the + neighbors of `v` that appear before `v` in the lexBFS ordering + corresponding to the slice decomposition. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of vertices. + + EXAMPLES: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.lexicographic_label('f') + ['x', 'a', 'c', 'd'] + sage: pos = lambda v: SD.lexBFS_order().index(v) + sage: set(SD.lexicographic_label('f')) \ + ....: == {v for v in G.neighbors('f') if pos(v) < pos('f')} + True + + TESTS: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.lexicographic_label('Eric') + Traceback (most recent call last): + ... + LookupError: vertex (Eric) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice "\ + "decomposition") + i = self.sigma_inv[v] + return self._xslice_lex_label(i) + + def active_edges(self, v): + r""" + Return the active edges of the vertex `v`. + + An edge `(u, w)` is said to be active for `v` if `u` and `w` belongs + to two differents slices of the x-slice sequence of `v`. Note that it + defines a partition of the edges of the underlying graph. + + INPUT: + + - ``v`` -- a vertex of the graph corresponding to the slice + decomposition. + + OUTPUT: + + A list of edges + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.xslice_sequence('a') + [['a'], ['b', 'c', 'd', 'e', 'f'], ['g']] + sage: ('c', 'g') in SD.active_edges('a') + True + sage: ('a', 'c') in SD.active_edges('a') + True + sage: ('c', 'd') in SD.active_edges('a') # c and d in the same slice + False + sage: ('a', 'u') in SD.active_edges('a') # u not in x-slice of a + False + + The set of active edges of every vertex is a partition of the edges:: + + sage: from itertools import chain + sage: E = list(chain(*(SD.active_edges(v) for v in G))) + sage: G.size() == len(E) == len(set(E)) \ + ....: and all(G.has_edge(u, w) for v in G for u, w in SD.active_edges(v)) + True + + TESTS: + + sage: SD = graphs.PetersenGraph().slice_decomposition() + sage: SD.active_edges('Graham') + Traceback (most recent call last): + ... + LookupError: vertex (Graham) does not appear in the slice decomposition + """ + if v not in self.sigma_inv: + raise LookupError(f"vertex ({v}) does not appear in the slice "\ + "decomposition") + i = self.sigma_inv[v] + return self._xslice_active_edges(i) + + def _slice(self, idx): + r""" + This method is for internal use only + """ + return list(self.sigma[idx:idx+self.xslice_len[idx]]) + + def _xslice_sequence(self, idx): + r""" + This method is for internal use only + """ + l = self.xslice_len[idx] + S = [ [self.sigma[idx]] ] + j = idx + 1 + while j < idx + l: + lj = self.xslice_len[j] + S.append(list(self.sigma[j:j+lj])) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return S + + def _xslice_lex_label(self, idx): + r""" + This method is for internal use only + """ + return list(self.lex_label[idx]) + + def _xslice_active_edges(self, idx): + r""" + This method is for internal use only + """ + l = self.xslice_len[idx] + llv_prefix = len(self.lex_label[idx]) + A = [] + j = idx + 1 + while j < idx + l: + lj = self.xslice_len[j] + llj = self.lex_label[j] + for u in self.sigma[j:j+lj]: + for w in llj[llv_prefix:]: + A.append((w, u)) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return A + + def underlying_graph(self): + r""" + Return the underlying graph corresponding to the slice decomposition. + + If `G` was the graph given as parameter to compute the slice + decomposition, the underlying graph corresponds to ``G.to_simple()``, + *i.e.*, it is the input graph without loops and multiple edges. + + EXAMPLES: + + :: + + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x') + sage: SD.underlying_graph() == G + True + + The graph can have loops or multiple edges but they are ignored:: + + sage: G = graphs.CubeConnectedCycle(2) # multiple edges + sage: SD = G.slice_decomposition() + sage: SD.underlying_graph() == G.to_simple(immutable=True) + True + + sage: G = graphs.CubeConnectedCycle(1) # loops + sage: SD = G.slice_decomposition() + sage: SD.underlying_graph() == G.to_simple(immutable=True) + True + + TESTS: + + sage: for _ in range(5): + ....: G = graphs.RandomGNP(15, 0.3) + ....: SD = G.slice_decomposition() + ....: SD.underlying_graph() == G + True + True + True + True + True + """ + if not hasattr(self, '_underlying_graph'): + vertices = self.sigma + edges = [(u, v) for i, v in enumerate(self.sigma) + for u in self.lex_label[i]] + data = [vertices, edges] + Gclass = self._graph_class + self._underlying_graph = Gclass(data, format='vertices_and_edges', + immutable=True) + return self._underlying_graph + + def _repr_(self): + def inner_repr(idx): + l = self.xslice_len[idx] + S = [] + if l > 1: + j = idx + 1 + while j < idx + l: + lj = self.xslice_len[j] + S.append(inner_repr(j)) + j += lj + assert j == idx + l, "slice decomposition is ill-formed" + return f'{self.sigma[idx]}' + ''.join(f'[{s}]' for s in S) + return f'[{inner_repr(0)}]' + + def _latex_(self): + r""" + TESTS: + + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: latex(SD) + \begin{tikzpicture} + ... + v0 -- {l0, v1, v4, v6, v7, v8, v9}; + v1 -- {l1, v2}; + v2 -- {l2, v3}; + v3 -- {l3}; + v4 -- {l4, v5}; + v5 -- {l5}; + v6 -- {l6}; + v7 -- {l7}; + v8 -- {l8}; + v9 -- {l9}; + ... + \end{tikzpicture} + + """ + from sage.misc.latex import latex + + latex.add_package_to_preamble_if_available("tikz") + latex.add_to_preamble(r"\usetikzlibrary{arrows,shapes,fit}") + latex.add_to_preamble(r"\usetikzlibrary{graphs,graphdrawing}") + latex.add_to_preamble(r"\usegdlibrary{trees}") + + # Call latex() on all vertices + sigma_latex = [ latex(v) for v in self.sigma ] + slices = [[] for _ in self.sigma] + + lines = [ r"\begin{tikzpicture}" ] + lines.append(r"\graph [tree layout,level distance=0,level sep=1em,"\ + r"sibling distance=0,sibling sep=0.6em,"\ + r"tail anchor=center,head anchor=north,"\ + r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]") + lines.append("{") + bo, bc = "{", "}" # to write { and } if f-strings + # Create the nodes and leaves of the slice decomposition tree + for i in range(len(self.sigma)): + l = self.xslice_len[i] + label = r"\ ".join(sigma_latex[i:i+l]) + lines.append(f" v{i}[as={bo}{label}{bc}];") + lines.append(f" l{i}[draw=none,as={bo}{sigma_latex[i]}{bc}];") + j = i + 1 + slices[i].append(f"l{i}") + while j < i + l: + slices[i].append(f"v{j}") + j += self.xslice_len[j] + # Create the edges of the slice decomposition tree + for i, S in enumerate(slices): + lines.append(f" v{i} -- " + "{" + ", ".join(S) + "};") + lines.append("};") + # Add dahsed red boxes around xslices + for i, S in enumerate(slices): + fit=" ".join(f"({s})" for s in S) + lines.append(rf"\node (s{i}) [rectangle,inner xsep=0.2em,draw=red,"\ + f"densely dashed,fit={fit}]{bo}{bc};") + + lines.append(r"\end{tikzpicture}") + return "\n".join(lines) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 4b9ba208b01..3e806e28817 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -20,12 +20,37 @@ Graph traversals :meth:`~maximum_cardinality_search` | Return an ordering of the vertices according a maximum cardinality search. :meth:`~maximum_cardinality_search_M` | Return the ordering and the edges of the triangulation produced by MCS-M. + +ALGORITHM: + +For :meth:`~lex_BFS` with ``algorithm="slow"``, :meth:`~lex_DFS`, +:meth:`~lex_UP` and :meth:`~lex_DOWN` the same generic implementation is used. +It corresponds to an implementation the generic algorithm described in +"Algorithm 1" of [Mil2017]_. + +This algorithm maintains for each vertex left in the graph a lexicographic label +corresponding to the vertices already removed. The vertex of maximal +lexicographic label is then removed, and the lexicographic labels of its +neighbors are updated. Depending on how the update is done, it corresponds to +LexBFS, LexUP, LexDFS or LexDOWN: during the `i`-th iteration of the algorithm +`n-i` (for LexBFS and LexDOWN) or `i` (for LexDFS and LexUP) is appended (for +LexBFS and LexUP) or prepended (for LexDFS and LexDOWN) to the lexicographic +labels of all neighbors of the selected vertex that are left in the graph. + +The time complexity of the algorithm is `O(mn)` for ``SparseGraph`` and +`O(max(mn, n^2))` for ``DenseGraph``, where `n` is the number of vertices +and `m` is the number of edges. + +See [CK2008]_ and [Mil2017]_ for more details on the algorithm and graphs +searching. + Methods ------- """ # **************************************************************************** # Copyright (C) 2019 Georgios Giapitzakis Tzintanos # David Coudert +# 2024 Cyril Bouvier # # 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 @@ -47,15 +72,167 @@ from memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from sage.graphs.base.static_sparse_graph cimport out_degree +from sage.graphs.base.c_graph cimport CGraph, CGraphBackend +from sage.graphs.graph_decompositions.slice_decomposition cimport \ + extended_lex_BFS + +def _lex_order_common(G, algo, reverse, tree, initial_vertex): + r""" + Perform a lexicographic search (LexBFS, LexUP, LexDFS or LexDOWN) on the + graph. + + INPUT: + + - ``G`` -- a sage graph + + - ``algo`` -- string; the name of the actual algorithm among: + + - ``"lex_BFS"`` + + - ``"lex_UP"`` + + - ``"lex_DFS"`` + + - ``"lex_DOWN"`` + + - ``reverse`` -- whether to return the vertices in discovery order, or the + reverse + + - ``tree`` -- whether to return the discovery directed tree (each vertex + being linked to the one that saw it last) + + - ``initial_vertex`` -- the first vertex to consider, can be None + + .. NOTE:: + + Loops and multiple edges are ignored and directed graphs are considered + as undirected graphs. + + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + + TESTS: + + Lex ordering of a graph on one vertex:: + + sage: Graph(1).lex_BFS(tree=True, algorithm="slow") + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_UP(tree=True) + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_DFS(tree=True) + ([0], Digraph on 1 vertex) + sage: Graph(1).lex_DOWN(tree=True) + ([0], Digraph on 1 vertex) + + Lex ordering of an empty (di)graph is an empty sequence:: + + sage: g = Graph() + sage: g.lex_BFS(algorithm="slow") + [] + sage: g.lex_BFS(algorithm="slow", tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_UP() + [] + sage: g.lex_UP(tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_DFS() + [] + sage: g.lex_DFS(tree=True) + ([], Digraph on 0 vertices) + sage: g.lex_DOWN() + [] + sage: g.lex_DFS(tree=True) + ([], Digraph on 0 vertices) + + Lex UP ordering of a symmetric digraph should be the same as the Lex UP + ordering of the corresponding undirected graph:: + + sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) + sage: H = DiGraph(G) + sage: G.lex_BFS(algorithm="slow") == H.lex_BFS(algorithm="slow") + True + sage: G.lex_UP() == H.lex_UP() + True + sage: G.lex_DFS() == H.lex_DFS() + True + sage: G.lex_DOWN() == H.lex_DOWN() + True + + ``initial_vertex`` should be a valid graph vertex:: + + sage: G = graphs.CompleteGraph(6) + sage: G.lex_BFS(initial_vertex='foo', algorithm="slow") + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_UP(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_DFS(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + sage: G.lex_DOWN(initial_vertex='foo') + Traceback (most recent call last): + ... + ValueError: 'foo' is not a graph vertex + + """ + if initial_vertex is not None and initial_vertex not in G: + raise ValueError(f"'{initial_vertex}' is not a graph vertex") + + if algo not in ("lex_BFS", "lex_UP", "lex_DFS", "lex_DOWN"): + raise ValueError(f"unknown algorithm '{algo}'") + + if tree: + from sage.graphs.digraph import DiGraph + + cdef size_t n = G.order() + cdef list sigma = [] + cdef dict predecessor = {} + + cdef bint right = algo in ("lex_BFS", "lex_UP") + cdef bint decr = algo in ("lex_BFS", "lex_DOWN") + + cdef size_t l = n if decr else -1 + + # Perform the search + lexicographic_label = { u: deque() for u in G } + if initial_vertex is not None: + # append or appendleft does not matter here, as the deque is empty + lexicographic_label[initial_vertex].append(l) + while lexicographic_label: + u = max(lexicographic_label, key=lexicographic_label.get) + lexicographic_label.pop(u) + sigma.append(u) + l += -1 if decr else 1 + for v in G.neighbor_iterator(u): # graphs are considered undirected + if v in lexicographic_label: + if right: + lexicographic_label[v].append(l) + else: + lexicographic_label[v].appendleft(l) + predecessor[v] = u + + if reverse: + sigma.reverse() + + if tree: + edges = predecessor.items() + g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) + return sigma, g + return sigma def _is_valid_lex_BFS_order(G, L): r""" - Check whether `L` is a valid lex BFS ordering of the vertices of `G`. + Check whether ``L`` is a valid LexBFS ordering of the vertices of ``G``. Given two vertices `a` and `b` of `G = (V, E)`, we write `a < b` if `a` has a smaller label than `b`, and so if `a` is after `b` in the ordering `L`. - It is proved in [DNB1996]_ that any lex BFS ordering satisfies that, + It is proved in [DNB1996]_ that any LexBFS ordering satisfies that, if `a < b < c` and `ac \in E` and `bc \not\in E`, then there exists `d\in V` such that `c < d`, `db \in E` and `da \not\in E`. @@ -65,6 +242,21 @@ def _is_valid_lex_BFS_order(G, L): - ``L`` -- list; an ordering of the vertices of `G` + OUTPUT: + + - ``True`` if ``L`` is a LexBFS ordering of ``G``; ``False`` otherwise + + .. NOTE:: + + Loops and multiple edges are ignored for LexBFS ordering and directed + graphs are considered as undirected graphs. + + .. SEEALSO:: + + * :wikipedia:`Lexicographic_breadth-first_search` + * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_BFS` -- perform a + lexicographic depth first search (LexBFS) on the graph + TESTS:: sage: from sage.graphs.traversals import _is_valid_lex_BFS_order @@ -76,8 +268,19 @@ def _is_valid_lex_BFS_order(G, L): sage: G = DiGraph("I?O@??A?CCA?A??C??") sage: _is_valid_lex_BFS_order(G, [0, 7, 1, 2, 3, 4, 5, 8, 6, 9]) + False + sage: _is_valid_lex_BFS_order(G, G.lex_BFS()) + True + sage: H = G.to_undirected() + sage: _is_valid_lex_BFS_order(H, G.lex_BFS()) + True + sage: _is_valid_lex_BFS_order(G, H.lex_BFS()) True """ + # Convert G to a simple undirected graph + if G.has_loops() or G.has_multiple_edges() or G.is_directed(): + G = G.to_simple(immutable=False, to_undirected=True) + cdef int n = G.order() if set(L) != set(G): @@ -86,11 +289,9 @@ def _is_valid_lex_BFS_order(G, L): cdef dict L_inv = {u: i for i, u in enumerate(L)} cdef int pos_a, pos_b, pos_c - neighbors = G.neighbor_in_iterator if G.is_directed() else G.neighbor_iterator - for pos_a in range(n - 1, -1, -1): a = L[pos_a] - for c in neighbors(a): + for c in G.neighbor_iterator(a): pos_c = L_inv[c] if pos_c > pos_a: continue @@ -99,133 +300,15 @@ def _is_valid_lex_BFS_order(G, L): if G.has_edge(c, b): continue if any(L_inv[d] < pos_c and not G.has_edge(d, a) - for d in neighbors(b)): + for d in G.neighbor_iterator(b)): # The condition is satisfied for a < b < c continue return False return True -cdef lex_BFS_fast_short_digraph(short_digraph sd, uint32_t *sigma, uint32_t *pred): - r""" - Perform a lexicographic breadth first search (LexBFS) on the graph. - - This method implements the `O(n+m)` time algorithm proposed in [HMPV2000]_. - - The method assumes that the initial vertex is vertex `0` and feeds input - arrays ``sigma`` and ``pred`` with respectively the ordering of the vertices - and the predecessor in the traversal. - - This algorithm uses the notion of *slices*, i.e., subsets of consecutive - vertices in the ordering, and iteratively refines the slices by subdividing - them into sub-slices to determine the exact position of the vertices in the - ordering. - - Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define - `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset - of neighbors of `v` appearing before the `i`-th vertex in the ordering - `\sigma`. Now, a slice of an ordering `\sigma` is a set of consecutive - vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in - S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < - \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a slice is the - first position of its vertices. - - The algorithm starts with a single slice containing all vertices. Then, when - the position of the `i`-th vertex `v` is fixed, it explores the neighbors of - `v` that have not yet been ordered. Consider a slice `S` such that `N(x)\cap - S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices - in `S` so that the first vertices are the neighbors of `v`. The sub-slice - containing the neighbors of `v` is assigned a new slice name, and the head - of slice `S` is set to the position of the first vertex of `S \setminus - N(v)` in the ordering `\sigma`. - - Observe that each arc of the graph can induce the subdivision of a - slice. Hence, the algorithm can use up to `m + 1` different slices. - - INPUT: - - - ``sd`` -- a ``short_digraph`` - - - ``sigma`` -- array of size ``n`` to store the ordering of the vertices - resulting from the LexBFS traversal from vertex 0. This method assumes - that this array has already been allocated. However, there is no need to - initialize it. - - - ``pred`` -- array of size ``n`` to store the predecessor of a vertex in - the LexBFS traversal from vertex 0. This method assumes that this array - has already been allocated and initialized (e.g., ``pred[i] = i``). - - EXAMPLES: - - Lex BFS ordering of the 3-sun graph:: - - sage: g = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: g.lex_BFS(algorithm="fast") - [1, 2, 3, 5, 4, 6] - """ - cdef uint32_t n = sd.n - cdef uint32_t n_slice = sd.m + 1 - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint32_t *sigma_inv = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *slice_of = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *slice_head = mem.allocarray(n_slice, sizeof(uint32_t)) - cdef uint32_t *subslice = mem.allocarray(n_slice, sizeof(uint32_t)) - cdef uint32_t i, j, k, l, a, old_k, v - cdef int wi - - # Initialize slices (slice_of, slice_head, subslice) to 0 - memset(slice_of, 0, n * sizeof(uint32_t)) - slice_head[0] = 0 - subslice[0] = 0 - - # Initialize the position of vertices in sigma - for i in range(n): - sigma[i] = i - sigma_inv[i] = i - - k = 1 - for i in range(n): - old_k = k - v = sigma[i] - - # We update the labeling of all unordered neighbors of v - for wi in range(out_degree(sd, v)): - w = sd.neighbors[v][wi] - j = sigma_inv[w] - if j <= i: - # w has already been ordered - continue - - # Get the name of the slice for position j - a = slice_of[j] - if slice_head[a] <= i: - # This slice cannot start at a position less than i - slice_head[a] = i + 1 - - # Get the position of the head of the slice - l = slice_head[a] - if l < n - 1 and slice_of[l + 1] == a: - if l != j: - # Place w at the position of the head of the slice - u = sigma[l] - sigma_inv[u], sigma_inv[w] = j, l - sigma[j], sigma[l] = u, w - j = l - slice_head[a] += 1 - if subslice[a] < old_k: - # Form a new slice - subslice[a] = k - slice_head[k] = j - subslice[k] = 0 - k += 1 - - # Finally, we update the name of the slice for position j and set v - # as predecessor of w - slice_of[j] = subslice[a] - pred[w] = v - - -def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"): +def lex_BFS( + G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"): r""" Perform a lexicographic breadth first search (LexBFS) on the graph. @@ -238,59 +321,38 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``); the first vertex to consider - ``algorithm`` -- string (default: ``"fast"``); algorithm to use among: - - ``"slow"`` -- This algorithm maintains for each vertex left in the graph - a code corresponding to the vertices already removed. The vertex of - maximal code (according to the lexicographic order) is then removed, and - the codes are updated. See for instance [CK2008]_ for more details. The - time complexity of this algorithm as described in [CK2008]_ is in - `O(n + m)`, where `n` is the number of vertices and `m` is the number of - edges, but our implementation is in `O(n^2)`. + - ``"slow"`` -- It use the generic algorithm for all the lexicographic + searchs. See the documentation of the :mod:`~sage.graphs.traversals` + module for more details. - - ``"fast"`` -- This algorithm uses the notion of *slices* to refine the - position of the vertices in the ordering. The time complexity of this - algorithm is in `O(n + m)`, and our implementation follows that - complexity for ``SparseGraph``. For ``DenseGraph``, the complexity is - `O(n^2)`. See [HMPV2000]_ and next section for more details. + - ``"fast"`` -- This algorithm uses the notion of *partition refinement* + to determine the position of the vertices in the ordering. The time + complexity of this algorithm is in `O(n + m)`, and our implementation + follows that complexity ``SparseGraph``. For ``DenseGraph``, + the complexity is `O(n^2)`. See [HMPV2000]_ and [TCHP2008]_ for more + details. This algorithm is also used to compute slice decompositions of + undirected graphs, a more thorough description can be found in the + documentation of the + :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module. - ALGORITHM: + .. NOTE:: - The ``"fast"`` algorithm is the `O(n + m)` time algorithm proposed in - [HMPV2000]_, where `n` is the number of vertices and `m` is the number of - edges. It uses the notion of *slices*, i.e., subsets of consecutive vertices - in the ordering, and iteratively refines the slices by subdividing them into - sub-slices to determine the exact position of the vertices in the ordering. - - Consider an ordering `\sigma` of the vertices. For a vertex `v`, we define - `N_i(v) = \{u | u \in N(v) \text{ and } \sigma(u) < i\}`, that is the subset - of neighbors of `v` appearing before the `i`-th vertex in the ordering - `\sigma`. Now, a slice of an ordering `\sigma` is a set of consecutive - vertices, `S = \{u | i \leq \sigma(u) \leq j\}`, such that for any `u \in - S`, we have `N_i(u) = N_i(\sigma^{-1}(i))` and for any `v` such that `j < - \sigma(v)`, `N_i(v) \neq N_i(\sigma^{-1}(i))`. The *head* of a slice is the - first position of its vertices. - - The algorithm starts with a single slice containing all vertices. Then, when - the position of the `i`-th vertex `v` is fixed, it explores the neighbors of - `v` that have not yet been ordered. Consider a slice `S` such that `N(x)\cap - S \neq \emptyset`. The algorithm will rearrange the ordering of the vertices - in `S` so that the first vertices are the neighbors of `v`. The sub-slice - containing the neighbors of `v` is assigned a new slice name, and the head - of slice `S` is set to the position of the first vertex of `S \setminus - N(v)` in the ordering `\sigma`. - - Observe that each arc of the graph can induce the subdivision of a - slice. Hence, the algorithm can use up to `m + 1` different slices. + Loops and multiple edges are ignored during the computation of lex_BFS + and directed graphs are considered as undirected graphs. .. SEEALSO:: * :wikipedia:`Lexicographic_breadth-first_search` + * :mod:`~sage.graphs.graph_decompositions.slice_decomposition` module + and :meth:`~sage.graphs.graph.Graph.slice_decomposition` -- compute a + slice decomposition of the graph using an extended lex BFS algorithm * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DFS` -- perform a lexicographic depth first search (LexDFS) on the graph * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a @@ -303,7 +365,7 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") A Lex BFS is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_BFS()) == g.order() + sage: set(g.lex_BFS()) == set(g) True Lex BFS ordering of the 3-sun graph:: @@ -315,10 +377,11 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") The method also works for directed graphs:: sage: G = DiGraph([(1, 2), (2, 3), (1, 3)]) - sage: G.lex_BFS(initial_vertex=2, algorithm="slow") - [2, 3, 1] - sage: G.lex_BFS(initial_vertex=2, algorithm="fast") - [2, 3, 1] + sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]] + sage: G.lex_BFS(initial_vertex=2, algorithm="slow") in correct_anwsers + True + sage: G.lex_BFS(initial_vertex=2, algorithm="fast") in correct_anwsers + True For a Chordal Graph, a reversed Lex BFS is a Perfect Elimination Order:: @@ -375,105 +438,80 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") Lex BFS ordering of a graph on one vertex:: - sage: Graph(1).lex_BFS(tree=True) + sage: Graph(1).lex_BFS(algorithm="fast", tree=True) ([0], Digraph on 1 vertex) Lex BFS ordering of an empty (di)graph is an empty sequence:: sage: g = Graph() - sage: g.lex_BFS() + sage: g.lex_BFS(algorithm="fast") [] + sage: g.lex_BFS(algorithm="fast", tree=True) + ([], Digraph on 0 vertices) Lex BFS ordering of a symmetric digraph should be the same as the Lex BFS ordering of the corresponding undirected graph:: sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) sage: H = DiGraph(G) - sage: G.lex_BFS() == H.lex_BFS() + sage: G.lex_BFS(algorithm="fast") == H.lex_BFS(algorithm="fast") True ``initial_vertex`` should be a valid graph vertex:: sage: G = graphs.CompleteGraph(6) - sage: G.lex_BFS(initial_vertex='foo') + sage: G.lex_BFS(algorithm="fast", initial_vertex='foo') Traceback (most recent call last): ... ValueError: 'foo' is not a graph vertex """ if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - if algorithm not in ['slow', 'fast']: - raise ValueError("unknown algorithm '{}'".format(algorithm)) - if tree: - from sage.graphs.digraph import DiGraph + raise ValueError(f"'{initial_vertex}' is not a graph vertex") - # Loops and multiple edges are not needed in Lex BFS - if G.has_loops() or G.has_multiple_edges(): - G = G.to_simple(immutable=False) + if algorithm == "slow": + return _lex_order_common(G, "lex_BFS", reverse, tree, initial_vertex) - cdef size_t n = G.order() - if not n: - return ([], DiGraph(sparse=True)) if tree else [] - - # Build adjacency list of G - cdef list int_to_v = list(G) + if algorithm != "fast": + raise ValueError(f"unknown algorithm '{algorithm}'") - # If an initial vertex is given, we place it at first position - cdef size_t i - if initial_vertex is not None: - i = int_to_v.index(initial_vertex) - int_to_v[0], int_to_v[i] = int_to_v[i], int_to_v[0] + if tree: + from sage.graphs.digraph import DiGraph - # Copying the whole graph to obtain the list of neighbors quicker than by - # calling out_neighbors. This data structure is well documented in the - # module sage.graphs.base.static_sparse_graph - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) + cdef size_t n = G.order() - # Initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint32_t *sigma_int = mem.allocarray(n, sizeof(uint32_t)) - cdef uint32_t *pred = mem.allocarray(n, sizeof(uint32_t)) - for i in range(n): - pred[i] = i + # For algorithm "fast" we need to convert G to an undirected graph + if G.is_directed(): + G = G.to_undirected() + # Initialize variables needed by the fast and slow algorithms + cdef CGraphBackend Gbackend = G._backend + cdef CGraph cg = Gbackend.cg() cdef list sigma = [] - cdef set vertices - cdef list code - cdef int now, v, vi, int_neighbor + cdef dict predecessor = {} + # Initialize variables needed by the fast algorithm + cdef vector[int] sigma_int + cdef vector[int] pred + # Initialize variables needed by the slow algorithm + cdef dict lexicographic_label + # Temporary variables + cdef int vi, i, initial_v_int # Perform Lex BFS - if algorithm == "fast": - lex_BFS_fast_short_digraph(sd, sigma_int, pred) - sigma = [int_to_v[sigma_int[i]] for i in range(n)] - - else: # "slow" algorithm - vertices = set(range(n)) - code = [[] for i in range(n)] - - # The initial_vertex is at position 0 and so named 0 in sd - code[0].append(n + 1) - now = 1 - while vertices: - v = max(vertices, key=code.__getitem__) - vertices.remove(v) - sigma.append(int_to_v[v]) - for vi in range(out_degree(sd, v)): - int_neighbor = sd.neighbors[v][vi] - if int_neighbor in vertices: - code[int_neighbor].append(n - now) - pred[int_neighbor] = v - now += 1 - - free_short_digraph(sd) + if initial_vertex is not None: + # we already checked that initial_vertex is in G + initial_v_int = Gbackend.get_vertex(initial_vertex) + else: + initial_v_int = -1 + extended_lex_BFS(cg, sigma_int, NULL, initial_v_int, &pred, NULL, NULL); + sigma = [ Gbackend.vertex_label(vi) for vi in sigma_int ] + predecessor = { u: sigma[i] for u, i in zip(sigma, pred) if i != -1 } if reverse: sigma.reverse() if tree: - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(n) if pred[i] != i] + edges = predecessor.items() g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) return sigma, g return sigma @@ -492,25 +530,15 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``); the first vertex to consider - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. During the `i`-th iteration of the algorithm `i` is - appended to the codes of all neighbors of the selected vertex that are left - in the graph. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [Mil2017]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of lex_BFS + and directed graphs are considered as undirected graphs. .. SEEALSO:: @@ -521,12 +549,16 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a lexicographic DOWN search (LexDOWN) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex UP is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_UP()) == g.order() + sage: set(g.lex_UP()) == set(g) True Lex UP ordering of the 3-sun graph:: @@ -538,8 +570,9 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): The method also works for directed graphs:: sage: G = DiGraph([(1, 2), (2, 3), (1, 3)]) - sage: G.lex_UP(initial_vertex=2) - [2, 3, 1] + sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]] + sage: G.lex_UP(initial_vertex=2) in correct_anwsers + True Different orderings for different traversals:: @@ -554,103 +587,8 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - TESTS: - - Lex UP ordering of a graph on one vertex:: - - sage: Graph(1).lex_UP(tree=True) - ([0], Digraph on 1 vertex) - - Lex UP ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_UP() - [] - - Lex UP ordering of a symmetric digraph should be the same as the Lex UP - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_UP() == H.lex_UP() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_UP(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex - """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Loops and multiple edges are not needed in Lex UP - if G.allows_loops() or G.allows_multiple_edges(): - G = G.to_simple(immutable=False) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex UP - - cdef list code = [[] for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # Initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].append(nV + 1) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].append(now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_UP", reverse, tree, initial_vertex) def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): @@ -666,24 +604,15 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it last) - ``initial_vertex`` -- (default: ``None``); the first vertex to consider - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. Lex DFS differs from Lex BFS only in the way codes are - updated after each iteration. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [CK2008]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of lex_BFS + and directed graphs are considered as undirected graphs. .. SEEALSO:: @@ -694,12 +623,16 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_DOWN` -- perform a lexicographic DOWN search (LexDOWN) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex DFS is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_DFS()) == g.order() + sage: set(g.lex_DFS()) == set(g) True Lex DFS ordering of the 3-sun graph:: @@ -711,8 +644,9 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): The method also works for directed graphs:: sage: G = DiGraph([(1, 2), (2, 3), (1, 3)]) - sage: G.lex_DFS(initial_vertex=2) - [2, 3, 1] + sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]] + sage: G.lex_DFS(initial_vertex=2) in correct_anwsers + True Different orderings for different traversals:: @@ -727,104 +661,8 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - TESTS: - - Lex DFS ordering of a graph on one vertex:: - - sage: Graph(1).lex_DFS(tree=True) - ([0], Digraph on 1 vertex) - - Lex DFS ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_DFS() - [] - - Lex DFS ordering of a symmetric digraph should be the same as the Lex DFS - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_DFS() == H.lex_DFS() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_DFS(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex - """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Loops and multiple edges are not needed in Lex DFS - if G.allows_loops() or G.allows_multiple_edges(): - G = G.to_simple(immutable=False) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex DFS - - # We are using deque in order to prepend items in list efficiently - cdef list code = [deque([]) for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].appendleft(0) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].appendleft(now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_DFS", reverse, tree, initial_vertex) def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): @@ -840,25 +678,15 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): - ``tree`` -- boolean (default: ``False``); whether to return the discovery directed tree (each vertex being linked to the one that saw - it for the first time) + it) - ``initial_vertex`` -- (default: ``None``); the first vertex to consider - ALGORITHM: - - This algorithm maintains for each vertex left in the graph a code - corresponding to the vertices already removed. The vertex of maximal - code (according to the lexicographic order) is then removed, and the - codes are updated. During the `i`-th iteration of the algorithm `n-i` is - prepended to the codes of all neighbors of the selected vertex that are left - in the graph. - - Time complexity is `O(n+m)` for ``SparseGraph`` and `O(n^2)` for - ``DenseGraph`` where `n` is the number of vertices and `m` is the number of - edges. + .. NOTE:: - See [Mil2017]_ for more details on the algorithm. + Loops and multiple edges are ignored during the computation of lex_BFS + and directed graphs are considered as undirected graphs. .. SEEALSO:: @@ -869,12 +697,16 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): * :meth:`~sage.graphs.generic_graph.GenericGraph.lex_UP` -- perform a lexicographic UP search (LexUP) on the graph + ALGORITHM: + + See the documentation of the :mod:`~sage.graphs.traversals` module. + EXAMPLES: A Lex DOWN is obviously an ordering of the vertices:: sage: g = graphs.CompleteGraph(6) - sage: len(g.lex_DOWN()) == g.order() + sage: set(g.lex_DOWN()) == set(g) True Lex DOWN ordering of the 3-sun graph:: @@ -886,8 +718,9 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): The method also works for directed graphs:: sage: G = DiGraph([(1, 2), (2, 3), (1, 3)]) - sage: G.lex_DOWN(initial_vertex=2) - [2, 3, 1] + sage: correct_anwsers = [[2, 1, 3], [2, 3, 1]] + sage: G.lex_DOWN(initial_vertex=2) in correct_anwsers + True Different orderings for different traversals:: @@ -902,104 +735,8 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): sage: G.lex_DOWN(initial_vertex='000') ['000', '001', '100', '011', '010', '110', '111', '101'] - TESTS: - - Lex DOWN ordering of a graph on one vertex:: - - sage: Graph(1).lex_DOWN(tree=True) - ([0], Digraph on 1 vertex) - - Lex DOWN ordering of an empty (di)graph is an empty sequence:: - - sage: g = Graph() - sage: g.lex_DOWN() - [] - - Lex DOWN ordering of a symmetric digraph should be the same as the Lex DOWN - ordering of the corresponding undirected graph:: - - sage: G = Graph([(1, 2), (1, 3), (2, 3), (2, 4), (2, 5), (3, 5), (3, 6), (4, 5), (5, 6)]) - sage: H = DiGraph(G) - sage: G.lex_DOWN() == H.lex_DOWN() - True - - ``initial_vertex`` should be a valid graph vertex:: - - sage: G = graphs.CompleteGraph(6) - sage: G.lex_DOWN(initial_vertex='foo') - Traceback (most recent call last): - ... - ValueError: 'foo' is not a graph vertex - """ - if initial_vertex is not None and initial_vertex not in G: - raise ValueError("'{}' is not a graph vertex".format(initial_vertex)) - - # Loops and multiple edges are not needed in Lex DOWN - if G.allows_loops() or G.allows_multiple_edges(): - G = G.to_simple(immutable=False) - - cdef int nV = G.order() - - if not nV: - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - return [], g - return [] - - # Build adjacency list of G - cdef list int_to_v = list(G) - - cdef short_digraph sd - init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_v, - sort_neighbors=False) - - # Perform Lex DOWN - - # We are using deque in order to prepend items in list efficiently - cdef list code = [deque([]) for i in range(nV)] - - def l_func(x): - return code[x] - - cdef list value = [] - - # initialize the predecessors array - cdef MemoryAllocator mem = MemoryAllocator() - cdef int *pred = mem.allocarray(nV, sizeof(int)) - memset(pred, -1, nV * sizeof(int)) - - cdef set vertices = set(range(nV)) - - cdef int source = 0 if initial_vertex is None else int_to_v.index(initial_vertex) - code[source].appendleft(0) - - cdef int now = 1, v, int_neighbor - while vertices: - v = max(vertices, key=l_func) - vertices.remove(v) - for i in range(0, out_degree(sd, v)): - int_neighbor = sd.neighbors[v][i] - if int_neighbor in vertices: - code[int_neighbor].appendleft(nV - now) - pred[int_neighbor] = v - value.append(int_to_v[v]) - now += 1 - - free_short_digraph(sd) - - if reverse: - value.reverse() - - if tree: - from sage.graphs.digraph import DiGraph - g = DiGraph(sparse=True) - g.add_vertices(G) - edges = [(int_to_v[i], int_to_v[pred[i]]) for i in range(nV) if pred[i] != -1] - g.add_edges(edges) - return value, g - return value + return _lex_order_common(G, "lex_DOWN", reverse, tree, initial_vertex) def lex_M(self, triangulation=False, labels=False, initial_vertex=None, algorithm=None): From bca274eea9eebb94b2b78186a64c8270051c0162 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Fri, 28 Jun 2024 09:49:01 +0200 Subject: [PATCH 008/193] Fix typo: EXAMPLES instead of EXAMPLE --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 08b2e712fc3..2de202e6912 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -134,7 +134,7 @@ cdef void extended_lex_BFS( adjacency list of vertices (by using, for example, a static sparse graph) to force the computed LexBFS order to respect a previous one. - EXAMPLE: + EXAMPLES: To see how it can be used, see the code of the lex_BFS method (in traversals.pyx) or of the class SliceDecomposition in this module. From 62ff2b7c0abf1c03a2f6f2d2304c0c085590ae6d Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Fri, 28 Jun 2024 10:05:07 +0200 Subject: [PATCH 009/193] Fix errors detected by linter --- .../slice_decomposition.pyx | 18 +++++++++--------- src/sage/graphs/traversals.pyx | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 2de202e6912..0725800a60d 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -506,7 +506,7 @@ cdef class SliceDecomposition(SageObject): LookupError: vertex (John) does not appear in the slice decomposition """ if v not in self.sigma_inv: - raise LookupError(f"vertex ({v}) does not appear in the slice "\ + raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") i = self.sigma_inv[v] return {'pivot': v, @@ -617,7 +617,7 @@ cdef class SliceDecomposition(SageObject): LookupError: vertex (Michael) does not appear in the slice decomposition """ if v not in self.sigma_inv: - raise LookupError(f"vertex ({v}) does not appear in the slice "\ + raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") i = self.sigma_inv[v] return self._slice(i) @@ -689,7 +689,7 @@ cdef class SliceDecomposition(SageObject): LookupError: vertex (Terry) does not appear in the slice decomposition """ if v not in self.sigma_inv: - raise LookupError(f"vertex ({v}) does not appear in the slice "\ + raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") i = self.sigma_inv[v] return self._xslice_sequence(i) @@ -731,7 +731,7 @@ cdef class SliceDecomposition(SageObject): LookupError: vertex (Eric) does not appear in the slice decomposition """ if v not in self.sigma_inv: - raise LookupError(f"vertex ({v}) does not appear in the slice "\ + raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") i = self.sigma_inv[v] return self._xslice_lex_label(i) @@ -787,7 +787,7 @@ cdef class SliceDecomposition(SageObject): LookupError: vertex (Graham) does not appear in the slice decomposition """ if v not in self.sigma_inv: - raise LookupError(f"vertex ({v}) does not appear in the slice "\ + raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") i = self.sigma_inv[v] return self._xslice_active_edges(i) @@ -935,9 +935,9 @@ cdef class SliceDecomposition(SageObject): slices = [[] for _ in self.sigma] lines = [ r"\begin{tikzpicture}" ] - lines.append(r"\graph [tree layout,level distance=0,level sep=1em,"\ - r"sibling distance=0,sibling sep=0.6em,"\ - r"tail anchor=center,head anchor=north,"\ + lines.append(r"\graph [tree layout,level distance=0,level sep=1em," + r"sibling distance=0,sibling sep=0.6em," + r"tail anchor=center,head anchor=north," r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]") lines.append("{") bo, bc = "{", "}" # to write { and } if f-strings @@ -959,7 +959,7 @@ cdef class SliceDecomposition(SageObject): # Add dahsed red boxes around xslices for i, S in enumerate(slices): fit=" ".join(f"({s})" for s in S) - lines.append(rf"\node (s{i}) [rectangle,inner xsep=0.2em,draw=red,"\ + lines.append(rf"\node (s{i}) [rectangle,inner xsep=0.2em,draw=red," f"densely dashed,fit={fit}]{bo}{bc};") lines.append(r"\end{tikzpicture}") diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 3e806e28817..a3fcdbaf9c5 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -503,7 +503,7 @@ def lex_BFS( initial_v_int = Gbackend.get_vertex(initial_vertex) else: initial_v_int = -1 - extended_lex_BFS(cg, sigma_int, NULL, initial_v_int, &pred, NULL, NULL); + extended_lex_BFS(cg, sigma_int, NULL, initial_v_int, &pred, NULL, NULL) sigma = [ Gbackend.vertex_label(vi) for vi in sigma_int ] predecessor = { u: sigma[i] for u, i in zip(sigma, pred) if i != -1 } From 86a9a7824293a653789b20c7584577f142df00bd Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Fri, 28 Jun 2024 10:52:59 +0200 Subject: [PATCH 010/193] Trying to fix failing tests with latex repr (by copying graph_latex.py) --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 0725800a60d..252dff4e9a8 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -306,6 +306,8 @@ def slice_decomposition(G, initial_vertex=None): The tree corresponding to the slice decomposition can be displayed using ``view``:: + sage: from sage.graphs.graph_latex import check_tkz_graph + sage: check_tkz_graph() # random - depends on Tex installation sage: view(G) # not tested sage: latex(G) # to obtain the corresponding LaTeX code \begin{tikzpicture} @@ -905,6 +907,8 @@ cdef class SliceDecomposition(SageObject): r""" TESTS: + sage: from sage.graphs.graph_latex import check_tkz_graph + sage: check_tkz_graph() # random - depends on Tex installation sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() sage: latex(SD) \begin{tikzpicture} From efae289cb3a77d563a0d62e72fc678b36c3a1ef1 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 14:24:03 +0200 Subject: [PATCH 011/193] EXAMPLES: -> EXAMPLES:: --- .../slice_decomposition.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 252dff4e9a8..0115b34a628 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -134,7 +134,7 @@ cdef void extended_lex_BFS( adjacency list of vertices (by using, for example, a static sparse graph) to force the computed LexBFS order to respect a previous one. - EXAMPLES: + EXAMPLES:: To see how it can be used, see the code of the lex_BFS method (in traversals.pyx) or of the class SliceDecomposition in this module. @@ -285,7 +285,7 @@ def slice_decomposition(G, initial_vertex=None): implementation follows that complexity for ``SparseGraph``. For ``DenseGraph``, the complexity is `O(n^2)`. - EXAMPLES: + EXAMPLES:: Slice decomposition of the Petersen Graph:: @@ -440,7 +440,7 @@ cdef class SliceDecomposition(SageObject): This method can also be called via :meth:`xslice_data`. - EXAMPLES: + EXAMPLES:: :: @@ -522,7 +522,7 @@ cdef class SliceDecomposition(SageObject): r""" Return the lexBFS order corresponding to the slice decomposition. - EXAMPLES: + EXAMPLES:: :: @@ -584,7 +584,7 @@ cdef class SliceDecomposition(SageObject): A list of vertices - EXAMPLES: + EXAMPLES:: :: @@ -637,7 +637,7 @@ cdef class SliceDecomposition(SageObject): A list of list corresponding to the x-slice sequence of ``v``. - EXAMPLES: + EXAMPLES:: :: @@ -713,7 +713,7 @@ cdef class SliceDecomposition(SageObject): A list of vertices. - EXAMPLES: + EXAMPLES:: sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) sage: SD = G.slice_decomposition(initial_vertex='x') @@ -755,7 +755,7 @@ cdef class SliceDecomposition(SageObject): A list of edges - EXAMPLES: + EXAMPLES:: :: @@ -846,7 +846,7 @@ cdef class SliceDecomposition(SageObject): decomposition, the underlying graph corresponds to ``G.to_simple()``, *i.e.*, it is the input graph without loops and multiple edges. - EXAMPLES: + EXAMPLES:: :: From 805d899755bf2fc2ae1c3070a6228f4d168f6f88 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 14:30:31 +0200 Subject: [PATCH 012/193] PEP 0008: at least two spaces before # --- .../slice_decomposition.pyx | 38 +++++++++---------- src/sage/graphs/traversals.pyx | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 0115b34a628..142a0c01b46 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -147,7 +147,7 @@ cdef void extended_lex_BFS( if sigma_inv == NULL: sigma_inv = new vector[int]() cdef vector[size_t] part_of = vector[size_t](n, 0) - cdef vector[size_t] part_len # only used if xslice_len != NULL (see below) + cdef vector[size_t] part_len # only used if xslice_len != NULL (see below) cdef vector[size_t] part_head = vector[size_t](max_nparts) cdef vector[size_t] subpart = vector[size_t](max_nparts) cdef size_t p, part_of_i, nparts, old_nparts @@ -160,7 +160,7 @@ cdef void extended_lex_BFS( deref(sigma_inv).resize(cg.active_vertices.size) if pred != NULL: deref(pred).clear() - deref(pred).resize(n, -1) # initialize pred[i] to -1 for 0 <= i < n + deref(pred).resize(n, -1) # initialize pred[i] to -1 for 0 <= i < n if xslice_len != NULL: deref(xslice_len).resize(n) part_len.resize(max_nparts) @@ -212,19 +212,19 @@ cdef void extended_lex_BFS( nneighbors = cg.out_neighbors_unsafe (v_int, neighbors.data(), max_degree) for k in range(nneighbors): u_int = neighbors[k] - j = deref(sigma_inv)[u_int] # get the position of u + j = deref(sigma_inv)[u_int] # get the position of u if j <= i: - continue # already taken care of + continue # already taken care of if lex_label != NULL: deref(lex_label)[j].push_back (v_int) - p = part_of[j] # get the part of u - l = part_head[p] # get the beginning of the part containing u + p = part_of[j] # get the part of u + l = part_head[p] # get the beginning of the part containing u # if not last and next elem belongs in the same part (ie #part >= 2) if l < n - 1 and part_of[l + 1] == p: - if l != j: # not already first elem of the part + if l != j: # not already first elem of the part # Place u at the position of the head of the part t_int = sigma[l] deref(sigma_inv)[t_int], deref(sigma_inv)[u_int] = j, l @@ -233,7 +233,7 @@ cdef void extended_lex_BFS( swap[vector[int]](deref(lex_label)[j], deref(lex_label)[l]) j = l - part_head[p] += 1 # move the head of the part to next elem + part_head[p] += 1 # move the head of the part to next elem # if part p was not already cut in two during this iteration, we # create a new part using subpart @@ -307,9 +307,9 @@ def slice_decomposition(G, initial_vertex=None): ``view``:: sage: from sage.graphs.graph_latex import check_tkz_graph - sage: check_tkz_graph() # random - depends on Tex installation - sage: view(G) # not tested - sage: latex(G) # to obtain the corresponding LaTeX code + sage: check_tkz_graph() # random - depends on Tex installation + sage: view(G) # not tested + sage: latex(G) # to obtain the corresponding LaTeX code \begin{tikzpicture} ... \end{tikzpicture} @@ -391,10 +391,10 @@ cdef class SliceDecomposition(SageObject): sage: SD1 == SD2 False sage: SD3 = graphs.CompleteGraph(3).slice_decomposition() - sage: SD1 == SD3 # same lexBFS but different slice for 1 + sage: SD1 == SD3 # same lexBFS but different slice for 1 False sage: SD4 = Graph([(0,1), (0,2)]).slice_decomposition() - sage: SD3 == SD4 # same lexBFS and slices but different active edges + sage: SD3 == SD4 # same lexBFS and slices but different active edges False """ if not isinstance(other, type(self)): @@ -767,9 +767,9 @@ cdef class SliceDecomposition(SageObject): True sage: ('a', 'c') in SD.active_edges('a') True - sage: ('c', 'd') in SD.active_edges('a') # c and d in the same slice + sage: ('c', 'd') in SD.active_edges('a') # c and d in same slice False - sage: ('a', 'u') in SD.active_edges('a') # u not in x-slice of a + sage: ('a', 'u') in SD.active_edges('a') # u not in x-slice of a False The set of active edges of every vertex is a partition of the edges:: @@ -857,12 +857,12 @@ cdef class SliceDecomposition(SageObject): The graph can have loops or multiple edges but they are ignored:: - sage: G = graphs.CubeConnectedCycle(2) # multiple edges + sage: G = graphs.CubeConnectedCycle(2) # multiple edges sage: SD = G.slice_decomposition() sage: SD.underlying_graph() == G.to_simple(immutable=True) True - sage: G = graphs.CubeConnectedCycle(1) # loops + sage: G = graphs.CubeConnectedCycle(1) # loops sage: SD = G.slice_decomposition() sage: SD.underlying_graph() == G.to_simple(immutable=True) True @@ -908,7 +908,7 @@ cdef class SliceDecomposition(SageObject): TESTS: sage: from sage.graphs.graph_latex import check_tkz_graph - sage: check_tkz_graph() # random - depends on Tex installation + sage: check_tkz_graph() # random - depends on Tex installation sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() sage: latex(SD) \begin{tikzpicture} @@ -944,7 +944,7 @@ cdef class SliceDecomposition(SageObject): r"tail anchor=center,head anchor=north," r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]") lines.append("{") - bo, bc = "{", "}" # to write { and } if f-strings + bo, bc = "{", "}" # to write { and } if f-strings # Create the nodes and leaves of the slice decomposition tree for i in range(len(self.sigma)): l = self.xslice_len[i] diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index a3fcdbaf9c5..b394c8f069a 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -208,7 +208,7 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): lexicographic_label.pop(u) sigma.append(u) l += -1 if decr else 1 - for v in G.neighbor_iterator(u): # graphs are considered undirected + for v in G.neighbor_iterator(u): # graphs are considered undirected if v in lexicographic_label: if right: lexicographic_label[v].append(l) From 201cbffcf4067690db6449d855214320e48fd2e7 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 14:32:28 +0200 Subject: [PATCH 013/193] graphs: break up first paragraph of docstring of extended_LEX_BFS --- .../graphs/graph_decompositions/slice_decomposition.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 142a0c01b46..9a366c8e3a6 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -34,9 +34,10 @@ cdef void extended_lex_BFS( vector[vector[int]] *lex_label) except *: r""" Perform a extended lexicographic breadth first search (LexBFS) on the - undirected graph `G`. In addition to computing a LexBFS ordering, the - extended LexBFS algorithm can be used to compute the slice decomposition of - the graph. + undirected graph `G`. + + In addition to computing a LexBFS ordering, the extended LexBFS algorithm + can be used to compute the slice decomposition of the graph. This function implements the `O(n+m)` time algorithm proposed in [HMPV2000]_ and [TCHP2008]_. From 2d5002fdfd6ed31286fcd8dc260c742ee408e90e Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 14:36:23 +0200 Subject: [PATCH 014/193] docstring of extended_LEX_BFS: detail on the case initial_v_int=-1 --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 9a366c8e3a6..7d975785d33 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -61,7 +61,9 @@ cdef void extended_lex_BFS( * sigma[deref(sigma_inv)[v_int]] = v_int * deref(sigma_inv)[sigma[i]] = i - - ``initial_v_int`` -- the first vertex to consider, can be -1. + - ``initial_v_int`` -- the first vertex to consider. It can be `-1`; in this + case the first active vertex (corresponding to the first bit set in + ``cg.active_vertices``) will be taken as first vertex. - ``pred`` -- a pointer to a vector of int to store the predecessor of a vertex in the LexBFS traversal. ``pred`` can be ``NULL`` if the caller From 8ffab789b46d677789f96faee3917ef439c667a3 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 14:43:40 +0200 Subject: [PATCH 015/193] Remove unnecessary empty lines and add needed one --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 7d975785d33..4e09a95c309 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -260,6 +260,7 @@ cdef void extended_lex_BFS( if need_to_delete_sigma_inv: del sigma_inv + def slice_decomposition(G, initial_vertex=None): r""" Compute a slice decomposition of the simple undirected graph @@ -324,7 +325,6 @@ def slice_decomposition(G, initial_vertex=None): Traceback (most recent call last): ... ValueError: Graph must be undirected - """ return SliceDecomposition(G, initial_vertex=initial_vertex) @@ -564,7 +564,6 @@ cdef class SliceDecomposition(SageObject): sage: SD = G.slice_decomposition() sage: all(SD[v] == SD.xslice_data(v) for v in G) True - """ return self[v] From bef41c8ffc670f6592cd63c3e4affd145e410254 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:05:12 +0200 Subject: [PATCH 016/193] Partial revert of efae289c: EXAMPLES: -> EXAMPLES:: --- .../graph_decompositions/slice_decomposition.pyx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 4e09a95c309..84b8503bb78 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -289,7 +289,7 @@ def slice_decomposition(G, initial_vertex=None): implementation follows that complexity for ``SparseGraph``. For ``DenseGraph``, the complexity is `O(n^2)`. - EXAMPLES:: + EXAMPLES: Slice decomposition of the Petersen Graph:: @@ -443,7 +443,7 @@ cdef class SliceDecomposition(SageObject): This method can also be called via :meth:`xslice_data`. - EXAMPLES:: + EXAMPLES: :: @@ -527,8 +527,6 @@ cdef class SliceDecomposition(SageObject): EXAMPLES:: - :: - sage: from sage.graphs.traversals import _is_valid_lex_BFS_order sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() sage: SD.lexBFS_order() @@ -586,7 +584,7 @@ cdef class SliceDecomposition(SageObject): A list of vertices - EXAMPLES:: + EXAMPLES: :: @@ -639,7 +637,7 @@ cdef class SliceDecomposition(SageObject): A list of list corresponding to the x-slice sequence of ``v``. - EXAMPLES:: + EXAMPLES: :: @@ -757,7 +755,7 @@ cdef class SliceDecomposition(SageObject): A list of edges - EXAMPLES:: + EXAMPLES: :: @@ -848,7 +846,7 @@ cdef class SliceDecomposition(SageObject): decomposition, the underlying graph corresponds to ``G.to_simple()``, *i.e.*, it is the input graph without loops and multiple edges. - EXAMPLES:: + EXAMPLES: :: From 474c551f5fb4c2783fa18d6204aaa8618ec35ef0 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:12:36 +0200 Subject: [PATCH 017/193] Use LaTeX in docstring of graphs.traversals module --- src/sage/graphs/traversals.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index b394c8f069a..9d3e7d88757 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -38,7 +38,7 @@ LexBFS and LexUP) or prepended (for LexDFS and LexDOWN) to the lexicographic labels of all neighbors of the selected vertex that are left in the graph. The time complexity of the algorithm is `O(mn)` for ``SparseGraph`` and -`O(max(mn, n^2))` for ``DenseGraph``, where `n` is the number of vertices +`O(\max\{mn, n^2\})` for ``DenseGraph``, where `n` is the number of vertices and `m` is the number of edges. See [CK2008]_ and [Mil2017]_ for more details on the algorithm and graphs From f7ad61fd650c56cb55570fd4fb64e59dfea10a3a Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:14:12 +0200 Subject: [PATCH 018/193] graphs.traversals: write function definition on one line --- src/sage/graphs/traversals.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 9d3e7d88757..9576d2dbaa5 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -307,8 +307,7 @@ def _is_valid_lex_BFS_order(G, L): return True -def lex_BFS( - G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"): +def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast"): r""" Perform a lexicographic breadth first search (LexBFS) on the graph. From 7c0b21ef3b179645a26ec435896f4b7327b16af0 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:15:08 +0200 Subject: [PATCH 019/193] graphs.traversals: remove extra spaces in dict declaration --- src/sage/graphs/traversals.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 9576d2dbaa5..5bbf3abe708 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -199,7 +199,7 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): cdef size_t l = n if decr else -1 # Perform the search - lexicographic_label = { u: deque() for u in G } + lexicographic_label = {u: deque() for u in G} if initial_vertex is not None: # append or appendleft does not matter here, as the deque is empty lexicographic_label[initial_vertex].append(l) From a589d2cba99b1436dd52ef21f0096c38551a0ab2 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:17:33 +0200 Subject: [PATCH 020/193] slice_decomposition: add 1 line description for _latex_ --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 84b8503bb78..6cdf9109e19 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -905,6 +905,9 @@ cdef class SliceDecomposition(SageObject): def _latex_(self): r""" + Return a string to render, using `\LaTeX`, the slice decomposition as a + tree. + TESTS: sage: from sage.graphs.graph_latex import check_tkz_graph From 1fbb00ebdb1de31ab09729fe20f6e949acefd770 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:29:25 +0200 Subject: [PATCH 021/193] slice_decomposition: add space in output of _repr_, add docstring --- .../graph_decompositions/slice_decomposition.pyx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 6cdf9109e19..769c3f17bc5 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -295,7 +295,7 @@ def slice_decomposition(G, initial_vertex=None): sage: G = graphs.PetersenGraph() sage: SD = G.slice_decomposition(); SD - [0[1[4[5]]][2[6]][3][9][7][8]] + [0[1[4[5]]] [2[6]] [3] [9] [7] [8]] The graph can have loops or multiple edges but they are ignored:: @@ -890,6 +890,18 @@ cdef class SliceDecomposition(SageObject): return self._underlying_graph def _repr_(self): + r""" + Return a string representation of a ``SliceDecomposition`` object. + + TESTS: + + sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() + sage: repr(SD) + '[0[1[4[5]]] [2[6]] [3] [9] [7] [8]]' + sage: G = Graph('L~mpn~Nrv{^o~_').relabel('abcdefguvwxyz',inplace=False) + sage: SD = G.slice_decomposition(initial_vertex='x'); repr(SD) + '[x[a[b[c[d]] [e[f]]] [g]] [u[y[z]]] [v[w]]]' + """ def inner_repr(idx): l = self.xslice_len[idx] S = [] @@ -900,7 +912,7 @@ cdef class SliceDecomposition(SageObject): S.append(inner_repr(j)) j += lj assert j == idx + l, "slice decomposition is ill-formed" - return f'{self.sigma[idx]}' + ''.join(f'[{s}]' for s in S) + return f'{self.sigma[idx]}' + ' '.join(f'[{s}]' for s in S) return f'[{inner_repr(0)}]' def _latex_(self): From f3d502e0a17cc39325d09f1d4a00c60dd3aa278e Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:37:07 +0200 Subject: [PATCH 022/193] slice_decomposition: declare types of some variables --- .../slice_decomposition.pyx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 769c3f17bc5..f126e877ca1 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -513,7 +513,7 @@ cdef class SliceDecomposition(SageObject): if v not in self.sigma_inv: raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") - i = self.sigma_inv[v] + cdef size_t i = self.sigma_inv[v] return {'pivot': v, 'slice': self._slice(i), 'sequence': self._xslice_sequence(i), @@ -621,7 +621,7 @@ cdef class SliceDecomposition(SageObject): if v not in self.sigma_inv: raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") - i = self.sigma_inv[v] + cdef size_t i = self.sigma_inv[v] return self._slice(i) def xslice_sequence(self, v): @@ -693,7 +693,7 @@ cdef class SliceDecomposition(SageObject): if v not in self.sigma_inv: raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") - i = self.sigma_inv[v] + cdef size_t i = self.sigma_inv[v] return self._xslice_sequence(i) def lexicographic_label(self, v): @@ -735,7 +735,7 @@ cdef class SliceDecomposition(SageObject): if v not in self.sigma_inv: raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") - i = self.sigma_inv[v] + cdef size_t i = self.sigma_inv[v] return self._xslice_lex_label(i) def active_edges(self, v): @@ -791,22 +791,24 @@ cdef class SliceDecomposition(SageObject): if v not in self.sigma_inv: raise LookupError(f"vertex ({v}) does not appear in the slice " "decomposition") - i = self.sigma_inv[v] + cdef size_t i = self.sigma_inv[v] return self._xslice_active_edges(i) - def _slice(self, idx): + def _slice(self, size_t idx): r""" This method is for internal use only """ return list(self.sigma[idx:idx+self.xslice_len[idx]]) - def _xslice_sequence(self, idx): + def _xslice_sequence(self, size_t idx): r""" This method is for internal use only """ - l = self.xslice_len[idx] + cdef size_t l = self.xslice_len[idx] + cdef size_t j = idx + 1 + cdef size_t lj + S = [ [self.sigma[idx]] ] - j = idx + 1 while j < idx + l: lj = self.xslice_len[j] S.append(list(self.sigma[j:j+lj])) @@ -814,20 +816,22 @@ cdef class SliceDecomposition(SageObject): assert j == idx + l, "slice decomposition is ill-formed" return S - def _xslice_lex_label(self, idx): + def _xslice_lex_label(self, size_t idx): r""" This method is for internal use only """ return list(self.lex_label[idx]) - def _xslice_active_edges(self, idx): + def _xslice_active_edges(self, size_t idx): r""" This method is for internal use only """ - l = self.xslice_len[idx] - llv_prefix = len(self.lex_label[idx]) + cdef size_t l = self.xslice_len[idx] + cdef size_t llv_prefix = len(self.lex_label[idx]) + cdef size_t j = idx + 1 + cdef size_t lj + A = [] - j = idx + 1 while j < idx + l: lj = self.xslice_len[j] llj = self.lex_label[j] From 9ae7edb792a86bc2e337f369c55d3cd220e8349f Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:37:42 +0200 Subject: [PATCH 023/193] slice_decomposition: remove extra space before ( in function declaration --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index f126e877ca1..ea1fd38fa76 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -565,7 +565,7 @@ cdef class SliceDecomposition(SageObject): """ return self[v] - def slice (self, v): + def slice(self, v): r""" Return the slice of the vertex `v`. From 306c3254289d0d3240ae05049ad653fb7756dbfa Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:45:37 +0200 Subject: [PATCH 024/193] slice_decomposition: add one line description for __eq__ --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index ea1fd38fa76..2640a61aa31 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -379,6 +379,8 @@ cdef class SliceDecomposition(SageObject): def __eq__(self, other): """ + Return whether ``self`` and ``other`` are equal. + TESTS: sage: G = graphs.PetersenGraph() From fbf7256965d80ebff8c78f1e32eb7821b354d047 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 15:45:53 +0200 Subject: [PATCH 025/193] slice_decomposition: add one line description + tests for __hash__ --- .../graph_decompositions/slice_decomposition.pyx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 2640a61aa31..0a08468f8d9 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -412,6 +412,21 @@ cdef class SliceDecomposition(SageObject): and self.xslice_len == sd.xslice_len def __hash__(self): + r""" + Compute a hash of a ``SliceDecomposition`` object. + + TESTS: + + sage: P3 = graphs.PathGraph(3) + sage: hash(P3.slice_decomposition(initial_vertex=0)) + -7313201005658437102 + sage: hash(P3.slice_decomposition(initial_vertex=2)) + 1181676064626878036 + sage: hash(graphs.CompleteGraph(3).slice_decomposition()) + 6162668211142297415 + sage: hash(Graph([(0,1), (0,2)]).slice_decomposition()) + 2898184589667302557 + """ return hash((tuple(self.sigma_inv.items()), tuple(self.lex_label.items()), tuple(self.xslice_len))) From 5b263e4e1a87d06180097bdd4751411e8be660c7 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Tue, 2 Jul 2024 16:45:54 +0200 Subject: [PATCH 026/193] slice_decomposition: add indirect doctests for internal methods --- .../slice_decomposition.pyx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 0a08468f8d9..75e71d19fec 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -142,6 +142,15 @@ cdef void extended_lex_BFS( To see how it can be used, see the code of the lex_BFS method (in traversals.pyx) or of the class SliceDecomposition in this module. + TESTS: + + Indirect doctests:: + + sage: G = graphs.HouseGraph() + sage: G.slice_decomposition() + [0[1[2]] [3] [4]] + sage: G.lex_BFS(algorithm="fast") + [0, 1, 2, 3, 4] """ cdef int n = cg.num_verts # Variables for the partition refinement algorithm @@ -814,12 +823,28 @@ cdef class SliceDecomposition(SageObject): def _slice(self, size_t idx): r""" This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.slice(1) + [1, 2] """ return list(self.sigma[idx:idx+self.xslice_len[idx]]) def _xslice_sequence(self, size_t idx): r""" This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.xslice_sequence(0) + [[0], [1, 2], [3], [4]] """ cdef size_t l = self.xslice_len[idx] cdef size_t j = idx + 1 @@ -836,12 +861,28 @@ cdef class SliceDecomposition(SageObject): def _xslice_lex_label(self, size_t idx): r""" This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.lexicographic_label(3) + [1, 2] """ return list(self.lex_label[idx]) def _xslice_active_edges(self, size_t idx): r""" This method is for internal use only + + TESTS: + + Indirect doctests:: + + sage: SD = graphs.HouseGraph().slice_decomposition() + sage: SD.active_edges(0) + [(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)] """ cdef size_t l = self.xslice_len[idx] cdef size_t llv_prefix = len(self.lex_label[idx]) From c78eb04724c17faaee2eb1a85adaaf57a576ac05 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Wed, 3 Jul 2024 09:48:20 +0200 Subject: [PATCH 027/193] slice_decomposition: improve docstring of underlying_graph method --- .../graph_decompositions/slice_decomposition.pyx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 75e71d19fec..b105c389e97 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -905,8 +905,14 @@ cdef class SliceDecomposition(SageObject): Return the underlying graph corresponding to the slice decomposition. If `G` was the graph given as parameter to compute the slice - decomposition, the underlying graph corresponds to ``G.to_simple()``, - *i.e.*, it is the input graph without loops and multiple edges. + decomposition, the underlying graph corresponds to ``G.to_simple()`` + where labels are ignored, *i.e.*, it is the input graph without loops, + multiple edges and labels. + + .. NOTE:: + + This method is mostly defined to test the computation of + lexicographic labels and actives edges. EXAMPLES: From 3b428a6cce9f72b22677be8a2262f4ac4963ec92 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Wed, 3 Jul 2024 10:35:03 +0200 Subject: [PATCH 028/193] slice_decomposition: more doc --- .../slice_decomposition.pyx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index b105c389e97..959feb2826a 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -8,6 +8,10 @@ This module implements an extended lexBFS algorithm for computing the slice decomposition of undirected graphs and the class :class:`~SliceDecomposition` to represent such decompositions. +A formal definition of slice decompositions can be found in Section 3.2 of +[TCHP2008]_ and a description of the extended lexBFS algorithm is given in +Section 3.3 of [TCHP2008]_. + AUTHORS: - Cyril Bouvier (2024-06-25): initial version @@ -351,7 +355,35 @@ cdef class SliceDecomposition(SageObject): - ``initial_vertex`` -- (default: ``None``); the first vertex to consider. - See :meth:`~slice_decomposition` for more details. + .. SEEALSO:: + + * :meth:`~slice_decomposition` -- compute a slice decomposition of + the simple undirected graph + * Section 3.2 of [TCHP2008]_ for a formal definition. + + EXAMPLES: + + The constructor of the :class:`~SliceDecomposition` class is called by + the :meth:`~slice_decomposition` method of undirected graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: G = graphs.PetersenGraph() + sage: SliceDecomposition(G) == G.slice_decomposition() + True + + The vertex appearing first in the slice decomposition can be specified:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: SliceDecomposition(graphs.PetersenGraph(), initial_vertex=3) + [3[2[4[8]]] [1[7]] [0] [9] [6] [5]] + + Slice decompositions are not defined for directed graphs:: + + sage: from sage.graphs.graph_decompositions.slice_decomposition import SliceDecomposition + sage: SliceDecomposition(DiGraph()) + Traceback (most recent call last): + ... + ValueError: Graph must be undirected .. automethod:: __getitem__ """ From 0620a9807c21245914099a27278006ab1ec8325b Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 4 Jul 2024 10:33:05 +0200 Subject: [PATCH 029/193] slice_decomposition: TESTS: => TESTS:: --- .../slice_decomposition.pyx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 959feb2826a..c94b5b4462a 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -422,7 +422,7 @@ cdef class SliceDecomposition(SageObject): """ Return whether ``self`` and ``other`` are equal. - TESTS: + TESTS:: sage: G = graphs.PetersenGraph() sage: SD = G.slice_decomposition() @@ -456,7 +456,7 @@ cdef class SliceDecomposition(SageObject): r""" Compute a hash of a ``SliceDecomposition`` object. - TESTS: + TESTS:: sage: P3 = graphs.PathGraph(3) sage: hash(P3.slice_decomposition(initial_vertex=0)) @@ -549,7 +549,7 @@ cdef class SliceDecomposition(SageObject): sage: SD.xslice_data('u')['lexicographic_label'] ['a', 'b', 'c', 'd', 'e', 'f', 'g'] - TESTS: + TESTS:: sage: G = graphs.RandomGNP(15, 0.3) sage: SD = G.slice_decomposition() @@ -592,7 +592,7 @@ cdef class SliceDecomposition(SageObject): sage: _is_valid_lex_BFS_order(G, SD.lexBFS_order()) True - TESTS: + TESTS:: sage: from sage.graphs.traversals import _is_valid_lex_BFS_order sage: for _ in range(5): @@ -614,7 +614,7 @@ cdef class SliceDecomposition(SageObject): This method is a wrapper around :meth:`SliceDecomposition.__getitem__` - TESTS: + TESTS:: sage: G = graphs.RandomGNP(15, 0.3) sage: SD = G.slice_decomposition() @@ -669,7 +669,7 @@ cdef class SliceDecomposition(SageObject): sage: SD.slice('x') == SD.lexBFS_order() True - TESTS: + TESTS:: sage: SD.slice('Michael') Traceback (most recent call last): @@ -740,7 +740,7 @@ cdef class SliceDecomposition(SageObject): True True - TESTS: + TESTS:: sage: SD = graphs.PetersenGraph().slice_decomposition() sage: SD.xslice_sequence('Terry') @@ -782,7 +782,7 @@ cdef class SliceDecomposition(SageObject): ....: == {v for v in G.neighbors('f') if pos(v) < pos('f')} True - TESTS: + TESTS:: sage: SD = graphs.PetersenGraph().slice_decomposition() sage: SD.lexicographic_label('Eric') @@ -838,7 +838,7 @@ cdef class SliceDecomposition(SageObject): ....: and all(G.has_edge(u, w) for v in G for u, w in SD.active_edges(v)) True - TESTS: + TESTS:: sage: SD = graphs.PetersenGraph().slice_decomposition() sage: SD.active_edges('Graham') @@ -967,7 +967,7 @@ cdef class SliceDecomposition(SageObject): sage: SD.underlying_graph() == G.to_simple(immutable=True) True - TESTS: + TESTS:: sage: for _ in range(5): ....: G = graphs.RandomGNP(15, 0.3) @@ -993,7 +993,7 @@ cdef class SliceDecomposition(SageObject): r""" Return a string representation of a ``SliceDecomposition`` object. - TESTS: + TESTS:: sage: G = graphs.PetersenGraph(); SD = G.slice_decomposition() sage: repr(SD) @@ -1020,7 +1020,7 @@ cdef class SliceDecomposition(SageObject): Return a string to render, using `\LaTeX`, the slice decomposition as a tree. - TESTS: + TESTS:: sage: from sage.graphs.graph_latex import check_tkz_graph sage: check_tkz_graph() # random - depends on Tex installation From add4fbc722feb0b3448d10fffcbbfea40dea1b61 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 4 Jul 2024 10:34:24 +0200 Subject: [PATCH 030/193] slice_decomposition: remove empty lines --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 2 -- src/sage/graphs/traversals.pyx | 1 - 2 files changed, 3 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index c94b5b4462a..eb6d048bda4 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -498,7 +498,6 @@ cdef class SliceDecomposition(SageObject): * ``"sequence"`` -- the x-slice sequence of `v` (see :meth:`~xslice_sequence`) - This method can also be called via :meth:`xslice_data`. EXAMPLES: @@ -604,7 +603,6 @@ cdef class SliceDecomposition(SageObject): True True True - """ return list(self.sigma) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 5bbf3abe708..d85faeeeab2 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -178,7 +178,6 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): Traceback (most recent call last): ... ValueError: 'foo' is not a graph vertex - """ if initial_vertex is not None and initial_vertex not in G: raise ValueError(f"'{initial_vertex}' is not a graph vertex") From 9ad0dab6a33b849e2d144329392888d0923dd9e7 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 4 Jul 2024 10:54:12 +0200 Subject: [PATCH 031/193] graphs.traversals: postpone import of DiGraph --- src/sage/graphs/traversals.pyx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index d85faeeeab2..2e0b880c141 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -185,9 +185,6 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): if algo not in ("lex_BFS", "lex_UP", "lex_DFS", "lex_DOWN"): raise ValueError(f"unknown algorithm '{algo}'") - if tree: - from sage.graphs.digraph import DiGraph - cdef size_t n = G.order() cdef list sigma = [] cdef dict predecessor = {} @@ -219,6 +216,7 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): sigma.reverse() if tree: + from sage.graphs.digraph import DiGraph edges = predecessor.items() g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) return sigma, g @@ -473,9 +471,6 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") if algorithm != "fast": raise ValueError(f"unknown algorithm '{algorithm}'") - if tree: - from sage.graphs.digraph import DiGraph - cdef size_t n = G.order() # For algorithm "fast" we need to convert G to an undirected graph @@ -509,6 +504,7 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") sigma.reverse() if tree: + from sage.graphs.digraph import DiGraph edges = predecessor.items() g = DiGraph([G, edges], format='vertices_and_edges', sparse=True) return sigma, g From ce3cd9fc4fc10869e652c70299b44a0ca7cb934d Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 4 Jul 2024 10:59:26 +0200 Subject: [PATCH 032/193] graphs.traversals: rename l (for clarity) and set increment on a var --- src/sage/graphs/traversals.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 2e0b880c141..c2c323c8692 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -192,24 +192,25 @@ def _lex_order_common(G, algo, reverse, tree, initial_vertex): cdef bint right = algo in ("lex_BFS", "lex_UP") cdef bint decr = algo in ("lex_BFS", "lex_DOWN") - cdef size_t l = n if decr else -1 + cdef size_t cur_label = n if decr else -1 + cdef int label_incr = -1 if decr else 1 # Perform the search lexicographic_label = {u: deque() for u in G} if initial_vertex is not None: # append or appendleft does not matter here, as the deque is empty - lexicographic_label[initial_vertex].append(l) + lexicographic_label[initial_vertex].append(cur_label) while lexicographic_label: u = max(lexicographic_label, key=lexicographic_label.get) lexicographic_label.pop(u) sigma.append(u) - l += -1 if decr else 1 + cur_label += label_incr for v in G.neighbor_iterator(u): # graphs are considered undirected if v in lexicographic_label: if right: - lexicographic_label[v].append(l) + lexicographic_label[v].append(cur_label) else: - lexicographic_label[v].appendleft(l) + lexicographic_label[v].appendleft(cur_label) predecessor[v] = u if reverse: From a477740da715c1c92fc80a2246e81ea32d2e7d89 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 4 Jul 2024 11:06:07 +0200 Subject: [PATCH 033/193] slice_decomposition: improve message of exception for directed graphs --- .../graphs/graph_decompositions/slice_decomposition.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index eb6d048bda4..d3d4bfee49c 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -337,7 +337,7 @@ def slice_decomposition(G, initial_vertex=None): sage: slice_decomposition(DiGraph()) Traceback (most recent call last): ... - ValueError: Graph must be undirected + ValueError: parameter G must be an undirected graph """ return SliceDecomposition(G, initial_vertex=initial_vertex) @@ -383,12 +383,12 @@ cdef class SliceDecomposition(SageObject): sage: SliceDecomposition(DiGraph()) Traceback (most recent call last): ... - ValueError: Graph must be undirected + ValueError: parameter G must be an undirected graph .. automethod:: __getitem__ """ if G.is_directed(): - raise ValueError("Graph must be undirected") + raise ValueError("parameter G must be an undirected graph") if initial_vertex is not None and initial_vertex not in G: raise LookupError(f"vertex ({initial_vertex}) is not a vertex of the graph") From 3adb9468814d993784fcada273824577a9bc3b52 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 25 Jul 2024 10:05:19 +0200 Subject: [PATCH 034/193] graphs/traversals.pyx: fix lint error --- src/sage/graphs/traversals.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 90b5a8ceef0..c4f48539f61 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -76,6 +76,7 @@ from sage.graphs.base.c_graph cimport CGraph, CGraphBackend from sage.graphs.graph_decompositions.slice_decomposition cimport \ extended_lex_BFS + def _lex_order_common(G, algo, reverse, tree, initial_vertex): r""" Perform a lexicographic search (LexBFS, LexUP, LexDFS or LexDOWN) on the From 5f0219f5e20b34965d2b87a35ecaa3ef0b5f6587 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 25 Jul 2024 10:44:54 +0200 Subject: [PATCH 035/193] graphs/traversals.pyx: fix indent in docstrings --- src/sage/graphs/traversals.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index c4f48539f61..d33b0c6d2ed 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -342,8 +342,8 @@ def lex_BFS(G, reverse=False, tree=False, initial_vertex=None, algorithm="fast") .. NOTE:: - Loops and multiple edges are ignored during the computation of ``lex_BFS`` - and directed graphs are converted to undirected graphs. + Loops and multiple edges are ignored during the computation of + ``lex_BFS`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -533,8 +533,8 @@ def lex_UP(G, reverse=False, tree=False, initial_vertex=None): .. NOTE:: - Loops and multiple edges are ignored during the computation of ``lex_UP`` - and directed graphs are converted to undirected graphs. + Loops and multiple edges are ignored during the computation of + ``lex_UP`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -607,8 +607,8 @@ def lex_DFS(G, reverse=False, tree=False, initial_vertex=None): .. NOTE:: - Loops and multiple edges are ignored during the computation of ``lex_DFS`` - and directed graphs are converted to undirected graphs. + Loops and multiple edges are ignored during the computation of + ``lex_DFS`` and directed graphs are converted to undirected graphs. .. SEEALSO:: @@ -681,8 +681,8 @@ def lex_DOWN(G, reverse=False, tree=False, initial_vertex=None): .. NOTE:: - Loops and multiple edges are ignored during the computation of ``lex_DOWN`` - and directed graphs are converted to undirected graphs. + Loops and multiple edges are ignored during the computation of + ``lex_DOWN`` and directed graphs are converted to undirected graphs. .. SEEALSO:: From 26469bddad0a2f788335b10cceae798539cc3d13 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Jul 2024 16:32:02 +0200 Subject: [PATCH 036/193] add method is_edge_cut --- src/sage/graphs/connectivity.pyx | 160 +++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 2d299e254fe..1b993277748 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -70,6 +70,7 @@ Methods # **************************************************************************** from sage.misc.superseded import deprecation +from sage.sets.disjoint_set cimport DisjointSet def is_connected(G): @@ -732,6 +733,165 @@ def blocks_and_cuts_tree(G): return g +def is_edge_cut(G, edges): + """ + Check whether `èdges`` is an edge cut of ``G``. + + A set of edges is an edge cut of a graph if its removal increases the number + of connected components. In a digraph, we consider the number of (weakly) + connected components. + + This method is not working for (di)graphs with multiple edges. Furthermore, + edge labels are ignored. + + INPUT: + + - ``G`` -- a Sage (Di)Graph + + - ``edges`` -- an iterable container of edges + + EXAMPLES: + + A cycle graph of order 4:: + + sage: from sage.graphs.connectivity import is_edge_cut + sage: G = graphs.CycleGraph(4) + sage: is_edge_cut(G, [(1, 2)]) + False + sage: is_edge_cut(G, [(1, 2), (2, 3)]) + True + sage: is_edge_cut(G, [(1, 2), (3, 0)]) + True + + A pending edge is a cut-edge:: + + sage: G.add_edge((0, 5, 'silly')) + sage: is_edge_cut(G, [(0, 5, 'silly')]) + True + + Edge labels are ignored, even if specified:: + + sage: G.add_edge((2, 5, 'xyz')) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz)]) + True + sage: is_edge_cut(G, [(0, 5, 'silly'), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 5, 'aa'), (2, 5, 'bb')]) + True + + The graph can have loops:: + + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + True + sage: is_edge_cut(G, [(0, 0), (0, 5), (2, 5)]) + True + + Multiple edges are not allowed:: + + sage: G.allow_multiple_edges(True) + sage: is_edge_cut(G, [(0, 5), (2, 5)]) + Traceback (most recent call last): + ... + ValueError: This method is not known to work on graphs with + multiedges. Perhaps this method can be updated to handle them, but in + the meantime if you want to use it please disallow multiedges using + allow_multiple_edges(). + + An error is raised if an element of ``edges`` is not an edge of `G`:: + + sage: G = graphs.CycleGraph(4) + sage: is_edge_cut(G, [(0, 2)]) + Traceback (most recent call last): + ... + ValueError: edge (0, 2) is not an edge of the graph + + For digraphs, this method considers the number of (weakly) connected + components:: + + sage: G = digraphs.Circuit(4) + sage: is_edge_cut(G, [(0, 1)]) + False + sage: G = digraphs.Circuit(4) + sage: is_edge_cut(G, [(0, 1), (1, 2)]) + True + + For disconnected (di)graphs, the method checks if the number of (weakly) + connected components increases:: + + sage: G = graphs.CycleGraph(4) * 2 + sage: is_edge_cut(G, [(1, 2), (2, 3)]) + True + sage: G = digraphs.Circuit(4) * 2 + sage: is_edge_cut(G, [(0, 1), (1, 2)]) + True + + TESTS: + + If `G` is not a Sage graph, an error is raised:: + + sage: is_edge_cut('I am not a graph', [(0, 0)]) + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + """ + from sage.graphs.generic_graph import GenericGraph + if not isinstance(G, GenericGraph): + raise TypeError("the input must be a Sage graph") + + G._scream_if_not_simple(allow_loops=True) + + cdef set C = set() # set of edges of the potential cut + cdef set S = set() # set of incident vertices + for e in edges: + u, v = e[0], e[1] + if not G.has_edge(u, v): + raise ValueError("edge {0} is not an edge of the graph".format(repr(e))) + if u == v: + # We ignore loops + continue + if G.degree(u) == 1 or G.degree(v) == 1: + # e is a pending edge and so a cut-edge + return True + S.add(u) + S.add(v) + C.add((u, v)) + if not G.is_directed(): + C.add((v, u)) + + cdef list queue + cdef set seen + DS = DisjointSet(G) + + CC = G.connected_components() + for comp in CC: + if not S.intersection(comp): + continue + # We run a DFS in comp from any vertex and avoid edges in C + start = comp[0] + queue = [start] + seen = set(queue) + while queue: + v = queue.pop() + for e in G.edge_iterator(vertices=[v], labels=False, ignore_direction=True, sort_vertices=False): + if e in C: + continue + w = e[1] if e[0] == v else e[0] + if w not in seen: + seen.add(w) + DS.union(v, w) + queue.append(w) + + # We now check if some vertices of comp have not been reached + if len(set(DS.find(v) for v in comp)) > 1: + return True + + return False + + def is_cut_edge(G, u, v=None, label=None): """ Return ``True`` if the input edge is a cut-edge or a bridge. From 3d03dae40e0a9497f50d90217b34eacfa513d42e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Jul 2024 16:37:30 +0200 Subject: [PATCH 037/193] expose in generic_graph.py and fix merge conflicts --- src/sage/graphs/connectivity.pyx | 13 +++++++++---- src/sage/graphs/generic_graph.py | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 1b993277748..2eddec08d22 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -23,7 +23,8 @@ Here is what the module can do: :meth:`connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. - :meth:`is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge. + :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. + :meth:`is_edge_cut` | Check whether ``edges`` is an edge cut of ``G``. :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex. :meth:`edge_connectivity` | Return the edge connectivity of the graph. :meth:`vertex_connectivity` | Return the vertex connectivity of the graph. @@ -735,7 +736,7 @@ def blocks_and_cuts_tree(G): def is_edge_cut(G, edges): """ - Check whether `èdges`` is an edge cut of ``G``. + Check whether ``edges`` is an edge cut of ``G``. A set of edges is an edge cut of a graph if its removal increases the number of connected components. In a digraph, we consider the number of (weakly) @@ -894,11 +895,15 @@ def is_edge_cut(G, edges): def is_cut_edge(G, u, v=None, label=None): """ +<<<<<<< HEAD Return ``True`` if the input edge is a cut-edge or a bridge. +======= + Check whether the input edge is a cut-edge or a bridge. +>>>>>>> 676db03765d (expose in generic_graph.py) A cut edge (or bridge) is an edge that when removed increases - the number of connected components. This function works with - simple graphs as well as graphs with loops and multiedges. In + the number of connected components. This function works with + simple graphs as well as graphs with loops and multiedges. In a digraph, a cut edge is an edge that when removed increases the number of (weakly) connected components. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 46523fdfda5..58d05850035 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -24997,6 +24997,7 @@ def is_self_complementary(self): from sage.graphs.connectivity import blocks_and_cut_vertices from sage.graphs.connectivity import blocks_and_cuts_tree from sage.graphs.connectivity import is_cut_edge + from sage.graphs.connectivity import is_edge_cut from sage.graphs.connectivity import is_cut_vertex from sage.graphs.connectivity import edge_connectivity from sage.graphs.connectivity import vertex_connectivity From 5c5b5cd119d73bd1d5c691fe2a899a415b2c3e7d Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Jul 2024 16:48:06 +0200 Subject: [PATCH 038/193] fix issue --- src/sage/graphs/connectivity.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 2eddec08d22..e570aa42e64 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -775,7 +775,7 @@ def is_edge_cut(G, edges): sage: G.add_edge((2, 5, 'xyz')) sage: is_edge_cut(G, [(0, 5), (2, 5)]) True - sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz)]) + sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz')]) True sage: is_edge_cut(G, [(0, 5, 'silly'), (2, 5)]) True @@ -867,10 +867,11 @@ def is_edge_cut(G, edges): cdef set seen DS = DisjointSet(G) - CC = G.connected_components() - for comp in CC: + for comp in G.connected_components(): if not S.intersection(comp): + # This component is not involved in the cut continue + # We run a DFS in comp from any vertex and avoid edges in C start = comp[0] queue = [start] From 5cab21d36cc119981dea6d8830b0f615445b64d9 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 27 Jul 2024 16:50:28 +0200 Subject: [PATCH 039/193] fix issues and fix merge conflicts --- src/sage/graphs/generic_graph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 58d05850035..b0400205574 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -243,7 +243,8 @@ :meth:`~GenericGraph.connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. - :meth:`~GenericGraph.is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge. + :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. + :meth:`~GenericGraph.is_edge_cut` | Check whether ``edges`` is an edge cut of ``G``. :meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex. :meth:`~GenericGraph.edge_cut` | Return a minimum edge cut between vertices `s` and `t` :meth:`~GenericGraph.vertex_cut` | Return a minimum vertex cut between non-adjacent vertices `s` and `t` From 238eed87a30592db82f35cc45e9309974c57af47 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 6 Aug 2024 10:36:49 +0200 Subject: [PATCH 040/193] fix conflict --- src/sage/graphs/connectivity.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index e570aa42e64..d7a7a613945 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -896,11 +896,7 @@ def is_edge_cut(G, edges): def is_cut_edge(G, u, v=None, label=None): """ -<<<<<<< HEAD - Return ``True`` if the input edge is a cut-edge or a bridge. -======= Check whether the input edge is a cut-edge or a bridge. ->>>>>>> 676db03765d (expose in generic_graph.py) A cut edge (or bridge) is an edge that when removed increases the number of connected components. This function works with From 6a6211d71ac941574e863b7cd6d535d29fc37535 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Mar 2024 14:16:37 -0800 Subject: [PATCH 041/193] src/sage/doctest/__main__.py: Move contents of src/bin/sage-runtests here --- src/bin/sage-runtests | 201 +--------------------------------- src/sage/doctest/__main__.py | 207 +++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 199 deletions(-) create mode 100644 src/sage/doctest/__main__.py diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 81efd01a4d8..8ab309685c7 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -1,206 +1,9 @@ #!/usr/bin/env sage-python -import argparse -import os import sys -# Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env -DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), - '.sage')) - -# Override to not pick up user configuration, see Issue #20270 -os.environ['SAGE_STARTUP_FILE'] = os.path.join(DOT_SAGE, 'init-doctests.sage') - - -def _get_optional_defaults(): - """Return the default value for the --optional flag.""" - optional = ['sage', 'optional'] - - return ','.join(optional) +from sage.doctest.__main__ import main if __name__ == "__main__": - parser = argparse.ArgumentParser(usage="sage -t [options] filenames", - description="Run all tests in a file or a list of files whose extensions " - "are one of the following: " - ".py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst.") - parser.add_argument("-p", "--nthreads", dest="nthreads", - type=int, nargs='?', const=0, default=1, metavar="N", - help="test in parallel using N threads, with 0 interpreted as max(2, min(8, cpu_count())); " - "when run under the control of the GNU make jobserver (make -j), request as most N job slots") - parser.add_argument("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout") - what = parser.add_mutually_exclusive_group() - what.add_argument("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") - what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library") - parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE") - - parser.add_argument("--format", choices=["sage", "github"], default="sage", - help="set format of error messages and warnings") - parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") - parser.add_argument("-s", "--short", dest="target_walltime", nargs='?', - type=int, default=-1, const=300, metavar="SECONDS", - help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)") - parser.add_argument("--warn-long", dest="warn_long", nargs='?', - type=float, default=-1.0, const=1.0, metavar="SECONDS", - help="warn if tests take more time than SECONDS") - # By default, include all tests marked 'sagemath_doc_html' -- see - # https://github.com/sagemath/sage/issues/25345 and - # https://github.com/sagemath/sage/issues/26110: - parser.add_argument("--optional", metavar="FEATURES", default=_get_optional_defaults(), - help='only run tests including one of the "# optional" tags listed in FEATURES (separated by commas); ' - 'if "sage" is listed, will also run the standard doctests; ' - 'if "sagemath_doc_html" is listed, will also run the tests relying on the HTML documentation; ' - 'if "optional" is listed, will also run tests for installed optional packages or detected features; ' - 'if "external" is listed, will also run tests for available external software; ' - 'if set to "all", then all tests will be run; ' - 'use "!FEATURE" to disable tests marked "# optional - FEATURE". ' - 'Note that "!" needs to be quoted or escaped in the shell.') - parser.add_argument("--hide", metavar="FEATURES", default="", - help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' - 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' - 'if "optional" is listed, will also hide features corresponding to optional packages.') - parser.add_argument("--probe", metavar="FEATURES", default="", - help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' - 'report the tests that pass nevertheless') - parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") - parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", - default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) - parser.add_argument("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") - parser.add_argument("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") - parser.add_argument("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests") - - parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") - parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") - parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") - parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") - parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") - parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") - parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") - parser.add_argument("--only-errors", action="store_true", default=False, help="only output failures, not test successes") - - parser.add_argument("--gdb", action="store_true", default=False, help="run doctests under the control of gdb") - parser.add_argument("--lldb", action="store_true", default=False, help="run doctests under the control of lldb") - parser.add_argument("--valgrind", "--memcheck", action="store_true", default=False, - help="run doctests using Valgrind's memcheck tool. The log " - "files are named sage-memcheck.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--massif", action="store_true", default=False, - help="run doctests using Valgrind's massif tool. The log " - "files are named sage-massif.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--cachegrind", action="store_true", default=False, - help="run doctests using Valgrind's cachegrind tool. The log " - "files are named sage-cachegrind.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - parser.add_argument("--omega", action="store_true", default=False, - help="run doctests using Valgrind's omega tool. The log " - "files are named sage-omega.PID and can be found in " + - os.path.join(DOT_SAGE, "valgrind")) - - parser.add_argument("-f", "--failed", action="store_true", default=False, - help="doctest only those files that failed in the previous run") - what.add_argument("-n", "--new", action="store_true", default=False, - help="doctest only those files that have been changed in the repository and not yet been committed") - parser.add_argument("--show-skipped", "--show_skipped", action="store_true", default=False, - help="print a summary at the end of each file of optional tests that were skipped") - - parser.add_argument("--stats_path", "--stats-path", default=os.path.join(DOT_SAGE, "timings2.json"), - help="path to a json dictionary for timings and failure status for each file from previous runs; it will be updated in this run") - parser.add_argument("--baseline_stats_path", "--baseline-stats-path", default=None, - help="path to a json dictionary for timings and failure status for each file, to be used as a baseline; it will not be updated") - - class GCAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - gcopts = dict(DEFAULT=0, ALWAYS=1, NEVER=-1) - new_value = gcopts[values] - setattr(namespace, self.dest, new_value) - - parser.add_argument("--gc", - choices=["DEFAULT", "ALWAYS", "NEVER"], - default=0, - action=GCAction, - help="control garbarge collection " - "(ALWAYS: collect garbage before every test; NEVER: disable gc; DEFAULT: Python default)") - - # The --serial option is only really for internal use, better not - # document it. - parser.add_argument("--serial", action="store_true", default=False, help=argparse.SUPPRESS) - # Same for --die_timeout - parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS) - - parser.add_argument("filenames", help="file names", nargs='*') - - # custom treatment to separate properly - # one or several file names at the end - new_arguments = [] - need_filenames = True - in_filenames = False - afterlog = False - for arg in sys.argv[1:]: - if arg in ('-n', '--new', '-a', '--all', '--installed'): - need_filenames = False - elif need_filenames and not (afterlog or in_filenames) and os.path.exists(arg): - in_filenames = True - new_arguments.append('--') - new_arguments.append(arg) - afterlog = arg in ['--logfile', '--stats_path', '--stats-path', - '--baseline_stats_path', '--baseline-stats-path'] - - args = parser.parse_args(new_arguments) - - if not args.filenames and not (args.all or args.new or args.installed): - print('either use --new, --all, --installed, or some filenames') - sys.exit(2) - - # Limit the number of threads to 2 to save system resources. - # See Issue #23713, #23892, #30351 - if sys.platform == 'darwin': - os.environ["OMP_NUM_THREADS"] = "1" - else: - os.environ["OMP_NUM_THREADS"] = "2" - - os.environ["SAGE_NUM_THREADS"] = "2" - - from sage.doctest.control import DocTestController - DC = DocTestController(args, args.filenames) - err = DC.run() - - # Issue #33521: Do not run pytest if the pytest configuration is not available. - # This happens when the source tree is not available and SAGE_SRC falls back - # to SAGE_LIB. - from sage.env import SAGE_SRC - if not all(os.path.isfile(os.path.join(SAGE_SRC, f)) - for f in ["conftest.py", "tox.ini"]): - sys.exit(err) - - try: - exit_code_pytest = 0 - import pytest - pytest_options = [] - if args.verbose: - pytest_options.append("-v") - - # #35999: no filename in arguments defaults to "src" - if not args.filenames: - filenames = [SAGE_SRC] - else: - # #31924: Do not run pytest on individual Python files unless - # they match the pytest file pattern. However, pass names - # of directories. We use 'not os.path.isfile(f)' for this so that - # we do not silently hide typos. - filenames = [f for f in args.filenames - if f.endswith("_test.py") or not os.path.isfile(f)] - if filenames: - print(f"Running pytest on {filenames} with options {pytest_options}") - exit_code_pytest = pytest.main(filenames + pytest_options) - if exit_code_pytest == 5: - # Exit code 5 means there were no test files, pass in this case - exit_code_pytest = 0 - - except ModuleNotFoundError: - print("pytest is not installed in the venv, skip checking tests that rely on it") - - if err == 0: - sys.exit(exit_code_pytest) - else: - sys.exit(err) + sys.exit(main()) diff --git a/src/sage/doctest/__main__.py b/src/sage/doctest/__main__.py new file mode 100644 index 00000000000..4310574c373 --- /dev/null +++ b/src/sage/doctest/__main__.py @@ -0,0 +1,207 @@ +import argparse +import os +import sys + +# Note: the DOT_SAGE and SAGE_STARTUP_FILE environment variables have already been set by sage-env +DOT_SAGE = os.environ.get('DOT_SAGE', os.path.join(os.environ.get('HOME'), + '.sage')) + +# Override to not pick up user configuration, see Issue #20270 +os.environ['SAGE_STARTUP_FILE'] = os.path.join(DOT_SAGE, 'init-doctests.sage') + + +def _get_optional_defaults(): + """Return the default value for the --optional flag.""" + optional = ['sage', 'optional'] + + return ','.join(optional) + + +def main(): + parser = argparse.ArgumentParser(usage="sage -t [options] filenames", + description="Run all tests in a file or a list of files whose extensions " + "are one of the following: " + ".py, .pyx, .pxd, .pxi, .sage, .spyx, .tex, .rst.") + parser.add_argument("-p", "--nthreads", dest="nthreads", + type=int, nargs='?', const=0, default=1, metavar="N", + help="test in parallel using N threads, with 0 interpreted as max(2, min(8, cpu_count())); " + "when run under the control of the GNU make jobserver (make -j), request as most N job slots") + parser.add_argument("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout") + what = parser.add_mutually_exclusive_group() + what.add_argument("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") + what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library") + parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE") + + parser.add_argument("--format", choices=["sage", "github"], default="sage", + help="set format of error messages and warnings") + parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") + parser.add_argument("-s", "--short", dest="target_walltime", nargs='?', + type=int, default=-1, const=300, metavar="SECONDS", + help="run as many doctests as possible in about 300 seconds (or the number of seconds given as an optional argument)") + parser.add_argument("--warn-long", dest="warn_long", nargs='?', + type=float, default=-1.0, const=1.0, metavar="SECONDS", + help="warn if tests take more time than SECONDS") + # By default, include all tests marked 'sagemath_doc_html' -- see + # https://github.com/sagemath/sage/issues/25345 and + # https://github.com/sagemath/sage/issues/26110: + parser.add_argument("--optional", metavar="FEATURES", default=_get_optional_defaults(), + help='only run tests including one of the "# optional" tags listed in FEATURES (separated by commas); ' + 'if "sage" is listed, will also run the standard doctests; ' + 'if "sagemath_doc_html" is listed, will also run the tests relying on the HTML documentation; ' + 'if "optional" is listed, will also run tests for installed optional packages or detected features; ' + 'if "external" is listed, will also run tests for available external software; ' + 'if set to "all", then all tests will be run; ' + 'use "!FEATURE" to disable tests marked "# optional - FEATURE". ' + 'Note that "!" needs to be quoted or escaped in the shell.') + parser.add_argument("--hide", metavar="FEATURES", default="", + help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' + 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' + 'if "optional" is listed, will also hide features corresponding to optional packages.') + parser.add_argument("--probe", metavar="FEATURES", default="", + help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' + 'report the tests that pass nevertheless') + parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") + parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", + default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) + parser.add_argument("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") + parser.add_argument("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") + parser.add_argument("--environment", type=str, default="sage.repl.ipython_kernel.all_jupyter", help="name of a module that provides the global environment for tests") + + parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") + parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") + parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") + parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") + parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") + parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") + parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") + parser.add_argument("--only-errors", action="store_true", default=False, help="only output failures, not test successes") + + parser.add_argument("--gdb", action="store_true", default=False, help="run doctests under the control of gdb") + parser.add_argument("--lldb", action="store_true", default=False, help="run doctests under the control of lldb") + parser.add_argument("--valgrind", "--memcheck", action="store_true", default=False, + help="run doctests using Valgrind's memcheck tool. The log " + "files are named sage-memcheck.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--massif", action="store_true", default=False, + help="run doctests using Valgrind's massif tool. The log " + "files are named sage-massif.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--cachegrind", action="store_true", default=False, + help="run doctests using Valgrind's cachegrind tool. The log " + "files are named sage-cachegrind.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + parser.add_argument("--omega", action="store_true", default=False, + help="run doctests using Valgrind's omega tool. The log " + "files are named sage-omega.PID and can be found in " + + os.path.join(DOT_SAGE, "valgrind")) + + parser.add_argument("-f", "--failed", action="store_true", default=False, + help="doctest only those files that failed in the previous run") + what.add_argument("-n", "--new", action="store_true", default=False, + help="doctest only those files that have been changed in the repository and not yet been committed") + parser.add_argument("--show-skipped", "--show_skipped", action="store_true", default=False, + help="print a summary at the end of each file of optional tests that were skipped") + + parser.add_argument("--stats_path", "--stats-path", default=os.path.join(DOT_SAGE, "timings2.json"), + help="path to a json dictionary for timings and failure status for each file from previous runs; it will be updated in this run") + parser.add_argument("--baseline_stats_path", "--baseline-stats-path", default=None, + help="path to a json dictionary for timings and failure status for each file, to be used as a baseline; it will not be updated") + + class GCAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + gcopts = dict(DEFAULT=0, ALWAYS=1, NEVER=-1) + new_value = gcopts[values] + setattr(namespace, self.dest, new_value) + + parser.add_argument("--gc", + choices=["DEFAULT", "ALWAYS", "NEVER"], + default=0, + action=GCAction, + help="control garbarge collection " + "(ALWAYS: collect garbage before every test; NEVER: disable gc; DEFAULT: Python default)") + + # The --serial option is only really for internal use, better not + # document it. + parser.add_argument("--serial", action="store_true", default=False, help=argparse.SUPPRESS) + # Same for --die_timeout + parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS) + + parser.add_argument("filenames", help="file names", nargs='*') + + # custom treatment to separate properly + # one or several file names at the end + new_arguments = [] + need_filenames = True + in_filenames = False + afterlog = False + for arg in sys.argv[1:]: + if arg in ('-n', '--new', '-a', '--all', '--installed'): + need_filenames = False + elif need_filenames and not (afterlog or in_filenames) and os.path.exists(arg): + in_filenames = True + new_arguments.append('--') + new_arguments.append(arg) + afterlog = arg in ['--logfile', '--stats_path', '--stats-path', + '--baseline_stats_path', '--baseline-stats-path'] + + args = parser.parse_args(new_arguments) + + if not args.filenames and not (args.all or args.new or args.installed): + print('either use --new, --all, --installed, or some filenames') + return 2 + + # Limit the number of threads to 2 to save system resources. + # See Issue #23713, #23892, #30351 + if sys.platform == 'darwin': + os.environ["OMP_NUM_THREADS"] = "1" + else: + os.environ["OMP_NUM_THREADS"] = "2" + + os.environ["SAGE_NUM_THREADS"] = "2" + + from sage.doctest.control import DocTestController + DC = DocTestController(args, args.filenames) + err = DC.run() + + # Issue #33521: Do not run pytest if the pytest configuration is not available. + # This happens when the source tree is not available and SAGE_SRC falls back + # to SAGE_LIB. + from sage.env import SAGE_SRC + if not all(os.path.isfile(os.path.join(SAGE_SRC, f)) + for f in ["conftest.py", "tox.ini"]): + return err + + try: + exit_code_pytest = 0 + import pytest + pytest_options = [] + if args.verbose: + pytest_options.append("-v") + + # #35999: no filename in arguments defaults to "src" + if not args.filenames: + filenames = [SAGE_SRC] + else: + # #31924: Do not run pytest on individual Python files unless + # they match the pytest file pattern. However, pass names + # of directories. We use 'not os.path.isfile(f)' for this so that + # we do not silently hide typos. + filenames = [f for f in args.filenames + if f.endswith("_test.py") or not os.path.isfile(f)] + if filenames: + print(f"Running pytest on {filenames} with options {pytest_options}") + exit_code_pytest = pytest.main(filenames + pytest_options) + if exit_code_pytest == 5: + # Exit code 5 means there were no test files, pass in this case + exit_code_pytest = 0 + + except ModuleNotFoundError: + print("pytest is not installed in the venv, skip checking tests that rely on it") + + if err == 0: + return exit_code_pytest + return err + + +if __name__ == "__main__": + sys.exit(main()) From 12d90f175df57c7fad00ef2c677d02c87531099c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Mar 2024 14:24:45 -0800 Subject: [PATCH 042/193] src/sage/doctest/control.py (DocTestController.run_val_gdb): Invoke doctester using 'python3 -m sage.doctest' --- src/sage/doctest/control.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 34c7e1299c5..cd7a33edc71 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1259,16 +1259,16 @@ def _optional_tags_string(self): def _assemble_cmd(self): """ - Assembles a shell command used in running tests under gdb, lldb, or valgrind. + Assemble a shell command used in running tests under gdb, lldb, or valgrind. EXAMPLES:: sage: from sage.doctest.control import DocTestDefaults, DocTestController sage: DC = DocTestController(DocTestDefaults(timeout=123), ["hello_world.py"]) sage: print(DC._assemble_cmd()) - sage-runtests --serial --timeout=123 hello_world.py + ...python... -m sage.doctest --serial --timeout=123 hello_world.py """ - cmd = "sage-runtests --serial " + cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) if "all" in opt: raise ValueError("You cannot run gdb/lldb/valgrind on the whole sage library") @@ -1301,7 +1301,7 @@ def run_val_gdb(self, testing=False): sage: DD = DocTestDefaults(gdb=True) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec gdb --eval-command="run" --args ...python... sage-runtests --serial --timeout=0 hello_world.py + exec gdb --eval-command="run" --args ...python... -m sage.doctest --serial --timeout=0 hello_world.py :: @@ -1318,13 +1318,12 @@ def run_val_gdb(self, testing=False): opt = self.options if opt.gdb: - cmd = f'''exec gdb --eval-command="run" --args {shlex.quote(sys.executable)} ''' + cmd = f'''exec gdb --eval-command="run" --args ''' flags = "" if opt.logfile: sage_cmd += f" --logfile {shlex.quote(opt.logfile)}" elif opt.lldb: - sage_cmd = sage_cmd.replace('sage-runtests', '$(command -v sage-runtests)') - cmd = f'''exec lldb --one-line "process launch" --one-line "cont" -- {sys.executable} ''' + cmd = f'''exec lldb --one-line "process launch" --one-line "cont" -- ''' flags = "" else: if opt.logfile is None: From e06cd18d01074b0ee932faeb75ed7db2ee7b239b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 25 Mar 2024 18:18:45 -0700 Subject: [PATCH 043/193] src/sage/doctest/control.py (DocTestController.run_val_gdb): Also use the valgrind-python.supp --- src/sage/doctest/control.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index cd7a33edc71..2e8b5e01c4c 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1342,9 +1342,9 @@ def run_val_gdb(self, testing=False): flags = os.getenv("SAGE_MEMCHECK_FLAGS") if flags is None: flags = "--leak-resolution=high --leak-check=full --num-callers=25 " - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "pyalloc.supp")) - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "sage.supp")) - flags += '''--suppressions="%s" ''' % (os.path.join(SAGE_EXTCODE, "valgrind", "sage-additional.supp")) + for supp in ["pyalloc.supp", "sage.supp", "sage-additional.supp", "valgrind-python.supp"]: + fname = os.path.join(SAGE_EXTCODE, "valgrind", supp) + flags += f"--suppressions={shlex.quote(fname)} " elif opt.massif: toolname = "massif" flags = os.getenv("SAGE_MASSIF_FLAGS", "--depth=6 ") From 1c1bb4320b575eeb9512b28ea46e793e0190224f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Mar 2024 15:09:40 -0800 Subject: [PATCH 044/193] src/bin/sage-valgrind: Also use the valgrind-python.supp --- src/bin/sage-valgrind | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/sage-valgrind b/src/bin/sage-valgrind index ae0d5dae389..9c668c3bdd0 100755 --- a/src/bin/sage-valgrind +++ b/src/bin/sage-valgrind @@ -20,6 +20,7 @@ fi SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/pyalloc.supp" SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/sage.supp" SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/sage-additional.supp" +SUPP+=" --suppressions=$SAGE_EXTCODE/valgrind/valgrind-python.supp" MEMCHECK_FLAGS="--leak-resolution=high --leak-check=full --num-callers=25 $SUPP" From 22401b9c56daf3d40cb6bb156514dab35ec2d48e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Aug 2024 21:06:50 -0700 Subject: [PATCH 045/193] src/sage/doctest/control.py: Update doctest output --- src/sage/doctest/control.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 2e8b5e01c4c..596997d2a07 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -970,7 +970,7 @@ def expand_files_into_sources(self): sage: DC = DocTestController(DD, [dirname]) sage: DC.expand_files_into_sources() sage: len(DC.sources) - 15 + 16 sage: DC.sources[0].options.optional True @@ -1082,6 +1082,7 @@ def sort_sources(self): sage.doctest.control sage.doctest.check_tolerance sage.doctest.all + sage.doctest.__main__ sage.doctest """ if self.options.nthreads > 1 and len(self.sources) > self.options.nthreads: @@ -1308,7 +1309,7 @@ def run_val_gdb(self, testing=False): sage: DD = DocTestDefaults(valgrind=True, optional='all', timeout=172800) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions="...valgrind/pyalloc.supp" --suppressions="...valgrind/sage.supp" --suppressions="...valgrind/sage-additional.supp" --log-file=.../valgrind/sage-memcheck.%p... sage-runtests --serial --timeout=172800 --optional=all hello_world.py + exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions=.../valgrind/pyalloc.supp --suppressions=.../valgrind/sage.supp --suppressions=.../valgrind/sage-additional.supp --suppressions=.../valgrind/valgrind-python.supp --log-file=.../valgrind/sage-memcheck.%p ...python... -m sage.doctest --serial --timeout=172800 --optional=all hello_world.py """ try: sage_cmd = self._assemble_cmd() From b79fb731b909a11459ded6ed532d12e5b7865b1d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 11 Mar 2024 00:50:22 -0700 Subject: [PATCH 046/193] src/sage/doctest/control.py (_assemble_cmd): Pass '--installed' to doctester under valgrind --- src/sage/doctest/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 596997d2a07..3cdb55bb31f 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1273,7 +1273,7 @@ def _assemble_cmd(self): opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) if "all" in opt: raise ValueError("You cannot run gdb/lldb/valgrind on the whole sage library") - for o in ("all", "long", "force_lib", "verbose", "failed", "new"): + for o in ("all", "installed", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s " % o for o in ("timeout", "randorder", "stats_path"): From 56bf8ca1e78396baae5644d79e288cb1deaadf9c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 21:11:37 -0700 Subject: [PATCH 047/193] src/sage/doctest/control.py: Remove restriction 'cannot run gdb/lldb/valgrind on the whole sage library' --- src/sage/doctest/control.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 3cdb55bb31f..625e4acbfa3 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1271,8 +1271,6 @@ def _assemble_cmd(self): """ cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - if "all" in opt: - raise ValueError("You cannot run gdb/lldb/valgrind on the whole sage library") for o in ("all", "installed", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s " % o From 7ccfe27cd2d5dcf958c7cead28014a2ed2b0da57 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 21:38:40 -0700 Subject: [PATCH 048/193] DocTestDefaults: When runtest_default is True, create defaults that are same as in sage-runtests --- src/sage/doctest/control.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 625e4acbfa3..10c7a95c3a6 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -63,28 +63,36 @@ class DocTestDefaults(SageObject): """ This class is used for doctesting the Sage doctest module. - It fills in attributes to be the same as the defaults defined in - ``sage-runtests``, expect for a few places, - which is mostly to make doctesting more predictable. + INPUT: + + - ``runtest_default`` -- (boolean, default ``False``); if ``True``, + fills in attribute to be the same as the defaults defined in + ``sage-runtests``. If ``False``, change defaults in a few places + for use in doctests of the doctester, which is mostly to make + doctesting more predictable. + + - ``**kwds`` -- attributes to override defaults EXAMPLES:: sage: from sage.doctest.control import DocTestDefaults - sage: D = DocTestDefaults() - sage: D + sage: D = DocTestDefaults(); D DocTestDefaults() sage: D.timeout -1 Keyword arguments become attributes:: - sage: D = DocTestDefaults(timeout=100) - sage: D + sage: D = DocTestDefaults(timeout=100); D DocTestDefaults(timeout=100) sage: D.timeout 100 + + The defaults for ``sage-runtests``:: + + sage: D = DocTestDefaults(runtest_default=True); D """ - def __init__(self, **kwds): + def __init__(self, runtest_default=False, **kwds): """ Edit these parameters after creating an instance. @@ -110,14 +118,14 @@ def __init__(self, **kwds): self.warn_long = -1.0 self.randorder = None self.random_seed = 0 - self.global_iterations = 1 # sage-runtests default is 0 - self.file_iterations = 1 # sage-runtests default is 0 + self.global_iterations = 0 if runtest_default else 1 + self.file_iterations = 0 if runtest_default else 1 self.environment = "sage.repl.ipython_kernel.all_jupyter" self.initial = False self.exitfirst = False self.force_lib = False self.if_installed = False - self.abspath = True # sage-runtests default is False + self.abspath = not runtest_default self.verbose = False self.debug = False self.only_errors = False @@ -149,7 +157,8 @@ def __init__(self, **kwds): # We don't want to use the real stats file by default so that # we don't overwrite timings for the actual running doctests. - self.stats_path = os.path.join(DOT_SAGE, "timings_dt_test.json") + self.stats_path = os.path.join( + DOT_SAGE, "timings2.json" if runtest_default else "timings_dt_test.json") self.__dict__.update(kwds) def _repr_(self): From 04ca38e54e2227cba4dc0652de03e0fbb5215ab6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 21:39:23 -0700 Subject: [PATCH 049/193] DocTestController._assemble_cmd: Add handling of all options --- src/sage/doctest/control.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 10c7a95c3a6..2c2bdbb4d5a 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1280,12 +1280,22 @@ def _assemble_cmd(self): """ cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - for o in ("all", "installed", "long", "force_lib", "verbose", "failed", "new"): + # Options with no argument + for o in ("all", "installed", "long", "initial", "exitfirst", + "force_lib", "if_installed", "abspath", "verbose", + "debug", "only_errors", "failed", "new", + "show_skipped"): if o in opt: cmd += "--%s " % o - for o in ("timeout", "randorder", "stats_path"): + # Options with one argument + for o in ("timeout", "die_timeout", "logfile", "warn_long", "randorder", + "random_seed", "global_iterations", "file_iterations", + "environment", "baseline_stats_path", "stats_path"): if o in opt: cmd += "--%s=%s " % (o, opt[o]) + # One with a different dest + if "target_walltime" in opt: + cmd += "--%s=%s " % ("short", opt[o]) if "optional" in opt: cmd += "--optional={} ".format(self._optional_tags_string()) return cmd + " ".join(self.files) From 0e07e7adce1f9eeaa844f9c28cf9b07ff447cd3b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 21:43:06 -0700 Subject: [PATCH 050/193] DocTestDefaults: Update doctest --- src/sage/doctest/control.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 2c2bdbb4d5a..bb49a9ff414 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -91,6 +91,8 @@ class DocTestDefaults(SageObject): The defaults for ``sage-runtests``:: sage: D = DocTestDefaults(runtest_default=True); D + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + stats_path='.../timings2.json') """ def __init__(self, runtest_default=False, **kwds): """ From 47edf02761e18a6920be9fc15935b8ece5540912 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 22:10:52 -0700 Subject: [PATCH 051/193] DocTestDefaults: When runtest_default is True, create defaults that are same as in sage-runtests (fixup) --- src/sage/doctest/control.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index bb49a9ff414..bce4ccd8fc0 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -92,6 +92,7 @@ class DocTestDefaults(SageObject): sage: D = DocTestDefaults(runtest_default=True); D DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, stats_path='.../timings2.json') """ def __init__(self, runtest_default=False, **kwds): @@ -119,7 +120,7 @@ def __init__(self, runtest_default=False, **kwds): self.long = False self.warn_long = -1.0 self.randorder = None - self.random_seed = 0 + self.random_seed = None if runtest_default else 0 self.global_iterations = 0 if runtest_default else 1 self.file_iterations = 0 if runtest_default else 1 self.environment = "sage.repl.ipython_kernel.all_jupyter" @@ -149,7 +150,10 @@ def __init__(self, runtest_default=False, **kwds): # automatically anyway. However, this default is still used for # displaying user-defined optional tags and we don't want to see # the auto_optional_tags there. - self.optional = {'sage'} | auto_optional_tags + if runtest_default: + self.optional = ','.join(['sage', 'optional']) + else: + self.optional = {'sage'} | auto_optional_tags self.hide = '' self.probe = '' From 34f35941c7476998f4d13f12e749bb3a88d7c2b6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 22:11:25 -0700 Subject: [PATCH 052/193] src/sage/doctest/__main__.py: Test that the command-line defaults are consistent --- src/sage/doctest/__main__.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/sage/doctest/__main__.py b/src/sage/doctest/__main__.py index 4310574c373..d1dae5c7947 100644 --- a/src/sage/doctest/__main__.py +++ b/src/sage/doctest/__main__.py @@ -17,7 +17,29 @@ def _get_optional_defaults(): return ','.join(optional) -def main(): +def _make_parser(): + r""" + Return the :class:`argparse.ArgumentParser`. + + TESTS: + + Test that the defaults are the consistent:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.__main__ import _make_parser + sage: parser = _make_parser() + sage: args = parser.parse_args([]) + sage: DD = DocTestDefaults(runtest_default=True); DD + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, + stats_path='.../timings2.json') + sage: D = copy(args.__dict__) + sage: del D['filenames'] + sage: DA = DocTestDefaults(runtest_default=True, **D); DA + DocTestDefaults(abspath=False, file_iterations=0, global_iterations=0, + optional='sage,optional', random_seed=None, + stats_path='.../timings2.json') + """ parser = argparse.ArgumentParser(usage="sage -t [options] filenames", description="Run all tests in a file or a list of files whose extensions " "are one of the following: " @@ -127,7 +149,11 @@ def __call__(self, parser, namespace, values, option_string=None): parser.add_argument("--die_timeout", type=int, default=-1, help=argparse.SUPPRESS) parser.add_argument("filenames", help="file names", nargs='*') + return parser + +def main(): + parser = _make_parser() # custom treatment to separate properly # one or several file names at the end new_arguments = [] From 0e8e857ebb8e945d863b35e86b12e5bde9dd07c7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 14 Mar 2024 22:15:45 -0700 Subject: [PATCH 053/193] DocTestController._assemble_cmd: Compare with the runtest defaults --- src/sage/doctest/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index bce4ccd8fc0..26a68cb24c5 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1285,7 +1285,7 @@ def _assemble_cmd(self): ...python... -m sage.doctest --serial --timeout=123 hello_world.py """ cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " - opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) + opt = dict_difference(self.options.__dict__, DocTestDefaults(runtest_default=True).__dict__) # Options with no argument for o in ("all", "installed", "long", "initial", "exitfirst", "force_lib", "if_installed", "abspath", "verbose", From 38e988b305b3da9bb8610aa55167cc9b7d6970e3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 28 Jun 2024 21:35:53 -0700 Subject: [PATCH 054/193] src/sage/doctest/control.py: Relax some doctests --- src/sage/doctest/control.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 26a68cb24c5..374d9b28002 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1282,7 +1282,7 @@ def _assemble_cmd(self): sage: from sage.doctest.control import DocTestDefaults, DocTestController sage: DC = DocTestController(DocTestDefaults(timeout=123), ["hello_world.py"]) sage: print(DC._assemble_cmd()) - ...python... -m sage.doctest --serial --timeout=123 hello_world.py + ...python... -m sage.doctest --serial... --timeout=123... hello_world.py """ cmd = f"{shlex.quote(sys.executable)} -m sage.doctest --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults(runtest_default=True).__dict__) @@ -1325,14 +1325,14 @@ def run_val_gdb(self, testing=False): sage: DD = DocTestDefaults(gdb=True) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec gdb --eval-command="run" --args ...python... -m sage.doctest --serial --timeout=0 hello_world.py + exec gdb --eval-command="run" --args ...python... -m sage.doctest --serial... --timeout=0... hello_world.py :: sage: DD = DocTestDefaults(valgrind=True, optional='all', timeout=172800) sage: DC = DocTestController(DD, ["hello_world.py"]) sage: DC.run_val_gdb(testing=True) - exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions=.../valgrind/pyalloc.supp --suppressions=.../valgrind/sage.supp --suppressions=.../valgrind/sage-additional.supp --suppressions=.../valgrind/valgrind-python.supp --log-file=.../valgrind/sage-memcheck.%p ...python... -m sage.doctest --serial --timeout=172800 --optional=all hello_world.py + exec valgrind --tool=memcheck --leak-resolution=high --leak-check=full --num-callers=25 --suppressions=.../valgrind/pyalloc.supp --suppressions=.../valgrind/sage.supp --suppressions=.../valgrind/sage-additional.supp --suppressions=.../valgrind/valgrind-python.supp --log-file=.../valgrind/sage-memcheck.%p ...python... -m sage.doctest --serial... --timeout=172800... --optional=all hello_world.py """ try: sage_cmd = self._assemble_cmd() From 4c8cbf2182f65a19abf76447b5d95060f9c22b14 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 11 Aug 2024 19:21:39 -0700 Subject: [PATCH 055/193] build/pkgs/cython: Update to 3.0.11 --- build/pkgs/cython/checksums.ini | 8 ++++---- build/pkgs/cython/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index 9393fe60b7f..e463df31628 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,4 @@ -tarball=Cython-VERSION.tar.gz -sha1=83d6428e3bb7869f44f92ed75d7dff867c2a38ce -sha256=dcc96739331fb854dcf503f94607576cfe8488066c61ca50dfd55836f132de99 -upstream_url=https://pypi.io/packages/source/C/Cython/Cython-VERSION.tar.gz +tarball=cython-VERSION.tar.gz +sha1=f692b0c6f209b75b6bbd69bdbd57fac23785c23e +sha256=7146dd2af8682b4ca61331851e6aebce9fe5158e75300343f80c07ca80b1faff +upstream_url=https://pypi.io/packages/source/c/cython/cython-VERSION.tar.gz diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index a909317fe5a..778bf95c00f 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -3.0.10 +3.0.11 From 4e57bf67077eb0d8d31ba0a7be6bee7804f88e92 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 20:48:05 -0700 Subject: [PATCH 056/193] DocTestController._assemble_cmd: Handle options with dashes correctly --- src/sage/doctest/control.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 374d9b28002..e6fcc29a61b 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1292,13 +1292,13 @@ def _assemble_cmd(self): "debug", "only_errors", "failed", "new", "show_skipped"): if o in opt: - cmd += "--%s " % o + cmd += "--%s " % o.replace('_', '-') # Options with one argument for o in ("timeout", "die_timeout", "logfile", "warn_long", "randorder", "random_seed", "global_iterations", "file_iterations", "environment", "baseline_stats_path", "stats_path"): if o in opt: - cmd += "--%s=%s " % (o, opt[o]) + cmd += "--%s=%s " % (o.replace('_', '-'), opt[o]) # One with a different dest if "target_walltime" in opt: cmd += "--%s=%s " % ("short", opt[o]) From 61a95e0c565ec92eb17d7112f4e42b0e7c0e8ce2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 19 Aug 2024 17:54:19 -0700 Subject: [PATCH 057/193] build/pkgs/jupyter_jsmol: Change to optional --- build/pkgs/jupyter_jsmol/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/jupyter_jsmol/type b/build/pkgs/jupyter_jsmol/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/jupyter_jsmol/type +++ b/build/pkgs/jupyter_jsmol/type @@ -1 +1 @@ -standard +optional From 330067bdddb9d1e809bb77ed5392d4c2aa35664d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 20 Aug 2024 20:46:54 +0200 Subject: [PATCH 058/193] some details in strassen.pyx --- src/sage/matrix/strassen.pyx | 136 +++++++++++++++++------------------ 1 file changed, 66 insertions(+), 70 deletions(-) diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index 56c4bf183ad..d1c6a50dbba 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -1,19 +1,19 @@ """ Generic Asymptotically Fast Strassen Algorithms -Sage implements asymptotically fast echelon form and matrix +This implements asymptotically fast echelon form and matrix multiplication algorithms. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2005, 2006 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/ +# *************************************************************************** from sage.matrix.matrix_window cimport MatrixWindow @@ -27,11 +27,13 @@ def strassen_window_multiply(C, A,B, cutoff): multiplied, and that C is the correct size to receive the product, and that they are all defined over the same ring. - Uses strassen multiplication at high levels and then uses - MatrixWindow methods at low levels. EXAMPLES: The following matrix + Uses Strassen multiplication at high levels and then uses + MatrixWindow methods at low levels. + + EXAMPLES: The following matrix dimensions are chosen especially to exercise the eight possible parity combinations that could occur while subdividing the matrix - in the strassen recursion. The base case in both cases will be a + in the Strassen recursion. The base case in both cases will be a (4x5) matrix times a (5x6) matrix. :: @@ -248,7 +250,9 @@ cdef subtract_strassen_product(MatrixWindow result, MatrixWindow A, MatrixWindow def strassen_echelon(MatrixWindow A, cutoff): """ Compute echelon form, in place. Internal function, call with - M.echelonize(algorithm='strassen') Based on work of Robert Bradshaw + M.echelonize(algorithm='strassen') + + Based on work of Robert Bradshaw and David Harvey at MSRI workshop in 2006. INPUT: @@ -324,13 +328,13 @@ cdef strassen_echelon_c(MatrixWindow A, Py_ssize_t cutoff, Py_ssize_t mul_cutoff nrows = A.nrows() ncols = A.ncols() - if (nrows <= cutoff or ncols <= cutoff): + if nrows <= cutoff or ncols <= cutoff: return list(A.echelon_in_place()) cdef Py_ssize_t top_h, bottom_cut, bottom_h, bottom_start, top_cut cdef Py_ssize_t prev_pivot_count cdef Py_ssize_t split - split = nrows / 2 + split = nrows // 2 cdef MatrixWindow top, bottom, top_left, top_right, bottom_left, bottom_right, clear @@ -485,7 +489,7 @@ class int_range: Repetitions are not considered. - Useful class for dealing with pivots in the strassen echelon, could + Useful class for dealing with pivots in the Strassen echelon, could have much more general application INPUT: @@ -555,7 +559,7 @@ class int_range: if indices is None: self._intervals = [] return - elif range is not None: + if range is not None: self._intervals = [(int(indices), int(range))] else: self._intervals = [] @@ -629,11 +633,8 @@ class int_range: sage: I.to_list() [1, 2, 3] """ - all = [] - for iv in self._intervals: - for i in range(iv[0], iv[0]+iv[1]): - all.append(i) - return all + return [i for iv0, iv1 in self._intervals + for i in range(iv0, iv0 + iv1)] def __iter__(self): r""" @@ -676,10 +677,7 @@ class int_range: sage: len(I) 3 """ - len = 0 - for iv in self._intervals: - len = len + iv[1] - return int(len) + return sum(iv1 for _, iv1 in self._intervals) def __add__(self, right): r""" @@ -734,10 +732,8 @@ class int_range: [(6, 1), (20, 4)] """ all = self.to_list() - for i in right.to_list(): - if i in all: - all.remove(i) - return int_range(all) + diff = [i for i in right.to_list() if i not in all] + return int_range(diff) def __mul__(self, right): r""" @@ -757,17 +753,16 @@ class int_range: sage: J * I [(4, 2)] """ - intersection = [] all = self.to_list() - for i in right.to_list(): - if i in all: - intersection.append(i) + intersection = [i for i in right.to_list() if i in all] return int_range(intersection) # Useful test code: def test(n, m, R, c=2): r""" + Test code for the Strassen algorithm. + INPUT: - ``n`` -- integer @@ -799,56 +794,57 @@ def test(n, m, R, c=2): # below aren't callable now without using Pyrex. -## todo: doc cutoff parameter as soon as I work out what it really means +# todo: doc cutoff parameter as soon as I work out what it really means + +# EXAMPLES: -## EXAMPLES: -## The following matrix dimensions are chosen especially to exercise the -## eight possible parity combinations that could occur while subdividing -## the matrix in the strassen recursion. The base case in both cases will -## be a (4x5) matrix times a (5x6) matrix. +# The following matrix dimensions are chosen especially to exercise the +# eight possible parity combinations that could occur while subdividing +# the matrix in the strassen recursion. The base case in both cases will +# be a (4x5) matrix times a (5x6) matrix. -## TODO -- the doctests below are currently not -## tested/enabled/working -- enable them when linear algebra -## restructing gets going. +# TODO -- the doctests below are currently not +# tested/enabled/working -- enable them when linear algebra +# restructing gets going. -## sage: dim1 = 64; dim2 = 83; dim3 = 101 -## sage: R = MatrixSpace(QQ, dim1, dim2) -## sage: S = MatrixSpace(QQ, dim2, dim3) -## sage: T = MatrixSpace(QQ, dim1, dim3) +# sage: dim1 = 64; dim2 = 83; dim3 = 101 +# sage: R = MatrixSpace(QQ, dim1, dim2) +# sage: S = MatrixSpace(QQ, dim2, dim3) +# sage: T = MatrixSpace(QQ, dim1, dim3) -## sage: A = R.random_element(range(-30, 30)) -## sage: B = S.random_element(range(-30, 30)) -## sage: C = T(0) -## sage: D = T(0) +# sage: A = R.random_element(range(-30, 30)) +# sage: B = S.random_element(range(-30, 30)) +# sage: C = T(0) +# sage: D = T(0) -## sage: A_window = A.matrix_window(0, 0, dim1, dim2) -## sage: B_window = B.matrix_window(0, 0, dim2, dim3) -## sage: C_window = C.matrix_window(0, 0, dim1, dim3) -## sage: D_window = D.matrix_window(0, 0, dim1, dim3) +# sage: A_window = A.matrix_window(0, 0, dim1, dim2) +# sage: B_window = B.matrix_window(0, 0, dim2, dim3) +# sage: C_window = C.matrix_window(0, 0, dim1, dim3) +# sage: D_window = D.matrix_window(0, 0, dim1, dim3) -## sage: from sage.matrix.strassen import strassen_window_multiply -## sage: strassen_window_multiply(C_window, A_window, B_window, 2) # use strassen method -## sage: D_window.set_to_prod(A_window, B_window) # use naive method -## sage: C_window == D_window -## True +# sage: from sage.matrix.strassen import strassen_window_multiply +# sage: strassen_window_multiply(C_window, A_window, B_window, 2) # use strassen method +# sage: D_window.set_to_prod(A_window, B_window) # use naive method +# sage: C_window == D_window +# True -## sage: dim1 = 79; dim2 = 83; dim3 = 101 -## sage: R = MatrixSpace(QQ, dim1, dim2) -## sage: S = MatrixSpace(QQ, dim2, dim3) -## sage: T = MatrixSpace(QQ, dim1, dim3) +# sage: dim1 = 79; dim2 = 83; dim3 = 101 +# sage: R = MatrixSpace(QQ, dim1, dim2) +# sage: S = MatrixSpace(QQ, dim2, dim3) +# sage: T = MatrixSpace(QQ, dim1, dim3) -## sage: A = R.random_element(range(30)) -## sage: B = S.random_element(range(30)) -## sage: C = T(0) -## sage: D = T(0) +# sage: A = R.random_element(range(30)) +# sage: B = S.random_element(range(30)) +# sage: C = T(0) +# sage: D = T(0) -## sage: A_window = A.matrix_window(0, 0, dim1, dim2) -## sage: B_window = B.matrix_window(0, 0, dim2, dim3) -## sage: C_window = C.matrix_window(0, 0, dim1, dim3) +# sage: A_window = A.matrix_window(0, 0, dim1, dim2) +# sage: B_window = B.matrix_window(0, 0, dim2, dim3) +# sage: C_window = C.matrix_window(0, 0, dim1, dim3) -## sage: strassen_window_multiply(C, A, B, 2) # use strassen method -## sage: D.set_to_prod(A, B) # use naive method +# sage: strassen_window_multiply(C, A, B, 2) # use strassen method +# sage: D.set_to_prod(A, B) # use naive method -## sage: C == D -## True +# sage: C == D +# True From acbda6e2e9f32d10cb2a8c5f5aaa874203dfc88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 21 Aug 2024 10:52:20 +0200 Subject: [PATCH 059/193] fix diff --- src/sage/matrix/strassen.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index d1c6a50dbba..4ea66e78d03 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -731,8 +731,8 @@ class int_range: sage: J - I [(6, 1), (20, 4)] """ - all = self.to_list() - diff = [i for i in right.to_list() if i not in all] + right_list = right.to_list() + diff = [i for i in self.to_list() if i not in right_list] return int_range(diff) def __mul__(self, right): From cc7aa1dc44e689330fec40148c78c0e3435f5c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 21 Aug 2024 13:20:47 +0200 Subject: [PATCH 060/193] fixing some cython --- src/sage/modular/hypergeometric_misc.pxd | 3 ++- src/sage/modular/hypergeometric_misc.pyx | 3 ++- src/sage/structure/sage_object.pyx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pxd b/src/sage/modular/hypergeometric_misc.pxd index 00bf9a97e9a..55355c15c2a 100644 --- a/src/sage/modular/hypergeometric_misc.pxd +++ b/src/sage/modular/hypergeometric_misc.pxd @@ -1,2 +1,3 @@ -cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, +cpdef hgm_coeffs(unsigned long long p, unsigned int f, + int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 3be8e4dd545..d7e8453c1e9 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -6,7 +6,8 @@ from cpython cimport array from cysignals.signals cimport sig_check from sage.rings.integer cimport Integer -cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, +cpdef hgm_coeffs(unsigned long long p, unsigned int f, + int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): r""" Compute coefficients for the hypergeometric trace formula. diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index fb6d4d0f686..7102316be70 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -719,7 +719,7 @@ cdef class SageObject: try: s = self._interface_init_(I) except Exception: - raise NotImplementedError("coercion of object %s to %s not implemented:\n%s\n%s" % (repr(self), I)) + raise NotImplementedError("coercion of object %s to %s not implemented" % (repr(self), I)) X = I(s) if c: try: From b08bea1d3c4b3219698b1038f4ce9351004c2e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 21 Aug 2024 14:29:54 +0200 Subject: [PATCH 061/193] some fixes in cython files (found using cython -a *.pyx) --- .../designs/evenly_distributed_sets.pyx | 10 +-- .../combinat/designs/subhypergraph_search.pyx | 8 +-- src/sage/matroids/matroid.pyx | 2 +- src/sage/modular/arithgroup/congroup.pyx | 12 ++-- src/sage/modular/hypergeometric_misc.pyx | 4 +- .../modular/modform/eis_series_cython.pyx | 4 +- src/sage/modular/modsym/heilbronn.pyx | 16 ++--- src/sage/modular/modsym/p1list.pyx | 42 ++++++------ .../schemes/elliptic_curves/mod_sym_num.pyx | 66 +++++++++---------- 9 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/sage/combinat/designs/evenly_distributed_sets.pyx b/src/sage/combinat/designs/evenly_distributed_sets.pyx index 744cb4e3513..1d1a7acb8ec 100644 --- a/src/sage/combinat/designs/evenly_distributed_sets.pyx +++ b/src/sage/combinat/designs/evenly_distributed_sets.pyx @@ -218,24 +218,24 @@ cdef class EvenlyDistributedSetsBacktracker: raise ValueError(f"{K} is not a field") cdef unsigned int q = K.cardinality() cdef unsigned int e = k*(k-1)/2 - if (q-1) % (2*e) != 0: + if (q-1) % (2*e): raise ValueError("k(k-1)={} does not divide q-1={}".format(k*(k-1),q-1)) - cdef unsigned int m = (q-1)/e + cdef unsigned int m = (q - 1) // e self.q = q self.e = e self.k = k - self.m = (q-1) / e + self.m = (q - 1) // e self.K = K self.diff = check_calloc(q, sizeof(unsigned int *)) self.diff[0] = check_malloc(q*q*sizeof(unsigned int)) - for i in range(1,self.q): + for i in range(1, self.q): self.diff[i] = self.diff[i-1] + q self.ratio = check_calloc(q, sizeof(unsigned int *)) self.ratio[0] = check_malloc(q*q*sizeof(unsigned int)) - for i in range(1,self.q): + for i in range(1, self.q): self.ratio[i] = self.ratio[i-1] + q self.B = check_malloc(k*sizeof(unsigned int)) diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index b7a71a17b86..b7ea8907682 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -133,7 +133,7 @@ cdef inline int bs_get(uint64_t * bitset, int index) noexcept: r""" Return a bit of a bitset """ - return (bitset[index/64]>>(index%64))&1 + return (bitset[index//64]>>(index%64))&1 cdef inline void bs_set(uint64_t * bitset, int index, int bit) noexcept: r""" @@ -142,8 +142,8 @@ cdef inline void bs_set(uint64_t * bitset, int index, int bit) noexcept: "bit" *MUST* be equal to either 0 or to 1. The code does not involve any "if". """ - bitset[index/64] &= ~(( 1)< bit)< 1)< bit)< sig_malloc(sizeof(int)*n) h.sets = sig_malloc(h.m*sizeof(uint64_t *)) h.set_space = sig_calloc(h.m*(h.limbs+1),sizeof(uint64_t)) diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index bfcf7d361a2..d479aaccec0 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -5393,7 +5393,7 @@ cdef class Matroid(SageObject): E = set(self.groundset()) Q = set(list(E)[:m]) E = E-Q - for r in range(len(Q)/2 + 1): + for r in range(len(Q) // 2 + 1): R = set(list(E)[:r]) for Q1 in map(set, combinations(Q, r)): Q2 = Q-Q1 diff --git a/src/sage/modular/arithgroup/congroup.pyx b/src/sage/modular/arithgroup/congroup.pyx index ee988672a4f..d55c0c0ce7c 100644 --- a/src/sage/modular/arithgroup/congroup.pyx +++ b/src/sage/modular/arithgroup/congroup.pyx @@ -116,7 +116,7 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): # total number of coset representatives that we'll find n = Gamma0(N).index() / Gamma0(M).index() k = 0 # number found so far - Ndivt = N / t + Ndivt = N // t R = check_allocarray(4 * n, sizeof(int)) halfmax = 2*(n+10) while k < n: @@ -126,10 +126,10 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): g = arith_int.c_xgcd_int(-cc,dd,&bb,&aa) if g == 0: continue - cc = cc / g + cc = cc // g if cc % M != 0: continue - dd = dd / g + dd = dd // g # Test if we've found a new coset representative. is_new = 1 for i in range(k): @@ -217,7 +217,7 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): # total number of coset representatives that we'll find n = Gamma1(N).index() / Gamma1(M).index() d = arith_int.c_gcd_int(t, N // t) - n = n / d + n = n // d k = 0 # number found so far Ndivt = N // t R = check_allocarray(4 * n, sizeof(int)) @@ -229,10 +229,10 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): g = arith_int.c_xgcd_int(-cc, dd, &bb, &aa) if g == 0: continue - cc = cc / g + cc = cc // g if cc % M != 0: continue - dd = dd / g + dd = dd // g if M != 1 and dd % M != 1: continue # Test if we've found a new coset representative. diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index d7e8453c1e9..80b0ca4d0d0 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -36,8 +36,8 @@ cpdef hgm_coeffs(unsigned long long p, unsigned int f, """ from sage.rings.padics.factory import Zp - cdef int gl, j, k, l, v, gv - cdef long long i, q1, w, w1, w2, q2, r, r1 + cdef int gl, j, k, v, gv + cdef long long i, l, q1, w, w1, w2, q2, r, r1 cdef bint flip, use_longlongs q1 = p ** f - 1 diff --git a/src/sage/modular/modform/eis_series_cython.pyx b/src/sage/modular/modform/eis_series_cython.pyx index 8126d1f8135..73ffdfab324 100644 --- a/src/sage/modular/modform/eis_series_cython.pyx +++ b/src/sage/modular/modform/eis_series_cython.pyx @@ -107,12 +107,12 @@ cpdef Ek_ZZ(int k, int prec=10): # compute the valuation of n at p additional_p_powers = 0 - temp_index = ind / p + temp_index = ind // p remainder = 0 while not remainder: additional_p_powers += 1 prev_index = temp_index - temp_index = temp_index / p + temp_index = temp_index // p remainder = prev_index - p*temp_index # if we need a new sum, it has to be the next uncomputed one. diff --git a/src/sage/modular/modsym/heilbronn.pyx b/src/sage/modular/modsym/heilbronn.pyx index a14f2e6ccac..82adf9efa20 100644 --- a/src/sage/modular/modsym/heilbronn.pyx +++ b/src/sage/modular/modsym/heilbronn.pyx @@ -383,7 +383,7 @@ cdef class HeilbronnCremona(Heilbronn): # NOTE: In C, -p/2 means "round toward 0", but in Python it # means "round down." sig_on() - for r in range(-p/2, p/2+1): + for r in range(-p // 2, p // 2 + 1): x1 = p x2 = -r y1 = 0 @@ -394,7 +394,7 @@ cdef class HeilbronnCremona(Heilbronn): x3 = 0 y3 = 0 q = 0 - list_append4(L, x1,x2,y1,y2) + list_append4(L, x1, x2, y1, y2) while b: q = roundf(a / b) c = a - b*q @@ -406,8 +406,8 @@ cdef class HeilbronnCremona(Heilbronn): y3 = q*y2 - y1 y1 = y2 y2 = y3 - list_append4(L, x1,x2, y1,y2) - self.length = L.i/4 + list_append4(L, x1, x2, y1, y2) + self.length = L.i // 4 sig_off() @@ -491,7 +491,7 @@ cdef class HeilbronnMerel(Heilbronn): for a in range(1, n+1): ## We have ad-bc=n so c=0 and ad=n, or b=(ad-n)/c ## Must have ad - n >= 0, so d must be >= Ceiling(n/a). - q = n/a + q = n // a if q*a == n: d = q for b in range(a): @@ -503,10 +503,10 @@ cdef class HeilbronnMerel(Heilbronn): ## Divisor c of bc must satisfy Floor(bc/c) lt a and c lt d. ## c ge (bc div a + 1) <=> Floor(bc/c) lt a (for integers) ## c le d - 1 <=> c lt d - for c in range(bc/a + 1, d): + for c in range(bc // a + 1, d): if bc % c == 0: - list_append4(L,a,bc/c,c,d) - self.length = L.i/4 + list_append4(L, a, bc // c, c, d) + self.length = L.i // 4 sig_off() diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 5e48d6b1369..7ceda12d5c8 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -94,8 +94,8 @@ cdef int c_p1_normalize_int(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -105,8 +105,8 @@ cdef int c_p1_normalize_int(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = (v*Ng) % N t = 1 for k in range(2, g + 1): @@ -355,8 +355,8 @@ cdef int c_p1_normalize_llong(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -367,8 +367,8 @@ cdef int c_p1_normalize_llong(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = ((v * Ng) % ll_N) t = 1 for k in range(2, g + 1): @@ -640,8 +640,8 @@ cdef int p1_normalize_xgcdtable(int N, int u, int v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. - if g!=1: - d = N/g + if g != 1: + d = N // g while t_g[s] != 1: # while arith_int.c_gcd_int(s,N) != 1: s = (s+d) % N @@ -651,8 +651,8 @@ cdef int p1_normalize_xgcdtable(int N, int u, int v, min_v = v min_t = 1 - if g!=1: - Ng = N/g + if g != 1: + Ng = N // g vNg = (v*Ng) % N t = 1 for k in range(2, g + 1): @@ -1237,17 +1237,17 @@ def lift_to_sl2z_int(int c, int d, int N): # compute prime-to-d part of m. while True: - g = arith_int.c_gcd_int(m,d) + g = arith_int.c_gcd_int(m, d) if g == 1: break - m = m / g + m = m // g # compute prime-to-N part of m. while True: - g = arith_int.c_gcd_int(m,N) + g = arith_int.c_gcd_int(m, N) if g == 1: break - m = m / g + m = m // g d += N * m g = arith_int.c_xgcd_int(c, d, &z1, &z2) @@ -1299,7 +1299,7 @@ def lift_to_sl2z_llong(llong c, llong d, int N): g = arith_llong.c_xgcd_longlong(c, d, &z1, &z2) # We're lucky: z1*c + z2*d = 1. - if g==1: + if g == 1: return [z2, -z1, c, d] # Have to try harder. @@ -1307,17 +1307,17 @@ def lift_to_sl2z_llong(llong c, llong d, int N): # compute prime-to-d part of m. while True: - g = arith_llong.c_gcd_longlong(m,d) + g = arith_llong.c_gcd_longlong(m, d) if g == 1: break - m = m / g + m = m // g # compute prime-to-N part of m. while True: - g = arith_llong.c_gcd_longlong(m,N) + g = arith_llong.c_gcd_longlong(m, N) if g == 1: break - m = m / g + m = m // g d += N * m g = arith_llong.c_xgcd_longlong(c, d, &z1, &z2) diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 927df5e5a42..402477da0d6 100755 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -256,9 +256,9 @@ cdef llong llxgcd(llong a, llong b, llong *ss, llong *tt) except -1: q = 0 r = 0 s = 1 - while (b): + while b: c = a % b - quot = a/b + quot = a // b a = b b = c new_r = p - quot*r @@ -365,9 +365,9 @@ cdef int proj_normalise(llong N, llong u, llong v, # Now g = s*u + t*N, so s is a "pseudo-inverse" of u mod N # Adjust s modulo N/g so it is coprime to N. if g != 1: - d = N / g + d = N // g while llgcd(s, N) != 1: - s = (s+d) % N + s = (s + d) % N # verbose(" now g=%s, s=%s, t=%s" % (g,s,t), level=5) # Multiply [u,v] by s; then [s*u,s*v] = [g,s*v] (mod N) @@ -376,7 +376,7 @@ cdef int proj_normalise(llong N, llong u, llong v, min_v = v min_t = 1 if g != 1: - Ng = N / g + Ng = N // g vNg = (v * Ng) % N t = 1 k = 2 @@ -456,24 +456,24 @@ cdef int best_proj_point(llong u, llong v, llong N, else: # cases like (p:q) mod p*q drop here p = llgcd(u, N) q = llgcd(v, N) - Nnew = N / p / q - w = ((u/p) * llinvmod(v/q, Nnew)) % Nnew - y0 = N/q + Nnew = N // p // q + w = ((u // p) * llinvmod(v // q, Nnew)) % Nnew + y0 = N // q y1 = 0 x0 = w*p x1 = q # y will always be the longer and x the shorter - while llabs(x0) + llabs(x1) < llabs(y0)+llabs(y1): + while llabs(x0) + llabs(x1) < llabs(y0) + llabs(y1): if llsign(x0) == llsign(x1): - r = (y0+y1) / (x0+x1) + r = (y0+y1) // (x0+x1) else: - r = (y0-y1) / (x0-x1) + r = (y0-y1) // (x0-x1) t0 = y0 - r * x0 t1 = y1 - r * x1 s0 = t0 - x0 s1 = t1 - x1 - if llabs(s0)+llabs(s1) < llabs(t0)+llabs(t1): + if llabs(s0) + llabs(s1) < llabs(t0) + llabs(t1): t0 = s0 t1 = s1 # t is now the shortest vector on the line y + RR x @@ -593,7 +593,7 @@ cdef class _CuspsForModularSymbolNumerical: a -= m self._r = Rational((a, m)) B = llgcd(m, N) - self._width = N / B + self._width = N // B self._a = a self._m = m self._N_level = N @@ -625,7 +625,7 @@ cdef class _CuspsForModularSymbolNumerical: # verbose(" enter atkin_lehner for cusp r=%s" % self._r, level=5) Q = self._width B = llgcd(self._m, self._N_level) - c = self._m / B + c = self._m // B if llgcd(Q, B) != 1: raise ValueError("This cusp is not in the Atkin-Lehner " "orbit of oo.") @@ -2125,12 +2125,12 @@ cdef class ModularSymbolNumerical: verbose(" yields %s " % int2c, level=3) ans = int2c + int1c else: # use_partials - g = llgcd(Q,QQ) + g = llgcd(Q, QQ) D = Q * QQ D /= g D *= llabs(a*mm-aa*m) - xi = (Q*aa*u+v*mm) * QQ /g * llsign(a*mm-aa*m) - xixi = (QQ*a*uu+vv*m) * Q /g * llsign(aa*m-a*mm) + xi = (Q*aa*u+v*mm) * (QQ // g) * llsign(a*mm-aa*m) + xixi = (QQ*a*uu+vv*m) * (Q // g) * llsign(aa*m-a*mm) z = Q * QQ * (a*mm-aa*m)**2 ka = self._kappa(D, z, eps/2) twopii = TWOPI * complex("j") @@ -2787,33 +2787,33 @@ cdef class ModularSymbolNumerical: else: # (c:d) = (u:v) but c and d are fairly small # in absolute value - Mu = llgcd(u,N) - Qu = N/Mu - Mv = llgcd(v,N) - Qv = N/Mv - isunitary = (llgcd(Qu,Mu) == 1 and llgcd(Qv,Mv) == 1) + Mu = llgcd(u, N) + Qu = N // Mu + Mv = llgcd(v, N) + Qv = N // Mv + isunitary = (llgcd(Qu, Mu) == 1 and llgcd(Qv, Mv) == 1) if isunitary: # unitary case _ = best_proj_point(u, v, self._N_E, &c, &d) else: # at least one of the two cusps is not unitary du = llgcd(Qu,Mu) - dv = llgcd(Qv,Mv) - NMM = N/Mv/Mu + dv = llgcd(Qv, Mv) + NMM = N // Mv // Mu if dv == 1: c = Mu - d = llinvmod(u/Mu, NMM) + d = llinvmod(u // Mu, NMM) d *= v - d = d % (N/Mu) + d = d % (N // Mu) while llgcd(c,d) != 1: - d += N/Mu + d += N // Mu d = d % N # now (u:v) = (c:d) with c as small as possible. else: d = Mv - c = llinvmod(v/Mv, NMM) + c = llinvmod(v // Mv, NMM) c *= u - c = c % (N/Mv) - while llgcd(c,d) != 1: - c += N/Mv + c = c % (N // Mv) + while llgcd(c, d) != 1: + c += N // Mv c = c % N # now (u:v) = (c:d) with d as small as possible. # verbose(" better representant on P^1: " @@ -3170,8 +3170,8 @@ cdef class ModularSymbolNumerical: RR = RealField(53) N = self._N_E - Q = N / llgcd(m,N) - if llgcd(m,Q) > 1: + Q = N // llgcd(m, N) + if llgcd(m, Q) > 1: raise NotImplementedError("Only implemented for cusps that are " "in the Atkin-Lehner orbit of oo") # verbose(" compute all partial sums with denominator m=%s" % m, From c71c0d5b8e3fcbd436a144c675e759dc9e8cac56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 21 Aug 2024 20:49:04 +0200 Subject: [PATCH 062/193] micro-fix in hgm --- src/sage/modular/hypergeometric_misc.pxd | 2 +- src/sage/modular/hypergeometric_misc.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pxd b/src/sage/modular/hypergeometric_misc.pxd index 55355c15c2a..ccd013ca2ee 100644 --- a/src/sage/modular/hypergeometric_misc.pxd +++ b/src/sage/modular/hypergeometric_misc.pxd @@ -1,3 +1,3 @@ -cpdef hgm_coeffs(unsigned long long p, unsigned int f, +cpdef hgm_coeffs(long long p, unsigned int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 80b0ca4d0d0..f3e37b515d9 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -6,7 +6,7 @@ from cpython cimport array from cysignals.signals cimport sig_check from sage.rings.integer cimport Integer -cpdef hgm_coeffs(unsigned long long p, unsigned int f, +cpdef hgm_coeffs(long long p, unsigned int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): r""" From a3cc79602082cf80131952a20da741249a569a82 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:50:30 -0700 Subject: [PATCH 063/193] build/pkgs/wheel: Update to 0.44.0 --- build/pkgs/wheel/checksums.ini | 4 ++-- build/pkgs/wheel/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini index ea4d40c1f27..3157a2efcec 100644 --- a/build/pkgs/wheel/checksums.ini +++ b/build/pkgs/wheel/checksums.ini @@ -1,4 +1,4 @@ tarball=wheel-VERSION-py3-none-any.whl -sha1=71a83a2237cb57ab45bdafed364564e36ca5dc95 -sha256=55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81 +sha1=65ec55742da04152c8b06d6586fb36d779d7883e +sha256=2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f upstream_url=https://pypi.io/packages/py3/w/wheel/wheel-VERSION-py3-none-any.whl diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt index 8298bb08b2d..a8ab6c9666a 100644 --- a/build/pkgs/wheel/package-version.txt +++ b/build/pkgs/wheel/package-version.txt @@ -1 +1 @@ -0.43.0 +0.44.0 From d078aac7200fb8d81b67285033696ded595896ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:52:13 -0700 Subject: [PATCH 064/193] build/pkgs/setuptools: Update to 73.0.1 --- build/pkgs/setuptools/checksums.ini | 4 ++-- build/pkgs/setuptools/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 2436821630d..97d83ec2dd7 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools-VERSION-py3-none-any.whl -sha1=49841be6743b2d129d01d02d5fd339dd693c99dc -sha256=c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32 +sha1=3756539d45341ca5cec9e2dfe11539faa066f5cd +sha256=b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e upstream_url=https://pypi.io/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 2a93f495d25..153e4cd6210 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -69.5.1 +73.0.1 From 348c6192638de5350796e725bccec674e936f56b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:52:52 -0700 Subject: [PATCH 065/193] build/pkgs/typing_extensions: Update to 4.12.2 --- build/pkgs/typing_extensions/checksums.ini | 4 ++-- build/pkgs/typing_extensions/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/typing_extensions/checksums.ini b/build/pkgs/typing_extensions/checksums.ini index c22c17569af..5d4ca8ca5f2 100644 --- a/build/pkgs/typing_extensions/checksums.ini +++ b/build/pkgs/typing_extensions/checksums.ini @@ -1,4 +1,4 @@ tarball=typing_extensions-VERSION-py3-none-any.whl -sha1=049c6031f754e1c33932ce1c2ad78b857a70a244 -sha256=b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594 +sha1=0fb5b2732cc421561b1348cac1334eb6a4e0bb7f +sha256=04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d upstream_url=https://pypi.io/packages/py3/t/typing_extensions/typing_extensions-VERSION-py3-none-any.whl diff --git a/build/pkgs/typing_extensions/package-version.txt b/build/pkgs/typing_extensions/package-version.txt index 815588ef140..f1cd7de1de5 100644 --- a/build/pkgs/typing_extensions/package-version.txt +++ b/build/pkgs/typing_extensions/package-version.txt @@ -1 +1 @@ -4.12.0 +4.12.2 From 32cd615d79637a6ecff6b56baf706799e7ada188 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:53:13 -0700 Subject: [PATCH 066/193] build/pkgs/trove_classifiers: Update to 2024.7.2 --- build/pkgs/trove_classifiers/checksums.ini | 4 ++-- build/pkgs/trove_classifiers/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/trove_classifiers/checksums.ini b/build/pkgs/trove_classifiers/checksums.ini index 8d25c121cc2..42e0ec6eefe 100644 --- a/build/pkgs/trove_classifiers/checksums.ini +++ b/build/pkgs/trove_classifiers/checksums.ini @@ -1,4 +1,4 @@ tarball=trove_classifiers-VERSION-py3-none-any.whl -sha1=36240d053d16400380aee01f0879785693008a96 -sha256=678bd6fcc5218d72e3304e27a608acc9b91e17bd00c3f3d8c968497c843ad98b +sha1=8219f839a8223a9dd0912cde22d579cfa75a516e +sha256=ccc57a33717644df4daca018e7ec3ef57a835c48e96a1e71fc07eb7edac67af6 upstream_url=https://pypi.io/packages/py3/t/trove_classifiers/trove_classifiers-VERSION-py3-none-any.whl diff --git a/build/pkgs/trove_classifiers/package-version.txt b/build/pkgs/trove_classifiers/package-version.txt index c296ac66b65..981877628c9 100644 --- a/build/pkgs/trove_classifiers/package-version.txt +++ b/build/pkgs/trove_classifiers/package-version.txt @@ -1 +1 @@ -2024.4.10 +2024.7.2 From 9ed4d8506b7cf83bc35c89a1e1cdb3e5e7c34c16 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:54:05 -0700 Subject: [PATCH 067/193] build/pkgs/pyproject_hooks: Update to 1.1.0 --- build/pkgs/pyproject_hooks/checksums.ini | 4 ++-- build/pkgs/pyproject_hooks/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/pyproject_hooks/checksums.ini b/build/pkgs/pyproject_hooks/checksums.ini index 4688d10283b..fbb1f71394e 100644 --- a/build/pkgs/pyproject_hooks/checksums.ini +++ b/build/pkgs/pyproject_hooks/checksums.ini @@ -1,4 +1,4 @@ tarball=pyproject_hooks-VERSION-py3-none-any.whl -sha1=6c99163c52786fb97eac8b4e38cc13fa3af141a9 -sha256=283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 +sha1=f8c16752e1deea6a3e9a261c6725c1af408d04e7 +sha256=7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2 upstream_url=https://pypi.io/packages/py3/p/pyproject_hooks/pyproject_hooks-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_hooks/package-version.txt b/build/pkgs/pyproject_hooks/package-version.txt index 3eefcb9dd5b..9084fa2f716 100644 --- a/build/pkgs/pyproject_hooks/package-version.txt +++ b/build/pkgs/pyproject_hooks/package-version.txt @@ -1 +1 @@ -1.0.0 +1.1.0 From 1c32adb1e2ea572b0bf4cf651fea785a3010c9ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:54:15 -0700 Subject: [PATCH 068/193] build/pkgs/pyproject_metadata: Update to 0.8.0 --- build/pkgs/pyproject_metadata/checksums.ini | 4 ++-- build/pkgs/pyproject_metadata/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini index 446d55ef265..154abb3ffd7 100644 --- a/build/pkgs/pyproject_metadata/checksums.ini +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -1,4 +1,4 @@ tarball=pyproject-metadata-VERSION.tar.gz -sha1=41fba5c33917d77b9364fadb76e590e86789634d -sha256=0a94f18b108b9b21f3a26a3d541f056c34edcb17dc872a144a15618fed7aef67 +sha1=3e17d00741ef74c1fc05acacffeee390cfe4895e +sha256=376d5a00764ac29440a54579f88e66b7d9cb7e629d35c35a1c7248bfebc9b455 upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt index 39e898a4f95..a3df0a6959e 100644 --- a/build/pkgs/pyproject_metadata/package-version.txt +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -1 +1 @@ -0.7.1 +0.8.0 From dbe0f549b746bbf5d07fa5e14d19062d79318bbf Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:54:22 -0700 Subject: [PATCH 069/193] build/pkgs/pyproject_api: Update to 1.7.1 --- build/pkgs/pyproject_api/checksums.ini | 4 ++-- build/pkgs/pyproject_api/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/pyproject_api/checksums.ini b/build/pkgs/pyproject_api/checksums.ini index cb551b5f7dd..dc2899fad91 100644 --- a/build/pkgs/pyproject_api/checksums.ini +++ b/build/pkgs/pyproject_api/checksums.ini @@ -1,4 +1,4 @@ tarball=pyproject_api-VERSION-py3-none-any.whl -sha1=5ea24c784a68fd0ef0228c332dc078ce64387eb8 -sha256=4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675 +sha1=3723eb52bd6844f30f30f6f5b3723ce0f57a3cbf +sha256=2dc1654062c2b27733d8fd4cdda672b22fe8741ef1dde8e3a998a9547b071eeb upstream_url=https://pypi.io/packages/py3/p/pyproject_api/pyproject_api-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_api/package-version.txt b/build/pkgs/pyproject_api/package-version.txt index 9c6d6293b1a..943f9cbc4ec 100644 --- a/build/pkgs/pyproject_api/package-version.txt +++ b/build/pkgs/pyproject_api/package-version.txt @@ -1 +1 @@ -1.6.1 +1.7.1 From b793a6f9774c1cf85e68da97e9f240f9ce2980e3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:55:15 -0700 Subject: [PATCH 070/193] build/pkgs/packaging: Update to 24.1 --- build/pkgs/packaging/checksums.ini | 4 ++-- build/pkgs/packaging/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini index 4b1b2974bde..560bd2a20a4 100644 --- a/build/pkgs/packaging/checksums.ini +++ b/build/pkgs/packaging/checksums.ini @@ -1,4 +1,4 @@ tarball=packaging-VERSION-py3-none-any.whl -sha1=21573cef174a05ac2794b34f3841d6f9ea9fa507 -sha256=2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 +sha1=a050029d1e0c1b95b3ddcd566be4ad352cd42666 +sha256=5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 upstream_url=https://pypi.io/packages/py3/p/packaging/packaging-VERSION-py3-none-any.whl diff --git a/build/pkgs/packaging/package-version.txt b/build/pkgs/packaging/package-version.txt index d9133a54b63..0dad123924d 100644 --- a/build/pkgs/packaging/package-version.txt +++ b/build/pkgs/packaging/package-version.txt @@ -1 +1 @@ -24.0 +24.1 From b23186962728efd14f142be63b1ab9fd1f0bebfb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:58:02 -0700 Subject: [PATCH 071/193] build/pkgs/pip: Update to 24.2 --- build/pkgs/pip/checksums.ini | 4 ++-- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 62ecf000278..0afec1866ed 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,4 +1,4 @@ tarball=pip-VERSION-py3-none-any.whl -sha1=e44313ae1e6af3c2bd3b60ab2fa8c34308d00555 -sha256=ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc +sha1=044a04440eef697c8ec9e03544117345c57aa683 +sha256=2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 upstream_url=https://pypi.io/packages/py3/p/pip/pip-VERSION-py3-none-any.whl diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index d9133a54b63..9dc0ade5023 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -24.0 +24.2 From 738eb7ec799f43e2023d9dae415f6a80c5ebf465 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 17:59:18 -0700 Subject: [PATCH 072/193] build/pkgs/pyproject_metadata: Change to wheel package --- build/pkgs/pyproject_metadata/SPKG.rst | 2 -- build/pkgs/pyproject_metadata/checksums.ini | 8 ++++---- build/pkgs/pyproject_metadata/dependencies | 2 +- build/pkgs/pyproject_metadata/spkg-install.in | 2 -- build/pkgs/pyproject_metadata/type | 2 +- 5 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 build/pkgs/pyproject_metadata/spkg-install.in diff --git a/build/pkgs/pyproject_metadata/SPKG.rst b/build/pkgs/pyproject_metadata/SPKG.rst index 6c0300e9a16..f3713046b71 100644 --- a/build/pkgs/pyproject_metadata/SPKG.rst +++ b/build/pkgs/pyproject_metadata/SPKG.rst @@ -9,8 +9,6 @@ PEP 621 metadata parsing License ------- -MIT - Upstream Contact ---------------- diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini index 154abb3ffd7..91d3eb258c8 100644 --- a/build/pkgs/pyproject_metadata/checksums.ini +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -1,4 +1,4 @@ -tarball=pyproject-metadata-VERSION.tar.gz -sha1=3e17d00741ef74c1fc05acacffeee390cfe4895e -sha256=376d5a00764ac29440a54579f88e66b7d9cb7e629d35c35a1c7248bfebc9b455 -upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz +tarball=pyproject_metadata-VERSION-py3-none-any.whl +sha1=59998bbcd31cc63f3e95f3ad8120ff71326595a0 +sha256=ad858d448e1d3a1fb408ac5bac9ea7743e7a8bbb472f2693aaa334d2db42f526 +upstream_url=https://pypi.io/packages/py3/p/pyproject_metadata/pyproject_metadata-VERSION-py3-none-any.whl diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies index 3df264eee42..a0e3d35a70e 100644 --- a/build/pkgs/pyproject_metadata/dependencies +++ b/build/pkgs/pyproject_metadata/dependencies @@ -1,4 +1,4 @@ - packaging pyparsing | $(PYTHON_TOOLCHAIN) $(PYTHON) +packaging | pip $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pyproject_metadata/spkg-install.in b/build/pkgs/pyproject_metadata/spkg-install.in deleted file mode 100644 index 37ac1a53437..00000000000 --- a/build/pkgs/pyproject_metadata/spkg-install.in +++ /dev/null @@ -1,2 +0,0 @@ -cd src -sdh_pip_install . diff --git a/build/pkgs/pyproject_metadata/type b/build/pkgs/pyproject_metadata/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/pyproject_metadata/type +++ b/build/pkgs/pyproject_metadata/type @@ -1 +1 @@ -standard +optional From e8558f663770c198a427651a4300994ddb53990f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 21 Aug 2024 18:00:15 -0700 Subject: [PATCH 073/193] build/pkgs/hatchling: Update to 1.25.0 --- build/pkgs/hatchling/checksums.ini | 4 ++-- build/pkgs/hatchling/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index ce647e83c52..1d456db2d53 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,4 +1,4 @@ tarball=hatchling-VERSION-py3-none-any.whl -sha1=2212af13a26dbaea72c7a4ecbdb950c05f6e7c00 -sha256=30ec7ee09f6e17b73257eedfd7f5bb5a9b028a6cf6d144d9faad1d826fa203b8 +sha1=cae79d374a238c7674687fb4fff0b15cf1af9be4 +sha256=b47948e45d4d973034584dd4cb39c14b6a70227cf287ab7ec0ad7983408a882c upstream_url=https://pypi.io/packages/py3/h/hatchling/hatchling-VERSION-py3-none-any.whl diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt index da9594fd66f..ad2191947f7 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.22.5 +1.25.0 From c5663289cf9144cea708972337d3613ce3d56ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 22 Aug 2024 11:27:45 +0200 Subject: [PATCH 074/193] most suggested fixes --- src/sage/matrix/strassen.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index 4ea66e78d03..00807e39c4d 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -703,9 +703,9 @@ class int_range: sage: I + J [(1, 6), (20, 4)] """ - all = self.to_list() - all.extend(right.to_list()) - return int_range(all) + full_list = self.to_list() + full_list.extend(right.to_list()) + return int_range(full_list) def __sub__(self, right): r""" @@ -731,7 +731,7 @@ class int_range: sage: J - I [(6, 1), (20, 4)] """ - right_list = right.to_list() + right_list = set(right.to_list()) diff = [i for i in self.to_list() if i not in right_list] return int_range(diff) @@ -753,8 +753,8 @@ class int_range: sage: J * I [(4, 2)] """ - all = self.to_list() - intersection = [i for i in right.to_list() if i in all] + self_list = set(self.to_list()) + intersection = [i for i in right.to_list() if i in self_list] return int_range(intersection) From eec37d3acf64eb3fe0f198a3198a8f58f476260e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Aug 2024 16:16:35 -0700 Subject: [PATCH 075/193] build/pkgs/pyproject_metadata/type: Revert accidental change --- build/pkgs/pyproject_metadata/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pyproject_metadata/type b/build/pkgs/pyproject_metadata/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/pyproject_metadata/type +++ b/build/pkgs/pyproject_metadata/type @@ -1 +1 @@ -optional +standard From 5d087ab08db8ac956537f26cf46fad1c5dd773aa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 25 Aug 2024 16:17:44 -0700 Subject: [PATCH 076/193] build/pkgs/pyproject_metadata/SPKG.rst: Revert accidental change --- build/pkgs/pyproject_metadata/SPKG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/pkgs/pyproject_metadata/SPKG.rst b/build/pkgs/pyproject_metadata/SPKG.rst index f3713046b71..6c0300e9a16 100644 --- a/build/pkgs/pyproject_metadata/SPKG.rst +++ b/build/pkgs/pyproject_metadata/SPKG.rst @@ -9,6 +9,8 @@ PEP 621 metadata parsing License ------- +MIT + Upstream Contact ---------------- From 52ef38c24bb2e10e1dea3c9b87b57b80c8195aa6 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 27 Aug 2024 06:46:31 -0700 Subject: [PATCH 077/193] Right-align sage inline tabs --- src/doc/common/static/custom-tabs.css | 30 +++++++++++++++++++++++++++ src/sage_docbuild/conf.py | 18 ++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/doc/common/static/custom-tabs.css diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css new file mode 100644 index 00000000000..6722251d251 --- /dev/null +++ b/src/doc/common/static/custom-tabs.css @@ -0,0 +1,30 @@ +/* Additional css for tabs */ +.tab-set { + flex-direction: row-reverse; + margin: -3em 0 0.75em 0; +} + +.tab-set > label { + margin-left:0; + margin-right: var(--tabs--margin-x); +} + +.tab-set > label:last-of-type { + margin-right: 0; +} + +.tab-set > input:nth-of-type(1), +.tab-set > label:nth-of-type(1) { + order:32; +} + +.tab-set > input:nth-of-type(2), +.tab-set > label:nth-of-type(2) { + order:16; +} + +.tab-set > input:nth-of-type(3), +.tab-set > label:nth-of-type(3) { + order:8; +} + diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index ddd64c2718f..97ec5752a23 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -491,6 +491,7 @@ def linkcode_resolve(domain, info): 'custom-furo.css', 'custom-jupyter-sphinx.css', 'custom-codemirror-monokai.css', + 'custom-tabs.css', ] html_js_files = [ @@ -963,6 +964,8 @@ def apply(self): parent.insert(index, Text('')) index += 1 parent.remove(node) + + # Tab for Sage code container = TabContainer("", type="tab", new_set=False) textnodes = [Text('Sage')] @@ -972,6 +975,8 @@ def apply(self): content += node container += content parent.insert(index, container) + index += 1 + if SAGE_PREPARSED_DOC == 'yes': # Tab for preparsed version from sage.repl.preparse import preparse @@ -1002,7 +1007,11 @@ def apply(self): preparsed_node = LiteralBlock(preparsed, preparsed, language='ipycon') content += preparsed_node container += content - parent.insert(index + 1, container) + parent.insert(index, container) + index += 1 + + + if SAGE_LIVE_DOC == 'yes': # Tab for Jupyter-sphinx cell from jupyter_sphinx.ast import JupyterCellNode, CellInputNode @@ -1034,7 +1043,12 @@ def apply(self): content = Container("", is_div=True, classes=["tab-content"]) content += cell_node container += content - parent.insert(index + 1, container) + parent.insert(index, container) + index += 1 + + + + # This replaces the setup() in sage.misc.sagedoc_conf From dedf4ad609572581b2c918e4878eb4a6e216ef8b Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 27 Aug 2024 07:42:18 -0700 Subject: [PATCH 078/193] Remove added whitespace from conf, force max width for tags before tabs so they don't run into each other --- src/doc/common/static/custom-tabs.css | 6 +++++- src/sage_docbuild/conf.py | 10 ---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css index 6722251d251..ea8c83299b0 100644 --- a/src/doc/common/static/custom-tabs.css +++ b/src/doc/common/static/custom-tabs.css @@ -1,7 +1,11 @@ /* Additional css for tabs */ +p:has(+ div.tab-set) { + margin-bottom:-3em; + max-width:80%; +} + .tab-set { flex-direction: row-reverse; - margin: -3em 0 0.75em 0; } .tab-set > label { diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 97ec5752a23..2af41a4a433 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -964,8 +964,6 @@ def apply(self): parent.insert(index, Text('')) index += 1 parent.remove(node) - - # Tab for Sage code container = TabContainer("", type="tab", new_set=False) textnodes = [Text('Sage')] @@ -976,7 +974,6 @@ def apply(self): container += content parent.insert(index, container) index += 1 - if SAGE_PREPARSED_DOC == 'yes': # Tab for preparsed version from sage.repl.preparse import preparse @@ -1009,9 +1006,6 @@ def apply(self): container += content parent.insert(index, container) index += 1 - - - if SAGE_LIVE_DOC == 'yes': # Tab for Jupyter-sphinx cell from jupyter_sphinx.ast import JupyterCellNode, CellInputNode @@ -1047,10 +1041,6 @@ def apply(self): index += 1 - - - - # This replaces the setup() in sage.misc.sagedoc_conf def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) From de13aaf985356596b4ad5698d4cbfe717e9ef5a0 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 27 Aug 2024 12:03:19 -0700 Subject: [PATCH 079/193] Add class to examples and move example text a little higher --- src/doc/common/static/custom-tabs.css | 4 ++++ src/sage_docbuild/conf.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css index ea8c83299b0..edb261f31fc 100644 --- a/src/doc/common/static/custom-tabs.css +++ b/src/doc/common/static/custom-tabs.css @@ -1,8 +1,12 @@ /* Additional css for tabs */ + p:has(+ div.tab-set) { margin-bottom:-3em; max-width:80%; } +p.example:has(+ div.tab-set) { + margin-bottom:-1em; +} .tab-set { flex-direction: row-reverse; diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 2af41a4a433..34fb513a32d 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -952,6 +952,16 @@ class SagecodeTransform(SphinxTransform): default_priority = 170 def apply(self): + for node in self.document.traverse(nodes.paragraph): + if isinstance(node.children[0], nodes.Text) and node.children[0].astext().strip() in ['EXAMPLE:', 'EXAMPLES:']: + text = node.children[0].astext() + parent = node.parent + index = parent.index(node) + parent.remove(node) + para = nodes.paragraph(classes=["example"]) + para += nodes.Text(text) + parent.insert(index, para) + if self.app.builder.tags.has('html') or self.app.builder.tags.has('inventory'): for node in self.document.traverse(nodes.literal_block): if node.get('language') is None and node.astext().startswith('sage:'): From 01ce6d76b9747cb8e6dd753b26a47ef7f515b495 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 14:10:11 -0700 Subject: [PATCH 080/193] src/sage/doctest/__main__.py: Make doctest insensitive to env var SAGE_DOCTEST_RANDOM_SEED --- src/sage/doctest/__main__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/doctest/__main__.py b/src/sage/doctest/__main__.py index d1dae5c7947..9c1c7931325 100644 --- a/src/sage/doctest/__main__.py +++ b/src/sage/doctest/__main__.py @@ -27,6 +27,8 @@ def _make_parser(): sage: from sage.doctest.control import DocTestDefaults sage: from sage.doctest.__main__ import _make_parser + sage: os.environ.pop('SAGE_DOCTEST_RANDOM_SEED', None) + ... sage: parser = _make_parser() sage: args = parser.parse_args([]) sage: DD = DocTestDefaults(runtest_default=True); DD From 2bfd7b38502debec1ba60122a3f21cd1cbbf2aab Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 27 Aug 2024 17:20:13 -0700 Subject: [PATCH 081/193] Allow for different widths, add class to previous paragraph --- src/doc/common/static/custom-tabs.css | 18 +++++++++++++++--- src/sage_docbuild/conf.py | 11 ++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css index edb261f31fc..4e279d89e71 100644 --- a/src/doc/common/static/custom-tabs.css +++ b/src/doc/common/static/custom-tabs.css @@ -1,10 +1,22 @@ /* Additional css for tabs */ -p:has(+ div.tab-set) { +p.with-sage-tab { + --sage-tab-width: 2.5em; + --python-tab-width: 0em; + --sage-live-tab-width: 0em; margin-bottom:-3em; - max-width:80%; + max-width:calc(100% - var(--sage-tab-width) - var(--python-tab-width) - var(--sage-live-tab-width)); } -p.example:has(+ div.tab-set) { + +p.with-python-tab { + --python-tab-width: calc(3.5em + var(--tabs--margin-x)); +} + +p.with-sage-live-tab { + --sage-live-tab-width: calc(5em + var(--tabs--margin-x)); +} + +p.example.with-sage-tab { margin-bottom:-1em; } diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 34fb513a32d..e103e564573 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -952,7 +952,7 @@ class SagecodeTransform(SphinxTransform): default_priority = 170 def apply(self): - for node in self.document.traverse(nodes.paragraph): + for node in self.document.findall(nodes.paragraph): if isinstance(node.children[0], nodes.Text) and node.children[0].astext().strip() in ['EXAMPLE:', 'EXAMPLES:']: text = node.children[0].astext() parent = node.parent @@ -969,6 +969,7 @@ def apply(self): from sphinx_inline_tabs._impl import TabContainer parent = node.parent index = parent.index(node) + prev_node = node.previous_sibling() if isinstance(node.previous_sibling(), TabContainer): # Make sure not to merge inline tabs for adjacent literal blocks parent.insert(index, Text('')) @@ -984,6 +985,9 @@ def apply(self): container += content parent.insert(index, container) index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-sage-tab') + if SAGE_PREPARSED_DOC == 'yes': # Tab for preparsed version from sage.repl.preparse import preparse @@ -1016,6 +1020,8 @@ def apply(self): container += content parent.insert(index, container) index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-python-tab') if SAGE_LIVE_DOC == 'yes': # Tab for Jupyter-sphinx cell from jupyter_sphinx.ast import JupyterCellNode, CellInputNode @@ -1049,6 +1055,9 @@ def apply(self): container += content parent.insert(index, container) index += 1 + if isinstance(prev_node, nodes.paragraph): + prev_node['classes'].append('with-sage-live-tab') + # This replaces the setup() in sage.misc.sagedoc_conf From bc529eb8f37a92a8826d9ae60386d4007a287f75 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 27 Aug 2024 18:38:32 -0700 Subject: [PATCH 082/193] Swap traverse (the list) for findall (the iterator) --- src/sage_docbuild/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index e103e564573..841279f6b39 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -963,7 +963,7 @@ def apply(self): parent.insert(index, para) if self.app.builder.tags.has('html') or self.app.builder.tags.has('inventory'): - for node in self.document.traverse(nodes.literal_block): + for node in self.document.findall(nodes.literal_block): if node.get('language') is None and node.astext().startswith('sage:'): from docutils.nodes import container as Container, label as Label, literal_block as LiteralBlock, Text from sphinx_inline_tabs._impl import TabContainer From ad02450cb792855d338b617c77ead159240bff74 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 28 Aug 2024 15:14:38 +0200 Subject: [PATCH 083/193] review comments --- src/sage/graphs/connectivity.pyx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index d7a7a613945..72a512fc175 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -24,7 +24,7 @@ Here is what the module can do: :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. - :meth:`is_edge_cut` | Check whether ``edges`` is an edge cut of ``G``. + :meth:`is_edge_cut` | Check whether the input edges form an edge cut. :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex. :meth:`edge_connectivity` | Return the edge connectivity of the graph. :meth:`vertex_connectivity` | Return the vertex connectivity of the graph. @@ -736,7 +736,7 @@ def blocks_and_cuts_tree(G): def is_edge_cut(G, edges): """ - Check whether ``edges`` is an edge cut of ``G``. + Check whether the input edges form an edge cut. A set of edges is an edge cut of a graph if its removal increases the number of connected components. In a digraph, we consider the number of (weakly) @@ -747,9 +747,9 @@ def is_edge_cut(G, edges): INPUT: - - ``G`` -- a Sage (Di)Graph + - ``G`` -- a (di)graph - - ``edges`` -- an iterable container of edges + - ``edges`` -- a list of edges EXAMPLES: @@ -829,15 +829,6 @@ def is_edge_cut(G, edges): sage: G = digraphs.Circuit(4) * 2 sage: is_edge_cut(G, [(0, 1), (1, 2)]) True - - TESTS: - - If `G` is not a Sage graph, an error is raised:: - - sage: is_edge_cut('I am not a graph', [(0, 0)]) - Traceback (most recent call last): - ... - TypeError: the input must be a Sage graph """ from sage.graphs.generic_graph import GenericGraph if not isinstance(G, GenericGraph): From 8c7cd90bcc444778587f63288a079fcb772dce86 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 28 Aug 2024 15:18:47 +0200 Subject: [PATCH 084/193] better description of input --- src/sage/graphs/connectivity.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 72a512fc175..f931e8f38ee 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -749,7 +749,7 @@ def is_edge_cut(G, edges): - ``G`` -- a (di)graph - - ``edges`` -- a list of edges + - ``edges`` -- a set of edges EXAMPLES: From 051bcfaee77d5a21e7f8aa2c94800816e10a06ca Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 28 Aug 2024 15:24:49 +0200 Subject: [PATCH 085/193] remove an input check --- src/sage/graphs/connectivity.pyx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index f931e8f38ee..06562f5b73c 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -830,10 +830,6 @@ def is_edge_cut(G, edges): sage: is_edge_cut(G, [(0, 1), (1, 2)]) True """ - from sage.graphs.generic_graph import GenericGraph - if not isinstance(G, GenericGraph): - raise TypeError("the input must be a Sage graph") - G._scream_if_not_simple(allow_loops=True) cdef set C = set() # set of edges of the potential cut @@ -940,20 +936,7 @@ def is_cut_edge(G, u, v=None, label=None): Traceback (most recent call last): ... ValueError: edge not in graph - - TESTS: - - If ``G`` is not a Sage graph, an error is raised:: - - sage: is_cut_edge('I am not a graph',0) - Traceback (most recent call last): - ... - TypeError: the input must be a Sage graph """ - from sage.graphs.generic_graph import GenericGraph - if not isinstance(G, GenericGraph): - raise TypeError("the input must be a Sage graph") - if label is None: if v is None: try: From f1c0b563e735d240c4c77b6f14c6d50ab1d2f822 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Wed, 28 Aug 2024 06:52:14 -0700 Subject: [PATCH 086/193] Update styling to shrink label spacing --- src/doc/common/static/custom-tabs.css | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css index 4e279d89e71..64decc394fa 100644 --- a/src/doc/common/static/custom-tabs.css +++ b/src/doc/common/static/custom-tabs.css @@ -1,5 +1,18 @@ /* Additional css for tabs */ +.tab-set { + margin-top: 0px; +} + +.tab-set > label { + padding: 0.1em var(--tabs--pading-x); +} + +p.with-sage-tab { + margin-bottom:0.25rem +} + +/* p.with-sage-tab { --sage-tab-width: 2.5em; --python-tab-width: 0em; @@ -47,4 +60,4 @@ p.example.with-sage-tab { .tab-set > label:nth-of-type(3) { order:8; } - +*/ From 8fa25d9b56de294a739173bffec2bc3ee73a6305 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 28 Aug 2024 17:09:47 +0100 Subject: [PATCH 087/193] fix is_homogeneous weight bug --- .../polynomial/multi_polynomial_element.py | 11 +++++++++- src/sage/rings/polynomial/polydict.pyx | 20 +++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 0e8d831b808..9d3203632f6 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -1285,8 +1285,17 @@ def is_homogeneous(self): False sage: (x^2*y + y^2*x).is_homogeneous() True + + The weight of the parent ring is respected:: + + sage: term_order = TermOrder("wdegrevlex", [1, 3]) + sage: R. = PolynomialRing(Qp(5), order=term_order) + sage: (x + y).is_homogeneous() + False + sage: (x^3 + y).is_homogeneous() + True """ - return self.element().is_homogeneous() + return self.element().is_homogeneous(self.parent().term_order().weights()) def _homogenize(self, var): r""" diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index 0fbc3fb3860..a82cdd8a51b 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -672,7 +672,7 @@ cdef class PolyDict: ans[ETuple(t)] = self.__repn[S] return self._new(ans) - def is_homogeneous(self): + def is_homogeneous(self, tuple w=None): r""" Return whether this polynomial is homogeneous. @@ -688,12 +688,20 @@ cdef class PolyDict: """ if not self.__repn: return True + cdef size_t s it = iter(self.__repn) - cdef size_t s = ( next(it)).unweighted_degree() - for elt in it: - if ( elt).unweighted_degree() != s: - return False - return True + if w is None: + s = ( next(it)).unweighted_degree() + for elt in it: + if ( elt).unweighted_degree() != s: + return False + return True + else: + s = ( next(it)).weighted_degree(w) + for elt in it: + if ( elt).weighted_degree(w) != s: + return False + return True def is_constant(self): """ From 1193ebe4660f5016e9bb5112fafd128d77a74337 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 28 Aug 2024 16:23:28 +0000 Subject: [PATCH 088/193] `crypto/sbox.pyx`: remove unreachable code --- src/sage/crypto/sbox.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/crypto/sbox.pyx b/src/sage/crypto/sbox.pyx index 85c8a562ddd..30b7a3cd8c9 100644 --- a/src/sage/crypto/sbox.pyx +++ b/src/sage/crypto/sbox.pyx @@ -445,7 +445,6 @@ cdef class SBox(SageObject): return K(self._S_list[ X]) except TypeError: raise TypeError("cannot apply SBox to %s" % (X,)) - raise TypeError("the characteristic of the base field must be 2") V = None try: V = K.vector_space(map=False) From 83baae039823afbf9be65cf7d543a4efe3d887b8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Aug 2024 14:44:51 -0700 Subject: [PATCH 089/193] README.md, src/doc/en/installation: Replace release tours links --- README.md | 6 +++--- src/doc/en/installation/index.rst | 3 +-- src/doc/en/installation/troubles.rst | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 23d10cca6d5..b9e4fe42353 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ virtualization). Detailed information on supported platforms for a specific version of Sage can be found in the section _Availability and installation help_ of the -[release tour](https://wiki.sagemath.org/ReleaseTours) for this version. +[release tour for this version](https://github.com/sagemath/sage/releases). We highly appreciate contributions to Sage that fix portability bugs and help port Sage to new platforms; let us know at the [sage-devel @@ -489,8 +489,8 @@ Troubleshooting --------------- If you have problems building Sage, check the Sage Installation Guide, -as well as the version-specific Sage Installation FAQ in the [Sage Release -Tour](https://wiki.sagemath.org/ReleaseTours) corresponding to the +as well as the version-specific Sage Installation FAQ in the [release +tour](https://github.com/sagemath/sage/releases) corresponding to the version that you are installing. Please do not hesitate to ask for help in the [SageMath forum diff --git a/src/doc/en/installation/index.rst b/src/doc/en/installation/index.rst index f88a92dc5c7..918d8fe9359 100644 --- a/src/doc/en/installation/index.rst +++ b/src/doc/en/installation/index.rst @@ -11,8 +11,7 @@ was made. More up-to-date information and details regarding supported platforms may have become available afterwards and can be found in the section "Availability and installation help" of the -`release tour `_ for each -SageMath release. +`release tour for each SageMath release `_. **Where would you like to run SageMath?** Pick one of the following sections. diff --git a/src/doc/en/installation/troubles.rst b/src/doc/en/installation/troubles.rst index 4a7c6d5581b..7cb13945f04 100644 --- a/src/doc/en/installation/troubles.rst +++ b/src/doc/en/installation/troubles.rst @@ -8,8 +8,8 @@ the :ref:`sec-installation-from-sources` or use one of the alternatives proposed at the end of :ref:`installation-guide`. If you have any problems building or running Sage, please take a look -at the Installation FAQ in the `Sage Release Tour -`_ corresponding to the version +at the Installation FAQ in the `release tour +`_ corresponding to the version that you are installing. It may offer version-specific installation help that has become available after the release was made and is therefore not covered by this manual. From 733843ed0613d9134b673814f92c42a9cd22101b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Aug 2024 17:38:24 -0700 Subject: [PATCH 090/193] Replace 'Installation FAQ' by other words --- README.md | 2 +- src/doc/en/installation/troubles.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b9e4fe42353..be36e92f711 100644 --- a/README.md +++ b/README.md @@ -489,7 +489,7 @@ Troubleshooting --------------- If you have problems building Sage, check the Sage Installation Guide, -as well as the version-specific Sage Installation FAQ in the [release +as well as the version-specific installation help in the [release tour](https://github.com/sagemath/sage/releases) corresponding to the version that you are installing. diff --git a/src/doc/en/installation/troubles.rst b/src/doc/en/installation/troubles.rst index 7cb13945f04..22d4e4772da 100644 --- a/src/doc/en/installation/troubles.rst +++ b/src/doc/en/installation/troubles.rst @@ -8,7 +8,7 @@ the :ref:`sec-installation-from-sources` or use one of the alternatives proposed at the end of :ref:`installation-guide`. If you have any problems building or running Sage, please take a look -at the Installation FAQ in the `release tour +at the `release tour `_ corresponding to the version that you are installing. It may offer version-specific installation help that has become available after the release was made and is From 0d430481fbae74653ccc3ee1f89d19277914198e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 29 Aug 2024 11:29:13 +0200 Subject: [PATCH 091/193] review comments --- src/sage/graphs/connectivity.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 06562f5b73c..eb45e7cbb44 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -736,7 +736,7 @@ def blocks_and_cuts_tree(G): def is_edge_cut(G, edges): """ - Check whether the input edges form an edge cut. + Check whether ``edges`` form an edge cut. A set of edges is an edge cut of a graph if its removal increases the number of connected components. In a digraph, we consider the number of (weakly) @@ -883,7 +883,7 @@ def is_edge_cut(G, edges): def is_cut_edge(G, u, v=None, label=None): """ - Check whether the input edge is a cut-edge or a bridge. + Check whether the edge ``(u, v)`` is a cut-edge or a bridge of graph ``G``. A cut edge (or bridge) is an edge that when removed increases the number of connected components. This function works with From 7f7bd65394afdac2892c5db31c3feaff72df401c Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 29 Aug 2024 15:19:26 +0200 Subject: [PATCH 092/193] change in generic_graph.py --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index b0400205574..9db5ec2c1b4 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -244,7 +244,7 @@ :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. - :meth:`~GenericGraph.is_edge_cut` | Check whether ``edges`` is an edge cut of ``G``. + :meth:`~GenericGraph.`is_edge_cut` | Check whether the input edges form an edge cut. :meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex. :meth:`~GenericGraph.edge_cut` | Return a minimum edge cut between vertices `s` and `t` :meth:`~GenericGraph.vertex_cut` | Return a minimum vertex cut between non-adjacent vertices `s` and `t` From cb6b8694901b3ad2abc860ae27096d915cff2773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 29 Aug 2024 16:50:03 +0200 Subject: [PATCH 093/193] adding a warning about iteration over words --- src/sage/combinat/words/words.py | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index c230c90057d..f33c868d636 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -293,7 +293,7 @@ def _sortkey_letters(self, letter1): rk = self.alphabet().rank return rk(letter1) - def __eq__(self, other): + def __eq__(self, other) -> bool: r""" TESTS:: @@ -309,7 +309,7 @@ def __eq__(self, other): return self is other or (type(self) is type(other) and self.alphabet() == other.alphabet()) - def __ne__(self, other): + def __ne__(self, other) -> bool: r""" TESTS:: @@ -479,7 +479,8 @@ def _word_from_word(self, data): return self._element_classes['list'](self, data) from sage.combinat.words.word_datatypes import (WordDatatype_str, - WordDatatype_list, WordDatatype_tuple) + WordDatatype_list, + WordDatatype_tuple) if isinstance(data, WordDatatype_str): return self._element_classes['str'](self, data._data) if isinstance(data, WordDatatype_tuple): @@ -487,8 +488,8 @@ def _word_from_word(self, data): if isinstance(data, WordDatatype_list): return self._element_classes['list'](self, data._data) - from sage.combinat.words.word_infinite_datatypes import (WordDatatype_callable, - WordDatatype_iter) + from sage.combinat.words.word_infinite_datatypes import \ + (WordDatatype_callable, WordDatatype_iter) if isinstance(data, WordDatatype_callable): length = data.length() data = data._func @@ -849,7 +850,7 @@ def __call__(self, data=None, length=None, datatype=None, caching=True, check=Tr self._check(w) return w - def _repr_(self): + def _repr_(self) -> str: """ EXAMPLES:: @@ -875,14 +876,14 @@ def _an_element_(self): """ try: some_letters = list(self.alphabet().some_elements()) - except Exception: + except (TypeError, ValueError, AttributeError, NotImplementedError): return self([]) if len(some_letters) == 1: return self([some_letters[0]] * 3) - else: - a, b = some_letters[:2] - return self([b, a, b]) + + a, b = some_letters[:2] + return self([b, a, b]) def iterate_by_length(self, l=1): r""" @@ -912,10 +913,20 @@ def iterate_by_length(self, l=1): Traceback (most recent call last): ... TypeError: the parameter l (='a') must be an integer + + TESTS:: + + sage: W = FiniteWords(NN) + sage: list(W.iterate_by_length(1)) + Traceback (most recent call last): + ... + NotImplementedError: cannot iterate over words for infinite alphabets """ if not isinstance(l, (int, Integer)): raise TypeError("the parameter l (=%r) must be an integer" % l) cls = self._element_classes['tuple'] + if not self.alphabet().is_finite(): + raise NotImplementedError("cannot iterate over words for infinite alphabets") for w in itertools.product(self.alphabet(), repeat=l): yield cls(self, w) @@ -961,7 +972,7 @@ def __iter__(self): for l in itertools.count(): yield from self.iterate_by_length(l) - def __contains__(self, x): + def __contains__(self, x) -> bool: """ Test whether ``self`` contains ``x``. From aeea2737db1f94965f52dd7f36213dd62cd4a74e Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 30 Aug 2024 12:21:28 -0400 Subject: [PATCH 094/193] src/sage/modular/modform/ring.py: added missing punctuations --- src/sage/modular/modform/ring.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index c7cfc04b5ae..bf4457d7748 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -347,7 +347,7 @@ def polynomial_ring(self, names, gens=None): generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` is used instead. Note that we do not check if the list is - indeed a generating set + indeed a generating set. OUTPUT: a multivariate polynomial ring in the variable ``names``. Each variable of the polynomial ring correspond to a @@ -422,13 +422,13 @@ def from_polynomial(self, polynomial, gens=None): - ``polynomial`` -- a multivariate polynomial. The variables names of the polynomial should be different from ``'q'``. The number of variable of this polynomial should equal the number - of given generators + of given generators. - ``gens`` -- list of modular forms generating this ring (default: ``None``); if ``gens`` is ``None`` then the list of generators returned by the method :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms` is used instead. Note that we do not check if the list is - indeed a generating set + indeed a generating set. OUTPUT: a ``GradedModularFormElement`` given by the polynomial relation ``polynomial`` @@ -845,7 +845,7 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2): - ``start_gens`` -- list (default: ``[]``); a list of modular forms. If this list is nonempty, we find a minimal - generating set containing these forms + generating set containing these forms. - ``start_weight`` -- integer (default: 2); calculate the graded subalgebra of forms of weight at least ``start_weight`` From 94b7d4a477223d5a7a461363044d2d3bc62cb50d Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 31 Aug 2024 00:26:25 +0700 Subject: [PATCH 095/193] Add is_integral method to algebraic numbers --- src/sage/rings/qqbar.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 5d2f2ca2216..a02d921ecb1 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4485,6 +4485,26 @@ def as_number_field_element(self, minimal=False, embedded=False, prec=53): """ return number_field_elements_from_algebraics(self, minimal=minimal, embedded=embedded, prec=prec) + def is_integral(self): + r""" + Determine if a number is an algebraic integer. + + EXAMPLES:: + + sage: QQbar(sqrt(-23)).is_integral() + True + sage: AA(sqrt(23/2)).is_integral() + False + + TESTS: + + Method should return the same value as :meth:`NumberFieldElement.is_integral`:: + + sage: for a in [QQbar(2^(1/3)), AA(2^(1/3)), QQbar(sqrt(1/2)), AA(1/2), AA(2), QQbar(1/2)]: + ....: assert a.as_number_field_element()[1].is_integral() == a.is_integral() + """ + return all(a in ZZ for a in self.minpoly()) + def exactify(self): """ Compute an exact representation for this number. From 480bd938770a58f7287880c34798a74c9e7d4765 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 31 Aug 2024 11:15:15 +0700 Subject: [PATCH 096/193] Apply suggested change --- 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 a02d921ecb1..074cf983b27 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -4487,7 +4487,7 @@ def as_number_field_element(self, minimal=False, embedded=False, prec=53): def is_integral(self): r""" - Determine if a number is an algebraic integer. + Check if this number is an algebraic integer. EXAMPLES:: From 4b48072f1a32382365ff2a45b8d3f054f3b77773 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 1 Sep 2024 13:31:01 +0900 Subject: [PATCH 097/193] Added reflection_index_set() and reflection() methods for permutations. --- src/sage/combinat/permutation.py | 46 +++++++++++++++++++++ src/sage/groups/perm_gps/permgroup_named.py | 44 ++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 7cf80d0fe92..377f01b132a 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -7722,6 +7722,52 @@ def simple_reflection(self, i): g[i] = i return self.element_class(self, g, check=False) + @cached_method + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections` + + EXAMPLES:: + + sage: P = Permutations(4) + sage: P.reflection_index_set() + ((1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)) + """ + return tuple([tuple(c) for c in itertools.combinations(range(1, self.n+1), 2)]) + + def reflection(self, i): + r""" + Return the reflection indexed by ``i`` of ``self``. + + This returns the permutation with cycle `i = (a, b)`. + + .. SEEALSO:: + + - :meth:`reflections_index_set` + - :meth:`reflections` + + EXAMPLES:: + + sage: P = Permutations(4) + sage: for i in P.reflection_index_set(): + ....: print('%s %s'%(i, P.reflection(i))) + (1, 2) [2, 1, 3, 4] + (1, 3) [3, 2, 1, 4] + (1, 4) [4, 2, 3, 1] + (2, 3) [1, 3, 2, 4] + (2, 4) [1, 4, 3, 2] + (3, 4) [1, 2, 4, 3] + """ + data = list(range(1, self.n+1)) + data[i[0]-1] = i[1] + data[i[1]-1] = i[0] + return self.element_class(self, data, check=False) + class Element(Permutation): def has_left_descent(self, i, mult=None): r""" diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 5c153b93d4c..153b46dea54 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -426,6 +426,50 @@ def simple_reflection(self, i): """ return self([(i, self._domain[self._domain.index(i)+1])], check=False) + @cached_method + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections` + + EXAMPLES:: + + sage: S5 = SymmetricGroup(5) + sage: S5.reflection_index_set() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + """ + return tuple(range(len(self.reflections()))) + + def reflection(self, i): + r""" + Return the `i`-th reflection of ``self``. + + For `i` in `1,\dots,N`, this gives the `i`-th reflection of + ``self``. + + .. SEEALSO:: + + - :meth:`reflections_index_set` + - :meth:`reflections` + + EXAMPLES:: + + sage: S4 = SymmetricGroup(4) + sage: for i in S4.reflection_index_set(): + ....: print('%s %s'%(i, S4.reflection(i))) + 0 (1,2) + 1 (1,3) + 2 (1,4) + 3 (2,3) + 4 (2,4) + 5 (3,4) + """ + return self.reflections()[i] + def reflections(self): """ Return the list of all reflections in ``self``. From 7bd98f331d53092897ae5161380dc89cc8235958 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Mon, 2 Sep 2024 17:05:40 -0700 Subject: [PATCH 098/193] Allow for python sets to be counted as sets --- src/sage/combinat/subset.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 5fea2f5f97b..68deef36a57 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -323,7 +323,10 @@ def __contains__(self, value): False """ if value not in Sets(): - return False + if isinstance(value, set): + value = Set(value) + else: + return False return all(v in self._s for v in value) def cardinality(self): From b5c0f3ea0328c13547bdbcbc3367e0fe23e6234f Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Mon, 2 Sep 2024 17:08:10 -0700 Subject: [PATCH 099/193] Add test for allowing python sets --- src/sage/combinat/subset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 68deef36a57..dba4e2459c0 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -321,6 +321,8 @@ def __contains__(self, value): True sage: 2 in S False + sage: {1, 2} in S + True """ if value not in Sets(): if isinstance(value, set): From 65d09cedbc20e7bc3e9032641d4682b9989821b6 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 3 Sep 2024 15:58:18 +0100 Subject: [PATCH 100/193] Update src/sage/combinat/subset.py Co-authored-by: Travis Scrimshaw --- src/sage/combinat/subset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index dba4e2459c0..28fc97bfe34 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -325,10 +325,7 @@ def __contains__(self, value): True """ if value not in Sets(): - if isinstance(value, set): - value = Set(value) - else: - return False + return False return all(v in self._s for v in value) def cardinality(self): From 234acb63eabd14950d6b5d404cd50796c2d3d795 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 3 Sep 2024 15:58:30 +0100 Subject: [PATCH 101/193] Update src/sage/combinat/subset.py Co-authored-by: Travis Scrimshaw --- src/sage/combinat/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 28fc97bfe34..7b95c7d8fa1 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -324,7 +324,7 @@ def __contains__(self, value): sage: {1, 2} in S True """ - if value not in Sets(): + if value not in Sets() and not isinstance(value, (set,frozenset)): return False return all(v in self._s for v in value) From 8035d12c4423290a585edb3436f8ba952e05e924 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Tue, 3 Sep 2024 15:45:27 -0700 Subject: [PATCH 102/193] Fix comma space --- src/sage/combinat/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 7b95c7d8fa1..6c8d923ce9f 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -324,7 +324,7 @@ def __contains__(self, value): sage: {1, 2} in S True """ - if value not in Sets() and not isinstance(value, (set,frozenset)): + if value not in Sets() and not isinstance(value, (set, frozenset)): return False return all(v in self._s for v in value) From 03653a1fcfca528768235dcb4ec8286cd18d2a01 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Aug 2024 15:58:37 -0700 Subject: [PATCH 103/193] build/pkgs/jmol: Change to optional --- build/pkgs/jmol/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/jmol/type b/build/pkgs/jmol/type index a6a7b9cd726..134d9bc32d5 100644 --- a/build/pkgs/jmol/type +++ b/build/pkgs/jmol/type @@ -1 +1 @@ -standard +optional From 40f7b7f339919eb712fe631af0196f20a2fa0207 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 3 Sep 2024 16:52:51 -0700 Subject: [PATCH 104/193] build/pkgs/sagemath_doc_html/dependencies: Remove jmol --- build/pkgs/sagemath_doc_html/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sagemath_doc_html/dependencies b/build/pkgs/sagemath_doc_html/dependencies index 801c36fcd9e..40717629a77 100644 --- a/build/pkgs/sagemath_doc_html/dependencies +++ b/build/pkgs/sagemath_doc_html/dependencies @@ -1,4 +1,4 @@ -sagelib sphinx sphinx_copybutton sphinx_inline_tabs pplpy_doc | $(SAGERUNTIME) maxima networkx scipy sympy matplotlib pillow mathjax mpmath ipykernel jupyter_client conway_polynomials tachyon jmol ipywidgets sage_docbuild elliptic_curves furo fpylll graphs +sagelib sphinx sphinx_copybutton sphinx_inline_tabs pplpy_doc | $(SAGERUNTIME) maxima networkx scipy sympy matplotlib pillow mathjax mpmath ipykernel jupyter_client conway_polynomials tachyon ipywidgets sage_docbuild elliptic_curves furo fpylll graphs # Building the documentation has many dependencies, because all # documented modules are imported and because we use matplotlib to From d4fba8097d322a472efea09a191a56c6ab37b004 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 11:57:45 -0700 Subject: [PATCH 105/193] src/sage/features/jmol.py: Make it optional --- src/sage/features/jmol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/features/jmol.py b/src/sage/features/jmol.py index 52104202a40..cf5780094bd 100644 --- a/src/sage/features/jmol.py +++ b/src/sage/features/jmol.py @@ -36,7 +36,7 @@ def __init__(self): filename='JmolData.jar', search_path=jmol_search_path, spkg='jmol', - type='standard', + type='optional', description="Java viewer for chemical structures in 3D") From 6d852ccd50a05682440db2f339c460982d70bd97 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 12:17:35 -0700 Subject: [PATCH 106/193] build/pkgs/sagetex/dependencies_check: Remove jmol --- build/pkgs/sagetex/dependencies_check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sagetex/dependencies_check b/build/pkgs/sagetex/dependencies_check index d24e23242d4..ff812202db7 100644 --- a/build/pkgs/sagetex/dependencies_check +++ b/build/pkgs/sagetex/dependencies_check @@ -1,4 +1,4 @@ -$(SAGERUNTIME) sympy elliptic_curves jmol +$(SAGERUNTIME) sympy elliptic_curves To build SageTeX, you just need Python, but to test (SAGE_CHECK=yes) SageTeX, you actually need to run Sage, produce plots,... From b5557539b6e4ef44638cc8f14cfa04b705508329 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 12:43:24 -0700 Subject: [PATCH 107/193] src/doc/{en,it}/faq/faq-usage.rst: Remove ancient jmol/java-related troubleshooting advice --- src/doc/en/faq/faq-usage.rst | 29 ----------------------------- src/doc/it/faq/faq-usage.rst | 31 ------------------------------- 2 files changed, 60 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index d03881c36a2..393a9c369e5 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -479,35 +479,6 @@ How do I run sage in daemon mode, i.e. as a service? There are several possibilities. Use ``screen``, ``nohup`` or ``disown``. -The show command for plotting 3-D objects does not work. -"""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -The default live 3-D plotting for Sage 6.4+ uses -`Jmol/JSmol `_ -for viewing. From the command line the Jmol Java application is used, -and for in browser viewing either pure javascript or a Java applet -is used. By default in browsers pure javascript is used to avoid -the problems with some browsers that do not support java applet -plugins (namely Chrome). On each browser worksheet there is a -checkbox which must be checked before a 3-D plot is generated if -the user wants to use the Java applet (the applet is a little faster -with complex plots). - -The most likely reason for a malfunction is that you do not have -a Java Run Time Environment (JRE) installed or you have one older than -version 1.7. If things work from the command line another possibility -is that your browser does not have the proper plugin to support Java -applets (at present, 2014, plugins do not work with most versions of -Chrome). Make sure you have installed either the IcedTea browser -plugin (for linux see your package manager), see: -`IcedTea `_, -or the Oracle Java plugin see: -`Java `_. - -If you are using a Sage server over the web and even javascript rendering -does not work, you may have a problem with your browser's javascript -engine or have it turned off. - May I use Sage tools in a commercial environment? """"""""""""""""""""""""""""""""""""""""""""""""" diff --git a/src/doc/it/faq/faq-usage.rst b/src/doc/it/faq/faq-usage.rst index 5d3183769f7..677d1a24bc2 100644 --- a/src/doc/it/faq/faq-usage.rst +++ b/src/doc/it/faq/faq-usage.rst @@ -469,37 +469,6 @@ Ci sono parecchie possibilità. Puoi usare i programmi a riga di comando ``screen``, ``nohup`` o ``disown``. -Il comando show (mostra) per la visualizzazione di oggetti 3D non funziona. -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -La visualizzazione 3D in tempo reale per Sage dalla versione 6.4 in -avanti usa il pacchetto `Jmol/JSmol `_. -Dalla linea di comando viene utilizzata l'applicazione Java Jmol, -mentre per la visualizzazione dal browser viene usato puro javascript -oppure una Java applet. In genere nei browser è usato javascript puro -per evitare problemi con quei browser che non supportano i plugin per -le applet Java (ad esempio Chrome). In ogni worksheet su browser c'è -una casella da spuntare prima di generare una vista tridimensionale -qualora l'utente voglia usare l'applet Java (essa è un po' più veloce -con viste complicate). - -La ragione più probabile di un malfunzionamento è che non hai -installato l'ambiente runtime di Java (JRE) o che è più vecchio della -versione 1.7. Se le cose funzionano dalla riga di comando, -un'altra possibilità è che il tuo browser non abbia il plugin giusto -per supportare le Java applet (al momento, nel 2014, tali plugin non -lavorano con la maggior parte delle versioni di Chrome). Assicurati di -aver installato il plugin IcedTea (su Linux vedi il tuo gestore dei -pacchetti) o il plugin di Oracle Java -(vedi: `IcedTea `_ -e `Java `_). - -Se stai usando un server Sage sul web e anche la visualizzazione -tramite javascript non funziona, potresti avere un problema con la -funzionalità javascript del tuo browser, o potresti aver disabilitato -javascript. - - Posso usare gli strumenti di Sage in un ambiente commerciale? """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" From 513efbf027170d3075116166a86bb65efc66ec00 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 12:45:29 -0700 Subject: [PATCH 108/193] src/doc/en/prep/Symbolics-and-Basic-Plotting.rst: Remove outdated comment on 3d graphics backends --- src/doc/en/prep/Symbolics-and-Basic-Plotting.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index 3bc9308669b..c97efdecee4 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -384,11 +384,6 @@ Below, you can experiment with several of the plotting options. Basic 3D Plotting ----------------- -There are several mechanisms for viewing three\-dimensional plots in -Sage, but we will stick to the default option in the notebook interface, -which is via javascript applets from the program `Jmol/JSmol -`_ . - Plotting a 3D plot is similar to plotting a 2D plot, but we need to specify ranges for two variables instead of one. From 4d1c2d74fd9d9b1c6fcdb819b332d49f1a49e17d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 12 Aug 2024 12:47:19 -0700 Subject: [PATCH 109/193] src/sage/graphs/graph.py: Remove outdated reference to jmol in docstring --- src/sage/graphs/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 4b0b43e5adb..6e5a6680662 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -362,7 +362,7 @@ sage: G = graphs.RandomGNP(15,.3) sage: G.show() # needs sage.plot -And you can view it in three dimensions via jmol with ``show3d()``. :: +And you can view it in three dimensions with ``show3d()``. :: sage: G.show3d() # needs sage.plot From 514f429cb4f017225683318dbb01309c8ac57b68 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Aug 2024 10:28:59 -0700 Subject: [PATCH 110/193] m4/pyproject_toml_metadata.m4: Add various project URLs --- m4/pyproject_toml_metadata.m4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/m4/pyproject_toml_metadata.m4 b/m4/pyproject_toml_metadata.m4 index 0d9824b1f2e..6a731e36c6a 100644 --- a/m4/pyproject_toml_metadata.m4 +++ b/m4/pyproject_toml_metadata.m4 @@ -17,5 +17,10 @@ classifiers = [ "Programming Language :: Python :: Implementation :: CPython", "Topic :: Scientific/Engineering :: Mathematics", ] -urls = {Homepage = "https://www.sagemath.org"} +urls = {download = "https://doc.sagemath.org/html/en/installation/index.html", + "release notes" = "https://github.com/sagemath/sage/releases", + source = "https://github.com/sagemath/sage", + documentation = "https://doc.sagemath.org", + homepage = "https://www.sagemath.org", + tracker = "https://github.com/sagemath/sage/issues"} requires-python = ">=3.9, <3.13" From daaf04b59a767065c03ea973b77956dc00384756 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 2 Sep 2024 17:33:33 +0900 Subject: [PATCH 111/193] Add warning filter --- src/sage_docbuild/sphinxbuild.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index 5621fe9e456..62b2d3cb112 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -307,6 +307,11 @@ def runsphinx(): saved_stdout = sys.stdout saved_stderr = sys.stderr + if not sys.warnoptions: + import warnings + original_filters = warnings.filters[:] + warnings.filterwarnings("ignore", category=DeprecationWarning, module='sphinx.util.inspect') + try: sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) @@ -323,3 +328,6 @@ def runsphinx(): sys.stderr = saved_stderr sys.stdout.flush() sys.stderr.flush() + + if not sys.warnoptions: + warnings.filters = original_filters[:] From b768c3a82ea2968bf5805db23072e67515e54dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 07:57:25 +0200 Subject: [PATCH 112/193] fix the linter --- src/sage/schemes/hyperelliptic_curves/constructor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/constructor.py b/src/sage/schemes/hyperelliptic_curves/constructor.py index a39b94b030b..b3c006b2c19 100755 --- a/src/sage/schemes/hyperelliptic_curves/constructor.py +++ b/src/sage/schemes/hyperelliptic_curves/constructor.py @@ -202,7 +202,7 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): # rather than f and h, one of which might be constant. F = h**2 + 4 * f if not isinstance(F, Polynomial): - raise TypeError(f"arguments {f = } and {h = } must be polynomials") + raise TypeError(f"arguments f = {f} and h = {h} must be polynomials") P = F.parent() f = P(f) h = P(h) @@ -220,7 +220,7 @@ def HyperellipticCurve(f, h=0, names=None, PP=None, check_squarefree=True): # characteristic 2 if h == 0: raise ValueError( - f"for characteristic 2, argument {h = } must be nonzero" + f"for characteristic 2, argument h = {h} must be nonzero" ) if h[g + 1] == 0 and f[2 * g + 1] ** 2 == f[2 * g + 2] * h[g] ** 2: raise ValueError( From a1f497fe8d5b8b8b6c8fda36fce287efe18a6d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 08:32:55 +0200 Subject: [PATCH 113/193] fix and activate E303 check in pyx files --- src/sage/libs/ecl.pxd | 1 - src/sage/libs/m4rie.pxd | 1 - src/sage/libs/meataxe.pxd | 1 - .../matrix/matrix_modn_dense_template.pxi | 3 --- .../rings/finite_rings/hom_finite_field.pyx | 15 ------------ .../polynomial_integer_dense_flint.pyx | 16 ++++--------- .../polynomial_integer_dense_ntl.pyx | 23 ++++--------------- .../rings/polynomial/polynomial_template.pxi | 1 - .../skew_polynomial_finite_field.pyx | 10 -------- .../skew_polynomial_finite_order.pyx | 1 - src/tox.ini | 2 +- 11 files changed, 10 insertions(+), 64 deletions(-) diff --git a/src/sage/libs/ecl.pxd b/src/sage/libs/ecl.pxd index 19472171403..202d0824e37 100644 --- a/src/sage/libs/ecl.pxd +++ b/src/sage/libs/ecl.pxd @@ -147,7 +147,6 @@ cdef extern from "ecl/ecl.h": ecl_character ecl_char(cl_object s, cl_index i) ecl_character ecl_char_set(cl_object s, cl_index i, ecl_character c) - # S-expr evaluation and function calls cl_object cl_safe_eval(cl_object form, cl_object env, cl_object value) diff --git a/src/sage/libs/m4rie.pxd b/src/sage/libs/m4rie.pxd index 51bbf8303d9..c56e33b6945 100644 --- a/src/sage/libs/m4rie.pxd +++ b/src/sage/libs/m4rie.pxd @@ -173,7 +173,6 @@ cdef extern from "m4rie/m4rie.h": void mzd_slice_row_add(mzd_slice_t *A, size_t sourcerow, size_t destrow) - void mzd_slice_row_clear_offset(mzd_slice_t *A, size_t row, size_t coloffset) void mzd_slice_print(mzd_slice_t *A) diff --git a/src/sage/libs/meataxe.pxd b/src/sage/libs/meataxe.pxd index 0a928e19c37..a3bbc810cdc 100644 --- a/src/sage/libs/meataxe.pxd +++ b/src/sage/libs/meataxe.pxd @@ -113,7 +113,6 @@ cdef extern from "meataxe.h": Matrix_t *MatLoad(char *fn) except? NULL int MatSave(Matrix_t *mat, char *fn) except -1 - ## Basic Arithmetic ## general rule: dest is changed, src/mat are unchanged! Matrix_t *MatTransposed(Matrix_t *src) except NULL Matrix_t *MatAdd(Matrix_t *dest, Matrix_t *src) except NULL diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index ebf1bdeb7e9..36830da0549 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -854,7 +854,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): A.subdivide(*self.subdivisions()) return A - cpdef _add_(self, right): r""" Add two dense matrices over `\Z/n\Z`. @@ -898,7 +897,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): sig_off() return M - cpdef _sub_(self, right): r""" Subtract two dense matrices over `\Z/n\Z`. @@ -1404,7 +1402,6 @@ cdef class Matrix_modn_dense_template(Matrix_dense): self.cache(cache_key, g) return g - def minpoly(self, var='x', algorithm='linbox', proof=None): """ Return the minimal polynomial of ``self``. diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index d55e1a0a178..9e825650fca 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -150,7 +150,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): return root raise ValueError("%s is not in the image of %s" % (x, self._inverse)) - def _repr_(self): """ Return a string representation of this section. @@ -167,7 +166,6 @@ cdef class SectionFiniteFieldHomomorphism_generic(Section): """ return "Section of %s" % self._inverse - def _latex_(self): r""" Return a latex representation of this section. @@ -321,7 +319,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): f = f.map_coefficients(bm) return f(self.im_gens()[0]) - def is_injective(self): """ Return ``True`` since a embedding between finite fields is @@ -338,7 +335,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): """ return True - def is_surjective(self): """ Return ``True`` if this embedding is surjective (and hence an @@ -358,7 +354,6 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): """ return self.domain().cardinality() == self.codomain().cardinality() - @cached_method def section(self): """ @@ -544,7 +539,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): self._q = domain.characteristic() ** self._power RingHomomorphism.__init__(self, Hom(domain, domain)) - def _repr_(self): """ Return a string representation of this endomorphism. @@ -568,7 +562,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s += " %s" % self.domain() return s - def _repr_short(self): """ Return a short string representation of this endomorphism. @@ -591,7 +584,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s = "%s |--> %s^(%s^%s)" % (name, name, self.domain().characteristic(), self._power) return s - def _latex_(self): r""" Return a latex representation of this endomorphism. @@ -615,7 +607,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): s = "%s \\mapsto %s^{%s^{%s}}" % (name, name, self.domain().characteristic(), self._power) return s - cpdef Element _call_(self, x): """ TESTS:: @@ -632,7 +623,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): else: return x.pth_power(self._power) - def order(self): """ Return the order of this endomorphism. @@ -673,7 +663,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return self._power - def __pow__(self, n, modulus): """ Return the `n`-th iterate of this endomorphism. @@ -735,7 +724,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): else: return RingHomomorphism._composition(self, right) - def fixed_field(self): """ Return the fixed field of ``self``. @@ -780,7 +768,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): f = FiniteFieldHomomorphism_generic(Hom(k, self.domain())) return k, f - def is_injective(self): """ Return ``True`` since any power of the Frobenius endomorphism @@ -795,7 +782,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return True - def is_surjective(self): """ Return ``True`` since any power of the Frobenius endomorphism @@ -810,7 +796,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return True - def is_identity(self): """ Return ``True`` if this morphism is the identity morphism. diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 5879909eb1d..8aa1d13b5c1 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -102,7 +102,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): """ fmpz_poly_init(self._poly) - def __dealloc__(self): r""" Call the underlying FLINT fmpz_poly destructor @@ -652,7 +651,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - cpdef _sub_(self, right): r""" Return ``self`` minus ``right``. @@ -672,7 +670,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - cpdef _neg_(self): r""" Return negative of ``self``. @@ -834,7 +831,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return x - @coerce_binop def lcm(self, right): """ @@ -955,7 +951,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): else: return self._parent(rr), ss, tt - cpdef _mul_(self, right): r""" Return ``self`` multiplied by ``right``. @@ -1360,11 +1355,10 @@ cdef class Polynomial_integer_dense_flint(Polynomial): return real_roots(self) -## def __copy__(self): -## f = Polynomial_integer_dense(self.parent()) -## f._poly = self._poly.copy() -## return f - + # def __copy__(self): + # f = Polynomial_integer_dense(self.parent()) + # f._poly = self._poly.copy() + # return f def degree(self, gen=None): """ @@ -1485,7 +1479,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): variable = self.parent().variable_name() return pari(self.list()).Polrev(variable) - def squarefree_decomposition(Polynomial_integer_dense_flint self): """ Return the square-free decomposition of ``self``. This is @@ -1743,7 +1736,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): """ return [self.get_unsafe(i) for i in range(self.degree()+1)] - @coerce_binop def resultant(self, other, proof=True): """ diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 3f94f66f90b..142c7b324ad 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -69,6 +69,7 @@ from sage.libs.ntl.ZZX cimport * from sage.rings.polynomial.evaluation_ntl cimport ZZX_evaluation_mpfr, ZZX_evaluation_mpfi + cdef class Polynomial_integer_dense_ntl(Polynomial): r""" A dense polynomial over the integers, implemented via NTL. @@ -83,7 +84,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): x._is_gen = 0 return x - def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): r""" EXAMPLES:: @@ -236,7 +236,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): mpz_to_ZZ(&y, (a).value) ZZX_SetCoeff(self._poly, i, y) - def content(self): r""" Return the greatest common divisor of the coefficients of this @@ -445,7 +444,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): (right)._poly) return x - cpdef _sub_(self, right): r""" Return ``self`` minus ``right``. @@ -463,7 +461,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): (right)._poly) return x - cpdef _neg_(self): r""" Return negative of ``self``. @@ -479,7 +476,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): ZZX_negate(x._poly, self._poly) return x - @coerce_binop def quo_rem(self, right): r""" @@ -565,7 +561,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): return qq, rr - @coerce_binop def gcd(self, right): r""" @@ -587,7 +582,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): del temp return x - @coerce_binop def lcm(self, right): """ @@ -677,7 +671,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): S = self.parent() return S(rr), ss, tt - cpdef _mul_(self, right): r""" Return ``self`` multiplied by ``right``. @@ -733,7 +726,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): ZZX_mul_ZZ(x._poly, self._poly, _right) return x - def __floordiv__(self, right): """ EXAMPLES:: @@ -785,7 +777,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): mpz_to_ZZ(&y, (value).value) ZZX_SetCoeff(self._poly, n, y) - def real_root_intervals(self): """ Return isolating intervals for the real roots of this polynomial. @@ -803,11 +794,10 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): return real_roots(self) -## def __copy__(self): -## f = Polynomial_integer_dense(self.parent()) -## f._poly = self._poly.copy() -## return f - + # def __copy__(self): + # f = Polynomial_integer_dense(self.parent()) + # f._poly = self._poly.copy() + # return f def degree(self, gen=None): """ @@ -855,7 +845,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): del temp return x - def __pari__(self, variable=None): """ EXAMPLES:: @@ -869,7 +858,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): variable = self.parent().variable_name() return pari(self.list()).Polrev(variable) - def squarefree_decomposition(self): """ Return the square-free decomposition of ``self``. This is @@ -1101,7 +1089,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): """ return [self.get_unsafe(i) for i in range(self.degree()+1)] - @coerce_binop def resultant(self, other, proof=True): """ diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 64398898d0d..402e6831245 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -588,7 +588,6 @@ cdef class Polynomial_template(Polynomial): return -2 return result - def __pow__(self, ee, modulus): """ EXAMPLES:: diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a901022b308..0c8c660c18c 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -100,7 +100,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): N = self._parent._working_center(self.reduced_norm(var=False)) return N.is_irreducible() - def type(self, N): r""" Return the `N`-type of this skew polynomial (see definition below). @@ -209,7 +208,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): self._types[N] = type return type - # Finding divisors # ---------------- @@ -288,7 +286,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): continue return D - def _reduced_norm_factor_uniform(self): r""" Return a factor of the reduced norm of this skew @@ -357,7 +354,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if random < count[i]: return F[i][0] - def _irreducible_divisors(self, bint right): r""" Return an iterator over all irreducible monic @@ -469,7 +465,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): d, _ = quo_rem2(P, d) yield d - def right_irreducible_divisor(self, uniform=False): r""" Return a right irreducible divisor of this skew polynomial. @@ -617,7 +612,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): if LD.degree() == degN: return LD - def right_irreducible_divisors(self): r""" Return an iterator over all irreducible monic right divisors @@ -686,7 +680,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): """ return self._irreducible_divisors(False) - def count_irreducible_divisors(self): r""" Return the number of irreducible monic divisors of @@ -744,7 +737,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): count += (cardL**m - 1) // (cardL - 1) return count - # Finding factorizations # ---------------------- @@ -926,7 +918,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): factors.reverse() return Factorization(factors, sort=False, unit=unit) - def factor(self, uniform=False): r""" Return a factorization of this skew polynomial. @@ -994,7 +985,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): F = self._factorization return F - def count_factorizations(self): r""" Return the number of factorizations (as a product of a diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 0abdfca57bc..f90484cba62 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -518,7 +518,6 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): return center(self._optbound) return self.reduced_norm() - def optimal_bound(self): r""" Return the optimal bound of this skew polynomial (i.e. diff --git a/src/tox.ini b/src/tox.ini index 404b040b452..970b2330aec 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -181,7 +181,7 @@ description = # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E271,E301,E302,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 From 1631dc919beec96656e2eba95b786595e0f5766e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 08:50:17 +0200 Subject: [PATCH 114/193] a few more E303 fixes in pxi/pxd files in libs --- src/sage/libs/coxeter3/decl.pxd | 1 - src/sage/libs/linbox/fflas.pxd | 1 - src/sage/libs/ntl/GF2X.pxd | 2 -- src/sage/libs/singular/decl.pxd | 5 ----- src/sage/libs/symmetrica/sb.pxi | 1 - src/sage/libs/symmetrica/schur.pxi | 1 - 6 files changed, 11 deletions(-) diff --git a/src/sage/libs/coxeter3/decl.pxd b/src/sage/libs/coxeter3/decl.pxd index 4f9c7b0c186..1f968c418bf 100644 --- a/src/sage/libs/coxeter3/decl.pxd +++ b/src/sage/libs/coxeter3/decl.pxd @@ -33,7 +33,6 @@ cdef extern from "coxeter/coxtypes.h" namespace "coxtypes": ctypedef unsigned short Length ctypedef Ulong StarOp # for numbering star operations - ################# # CoxWord # ################# diff --git a/src/sage/libs/linbox/fflas.pxd b/src/sage/libs/linbox/fflas.pxd index d5b077cf045..886f5c44cfa 100644 --- a/src/sage/libs/linbox/fflas.pxd +++ b/src/sage/libs/linbox/fflas.pxd @@ -28,7 +28,6 @@ cdef extern from "fflas-ffpack/fflas-ffpack.h" namespace "FFLAS": FflasNoTrans FflasTrans - ctypedef enum FFLAS_SIDE: FflasRight diff --git a/src/sage/libs/ntl/GF2X.pxd b/src/sage/libs/ntl/GF2X.pxd index 9342f63244c..2d3b5edafad 100644 --- a/src/sage/libs/ntl/GF2X.pxd +++ b/src/sage/libs/ntl/GF2X.pxd @@ -58,7 +58,6 @@ cdef extern from "ntlwrap.h": void GF2XModulus_build "build"(GF2XModulus_c F, GF2X_c f) # MUST be called before using the modulus long GF2XModulus_deg "deg"(GF2XModulus_c F) - GF2X_c GF2XModulus_GF2X "GF2X" (GF2XModulus_c m) GF2X_c GF2X_IrredPolyMod "IrredPolyMod" (GF2X_c g, GF2XModulus_c F) @@ -72,7 +71,6 @@ cdef extern from "ntlwrap.h": void GF2X_PowerXPlusAMod_pre "PowerXPlusAMod"(GF2X_c x, GF2_c a, GF2_c e, GF2XModulus_c F) void GF2X_PowerXPlusAMod_long_pre "PowerXPlusAMod"(GF2X_c x, GF2_c a, long e, GF2XModulus_c F) - # x = g(h) mod f; deg(h) < n void GF2X_CompMod "CompMod"(GF2X_c x, GF2X_c g, GF2X_c h, GF2XModulus_c F) # xi = gi(h) mod f (i=1,2), deg(h) < n. diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 0c9f2cc993c..dddc452fd98 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -151,7 +151,6 @@ cdef extern from "singular/Singular/libsingular.h": void (*cfWrite)(number* a, const n_Procs_s* r) void (*cfNormalize)(number* a, const n_Procs_s* r) - bint (*cfDivBy)(number* a, number* b, const n_Procs_s* r) bint (*cfEqual)(number* a,number* b, const n_Procs_s* ) bint (*cfIsZero)(number* a, const n_Procs_s* ) # algebraic number comparison with zero @@ -160,7 +159,6 @@ cdef extern from "singular/Singular/libsingular.h": bint (*cfGreaterZero)(number* a, const n_Procs_s* ) void (*cfPower)(number* a, int i, number* * result, const n_Procs_s* r) # algebraic number power - ring *extRing int ch mpz_ptr modBase @@ -211,7 +209,6 @@ cdef extern from "singular/Singular/libsingular.h": int pCompIndex # index of components unsigned long bitmask # mask for getting single exponents - n_Procs_s* cf # coefficient field/ring int ref @@ -793,7 +790,6 @@ cdef extern from "singular/Singular/libsingular.h": number *nlCopy(number *) - # number to integer handle long SR_TO_INT(number *) @@ -813,7 +809,6 @@ cdef extern from "singular/Singular/libsingular.h": void id_Delete(ideal **, ring *) - # lifting ideal *idLift(ideal *mod, ideal *submod, ideal **rest, int goodShape, int isSB, int divide) diff --git a/src/sage/libs/symmetrica/sb.pxi b/src/sage/libs/symmetrica/sb.pxi index 5ea0b3ad62c..b3bbb061596 100644 --- a/src/sage/libs/symmetrica/sb.pxi +++ b/src/sage/libs/symmetrica/sb.pxi @@ -51,7 +51,6 @@ def mult_schubert_schubert_symmetrica(a, b): freeall(cres) raise err - sig_on() mult_schubert_schubert(ca, cb, cres) sig_off() diff --git a/src/sage/libs/symmetrica/schur.pxi b/src/sage/libs/symmetrica/schur.pxi index a5257cf304b..4fe867ce93a 100644 --- a/src/sage/libs/symmetrica/schur.pxi +++ b/src/sage/libs/symmetrica/schur.pxi @@ -28,7 +28,6 @@ cdef extern from 'symmetrica/def.h': INT t_HOMSYM_MONOMIAL(OP a, OP b) INT t_HOMSYM_ELMSYM(OP a, OP b) - INT t_POWSYM_SCHUR(OP a, OP b) INT t_SCHUR_POWSYM(OP a, OP b) INT t_POWSYM_HOMSYM(OP a, OP b) From 83f075ebddfe9b1d0132074891f90a39587adc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 11:50:09 +0200 Subject: [PATCH 115/193] suggested details --- src/sage/combinat/designs/subhypergraph_search.pyx | 2 +- src/sage/schemes/elliptic_curves/mod_sym_num.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index b7ea8907682..8de300efe2a 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -180,7 +180,7 @@ cdef hypergraph h_init(int n,list H) noexcept: cdef hypergraph h h.n = n h.m = len(H) - h.limbs = (n+63)//64 # =ceil(n/64) + h.limbs = (n+63) // 64 # =ceil(n/64) h.names = sig_malloc(sizeof(int)*n) h.sets = sig_malloc(h.m*sizeof(uint64_t *)) h.set_space = sig_calloc(h.m*(h.limbs+1),sizeof(uint64_t)) diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 402477da0d6..e55500f0d0f 100755 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -456,7 +456,7 @@ cdef int best_proj_point(llong u, llong v, llong N, else: # cases like (p:q) mod p*q drop here p = llgcd(u, N) q = llgcd(v, N) - Nnew = N // p // q + Nnew = (N // p) // q w = ((u // p) * llinvmod(v // q, Nnew)) % Nnew y0 = N // q y1 = 0 From 9d44764f0702fd8d4b11484a2afccd139300c0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 13:45:52 +0200 Subject: [PATCH 116/193] pep and ruff cleanup in semirings and tropicals --- .../non_negative_integer_semiring.py | 12 +++--- .../rings/semirings/tropical_mpolynomial.py | 8 ++-- .../rings/semirings/tropical_polynomial.py | 20 +++++----- src/sage/rings/semirings/tropical_variety.py | 40 ++++++++----------- 4 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/sage/rings/semirings/non_negative_integer_semiring.py b/src/sage/rings/semirings/non_negative_integer_semiring.py index cc144253328..4fc0309a44d 100644 --- a/src/sage/rings/semirings/non_negative_integer_semiring.py +++ b/src/sage/rings/semirings/non_negative_integer_semiring.py @@ -1,18 +1,19 @@ r""" Non Negative Integer Semiring """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Nicolas Borie # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.sets.non_negative_integers import NonNegativeIntegers from sage.categories.semirings import Semirings from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.sets.family import Family + class NonNegativeIntegerSemiring(NonNegativeIntegers): r""" A class for the semiring of the nonnegative integers. @@ -69,9 +70,10 @@ def __init__(self): Category of facade infinite enumerated commutative semirings sage: TestSuite(NN).run() """ - NonNegativeIntegers.__init__(self, category=(Semirings().Commutative(), InfiniteEnumeratedSets()) ) + NonNegativeIntegers.__init__(self, category=(Semirings().Commutative(), + InfiniteEnumeratedSets())) - def _repr_(self): + def _repr_(self) -> str: r""" EXAMPLES:: diff --git a/src/sage/rings/semirings/tropical_mpolynomial.py b/src/sage/rings/semirings/tropical_mpolynomial.py index a482e097f89..0fa89866a12 100644 --- a/src/sage/rings/semirings/tropical_mpolynomial.py +++ b/src/sage/rings/semirings/tropical_mpolynomial.py @@ -37,6 +37,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.multi_polynomial_element import MPolynomial_polydict + class TropicalMPolynomial(MPolynomial_polydict): r""" A multivariate tropical polynomial. @@ -305,7 +306,7 @@ def plot3d(self, color='random'): corner = set() for i in axes[0]: for j in axes[1]: - corner.add((i,j)) + corner.add((i, j)) marks = corner | vertices | edge # Calculate the value of polynomial at each marked point @@ -316,9 +317,8 @@ def plot3d(self, color='random'): mark_terms = [] value = self(T(mark[0]), T(mark[1])) value_terms = [term(T(mark[0]), T(mark[1])) for term in terms] - for i in range(len(terms)): - if value_terms[i] == value: - mark_terms.append(terms[i]) + mark_terms.extend(terms[i] for i in range(len(terms)) + if value_terms[i] == value) point_terms[(R(mark[0]), R(mark[1]), value.lift())] = mark_terms # Plot the points that attained its value at one term only diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index f1d41c17ee0..27c4a37d4dc 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -37,6 +37,7 @@ from sage.structure.parent import Parent from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_sparse + class TropicalPolynomial(Polynomial_generic_sparse): r""" A univariate tropical polynomial. @@ -332,9 +333,8 @@ def factor(self): roots_order[root] += 1 else: roots_order[root] = 1 - factors = [] - for root in roots_order: - factors.append((R([root, 0]), roots_order[root])) + factors = [(R([root, 0]), roots_order[root]) + for root in roots_order] return Factorization(factors, unit=unit) def piecewise_function(self): @@ -383,13 +383,13 @@ def piecewise_function(self): if len(data) == 1: gradient = list(data)[0] intercept = data[gradient].lift() - f = intercept + gradient*x + f = intercept + gradient * x return f - unique_root = sorted(list(set(self.roots()))) + unique_root = sorted(set(self.roots())) pieces = [] domain = [] - for i in range(len(unique_root)+1): + for i in range(len(unique_root) + 1): if i == 0: test_number = R(unique_root[i] - 1) elif i == len(unique_root): @@ -579,12 +579,12 @@ def _latex_(self): if x.find("-") == 0: x = "\\left(" + x + "\\right)" if n > 1: - v = "|%s^{%s}" % (name, n) + v = f"|{name}^{{{n}}}" elif n == 1: - v = "|%s" % name + v = f"|{name}" else: v = "" - s += "%s %s" % (x, v) + s += f"{x} {v}" s = s.replace("|", "") if s == " ": return self.parent().base().zero()._latex_() @@ -968,7 +968,7 @@ def interpolation(self, points): result = self.one() for root, order in roots.items(): - result *= self([root,0])**order + result *= self([root, 0])**order test_value = result(R(points[0][0])) unit = R(points[0][1] - test_value.lift()) result *= unit diff --git a/src/sage/rings/semirings/tropical_variety.py b/src/sage/rings/semirings/tropical_variety.py index a8328ee3e48..e65b2408904 100644 --- a/src/sage/rings/semirings/tropical_variety.py +++ b/src/sage/rings/semirings/tropical_variety.py @@ -29,6 +29,7 @@ from sage.rings.infinity import infinity from sage.structure.unique_representation import UniqueRepresentation + class TropicalVariety(UniqueRepresentation, SageObject): r""" A tropical variety in `\RR^n`. @@ -195,9 +196,8 @@ def __init__(self, poly): self._poly = poly self._hypersurface = [] tropical_roots = [] - variables = [] - for name in poly.parent().variable_names(): - variables.append(SR.var(name)) + variables = [SR.var(name) + for name in poly.parent().variable_names()] # Convert each term to its linear function linear_eq = {} @@ -214,9 +214,7 @@ def __init__(self, poly): sol = solve(linear_eq[keys[0]] == linear_eq[keys[1]], variables) # Parametric solution of the chosen two terms - final_sol = [] - for s in sol[0]: - final_sol.append(s.right()) + final_sol = [s.right() for s in sol[0]] xy_interval = [] xy_interval.append(tuple(final_sol)) @@ -257,9 +255,8 @@ def __init__(self, poly): xy_interval.append(parameter_solution[0]) tropical_roots.append(xy_interval) # Calculate the order - index_diff = [] - for i in range(len(keys[0])): - index_diff.append(abs(keys[0][i] - keys[1][i])) + index_diff = [abs(ai - bi) + for ai, bi in zip(keys[0], keys[1])] order = gcd(index_diff) temp_order.append(order) temp_keys.append(keys) @@ -270,7 +267,7 @@ def __init__(self, poly): dim_param = 0 if tropical_roots: dim_param = len(tropical_roots[0][0]) - 1 - vars = [SR.var('t{}'.format(i)) for i in range(1, dim_param+1)] + vars = [SR.var(f't{i}') for i in range(1, dim_param + 1)] for arg in tropical_roots: subs_dict = {} index_vars = 0 @@ -450,7 +447,6 @@ def _components_intersection(self): import operator from sage.functions.min_max import max_symbolic, min_symbolic from sage.symbolic.relation import solve - from sage.symbolic.expression import Expression from sage.sets.set import Set def update_result(result): @@ -480,7 +476,7 @@ def update_result(result): # Checking there are no conditions with the same variables # that use the <= and >= operators simultaneously unique_sol_param = set() - temp = [s for s in sol_param_sim] + temp = list(sol_param_sim) op_temp = {i: set(temp[i].operands()) for i in range(len(temp))} for s_value in op_temp.values(): match_keys = [k for k, v in op_temp.items() if v == s_value] @@ -598,8 +594,7 @@ def _axes(self): for eqn in self._hypersurface[0][0]: for op in eqn.operands(): if op.is_numeric(): - if op > bound: - bound = op + bound = max(op, bound) return [[-bound, bound]] * 3 u_set = set() @@ -668,10 +663,8 @@ def _axes(self): zmin = z zmax = z else: - if z < zmin: - zmin = z - if z > zmax: - zmax = z + zmin = min(z, zmin) + zmax = max(z, zmax) axes.append([zmin, zmax]) return axes @@ -783,8 +776,8 @@ def find_edge_vertices(i): # Find the interval of parameter for outer vertex for index in range(len(comps)): - interval1 = RealSet(-infinity,infinity) # represent t1 - interval2 = RealSet(-infinity,infinity) # represent t2 + interval1 = RealSet(-infinity, infinity) # represent t1 + interval2 = RealSet(-infinity, infinity) # represent t2 is_doublevar = False for i, point in enumerate(comps[index][0]): pv = point.variables() @@ -952,8 +945,7 @@ def _axes(self): temp_operands += eq.operands() for op in temp_operands: if op.is_numeric(): - if abs(op) > bound: - bound = abs(op) + bound = max(abs(op), bound) return [[-bound, bound]] * 2 verts = self.vertices() @@ -1002,11 +994,11 @@ def vertices(self): if lower != -infinity: x = parametric_function[0].subs(**{str(var): lower}) y = parametric_function[1].subs(**{str(var): lower}) - vertices.add((x,y)) + vertices.add((x, y)) if upper != infinity: x = parametric_function[0].subs(**{str(var): upper}) y = parametric_function[1].subs(**{str(var): upper}) - vertices.add((x,y)) + vertices.add((x, y)) return vertices def _parameter_intervals(self): From 2fe6b83898dfa3f7dc40342ef02e294b692b7212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 14:01:55 +0200 Subject: [PATCH 117/193] pep8 cleanup in algebras --- src/sage/algebras/clifford_algebra.py | 2 +- src/sage/algebras/fusion_rings/fusion_ring.py | 2 +- src/sage/algebras/group_algebra.py | 5 ++- src/sage/algebras/hall_algebra.py | 8 ++-- .../hecke_algebras/ariki_koike_algebra.py | 33 +++++++-------- .../algebras/lie_algebras/bgg_dual_module.py | 2 +- src/sage/algebras/lie_algebras/center_uea.py | 12 +++--- .../lie_algebras/classical_lie_algebra.py | 14 +++---- src/sage/algebras/lie_algebras/onsager.py | 18 ++++----- .../rank_two_heisenberg_virasoro.py | 20 +++++----- .../lie_algebras/symplectic_derivation.py | 10 ++--- .../algebras/lie_algebras/verma_module.py | 2 +- src/sage/algebras/lie_algebras/virasoro.py | 2 +- .../affine_lie_conformal_algebra.py | 2 +- .../free_bosons_lie_conformal_algebra.py | 2 +- .../free_fermions_lie_conformal_algebra.py | 2 +- .../lie_conformal_algebra.py | 5 ++- ..._conformal_algebra_with_structure_coefs.py | 2 +- src/sage/algebras/orlik_solomon.py | 6 +-- src/sage/algebras/orlik_terao.py | 2 +- src/sage/algebras/q_system.py | 4 +- .../algebras/quantum_groups/fock_space.py | 12 +++--- .../quantum_groups/representations.py | 2 +- src/sage/algebras/quantum_oscillator.py | 2 +- .../algebras/rational_cherednik_algebra.py | 40 +++++++++---------- src/sage/algebras/splitting_algebra.py | 6 +-- .../steenrod/steenrod_algebra_mult.py | 8 ++-- src/sage/algebras/tensor_algebra.py | 4 +- src/sage/algebras/yangian.py | 24 +++++------ src/sage/algebras/yokonuma_hecke_algebra.py | 6 +-- 30 files changed, 131 insertions(+), 128 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index 2fe4a873ec6..92ad6c34d64 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -2914,7 +2914,7 @@ def __richcmp__(self, other, op): return contained and contains if op == op_NE: return not (contained and contains) - # remaining case < + # remaining case < return contained and not contains def __mul__(self, other): diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 1ce6b920daa..56045d2dce6 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1113,7 +1113,7 @@ def is_multiplicity_free(self): return k <= 2 ################################### - ### Braid group representations ### + # Braid group representations # ################################### def get_computational_basis(self, a, b, n_strands): diff --git a/src/sage/algebras/group_algebra.py b/src/sage/algebras/group_algebra.py index eb70b0c699f..8dc952f23b9 100644 --- a/src/sage/algebras/group_algebra.py +++ b/src/sage/algebras/group_algebra.py @@ -222,8 +222,9 @@ def _coerce_map_from_(self, S): hom_G = G.coerce_map_from(S_G) if hom_K is not None and hom_G is not None: return SetMorphism(S.Hom(self, category=self.category() | S.category()), - lambda x: self.sum_of_terms( (hom_G(g), hom_K(c)) for g,c in x )) + lambda x: self.sum_of_terms((hom_G(g), hom_K(c)) for g, c in x)) from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.algebras.group_algebras', 'GroupAlgebra', GroupAlgebra_class) +register_unpickle_override('sage.algebras.group_algebras', 'GroupAlgebra', + GroupAlgebra_class) diff --git a/src/sage/algebras/hall_algebra.py b/src/sage/algebras/hall_algebra.py index 84bebb13e38..bac7fa4f2fa 100644 --- a/src/sage/algebras/hall_algebra.py +++ b/src/sage/algebras/hall_algebra.py @@ -349,7 +349,7 @@ def coproduct_on_basis(self, la): S = self.tensor_square() if all(x == 1 for x in la): n = len(la) - return S.sum_of_terms([( (Partition([1]*r), Partition([1]*(n-r))), self._q**(-r*(n-r)) ) + return S.sum_of_terms([((Partition([1]*r), Partition([1]*(n-r))), self._q**(-r*(n-r))) for r in range(n+1)], distinct=True) I = HallAlgebraMonomials(self.base_ring(), self._q) @@ -482,9 +482,9 @@ def scalar(self, y): (4*q^2 + 9)/(q^2 - q) """ q = self.parent()._q - f = lambda la: ~( q**(sum(la) + 2*la.weighted_size()) + f = lambda la: ~(q**(sum(la) + 2*la.weighted_size()) * prod(prod((1 - q**-i) for i in range(1,k+1)) - for k in la.to_exp()) ) + for k in la.to_exp())) y = self.parent()(y) ret = q.parent().zero() for mx, cx in self: @@ -687,7 +687,7 @@ def coproduct_on_basis(self, a): + (q^-1)*I[1, 1] # I[1] + I[2] # I[1] + I[2, 1] # I[] """ S = self.tensor_square() - return S.prod(S.sum_of_terms([( (Partition([r]), Partition([n-r]) ), self._q**(-r*(n-r)) ) + return S.prod(S.sum_of_terms([((Partition([r]), Partition([n-r])), self._q**(-r*(n-r))) for r in range(n+1)], distinct=True) for n in a) def antipode_on_basis(self, a): diff --git a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py index 3177185c088..8d674d0aaf1 100644 --- a/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py +++ b/src/sage/algebras/hecke_algebras/ariki_koike_algebra.py @@ -738,10 +738,10 @@ def algebra_generators(self): for i in range(self._n): r = list(self._zero_tuple) # Make a copy r[i] = 1 - d['L%s' % (i+1)] = self.monomial( (tuple(r), self._one_perm) ) + d['L%s' % (i+1)] = self.monomial((tuple(r), self._one_perm)) G = self._Pn.group_generators() for i in range(1, self._n): - d['T%s' % i] = self.monomial( (self._zero_tuple, G[i]) ) + d['T%s' % i] = self.monomial((self._zero_tuple, G[i])) return Family(sorted(d), lambda i: d[i]) def T(self, i=None): @@ -896,9 +896,9 @@ def product_on_basis(self, m1, m2): # combination of standard basis elements using the method and then, # recursively, multiply on the left and right by L1 and T2, # respectively. In other words, we multiply as L1*(T1*L2)*T2. - return ( self.monomial((L1, self._one_perm)) + return (self.monomial((L1, self._one_perm)) * self._product_Tw_L(T1, L2) - * self.monomial((self._zero_tuple, T2)) ) + * self.monomial((self._zero_tuple, T2))) def _product_LTwTv(self, L, w, v): r""" @@ -1023,7 +1023,7 @@ def _product_Tw_L(self, w, L): iaxpy(c, self._product_LTwTv(tuple(L), self._Pn.simple_reflections()[i], v), iL) # need T_i*T_v if a < b: - Ls = [ list(L) for k in range(b-a) ] # make copies of L + Ls = [list(L) for k in range(b-a)] # make copies of L for k in range(b-a): Ls[k][i-1] = a + k Ls[k][i] = b - k @@ -1031,7 +1031,7 @@ def _product_Tw_L(self, w, L): iaxpy(1, {(tuple(l), v): c for l in Ls}, iL) elif a > b: - Ls = [ list(L) for k in range(a-b) ] # make copies of L + Ls = [list(L) for k in range(a-b)] # make copies of L for k in range(a-b): Ls[k][i-1] = b + k Ls[k][i] = a - k @@ -1110,25 +1110,26 @@ def Ltuple(a, b): # return "small" powers of the generators without change if m < self._r: - return self.monomial( (Ltuple(0, m), self._one_perm) ) + return self.monomial((Ltuple(0, m), self._one_perm)) if i > 1: si = self._Pn.simple_reflections()[i-1] qsum = self.base_ring().one() - self._q**-1 # by calling _Li_power we avoid infinite recursion here - return ( self.sum_of_terms( ((Ltuple(c, m-c), si), qsum) for c in range(1, m) ) - + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1) ) + return (self.sum_of_terms(((Ltuple(c, m-c), si), qsum) for c in range(1, m)) + + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1)) # now left with the case i = 1 and m >= r if m > self._r: return self.monomial((Ltuple(0, 1), self._one_perm)) * self._Li_power(i,m-1) z = PolynomialRing(self.base_ring(), 'DUMMY').gen() - p = list(prod(z - val for val in self._u))#[:-1] - p.pop() # remove the highest power + p = list(prod(z - val for val in self._u)) # [:-1] + p.pop() # remove the highest power zero = self.base_ring().zero() return self._from_dict({(Ltuple(0, exp), self._one_perm): -coeff - for exp,coeff in enumerate(p) if coeff != zero}, + for exp, coeff in enumerate(p) + if coeff != zero}, remove_zeros=False, coerce=False) @cached_method @@ -1294,7 +1295,7 @@ def _from_LT_basis(self, m): True """ ret = self.prod(self.L(i+1)**k for i,k in enumerate(m[0])) - return ret * self.monomial( (self._zero_tuple, m[1]) ) + return ret * self.monomial((self._zero_tuple, m[1])) @cached_method def algebra_generators(self): @@ -1337,9 +1338,9 @@ def T(self, i=None): return [self.T(j) for j in range(self._n)] if i == 0: - return self.monomial( ((1,) + self._zero_tuple[1:], self._one_perm) ) + return self.monomial(((1,) + self._zero_tuple[1:], self._one_perm)) s = self._Pn.simple_reflections() - return self.monomial( (self._zero_tuple, s[i]) ) + return self.monomial((self._zero_tuple, s[i])) @cached_method def L(self, i=None): @@ -1514,7 +1515,7 @@ def product_on_basis(self, m1, m2): return L * M * R # The current product of T's and the type A Hecke algebra - tprod = [( [(k, a) for k, a in enumerate(t2) if a != 0], {s2: one} )] + tprod = [([(k, a) for k, a in enumerate(t2) if a != 0], {s2: one})] # s1 through t2 for i in reversed(s1.reduced_word()): diff --git a/src/sage/algebras/lie_algebras/bgg_dual_module.py b/src/sage/algebras/lie_algebras/bgg_dual_module.py index 5f0fa10ddfc..c4e060131da 100644 --- a/src/sage/algebras/lie_algebras/bgg_dual_module.py +++ b/src/sage/algebras/lie_algebras/bgg_dual_module.py @@ -423,7 +423,7 @@ def _acted_upon_(self, scalar, self_on_left=False): ##################################################################### -## Simple modules +# Simple modules # This is an abuse as the monoid is not free. diff --git a/src/sage/algebras/lie_algebras/center_uea.py b/src/sage/algebras/lie_algebras/center_uea.py index c155a797ca5..54057bc9735 100644 --- a/src/sage/algebras/lie_algebras/center_uea.py +++ b/src/sage/algebras/lie_algebras/center_uea.py @@ -6,22 +6,22 @@ - Travis Scrimshaw (2024-01-02): Initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2024 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** -#from sage.structure.unique_representation import UniqueRepresentation -#from sage.structure.parent import Parent +# from sage.structure.unique_representation import UniqueRepresentation +# from sage.structure.parent import Parent from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.integer_lists.invlex import IntegerListsLex from sage.matrix.constructor import matrix -from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid #, IndexedFreeAbelianMonoidElement +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid from sage.monoids.indexed_free_monoid import IndexedMonoid from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.integer_vector_weighted import iterator_fast as intvecwt_iterator diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index ebe892d3436..15badc6881f 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -379,15 +379,15 @@ def build_assoc(row): continue basis_pivots.add(p) if self._sparse: - added.append(self.element_class( self, build_assoc(cur_mat[i]) )) + added.append(self.element_class(self, build_assoc(cur_mat[i]))) else: - added.append(self.element_class( self, self._assoc(cur_mat[i].list()) )) + added.append(self.element_class(self, self._assoc(cur_mat[i].list()))) cur_mat = cur_mat.submatrix(nrows=len(pivots)) if self._sparse: - basis = [self.element_class( self, build_assoc(cur_mat[i]) ) + basis = [self.element_class(self, build_assoc(cur_mat[i])) for i in range(cur_mat.rank())] else: - basis = [self.element_class( self, self._assoc(cur_mat[i].list()) ) + basis = [self.element_class(self, self._assoc(cur_mat[i].list())) for i in range(cur_mat.rank())] return Family(basis) @@ -1077,7 +1077,7 @@ def __init__(self, R): ####################################### -## Compact real form +# Compact real form class MatrixCompactRealForm(FinitelyGeneratedLieAlgebra): r""" @@ -1558,7 +1558,7 @@ def monomial_coefficients(self, copy=False): ####################################### -## Chevalley Basis +# Chevalley Basis class LieAlgebraChevalleyBasis(LieAlgebraWithStructureCoefficients): r""" @@ -2054,7 +2054,7 @@ def _weight_action(self, m, wt): # enough in the ambient space to correctly convert things to do # the scalar product. alc = wt.parent().simple_coroots() - return R(wt.scalar( alc[aci[m]] )) + return R(wt.scalar(alc[aci[m]])) def affine(self, kac_moody=True): r""" diff --git a/src/sage/algebras/lie_algebras/onsager.py b/src/sage/algebras/lie_algebras/onsager.py index d4d224ea847..0de70c0a630 100644 --- a/src/sage/algebras/lie_algebras/onsager.py +++ b/src/sage/algebras/lie_algebras/onsager.py @@ -318,7 +318,7 @@ def alternating_central_extension(self): Element = LieAlgebraElement ##################################################################### -## q-Onsager algebra (the quantum group) +# q-Onsager algebra (the quantum group) class QuantumOnsagerAlgebra(CombinatorialFreeModule): @@ -795,10 +795,10 @@ def a(m, p): assert m > 0 terms = q**-2 * self.monomial(B[kr] * B[kl]) terms -= self.monomial(B[1,m]) - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,kr[1]-p]) * self.monomial(B[0,p+kl[1]]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * temp else: r = -kr[1] - 1 @@ -812,10 +812,10 @@ def a(m, p): terms -= (q**2-q**-2) * sum(q**(2*(r-1-k)) * self.monomial(B[0,-(k+1)]) * self.monomial(B[0,-r+kl[1]+k]) for k in range(r)) m = -r + kl[1] + 1 - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,m-p-1]) * self.monomial(B[0,p-1]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * q**(2*r) * temp else: # [B[rd+a0], B[sd+a1]] r > s @@ -826,10 +826,10 @@ def a(m, p): terms -= (q**2-q**-2) * sum(q**(2*(kl[1]-1-k)) * self.monomial(B[0,-(r-kl[1]+k+1)]) * self.monomial(B[0,k]) for k in range(kl[1])) m = r - kl[1] + 1 - temp = ( -sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) + temp = (-sum(q**(-2*(p-1)) * self.monomial(B[1,m-2*p]) for p in range(1, (m - 1) // 2 + 1)) + sum(a(m,p) * self.monomial(B[0,-p]) * self.monomial(B[0,p-m]) - for p in range(1, m // 2 + 1)) ) + for p in range(1, m // 2 + 1))) terms += (q**-2 - 1) * q**(2*kl[1]) * temp terms = -q**2 * terms elif kl[0] == 1 and kr[0] == 0: @@ -877,7 +877,7 @@ def a(m, p): for h in range(1, ell)) - q**(2*(ell-1)) * self.monomial(B[0,-(p-ell+1)] * B[1,kl[1]-ell]) for ell in range(1, kl[1])) - else: #kl[0] == 0 and kr[0] == 1: + else: # kl[0] == 0 and kr[0] == 1: terms = self.monomial(B[kr] * B[kl]) if kl[1] < kr[1]: # [B[pd+a1], B[md]] with p < m @@ -923,7 +923,7 @@ def a(m, p): return self.monomial(lhs // B[kl]) * terms * self.monomial(rhs // B[kr]) ##################################################################### -## ACE of the Onsager algebra +# ACE of the Onsager algebra class OnsagerAlgebraACE(InfinitelyGeneratedLieAlgebra, IndexedGenerators): diff --git a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py index 4957ecf7bc9..b3fa2b0c7f4 100644 --- a/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py +++ b/src/sage/algebras/lie_algebras/rank_two_heisenberg_virasoro.py @@ -211,7 +211,7 @@ def K(self, i=None): """ if i is None: return Family(self._KI, self.K) - return self.monomial( ('K', i) ) + return self.monomial(('K', i)) def t(self, a, b): r""" @@ -225,7 +225,7 @@ def t(self, a, b): """ if a == b == 0: raise ValueError("no t(0, 0) element") - return self.monomial( ('t', self._v(a,b)) ) + return self.monomial(('t', self._v(a,b))) def E(self, a, b): r""" @@ -239,7 +239,7 @@ def E(self, a, b): """ if a == b == 0: raise ValueError("no E(0, 0) element") - return self.monomial( ('E', self._v(a,b)) ) + return self.monomial(('E', self._v(a,b))) def _v(self, a, b): r""" @@ -324,10 +324,10 @@ def _an_element_(self): d = self.monomial v = self._v return ( - d( ('E',v(1,-3)) ) - - self.base_ring().an_element() * d( ('t',v(-1,3)) ) - + d( ('E',v(2,2)) ) - + d( ('K',3) ) + d(('E',v(1,-3))) + - self.base_ring().an_element() * d(('t',v(-1,3))) + + d(('E',v(2,2))) + + d(('K',3)) ) def some_elements(self): @@ -345,9 +345,9 @@ def some_elements(self): """ d = self.monomial v = self._v - return [d( ('E',v(1,1)) ), d( ('E',v(-2,-2)) ), d( ('E',v(0,1)) ), - d( ('t',v(1,1)) ), d( ('t',v(4,-1)) ), d( ('t',v(2,3)) ), - d( ('K',2) ), d( ('K',4) ), self.an_element()] + return [d(('E',v(1,1))), d(('E',v(-2,-2))), d(('E',v(0,1))), + d(('t',v(1,1))), d(('t',v(4,-1))), d(('t',v(2,3))), + d(('K',2)), d(('K',4)), self.an_element()] class Element(LieAlgebraElement): pass diff --git a/src/sage/algebras/lie_algebras/symplectic_derivation.py b/src/sage/algebras/lie_algebras/symplectic_derivation.py index f9a2e483330..af294c4c4b0 100644 --- a/src/sage/algebras/lie_algebras/symplectic_derivation.py +++ b/src/sage/algebras/lie_algebras/symplectic_derivation.py @@ -261,9 +261,9 @@ def _an_element_(self): """ d = self.monomial return ( - d( _Partitions([2,1]) ) - - self.base_ring().an_element() * d( _Partitions([5,2,2,1]) ) - + d( _Partitions([2*self._g-1, self._g+1, 2, 1, 1]) ) + d(_Partitions([2,1])) + - self.base_ring().an_element() * d(_Partitions([5,2,2,1])) + + d(_Partitions([2*self._g-1, self._g+1, 2, 1, 1])) ) def some_elements(self): @@ -279,8 +279,8 @@ def some_elements(self): """ d = self.monomial g = self._g - return [d( _Partitions([2,1]) ), d( _Partitions([g+3,g+1]) ), d( _Partitions([2,1,1])), - d( _Partitions([2*g-1,2*g-2]) ), d( _Partitions([2*g-2,g-1,1]) ), + return [d(_Partitions([2,1])), d(_Partitions([g+3,g+1])), d(_Partitions([2,1,1])), + d(_Partitions([2*g-1,2*g-2])), d(_Partitions([2*g-2,g-1,1])), self.an_element()] class Element(LieAlgebraElement): diff --git a/src/sage/algebras/lie_algebras/verma_module.py b/src/sage/algebras/lie_algebras/verma_module.py index 778a9486707..01320616c5a 100644 --- a/src/sage/algebras/lie_algebras/verma_module.py +++ b/src/sage/algebras/lie_algebras/verma_module.py @@ -843,7 +843,7 @@ def _acted_upon_(self, scalar, self_on_left=False): ##################################################################### -## Morphisms and Homset +# Morphisms and Homset class VermaModuleMorphism(Morphism): diff --git a/src/sage/algebras/lie_algebras/virasoro.py b/src/sage/algebras/lie_algebras/virasoro.py index 0284a10e5e5..961c6fb9bbd 100644 --- a/src/sage/algebras/lie_algebras/virasoro.py +++ b/src/sage/algebras/lie_algebras/virasoro.py @@ -652,7 +652,7 @@ class Element(LieAlgebraElement): pass ##################################################################### -## Representations +# Representations class ChargelessRepresentation(CombinatorialFreeModule): diff --git a/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py index a1ccb69653d..e9f697e8257 100644 --- a/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/affine_lie_conformal_algebra.py @@ -96,7 +96,7 @@ def __init__(self, R, ct, names=None, prefix=None, bracket=None): ct = CartanType(ct) except IndexError: raise ValueError("ct must be a valid Cartan Type") - if not (ct.is_finite() and ct.is_irreducible ): + if not (ct.is_finite() and ct.is_irreducible): raise ValueError("only affine algebras of simple finite dimensional" "Lie algebras are implemented") hv = Integer(ct.dual_coxeter_number()) diff --git a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py index fe7954e1aea..e66489d49ca 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_bosons_lie_conformal_algebra.py @@ -134,7 +134,7 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, names,index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) - bosondict = { (i,j): {1: {('K',0): gram_matrix[index_set.rank(i), + bosondict = {(i,j): {1: {('K',0): gram_matrix[index_set.rank(i), index_set.rank(j)]}} for i in index_set for j in index_set} GradedLieConformalAlgebra.__init__(self,R,bosondict,names=names, diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index 4b8cb428f0b..f4723d32372 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -125,7 +125,7 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, names,index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) - fermiondict = { (i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + fermiondict = {(i,j): {0: {('K',0): gram_matrix[index_set.rank(i), index_set.rank(j)]}} for i in index_set for j in index_set} from sage.rings.rational_field import QQ diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py index 2631d965307..528a587d795 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra.py @@ -308,8 +308,9 @@ class LieConformalAlgebra(UniqueRepresentation, Parent): """ @staticmethod def __classcall_private__(cls, R=None, arg0=None, index_set=None, - central_elements=None, category=None, prefix=None, - names=None, latex_names=None, parity=None, weights=None, **kwds): + central_elements=None, category=None, + prefix=None, names=None, latex_names=None, + parity=None, weights=None, **kwds): """ Lie conformal algebra factory. diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py index 4fe54a98963..3cb8f645cd5 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_with_structure_coefs.py @@ -275,7 +275,7 @@ def __init__(self, R, s_coeff, index_set=None, central_elements=None, s_coeff = dict(s_coeff) self._s_coeff = Family({k: tuple((j, sum(c*self.monomial(i) - for i,c in v )) for j,v in s_coeff[k]) for k in s_coeff}) + for i,c in v)) for j,v in s_coeff[k]) for k in s_coeff}) self._parity = dict(zip(self.gens(),parity+(0,)*len(central_elements))) def structure_coefficients(self): diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py index 7302b430738..113bb29418f 100644 --- a/src/sage/algebras/orlik_solomon.py +++ b/src/sage/algebras/orlik_solomon.py @@ -791,9 +791,9 @@ def action(g, m): # computing the invariant will be a block matrix. To avoid dealing # with huge matrices, we can split it up into graded pieces. - max_deg = max([b.degree() for b in OS.basis()]) - B = [] #initialize the basis - for d in range(max_deg+1): + max_deg = max(b.degree() for b in OS.basis()) + B = [] # initialize the basis + for d in range(max_deg + 1): OS_d = OS.homogeneous_component(d) OSG_d = OS_d.invariant_module(G, action=action, category=category) B += [OS_d.lift(OSG_d.lift(b)) for b in OSG_d.basis()] diff --git a/src/sage/algebras/orlik_terao.py b/src/sage/algebras/orlik_terao.py index 707b7f51060..60c3c60e6db 100644 --- a/src/sage/algebras/orlik_terao.py +++ b/src/sage/algebras/orlik_terao.py @@ -292,7 +292,7 @@ def degree_on_basis(self, m): """ return len(m) - ## Multiplication + # Multiplication def product_on_basis(self, a, b): r""" diff --git a/src/sage/algebras/q_system.py b/src/sage/algebras/q_system.py index cea0332d4f4..a8342434847 100644 --- a/src/sage/algebras/q_system.py +++ b/src/sage/algebras/q_system.py @@ -446,12 +446,12 @@ def Q(self, a, m): if m == t[a] * self._level: return self.one() if m == 1: - return self.monomial( self._indices.gen((a,1)) ) + return self.monomial(self._indices.gen((a,1))) #if self._cartan_type.type() == 'A' and self._level is None: # return self._jacobi_trudy(a, m) I = self._cm.index_set() p = self._Q_poly(a, m) - return p.subs({ g: self.Q(I[i], 1) for i,g in enumerate(self._poly.gens()) }) + return p.subs({g: self.Q(I[i], 1) for i,g in enumerate(self._poly.gens())}) @cached_method def _Q_poly(self, a, m): diff --git a/src/sage/algebras/quantum_groups/fock_space.py b/src/sage/algebras/quantum_groups/fock_space.py index 8a7ce3e9386..d1d98d3c184 100644 --- a/src/sage/algebras/quantum_groups/fock_space.py +++ b/src/sage/algebras/quantum_groups/fock_space.py @@ -749,7 +749,7 @@ def N_left(la, x, i): return (sum(1 for y in P._addable(la, i) if P._above(x, y)) - sum(1 for y in P._removable(la, i) if P._above(x, y))) q = P.realization_of()._q - return P.sum_of_terms(( la.remove_cell(*x), c * q**(-N_left(la, x, i)) ) + return P.sum_of_terms((la.remove_cell(*x), c * q**(-N_left(la, x, i))) for la,c in self for x in P._removable(la, i)) def e(self, *data): @@ -845,8 +845,8 @@ def N_right(la, x, i): return (sum(1 for y in P._addable(la, i) if P._above(y, x)) - sum(1 for y in P._removable(la, i) if P._above(y, x))) q = P.realization_of()._q - return P.sum_of_terms( (la.add_cell(*x), c * q**N_right(la, x, i)) - for la,c in self for x in P._addable(la, i) ) + return P.sum_of_terms((la.add_cell(*x), c * q**N_right(la, x, i)) + for la,c in self for x in P._addable(la, i)) def f(self, *data): r""" @@ -1384,7 +1384,7 @@ def _G_to_fock_basis(self, la): ############################################################################### -## Bases Category +# Bases Category class FockSpaceBases(Category_realization_of_parent): r""" @@ -1605,7 +1605,7 @@ def __getitem__(self, i): return self.monomial(i) ############################################################################### -## Truncated Fock space +# Truncated Fock space class FockSpaceTruncated(FockSpace): @@ -2178,7 +2178,7 @@ def _G_to_fock_basis(self, la, algorithm='GW'): mu = _Partitions([p - x for p in la]) def add_cols(nu): - return _Partitions([ v + x for v in list(nu) + [0]*(k - len(nu)) ]) + return _Partitions([v + x for v in list(nu) + [0]*(k - len(nu))]) return fock.sum_of_terms((add_cols(nu), c) for nu,c in self._G_to_fock_basis(mu)) # For critical partitions diff --git a/src/sage/algebras/quantum_groups/representations.py b/src/sage/algebras/quantum_groups/representations.py index 7042c5aba76..7d42aa78787 100644 --- a/src/sage/algebras/quantum_groups/representations.py +++ b/src/sage/algebras/quantum_groups/representations.py @@ -124,7 +124,7 @@ def K_on_basis(self, i, b, power=1): """ WLR = self.basis().keys().weight_lattice_realization() alc = WLR.simple_coroots() - return self.term( b, self._q**(b.weight().scalar(alc[i]) * self._d[i] * power) ) + return self.term(b, self._q**(b.weight().scalar(alc[i]) * self._d[i] * power)) class CyclicRepresentation(QuantumGroupRepresentation): diff --git a/src/sage/algebras/quantum_oscillator.py b/src/sage/algebras/quantum_oscillator.py index a688283705f..7ac3509238f 100644 --- a/src/sage/algebras/quantum_oscillator.py +++ b/src/sage/algebras/quantum_oscillator.py @@ -122,7 +122,7 @@ class QuantumOscillatorAlgebra(CombinatorialFreeModule): - [Kuniba2022]_ Section 3.2 """ @staticmethod - def __classcall_private__(cls, q=None, R=None): + def __classcall_private__(cls, q=None, R=None): r""" Standardize input to ensure a unique representation. diff --git a/src/sage/algebras/rational_cherednik_algebra.py b/src/sage/algebras/rational_cherednik_algebra.py index 58b3ce5441d..c3ff9ff25e6 100644 --- a/src/sage/algebras/rational_cherednik_algebra.py +++ b/src/sage/algebras/rational_cherednik_algebra.py @@ -245,19 +245,19 @@ def algebra_generators(self): def gen_map(k): if k[0] == 's': i = int(k[1:]) - return self.monomial( (self._hd.one(), + return self.monomial((self._hd.one(), self._weyl.group_generators()[i], - self._h.one()) ) + self._h.one())) if k[1] == 'c': i = int(k[2:]) - return self.monomial( (self._hd.one(), + return self.monomial((self._hd.one(), self._weyl.one(), - self._h.monoid_generators()[i]) ) + self._h.monoid_generators()[i])) i = int(k[1:]) - return self.monomial( (self._hd.monoid_generators()[i], + return self.monomial((self._hd.monoid_generators()[i], self._weyl.one(), - self._h.one()) ) + self._h.one())) return Family(keys, gen_map) @cached_method @@ -351,16 +351,16 @@ def commute_w_hd(w, al): # al is given as a dictionary del dr[ir] # We now commute right roots past the left reflections: s Ra = Ra' s - cur = self._from_dict({ (hd, s*right[1], right[2]): c * cc + cur = self._from_dict({(hd, s*right[1], right[2]): c * cc for s,c in terms - for hd, cc in commute_w_hd(s, dr) }) - cur = self.monomial( (left[0], left[1], self._h(dl)) ) * cur + for hd, cc in commute_w_hd(s, dr)}) + cur = self.monomial((left[0], left[1], self._h(dl))) * cur # Add back in the commuted h and hd elements - rem = self.monomial( (left[0], left[1], self._h(dl)) ) - rem = rem * self.monomial( (self._hd({ir:1}), self._weyl.one(), - self._h({il:1})) ) - rem = rem * self.monomial( (self._hd(dr), right[1], right[2]) ) + rem = self.monomial((left[0], left[1], self._h(dl))) + rem = rem * self.monomial((self._hd({ir:1}), self._weyl.one(), + self._h({il:1}))) + rem = rem * self.monomial((self._hd(dr), right[1], right[2])) return cur + rem @@ -376,17 +376,17 @@ def commute_w_hd(w, al): # al is given as a dictionary ret *= x**dl[k] ret = ret.dict() w = left[1]*right[1] - return self._from_dict({ (left[0], w, + return self._from_dict({(left[0], w, self._h({I[i]: e for i,e in enumerate(k) if e != 0}) * right[2] ): ret[k] - for k in ret }) + for k in ret}) # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac, # so we must commute Ls Ra = Ra' Ls w = left[1]*right[1] - return self._from_dict({ (left[0] * hd, w, right[2]): c - for hd, c in commute_w_hd(left[1], dr) }) + return self._from_dict({(left[0] * hd, w, right[2]): c + for hd, c in commute_w_hd(left[1], dr)}) @cached_method def _product_coroot_root(self, i, j): @@ -429,12 +429,12 @@ def _product_coroot_root(self, i, j): al = Q.simple_root(j) R = self.base_ring() - terms = [( self._weyl.one(), self._t * R(ac.scalar(al)) )] + terms = [(self._weyl.one(), self._t * R(ac.scalar(al)))] for s in self._reflections: # p[0] is the root, p[1] is the coroot, p[2] the value c_s pr, pc, c = self._reflections[s] - terms.append(( s, c * R(ac.scalar(pr) * pc.scalar(al) - / pc.scalar(pr)) )) + terms.append((s, c * R(ac.scalar(pr) * pc.scalar(al) + / pc.scalar(pr)))) return tuple(terms) def degree_on_basis(self, m): diff --git a/src/sage/algebras/splitting_algebra.py b/src/sage/algebras/splitting_algebra.py index 6fe2880d825..cfcb86e5c28 100644 --- a/src/sage/algebras/splitting_algebra.py +++ b/src/sage/algebras/splitting_algebra.py @@ -349,10 +349,10 @@ def __init__(self, monic_polynomial, names='X', iterate=True, warning=True): if not check.is_zero(): continue root_inv = self.one() - for pos in range(deg_cf-1 ): - root_inv = (-1 )**(pos+1 ) * cf[deg_cf-pos-1 ] - root_inv * root + for pos in range(deg_cf-1): + root_inv = (-1)**(pos+1) * cf[deg_cf-pos-1] - root_inv * root verbose("inverse %s of root %s" % (root_inv, root)) - root_inv = (-1 )**(deg_cf) * cf0_inv * root_inv + root_inv = (-1)**(deg_cf) * cf0_inv * root_inv self._invertible_elements.update({root:root_inv}) verbose("adding inverse %s of root %s" % (root_inv, root)) invert_items = list(self._invertible_elements.items()) diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index c087bb65ba4..053290bc5ed 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -312,9 +312,9 @@ def milnor_multiplication(r,s): else: sum = sum + M[i][j] * 2**j else: - sum = sum + M[i][j] * 2**j - j = j + 1 - i = i + 1 + sum = sum + M[i][j] * 2**j + j += 1 + i += 1 return result @@ -784,7 +784,7 @@ def adem(a, b, c=0, p=2, generic=None): return result # p odd if a == 0 and b == 0: - return {(c,): 1} + return {(c,): 1} if c == 0: bockstein = 0 A = a diff --git a/src/sage/algebras/tensor_algebra.py b/src/sage/algebras/tensor_algebra.py index 0d323a6ebb7..3a5f8d9c033 100644 --- a/src/sage/algebras/tensor_algebra.py +++ b/src/sage/algebras/tensor_algebra.py @@ -583,7 +583,7 @@ def coproduct_on_basis(self, m): # for w in Word(range(p)).shuffle(range(p, k)) ) ##################################################################### -## TensorAlgebra functor +# TensorAlgebra functor class TensorAlgebraFunctor(ConstructionFunctor): @@ -684,7 +684,7 @@ def _apply_functor_to_morphism(self, f): return D.module_morphism(phi, codomain=C) ##################################################################### -## Lift map from the base ring +# Lift map from the base ring class BaseRingLift(Morphism): diff --git a/src/sage/algebras/yangian.py b/src/sage/algebras/yangian.py index b17a1de0554..748586a30b9 100644 --- a/src/sage/algebras/yangian.py +++ b/src/sage/algebras/yangian.py @@ -575,13 +575,13 @@ def product_on_gens(self, a, b): # This is the special term of x = 1 x1 = self.zero() if b[1] == a[2]: - x1 += self.monomial( I.gen((a[0]+b[0]-1, a[1], b[2])) ) + x1 += self.monomial(I.gen((a[0]+b[0]-1, a[1], b[2]))) if a[1] == b[2]: - x1 -= self.monomial( I.gen((a[0]+b[0]-1, b[1], a[2])) ) + x1 -= self.monomial(I.gen((a[0]+b[0]-1, b[1], a[2]))) return self.monomial(I.gen(b) * I.gen(a)) + x1 + self.sum( - self.monomial( I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2])) ) - - self.product_on_gens( (a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2]) ) + self.monomial(I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2]))) + - self.product_on_gens((a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2])) for x in range(2, b[0]+1)) def coproduct_on_basis(self, m): @@ -610,9 +610,9 @@ def coproduct_on_basis(self, m): """ T = self.tensor_square() I = self._indices - return T.prod(T.monomial( (I.one(), I.gen((a[0],a[1],a[2]))) ) - + T.monomial( (I.gen((a[0],a[1],a[2])), I.one()) ) - + T.sum_of_terms([(( I.gen((s,a[1],k)), I.gen((a[0]-s,k,a[2])) ), 1) + return T.prod(T.monomial((I.one(), I.gen((a[0],a[1],a[2])))) + + T.monomial((I.gen((a[0],a[1],a[2])), I.one())) + + T.sum_of_terms([((I.gen((s,a[1],k)), I.gen((a[0]-s,k,a[2]))), 1) for k in range(1, self._n+1) for s in range(1, a[0])]) for a,exp in m._sorted_items() for p in range(exp)) @@ -881,12 +881,12 @@ def product_on_gens(self, a, b): x1 = self.zero() if a[0]+b[0]-1 <= self._level: if b[1] == a[2]: - x1 += self.monomial( I.gen((a[0]+b[0]-1, a[1], b[2])) ) + x1 += self.monomial(I.gen((a[0]+b[0]-1, a[1], b[2]))) if a[1] == b[2]: - x1 -= self.monomial( I.gen((a[0]+b[0]-1, b[1], a[2])) ) + x1 -= self.monomial(I.gen((a[0]+b[0]-1, b[1], a[2]))) return self.monomial(I.gen(b) * I.gen(a)) + x1 + self.sum( - self.monomial( I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2])) ) + self.monomial(I.gen((x-1, b[1], a[2])) * I.gen((a[0]+b[0]-x, a[1], b[2]))) - self.product_on_gens((a[0]+b[0]-x, b[1], a[2]), (x-1, a[1], b[2])) for x in range(2, b[0]+1) if a[0]+b[0]-x <= self._level) @@ -1043,8 +1043,8 @@ def antipode_on_basis(self, m): + 10*tbar(1)[1,2]*tbar(1)[1,3]^3*tbar(3)[1,2] + 15*tbar(1)[1,2]^2*tbar(1)[1,3]^2*tbar(3)[1,3] """ - return self.prod( (-1)**exp * self.monomial(a**exp) - for a,exp in reversed(list(m)) ) + return self.prod((-1)**exp * self.monomial(a**exp) + for a,exp in reversed(list(m))) def coproduct_on_basis(self, m): """ diff --git a/src/sage/algebras/yokonuma_hecke_algebra.py b/src/sage/algebras/yokonuma_hecke_algebra.py index 8f366ff3401..7de8ff07798 100644 --- a/src/sage/algebras/yokonuma_hecke_algebra.py +++ b/src/sage/algebras/yokonuma_hecke_algebra.py @@ -245,10 +245,10 @@ def algebra_generators(self): for i in range(self._n): r = list(zero) # Make a copy r[i] = 1 - d['t%s' % (i+1)] = self.monomial( (tuple(r), one) ) + d['t%s' % (i+1)] = self.monomial((tuple(r), one)) G = self._Pn.group_generators() for i in range(1, self._n): - d['g%s' % i] = self.monomial( (tuple(zero), G[i]) ) + d['g%s' % i] = self.monomial((tuple(zero), G[i])) return Family(sorted(d), lambda i: d[i]) @cached_method @@ -494,5 +494,5 @@ def __invert__(self): H = self.parent() t,w = self.support_of_term() c = ~self.coefficients()[0] - telt = H.monomial( (tuple((H._d - e) % H._d for e in t), H._Pn.one()) ) + 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())) From 36316c02f45a2107c34a57bab9f7badc2874f35c Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 4 Sep 2024 12:17:59 +0000 Subject: [PATCH 118/193] categories/morphism.pyx: remove some `noexcept`s For Python return types. --- src/sage/categories/morphism.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 2ed9514a469..34bcd1ca5f5 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -798,7 +798,7 @@ cdef class SetIsomorphism(SetMorphism): raise RuntimeError('inverse morphism has not been set') return self._inverse - cdef dict _extra_slots(self) noexcept: + cdef dict _extra_slots(self): """ Extend the dictionary with extra slots for this class. @@ -823,7 +823,7 @@ cdef class SetIsomorphism(SetMorphism): slots['_inverse'] = self._inverse return slots - cdef _update_slots(self, dict _slots) noexcept: + cdef _update_slots(self, dict _slots): """ Update the slots of ``self`` from the data in the dictionary. From e712c07a081465eda6e410874dfd8b06c5387aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 15:49:18 +0200 Subject: [PATCH 119/193] Update src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py Co-authored-by: gmou3 <32706872+gmou3@users.noreply.github.com> --- .../free_fermions_lie_conformal_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py index f4723d32372..40810602ac4 100644 --- a/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py +++ b/src/sage/algebras/lie_conformal_algebras/free_fermions_lie_conformal_algebra.py @@ -125,7 +125,7 @@ def __init__(self, R, ngens=None, gram_matrix=None, names=None, names,index_set = standardize_names_index_set(names=names, index_set=index_set, ngens=ngens) - fermiondict = {(i,j): {0: {('K',0): gram_matrix[index_set.rank(i), + fermiondict = {(i,j): {0: {('K', 0): gram_matrix[index_set.rank(i), index_set.rank(j)]}} for i in index_set for j in index_set} from sage.rings.rational_field import QQ From 5d059876c0cd867c8b41a8c54add1c7dc790a1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 15:50:59 +0200 Subject: [PATCH 120/193] suggested detail --- src/sage/rings/semirings/tropical_polynomial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/semirings/tropical_polynomial.py b/src/sage/rings/semirings/tropical_polynomial.py index 27c4a37d4dc..b2d595e7e0a 100644 --- a/src/sage/rings/semirings/tropical_polynomial.py +++ b/src/sage/rings/semirings/tropical_polynomial.py @@ -383,7 +383,7 @@ def piecewise_function(self): if len(data) == 1: gradient = list(data)[0] intercept = data[gradient].lift() - f = intercept + gradient * x + f = intercept + gradient*x return f unique_root = sorted(set(self.roots())) From 2efa372a2de5ede7a1a686c6c33e25f9e6713c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 4 Sep 2024 17:03:32 +0200 Subject: [PATCH 121/193] some pep8 fixes in combinat/designs --- src/sage/combinat/designs/bibd.py | 18 +-- src/sage/combinat/designs/block_design.py | 27 ++-- src/sage/combinat/designs/database.py | 18 +-- .../combinat/designs/difference_family.py | 43 ++--- src/sage/combinat/designs/ext_rep.py | 21 ++- .../combinat/designs/incidence_structures.py | 148 +++++++++--------- .../combinat/designs/orthogonal_arrays.py | 22 +-- .../orthogonal_arrays_build_recursive.py | 12 +- src/sage/combinat/designs/resolvable_bibd.py | 14 +- src/sage/combinat/designs/twographs.py | 2 +- 10 files changed, 159 insertions(+), 166 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index da6dd6c09dd..49c930ccb38 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1355,10 +1355,10 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): :doi:`10.1016/S0021-9800(69)80095-5` """ q = (n-1)//(k-1)-1 - if (k % 2 or - q % 2 or - q <= k or - n != (k-1)*(q+1)+1 or + if (k % 2 or + q % 2 or + q <= k or + n != (k-1)*(q+1)+1 or not is_prime_power(k) or not is_prime_power(q)): if existence: @@ -1391,12 +1391,10 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): # [Denniston69] is the set of all elements of K of degree < log_n # (seeing elements of K as polynomials in 'a') - K_iter = list(K) # faster iterations - log_n = is_prime_power(n,get_data=True)[1] - C = [(x,y,one) - for x in K_iter - for y in K_iter - if Q(x,y).polynomial().degree() < log_n] + K_iter = list(K) # faster iterations + log_n = is_prime_power(n, get_data=True)[1] + C = [(x, y, one) for x in K_iter for y in K_iter + if Q(x, y).polynomial().degree() < log_n] from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index bdf37227019..0c8b5b3fd38 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -42,7 +42,7 @@ --------------------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Peter Dobcsanyi # Copyright (C) 2007 David Joyner # @@ -51,7 +51,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.arith.misc import binomial, integer_floor, is_prime_power from sage.categories.sets_cat import EmptySetError from sage.misc.lazy_import import lazy_import @@ -69,7 +69,7 @@ BlockDesign = IncidenceStructure -### utility functions ------------------------------------------------------- +# utility functions ----------------------------------------------------- def tdesign_params(t, v, k, L): @@ -338,9 +338,10 @@ def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, check=True): """ K = FiniteField(n, 'a') n2 = n**2 - relabel = {x:i for i,x in enumerate(K)} - Kiter = relabel # it is much faster to iterate through a dict than through - # the finite field K + relabel = {x: i for i, x in enumerate(K)} + Kiter = relabel + # it is much faster to iterate through a dict than through + # the finite field K # we decompose the (equivalence class) of points [x:y:z] of the projective # plane into an affine plane, an affine line and a point. At the same time, @@ -996,14 +997,14 @@ def HadamardDesign(n): """ from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix - H = hadamard_matrix(n+1) #assumed to be normalised. + H = hadamard_matrix(n + 1) # assumed to be normalised. H1 = H.matrix_from_columns(range(1,n+1)) H2 = H1.matrix_from_rows(range(1,n+1)) J = matrix(ZZ,n,n,[1]*n*n) MS = J.parent() - A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ + A = MS((H2+J)/2) # convert -1's to 0's; coerce entries to ZZ # A is the incidence matrix of the block design - return IncidenceStructure(incidence_matrix=A,name='HadamardDesign') + return IncidenceStructure(incidence_matrix=A, name='HadamardDesign') def Hadamard3Design(n): @@ -1060,10 +1061,10 @@ def Hadamard3Design(n): raise ValueError("The Hadamard design with n = %s does not extend to a three design." % n) from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix, block_matrix - H = hadamard_matrix(n) #assumed to be normalised. + H = hadamard_matrix(n) # assumed to be normalised. H1 = H.matrix_from_columns(range(1, n)) J = matrix(ZZ, n, n-1, [1]*(n-1)*n) - A1 = (H1+J)/2 - A2 = (J-H1)/2 - A = block_matrix(1, 2, [A1, A2]) #the incidence matrix of the design. + A1 = (H1 + J) / 2 + A2 = (J - H1) / 2 + A = block_matrix(1, 2, [A1, A2]) # the incidence matrix of the design. return IncidenceStructure(incidence_matrix=A, name='HadamardThreeDesign') diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 7c797481773..dc614bd7b25 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1109,7 +1109,7 @@ def OA_10_205(): baer_subplane_size = 4**2+4+1 B = [0, 1, 22, 33, 83, 122, 135, 141, 145, 159, 175, 200, 226, 229, 231, 238, 246] - pplane = [[(xx+i) % pplane_size for xx in B] for i in range(pplane_size)] + pplane = [[(xx+i) % pplane_size for xx in B] for i in range(pplane_size)] baer_subplane = set([i*pplane_size/baer_subplane_size for i in range(baer_subplane_size)]) p = list(baer_subplane)[0] @@ -1284,7 +1284,7 @@ def OA_11_254(): # Base block of a PG(2,19) B = (0,1,19,28,96,118,151,153,176,202,240,254,290,296,300,307,337,361,366,369) - BIBD = [[(x+i) % 381 for x in B] for i in range(381)] + BIBD = [[(x+i) % 381 for x in B] for i in range(381)] # We only keep points congruent to 0,1 mod 3 and relabel the PBD. The result is # a (254,{11,13,16})-PBD @@ -1708,7 +1708,7 @@ def OA_10_469(): 731,824,837,848,932,1002,1051,1055,1089,1105,1145,1165,1196,1217,1226, 1274,1281,1309,1405) - BIBD = [[(x+i) % 1407 for x in B] for i in range(1407)] + BIBD = [[(x+i) % 1407 for x in B] for i in range(1407)] # Only keep points v congruent to 0 mod 3 and relabel PBD = [[x//3 for x in B if x % 3 == 0] for B in BIBD] @@ -4171,7 +4171,7 @@ def RBIBD_120_8_1(): # A (precomputed) set that every block of the BIBD intersects on 0 or 2 points hyperoval = [128, 192, 194, 4, 262, 140, 175, 48, 81, 180, 245, 271, 119, 212, 249, 189, 62, 255] - #for B in BIBD: + # for B in BIBD: # len_trace = sum(x in hyperoval for x in B) # assert len_trace == 0 or len_trace == 2 @@ -4577,13 +4577,9 @@ def HigmanSimsDesign(): from sage.combinat.designs.block_design import WittDesign from .incidence_structures import IncidenceStructure W = WittDesign(24) - a,b = 0,1 - Wa = [set(B) for B in W - if (a in B and - b not in B)] - Wb = [set(B) for B in W - if (b in B and - a not in B)] + a, b = 0, 1 + Wa = [set(B) for B in W if a in B and b not in B] + Wb = [set(B) for B in W if b in B and a not in B] H = [[i for i, A in enumerate(Wa) if len(A & B) != 2] for B in Wb] diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 789f5d9a281..2ec008f16db 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -123,8 +123,8 @@ def block_stabilizer(G, B): S = [] for b in B: # fun: if we replace +(-b) with -b it completely fails!! - bb0 = op(b,b0) # bb0 = b-B[0] - if all(op(bb0,c) in B for c in B): + bb0 = op(b, b0) # bb0 = b-B[0] + if all(op(bb0, c) in B for c in B): S.append(bb0) return S @@ -279,7 +279,7 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): for c in d: if b == c: continue - gg = mul(b,inv(c)) # = b-c or bc^{-1} + gg = mul(b, inv(c)) # = b-c or bc^{-1} if gg not in tmp_counter: tmp_counter[gg] = 0 where[gg].add(i) @@ -319,7 +319,7 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): g, counter[g], sorted(where[g]))) if too_much: print("Too much:") - for g in too_much: + for g in too_much: print(" {} is obtained {} times in blocks {}".format( g, counter[g], sorted(where[g]))) if too_few or too_much: @@ -388,9 +388,10 @@ def singer_difference_set(q,d): # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a # GF(q**(d+1)) and such that x is a multiplicative generator. p,e = q.factor()[0] - c = conway_polynomial(p,e*(d+1)) - if e != 1: # i.e. q is not a prime, so we factorize c over GF(q) and pick - # one of its factor + c = conway_polynomial(p, e*(d+1)) + if e != 1: + # i.e. q is not a prime, so we factorize c over GF(q) and pick + # one of its factor K = GF(q,'z') c = c.change_ring(K).factor()[0][0] else: @@ -454,7 +455,7 @@ def df_q_6_1(K, existence=False, check=True): xx = x**5 to_coset = {x**i * xx**j: i for i in range(5) for j in range((v-1)/5)} - for c in to_coset: # the loop runs through all nonzero elements of K + for c in to_coset: # the loop runs through all nonzero elements of K if c == one or c == r or c == r2: continue if len(set(to_coset[elt] for elt in (r-one, c*(r-one), c-one, c-r, c-r**2))) == 5: @@ -796,13 +797,13 @@ def one_radical_difference_family(K, k): A = [r**i - 1 for i in range(1,m+1)] else: m = k // 2 - r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity + r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity A = [r**i - 1 for i in range(1,m)] A.append(K.one()) # instead of the complicated multiplicative group K^*/(±C) we use the # discrete logarithm to convert everything into the additive group Z/cZ - c = m * (q-1) // e # cardinal of ±C + c = m * (q-1) // e # cardinal of ±C from sage.groups.generic import discrete_log logA = [discrete_log(a,x) % c for a in A] @@ -1036,18 +1037,18 @@ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): 96 20 4 4 1 """ if v <= k or k <= lmbda: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False k = ZZ(k) lmbda = ZZ(lmbda) - qs,r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s + qs, r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s if r or (qs*(qs-1)) % lmbda: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False q = qs*(qs-1) // lmbda + 1 if (q <= 1 or - v * (q-1) != qs*q * (qs*q+q-2) or + v * (q-1) != qs*q * (qs*q+q-2) or k * (q-1) != qs * (qs*q-1)): - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False # NOTE: below we compute the value of s so that qs = q^s. If the method # is_power_of of integers would be able to return the exponent, we could use @@ -1057,7 +1058,7 @@ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): p2,a2 = q.is_prime_power(get_data=True) if a1 == 0 or a2 == 0 or p1 != p2 or a1 % a2: - return (False,None) if return_parameters else False + return (False, None) if return_parameters else False return (True, (q, a1//a2)) if return_parameters else True @@ -1265,10 +1266,10 @@ def turyn_1965_3x3xK(k=4): else: raise ValueError("k must be 2 or 4") - L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0 - [(0,0),(1,1),(2,2)], # x-y=0 - [(0,0),(1,2),(2,1)], # x+y=0 - [(0,0),(0,1),(0,2)]] # x=0 + L = [[(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)], # complement of y=0 + [(0,0),(1,1),(2,2)], # x-y=0 + [(0,0),(1,2),(2,1)], # x+y=0 + [(0,0),(0,1),(0,2)]] # x=0 return G, [[G(v + k) for l, k in zip(L, K) for v in l]] @@ -1835,7 +1836,7 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing P = PolynomialRing(ZZ, 'x') - #Compute psi3, psi4 + # Compute psi3, psi4 hall = 0 for d in set1: hall += P.monomial(d[0]) diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index 5297ad3357a..93f55f6daed 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -869,11 +869,11 @@ def _start_element(self, name, attrs): check_dtrs_protocols('source', attrs['dtrs_protocol']) if self.list_of_designs_start_proc: self.list_of_designs_start_proc(attrs) - #self.outf.write('<%s' % name) - #pp_attributes(self.outf, attrs, indent='', precision_stack=[]) - #self.outf.write('>\n') + # self.outf.write('<%s' % name) + # pp_attributes(self.outf, attrs, indent='', precision_stack=[]) + # self.outf.write('>\n') elif name == 'designs': - pass # self.outf.write(' <%s>\n' % name) + pass # self.outf.write(' <%s>\n' % name) if self.in_item: for k, v in attrs.items(): attrs[k] = _encode_attribute(v) @@ -916,7 +916,7 @@ def _end_element(self, name): if name == 'block' or name == 'permutation' \ or name == 'preimage' or name == 'ksubset' \ or name == 'cycle_type' or name == 'row': - # these enclose lists of numbers + # these enclose lists of numbers children.append(ps) else: # the rest is a single number @@ -929,19 +929,19 @@ def _end_element(self, name): if self.save_designs: init_bd = XTree(self.current_node[2][0]) self.list_of_designs.append((init_bd.v, list(init_bd.blocks))) - #print_subxt(self.current_node[2][0], level=2, outf=self.outf) + # print_subxt(self.current_node[2][0], level=2, outf=self.outf) self._init() elif name == 'info': if self.info_proc: self.info_proc(self.current_node[2][0]) - #print_subxt(self.current_node[2][0], level=1, outf=self.outf) + # print_subxt(self.current_node[2][0], level=1, outf=self.outf) self._init() else: if name == 'designs': if self.designs_end_proc: self.designs_end_proc() - #self.outf.write(' ') - #self.outf.write('\n' % name) + # self.outf.write(' ') + # self.outf.write('\n' % name) def _char_data(self, data): """ @@ -962,9 +962,8 @@ def _char_data(self, data): {'b': 26, 'id': 't2-v13-b26-r6-k3-L1-0', 'v': 13}, ['[ DESIGN-1.1, GRAPE-4.2, GAPDoc-0.9999, GAP-4.4.3]']) """ - if self.in_item: - #@ this stripping may distort char data in the subtree + # @ this stripping may distort char data in the subtree # if they are not bracketed in some way. data = data.strip() if data: diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 148cc11fdfa..e9964af900c 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -142,7 +142,7 @@ class IncidenceStructure: True """ def __init__(self, points=None, blocks=None, incidence_matrix=None, - name=None, check=True, copy=True): + name=None, check=True, copy=True): r""" TESTS:: @@ -271,7 +271,7 @@ def __repr__(self): Incidence structure with 7 points and 7 blocks """ return 'Incidence structure with {} points and {} blocks'.format( - self.num_points(), self.num_blocks()) + self.num_points(), self.num_blocks()) __str__ = __repr__ @@ -309,7 +309,7 @@ def __eq__(self, other): return self._blocks == other._blocks if (self.num_points() != other.num_points() or - self.num_blocks() != other.num_blocks()): + self.num_blocks() != other.num_blocks()): return False p_to_i = self._point_to_index if self._point_to_index else list(range(self.num_points())) @@ -409,12 +409,12 @@ def canonical_label(self): from sage.graphs.graph import Graph g = Graph() n = self.num_points() - g.add_edges((i+n,x) for i,b in enumerate(self._blocks) for x in b) - canonical_label = g.canonical_label([list(range(n)),list(range(n,n+self.num_blocks()))],certificate=True)[1] + g.add_edges((i+n, x) for i, b in enumerate(self._blocks) for x in b) + canonical_label = g.canonical_label([list(range(n)), list(range(n, n+self.num_blocks()))], certificate=True)[1] canonical_label = [canonical_label[x] for x in range(n)] self._canonical_label = canonical_label - return dict(zip(self._points,self._canonical_label)) + return dict(zip(self._points, self._canonical_label)) def is_isomorphic(self, other, certificate=False): r""" @@ -475,25 +475,25 @@ def is_isomorphic(self, other, certificate=False): """ if (self.num_points() != other.num_points() or self.num_blocks() != other.num_blocks() or - sorted(self.block_sizes()) != sorted(other.block_sizes())): + sorted(self.block_sizes()) != sorted(other.block_sizes())): return {} if certificate else False A_canon = self.canonical_label() B_canon = other.canonical_label() - A = self.relabel(A_canon,inplace=False) - B = other.relabel(B_canon,inplace=False) + A = self.relabel(A_canon, inplace=False) + B = other.relabel(B_canon, inplace=False) if A == B: if certificate: - B_canon_rev = {y:x for x,y in B_canon.items()} - return {x:B_canon_rev[xint] for x,xint in A_canon.items()} + B_canon_rev = {y: x for x, y in B_canon.items()} + return {x: B_canon_rev[xint] for x, xint in A_canon.items()} else: return True else: return {} if certificate else False - def isomorphic_substructures_iterator(self, H2,induced=False): + def isomorphic_substructures_iterator(self, H2, induced=False): r""" Iterate over all copies of ``H2`` contained in ``self``. @@ -560,7 +560,7 @@ def isomorphic_substructures_iterator(self, H2,induced=False): 5616 """ from sage.combinat.designs.subhypergraph_search import SubHypergraphSearch - return SubHypergraphSearch(self,H2,induced=induced) + return SubHypergraphSearch(self, H2, induced=induced) def copy(self): r""" @@ -581,7 +581,7 @@ def copy(self): IS = IncidenceStructure(self._blocks, name=self._name, check=False) - IS.relabel(dict(zip(range(self.num_points()),self._points))) + IS.relabel(dict(zip(range(self.num_points()), self._points))) IS._canonical_label = None if self._canonical_label is None else self._canonical_label[:] return IS @@ -723,7 +723,7 @@ def trace(self, points, min_size=1, multiset=True): if not multiset: blocks = set(blocks) IS = IncidenceStructure(blocks) - IS.relabel({i:self._points[i] for i in int_points}) + IS.relabel({i: self._points[i] for i in int_points}) return IS def ground_set(self): @@ -830,7 +830,7 @@ def degree(self, p=None, subset=False): # degree of a point if not subset: if self._point_to_index: - p = self._point_to_index.get(p,-1) + p = self._point_to_index.get(p, -1) else: p = p if (p >= 0 and p < len(self._points)) else -1 return sum((p in b) for b in self._blocks) if p != -1 else 0 @@ -838,7 +838,7 @@ def degree(self, p=None, subset=False): # degree of a set else: if self._point_to_index: - p = set(self._point_to_index.get(x,-1) for x in p) + p = set(self._point_to_index.get(x, -1) for x in p) else: p = set(p) if all(x >= 0 and x < len(self._points) for x in p) else set([-1]) @@ -888,12 +888,12 @@ def degrees(self, size=None): return {p: d[i] for i, p in enumerate(self._points)} else: from itertools import combinations - d = {t:0 for t in combinations(range(self.num_points()),size)} + d = {t: 0 for t in combinations(range(self.num_points()), size)} for b in self._blocks: - for s in combinations(b,size): + for s in combinations(b, size): d[s] += 1 if self._point_to_index: - return {tuple([self._points[x] for x in s]):v for s,v in d.items()} + return {tuple([self._points[x] for x in s]): v for s, v in d.items()} else: return d @@ -1112,8 +1112,8 @@ def intersection_graph(self, sizes=None): sizes = PositiveIntegers() elif sizes in PositiveIntegers(): sizes = (sizes,) - V = [Set(v) for v in self] - return Graph([V, lambda x,y: len(x & y) in sizes], loops=False) + V = [Set(v) for v in self] + return Graph([V, lambda x, y: len(x & y) in sizes], loops=False) def incidence_matrix(self): r""" @@ -1150,7 +1150,7 @@ def incidence_matrix(self): A[i, j] = 1 return A - def incidence_graph(self,labels=False): + def incidence_graph(self, labels=False): r""" Return the incidence graph of the incidence structure. @@ -1201,7 +1201,7 @@ def incidence_graph(self,labels=False): for b in self.blocks(): b = Set(b) G.add_vertex(b) - G.add_edges((b,x) for x in b) + G.add_edges((b, x) for x in b) return G else: @@ -1243,7 +1243,7 @@ def is_berge_cyclic(self): return not self.incidence_graph().is_forest() - def complement(self,uniform=False): + def complement(self, uniform=False): r""" Return the complement of the incidence structure. @@ -1303,7 +1303,7 @@ def complement(self,uniform=False): num_blocks = self.num_blocks() i = 0 from itertools import combinations - for B in combinations(range(self.num_points()),k): + for B in combinations(range(self.num_points()), k): B = list(B) while i < num_blocks and self._blocks[i] < B: i += 1 @@ -1311,12 +1311,12 @@ def complement(self,uniform=False): i += 1 continue blocks.append(B) - I = IncidenceStructure(blocks,copy=False) + I = IncidenceStructure(blocks, copy=False) else: X = set(range(self.num_points())) I = IncidenceStructure([X.difference(B) for B in self._blocks]) - I.relabel({i:self._points[i] for i in range(self.num_points())}) + I.relabel({i: self._points[i] for i in range(self.num_points())}) return I def relabel(self, perm=None, inplace=True): @@ -1387,7 +1387,7 @@ def relabel(self, perm=None, inplace=True): self._point_to_index = None return - if isinstance(perm, (list,tuple)): + if isinstance(perm, (list, tuple)): perm = dict(zip(self._points, perm)) if not isinstance(perm, dict): @@ -1608,32 +1608,32 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): b = self.num_blocks() # Trivial wrong answers - if (any(len(block) != k for block in self._blocks) or # non k-uniform - v != self.num_points()): - return (False, (0,0,0,0)) if return_parameters else False + if (any(len(block) != k for block in self._blocks) or # non k-uniform + v != self.num_points()): + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case t>k if (t is not None and t > k): if (l is None or l == 0): - return (True, (t,v,k,0)) if return_parameters else True + return (True, (t, v, k, 0)) if return_parameters else True else: - return (False, (0,0,0,0)) if return_parameters else False + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case k=0 if k == 0: if (l is None or l == 0): - return (True, (0,v,k,b)) if return_parameters else True + return (True, (0, v, k, b)) if return_parameters else True else: - return (False, (0,0,0,0)) if return_parameters else False + return (False, (0, 0, 0, 0)) if return_parameters else False # Trivial case k=v (includes v=0) if k == v: if t is None: t = v if l is None or b == l: - return (True, (t,v,k,b)) if return_parameters else True + return (True, (t, v, k, b)) if return_parameters else True else: - return (True, (0,0,0,0)) if return_parameters else False + return (True, (0, 0, 0, 0)) if return_parameters else False # Handbook of combinatorial design theorem II.4.8: # @@ -1642,30 +1642,30 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): # # We look for the largest t such that self is a t-design from itertools import combinations - for tt in (range(1,k+1) if t is None else [t]): + for tt in (range(1, k + 1) if t is None else [t]): # is lambda an integer? - if (b*binomial(k,tt)) % binomial(v,tt) != 0: + if (b * binomial(k, tt)) % binomial(v, tt): tt -= 1 break s = {} for block in self._blocks: - for i in combinations(block,tt): - s[i] = s.get(i,0) + 1 + for i in combinations(block, tt): + s[i] = s.get(i, 0) + 1 if len(set(s.values())) != 1: tt -= 1 break - ll = b*binomial(k,tt) // binomial(v,tt) + ll = (b * binomial(k, tt)) // binomial(v, tt) if ((t is not None and t != tt) or - (l is not None and l != ll)): - return (False, (0,0,0,0)) if return_parameters else False + (l is not None and l != ll)): + return (False, (0, 0, 0, 0)) if return_parameters else False else: if tt == 0: ll = b - return (True, (tt,v,k,ll)) if return_parameters else True + return (True, (tt, v, k, ll)) if return_parameters else True def is_generalized_quadrangle(self, verbose=False, parameters=False): r""" @@ -1758,9 +1758,9 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): if parameters: s = self.is_uniform() t = self.is_regular() - s = s-1 if (s is not False and s >= 2) else False - t = t-1 if (t is not False and t >= 2) else False - return (s,t) + s = s - 1 if (s is not False and s >= 2) else False + t = t - 1 if (t is not False and t >= 2) else False + return (s, t) else: return True @@ -1812,10 +1812,10 @@ def dual(self, algorithm=None): v = DD['v'].sage() gB = [[x - 1 for x in b] for b in DD['blocks'].sage()] return IncidenceStructure(list(range(v)), gB, name=None, check=False) - else: - return IncidenceStructure( - incidence_matrix=self.incidence_matrix().transpose(), - check=False) + + return IncidenceStructure( + incidence_matrix=self.incidence_matrix().transpose(), + check=False) def automorphism_group(self): r""" @@ -1856,9 +1856,9 @@ def automorphism_group(self): from sage.groups.perm_gps.permgroup import PermutationGroup g = Graph() n = self.num_points() - g.add_edges((i+n,x) for i,b in enumerate(self._blocks) for x in b) + g.add_edges((i + n, x) for i, b in enumerate(self._blocks) for x in b) ag = g.automorphism_group(partition=[list(range(n)), - list(range(n,n+self.num_blocks()))]) + list(range(n, n + self.num_blocks()))]) if self._point_to_index: gens = [[tuple([self._points[i] for i in cycle if (not cycle or cycle[0] < n)]) @@ -1971,18 +1971,18 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, # Lists of blocks containing i for every i dual = [[] for _ in domain] - for i,B in enumerate(self._blocks): + for i, B in enumerate(self._blocks): for x in B: dual[x].append(i) # Each class is a partition for t in range(n_classes): for x in domain: - p.add_constraint(p.sum(b[t,i] for i in dual[x]) == 1) + p.add_constraint(p.sum(b[t, i] for i in dual[x]) == 1) # Each set appears exactly once for i in range(len(self._blocks)): - p.add_constraint(p.sum(b[t,i] for t in range(n_classes)) == 1) + p.add_constraint(p.sum(b[t, i] for t in range(n_classes)) == 1) try: p.solve(log=verbose) @@ -1998,8 +1998,8 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, if check and self._classes is not False: assert sorted(id(c) for cls in self._classes for c in cls) == sorted(id(b) for b in self._blocks), "some set does not appear exactly once" domain = list(range(self.num_points())) - for i,c in enumerate(self._classes): - assert sorted(sum(c,[])) == domain, "class {} is not a partition".format(i) + for i, c in enumerate(self._classes): + assert sorted(sum(c, [])) == domain, "class {} is not a partition".format(i) if self._classes is False: return (False, []) if certificate else False @@ -2069,7 +2069,7 @@ def coloring(self, k=None, solver=None, verbose=0, 3 """ if k is None: - for k in range(self.num_points()+1): + for k in range(self.num_points() + 1): try: return self.coloring(k) except ValueError: @@ -2093,11 +2093,11 @@ def coloring(self, k=None, solver=None, verbose=0, b = p.new_variable(binary=True) for x in range(self.num_points()): - p.add_constraint(p.sum(b[x,i] for i in range(k)) == 1) + p.add_constraint(p.sum(b[x, i] for i in range(k)) == 1) for s in self._blocks: for i in range(k): - p.add_constraint(p.sum(b[x,i] for x in s) <= len(s)-1) + p.add_constraint(p.sum(b[x, i] for x in s) <= len(s) - 1) try: p.solve(log=verbose) @@ -2106,13 +2106,13 @@ def coloring(self, k=None, solver=None, verbose=0, col = [[] for _ in range(k)] - for (x,i),v in p.get_values(b, convert=bool, tolerance=integrality_tolerance).items(): + for (x, i), v in p.get_values(b, convert=bool, tolerance=integrality_tolerance).items(): if v: col[i].append(self._points[x]) return col - def edge_coloring(self): + def edge_coloring(self) -> list: r""" Compute a proper edge-coloring. @@ -2185,13 +2185,13 @@ def _spring_layout(self): for x in s: g.add_edge((0, s), (1, x)) - _ = g.plot(iterations=50000,save_pos=True) + _ = g.plot(iterations=50000, save_pos=True) # The values are rounded as TikZ does not like accuracy. return {k[1]: (round(x, 3), round(y, 3)) for k, (x, y) in g.get_pos().items()} - def _latex_(self): + def _latex_(self) -> str: r""" Return a TikZ representation of the incidence structure. @@ -2246,11 +2246,12 @@ def _latex_(self): pos = self._spring_layout() tex = "\\begin{tikzpicture}[scale=3]\n" - colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"] - colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S] + colors = ["black", "red", "green", "blue", "cyan", + "magenta", "yellow", "pink", "brown"] + colored_sets = [(s, i) for i, S in enumerate(self.edge_coloring()) for s in S] # Prints each set with its color - for s,i in colored_sets: + for s, i in colored_sets: current_color = colors[i % len(colors)] if len(s) == 2: @@ -2283,7 +2284,7 @@ def _latex_(self): tex += "\\end{tikzpicture}" return tex - def is_spread(self, spread): + def is_spread(self, spread) -> bool: r""" Check whether the input is a spread for ``self``. @@ -2340,10 +2341,7 @@ def is_spread(self, spread): points.difference_update(sblock) - if points: - return False - - return True + return not points from sage.misc.rest_index_of_methods import gen_rest_table_index diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index ae1d48388bb..daac0f322cf 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -955,12 +955,12 @@ def orthogonal_array(k,n,t=2,resolvable=False, check=True,existence=False,explai # Constructions from the database III (Quasi-difference matrices) elif (may_be_available and - (n,1) in QDM and + (n, 1) in QDM and any(kk >= k and mu <= lmbda and (orthogonal_array(k,u,existence=True) is True) for (_,lmbda,mu,u),(kk,_) in QDM[n,1].items())): _OA_cache_set(k,n,True) - for (nn,lmbda,mu,u),(kk,f) in QDM[n,1].items(): - if (kk >= k and + for (nn, lmbda, mu, u), (kk, f) in QDM[n,1].items(): + if (kk >= k and mu <= lmbda and (orthogonal_array(k,u,existence=True) is True)): if existence: @@ -1236,11 +1236,11 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): raise EmptySetError("The total size of holes must be smaller or equal than the size of the ground set") if (max_hole == 1 and - resolvable and + resolvable and sum_of_holes != n): if existence: return False - raise EmptySetError("There is no resolvable incomplete OA({},{}) whose holes' sizes sum to {} equivalent to OA(k+1,n) if max_hole == 1 and resolvable: @@ -1366,11 +1366,11 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): # Equal holes [h,h,...] with h>1 through OA product construction # # (i.e. OA(k,n1)-x.OA(k,1) and OA(k,n2) ==> OA(k,n1.n2)-x.OA(k,n2) ) - elif (min_hole > 1 and - max_hole == min_hole and - n % min_hole == 0 and # h divides n + elif (min_hole > 1 and + max_hole == min_hole and + n % min_hole == 0 and # h divides n orthogonal_array(k,min_hole,existence=True) and # OA(k,h) - incomplete_orthogonal_array(k,n//min_hole,[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) + incomplete_orthogonal_array(k,n//min_hole,[1]*number_of_holes,existence=True)): # OA(k,n/h)-x.OA(k,1) if existence: return True h = min_hole @@ -1540,7 +1540,7 @@ def OA_relabel(OA, k, n, blocks=tuple(), matrix=None, symbol_list=None): """ if blocks: l = [] - for i,B in enumerate(zip(*blocks)): # the blocks are disjoint + for i, B in enumerate(zip(*blocks)): # the blocks are disjoint if len(B) != len(set(B)): raise RuntimeError("Two block have the same coordinate for one of the k dimensions") @@ -1941,7 +1941,7 @@ def QDM_from_Vmt(m,t,V): for e in V: L.append(e*wm**i) for ii in range(m+2): - M.append(L[-ii:]+L[:-ii]) # cyclic shift + M.append(L[-ii:]+L[:-ii]) # cyclic shift M.append([0]*(m+2)) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index edad950fc88..7366fb42a5a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -537,9 +537,9 @@ def construction_q_x(k, q, x, check=True, explain_construction=False): # Add rows, extended with p1 and p2 p1 = q**2 - p2 = p1+1 - TD.extend([[ii*q+i for ii in range(q)]+[p1] for i in range(1,q)]) - TD.append( [ii*q for ii in range(q)]+[p1,p2]) + p2 = p1 + 1 + TD.extend(([ii*q + i for ii in range(q)] + [p1] for i in range(1, q))) + TD.append([ii*q for ii in range(q)] + [p1, p2]) # Add Columns. We do not add some columns which would have size 1 after we # delete points. @@ -1453,9 +1453,9 @@ def brouwer_separable_design(k,t,q,x,check=False,verbose=False,explain_construct else: partition_of_blocks_of_size_t[plane-t].append([relabel[xx] for xx in B if xx % m < t]) - ############################################################################### + ########################################################################### # Separable design built ! - #------------------------- + # ------------------------ # # At this point we have a PBD on t*(q**2+q+1) points. Its blocks are # split into: @@ -1466,7 +1466,7 @@ def brouwer_separable_design(k,t,q,x,check=False,verbose=False,explain_construct # - blocks_of_size_q_plus_t : contains all t*(q**2+q+1)blocks of size q+t, # covering the same number of points: it is a # symmetric design. - ############################################################################### + ########################################################################### ############################################## # Part 2: Build an OA on t(q^2+q+1)+x points # diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 27a5bdcae32..ccc11176d54 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -311,14 +311,14 @@ def kirkman_triple_system(v,existence=False): b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v:i for i,v in enumerate(X)}) - gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes + gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v:i for i,v in enumerate(X)}) - gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes + gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. @@ -573,7 +573,7 @@ def PBD_4_7(v,check=True, existence=False): # On these groups a (15+7,{4,7})-PBD is pasted, in such a way that the 7 # new points are a set of the final PBD PBD22 = PBD_4_7(15+7) - S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7 + S = next(SS for SS in PBD22 if len(SS) == 7) # a set of size 7 PBD22.relabel({v:i for i,v in enumerate([i for i in range(15+7) if i not in S] + S)}) for B in PBD22: @@ -741,19 +741,19 @@ def PBD_4_7_from_Y(gdd,check=True): raise RuntimeError("A group has size {} but I do not know how to " "build a ({},[4,7])-PBD".format(gs,3*gs+1)) - GDD = {} # the GDD we will need + GDD = {} # the GDD we will need if 4 in block_sizes: - #GDD[4] = GDD_from_BIBD(3*4,4) + # GDD[4] = GDD_from_BIBD(3*4,4) GDD[4] = group_divisible_design(3*4,K=[4],G=[3]) if 5 in block_sizes: - #GDD[5] = GDD_from_BIBD(3*5,4) + # GDD[5] = GDD_from_BIBD(3*5,4) GDD[5] = group_divisible_design(3*5,K=[4],G=[3]) if 7 in block_sizes: # It is obtained from a PBD_4_7(22) by removing a point only contained # in sets of size 4 GDD[7] = PBD_4_7(22) x = set(range(22)).difference(*[S for S in GDD[7] if len(S) != 4]).pop() - relabel = sum((S for S in GDD[7] if x in S),[]) # the groups must be 012,345,... + relabel = sum((S for S in GDD[7] if x in S),[]) # the groups must be 012,345,... relabel = [xx for xx in relabel if xx != x]+[x] GDD[7].relabel({v:i for i,v in enumerate(relabel)}) GDD[7] = [S for S in GDD[7] if 21 not in S] diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 281f4fac153..131916b89c9 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -70,7 +70,7 @@ class TwoGraph(IncidenceStructure): :mod:`~sage.combinat.designs.twographs` module. """ def __init__(self, points=None, blocks=None, incidence_matrix=None, - name=None, check=False, copy=True): + name=None, check=False, copy=True): r""" Constructor of the class. From 02f46bfae03647d054a735c23292dc5ca2a481b2 Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 4 Sep 2024 17:27:49 +0000 Subject: [PATCH 122/193] Add `const`s in `permgroup_element.pyx` --- src/sage/groups/perm_gps/permgroup_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 2addd364812..86b3ced83ea 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -596,8 +596,8 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): ... ValueError: invalid data to initialize a permutation """ - cdef UInt2* p2 - cdef UInt4* p4 + cdef const UInt2* p2 + cdef const UInt4* p4 cdef int i cdef UInt d From cd98d17da61fcc5f7c0298b2b6afb0cbd7c7c0bc Mon Sep 17 00:00:00 2001 From: Giorgos Mousa Date: Wed, 4 Sep 2024 17:49:47 +0000 Subject: [PATCH 123/193] Remove excess "%s"s in `structure/sage_object.pyx` --- src/sage/structure/sage_object.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index fb6d4d0f686..7102316be70 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -719,7 +719,7 @@ cdef class SageObject: try: s = self._interface_init_(I) except Exception: - raise NotImplementedError("coercion of object %s to %s not implemented:\n%s\n%s" % (repr(self), I)) + raise NotImplementedError("coercion of object %s to %s not implemented" % (repr(self), I)) X = I(s) if c: try: From aad8750d1dcc83e11048f77e42a2a4115e3e0cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 5 Sep 2024 11:42:09 +1200 Subject: [PATCH 124/193] add py3.9 compatibility section to sage_autodoc.py --- src/sage_docbuild/ext/sage_autodoc.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 220067f125f..68596af4401 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1903,6 +1903,28 @@ def get_doc(self) -> list[list[str]] | None: if isinstance(self.object, TypeVar): if self.object.__doc__ == TypeVar.__doc__: return [] + # ------------------------------------------------------------------ + # This section is kept for compatibility with python 3.9 + # see https://github.com/sagemath/sage/pull/38549#issuecomment-2327790930 + if sys.version_info[:2] < (3, 10): + if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + parts = self.modname.strip('.').split('.') + orig_objpath = self.objpath + for i in range(len(parts)): + new_modname = '.'.join(parts[:len(parts) - i]) + new_objpath = parts[len(parts) - i:] + orig_objpath + try: + analyzer = ModuleAnalyzer.for_module(new_modname) + analyzer.analyze() + key = ('', new_objpath[-1]) + comment = list(analyzer.attr_docs.get(key, [])) + if comment: + self.objpath = new_objpath + self.modname = new_modname + return [comment] + except PycodeError: + pass + # ------------------------------------------------------------------ if self.doc_as_attr: # Don't show the docstring of the class when it is an alias. if self.get_variable_comment(): From 46b390d9d0493d32f4e03b8a87b29f8c2e09a2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Thu, 5 Sep 2024 13:59:20 +1200 Subject: [PATCH 125/193] import sys as it is needed to select py3.9 code --- src/sage_docbuild/ext/sage_autodoc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 68596af4401..d874ba0b59c 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -39,6 +39,7 @@ import functools import operator +import sys import re from inspect import Parameter, Signature from typing import TYPE_CHECKING, Any, ClassVar, NewType, TypeVar From d417a6cac729503afc5b5274c74ccc6964f3d61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 5 Sep 2024 09:11:05 +0200 Subject: [PATCH 126/193] cleaning the deprecation and removal of NoetherianRing class --- src/sage/rings/ring.pyx | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index d3915484b8b..59cf58e3513 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -21,7 +21,7 @@ The class inheritance hierarchy is: - :class:`Algebra` (deprecated and essentially removed) - :class:`CommutativeRing` - - :class:`NoetherianRing` (deprecated) + - :class:`NoetherianRing` (deprecated and essentially removed) - :class:`CommutativeAlgebra` (deprecated and essentially removed) - :class:`IntegralDomain` (deprecated) @@ -1160,8 +1160,7 @@ cdef class IntegralDomain(CommutativeRing): - ``category`` -- (default: ``None``) a category, or ``None`` This method is used by all the abstract subclasses of - :class:`IntegralDomain`, like :class:`NoetherianRing`, - :class:`Field`, ... in order to + :class:`IntegralDomain`, like :class:`Field`, ... in order to avoid cascade calls Field.__init__ -> IntegralDomain.__init__ -> ... @@ -1247,7 +1246,7 @@ cdef class NoetherianRing(CommutativeRing): _default_category = NoetherianRings() def __init__(self, *args, **kwds): - deprecation(37234, "use the category DedekindDomains") + deprecation(37234, "use the category NoetherianRings") super().__init__(*args, **kwds) @@ -1306,6 +1305,11 @@ _Fields = Fields() cdef class Field(CommutativeRing): """ Generic field + + TESTS:: + + sage: QQ.is_noetherian() + True """ _default_category = _Fields @@ -1425,17 +1429,6 @@ cdef class Field(CommutativeRing): """ return True - def is_noetherian(self): - """ - Return ``True`` since fields are Noetherian rings. - - EXAMPLES:: - - sage: QQ.is_noetherian() - True - """ - return True - def krull_dimension(self): """ Return the Krull dimension of this field, which is 0. From 0dc4a5420ce8cd7b6e2fdcd4da249b7836832b60 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Thu, 5 Sep 2024 09:19:09 +0200 Subject: [PATCH 127/193] use genbgL instead of genbg --- src/sage/graphs/digraph_generators.py | 12 +++--- src/sage/graphs/graph_generators.py | 55 ++++++++++++++------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 02590ac8f7c..1fc03552a9e 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -558,14 +558,14 @@ def tournaments_nauty(self, n, ``None`` (default), then the min/max out-degree is not constrained - ``debug`` -- boolean (default: ``False``); if ``True`` the first line - of genbg's output to standard error is captured and the first call to - the generator's ``next()`` function will return this line as a string. - A line leading with ">A" indicates a successful initiation of the - program with some information on the arguments, while a line beginning - with ">E" indicates an error with the input. + of gentourng's output to standard error is captured and the first call + to the generator's ``next()`` function will return this line as a + string. A line leading with ">A" indicates a successful initiation of + the program with some information on the arguments, while a line + beginning with ">E" indicates an error with the input. - ``options`` -- string; anything else that should be forwarded as input - to Nauty's genbg. See its documentation for more information : + to Nauty's gentourng. See its documentation for more information : ``_. EXAMPLES:: diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index e8049664aae..f0b6c4472ed 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -1007,12 +1007,12 @@ def nauty_geng(self, options='', debug=False): def nauty_genbg(self, options='', debug=False): r""" - Return a generator which creates bipartite graphs from nauty's ``genbg`` + Return a generator which creates bipartite graphs from nauty's ``genbgL`` program. INPUT: - - ``options`` -- string (default: ``""``); a string passed to ``genbg`` + - ``options`` -- string (default: ``""``); a string passed to ``genbgL`` as if it was run at a system command line. At a minimum, you *must* pass the number of vertices you desire in each side. Sage expects the bipartite graphs to be in nauty's "graph6" format, do not set an @@ -1025,12 +1025,12 @@ def nauty_genbg(self, options='', debug=False): the program with some information on the arguments, while a line beginning with ">E" indicates an error with the input. - The possible options, obtained as output of ``genbg --help``:: + The possible options, obtained as output of ``genbgL --help``:: n1 : the number of vertices in the first class. - We must have n1=1..24. + We must have n1=1..30. n2 : the number of vertices in the second class. - We must have n2=0..32 and n1+n2=1..32. + We must have n2=0..64 and n1+n2=1..64. mine:maxe : : a range for the number of edges :0 means ' or more' except in the case 0:0 res/mod : only generate subset res out of subsets 0..mod-1 @@ -1058,8 +1058,8 @@ def nauty_genbg(self, options='', debug=False): -v : display counts by number of edges to stderr -l : canonically label output graphs - Options which cause ``genbg`` to use an output format different than the - ``graph6`` format are not listed above (``-s``, ``-a``) as they will + Options which cause ``genbgL`` to use an output format different than + the ``graph6`` format are not listed above (``-s``, ``-a``) as they will confuse the creation of a Sage graph. Option ``-q`` which suppress auxiliary output (except from ``-v``) should never be used as we are unable to recover the partition of the vertices of the bipartite graph @@ -1113,16 +1113,16 @@ def nauty_genbg(self, options='', debug=False): sage: len(list(gen)) 17 - The ``debug`` switch can be used to examine ``genbg``'s reaction to the + The ``debug`` switch can be used to examine ``genbgL``'s reaction to the input in the ``options`` string. A message starting with ">A" indicates success and a message starting with ">E" indicates a failure:: sage: gen = graphs.nauty_genbg("2 3", debug=True) sage: print(next(gen)) - >A ...genbg n=2+3 e=0:6 d=0:0 D=3:2 + >A ...genbg... n=2+3 e=0:6 d=0:0 D=3:2 sage: gen = graphs.nauty_genbg("-c2 3", debug=True) sage: next(gen) - '>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + '>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... Check that the partition of the bipartite graph is consistent:: @@ -1141,34 +1141,35 @@ def nauty_genbg(self, options='', debug=False): ... ValueError: wrong format of parameter options sage: list(graphs.nauty_genbg("-c1 2", debug=True)) - ['>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + ['>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... sage: list(graphs.nauty_genbg("-c 1 2", debug=True)) - ['>A ...genbg n=1+2 e=2:2 d=1:1 D=2:1 c...\n', Bipartite graph on 3 vertices] + ['>A ...genbg... n=1+2 e=2:2 d=1:1 D=2:1 c...\n', Bipartite graph on 3 vertices] - We must have n1=1..24, n2=0..32 and n1+n2=1..32 (:issue:`34179`):: + We must have n1=1..30, n2=0..64 and n1+n2=1..64 (:issue:`34179`, + :issue:`38618`):: - sage: next(graphs.nauty_genbg("25 1", debug=False)) + sage: next(graphs.nauty_genbg("31 1", debug=False)) Traceback (most recent call last): ... ValueError: wrong format of parameter options - sage: next(graphs.nauty_genbg("25 1", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("24 9", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("1 31", debug=False)) - Bipartite graph on 32 vertices - sage: next(graphs.nauty_genbg("1 32", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... - sage: next(graphs.nauty_genbg("0 32", debug=True)) - '>E ...genbg: must have n1=1..24, n1+n2=1..32... + sage: next(graphs.nauty_genbg("31 1", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("30 40", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("1 63", debug=False)) + Bipartite graph on 64 vertices + sage: next(graphs.nauty_genbg("1 64", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... + sage: next(graphs.nauty_genbg("0 2", debug=True)) + '>E ...genbg...: must have n1=1..30, n1+n2=1..64... sage: next(graphs.nauty_genbg("2 0", debug=False)) Bipartite graph on 2 vertices sage: next(graphs.nauty_genbg("2 -1", debug=True)) - '>E Usage: ...genbg [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... + '>E Usage: ...genbg... [-c -ugs -vq -lzF] [-Z#] [-D#] [-A] [-d#|-d#:#] [-D#|-D#:#] n1 n2... """ import shlex from sage.features.nauty import NautyExecutable - genbg_path = NautyExecutable("genbg").absolute_filename() + genbg_path = NautyExecutable("genbgL").absolute_filename() sp = subprocess.Popen(shlex.quote(genbg_path) + " {0}".format(options), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, @@ -1200,7 +1201,7 @@ def nauty_genbg(self, options='', debug=False): try: s = next(gen) except StopIteration: - # Exhausted list of bipartite graphs from nauty genbg + # Exhausted list of bipartite graphs from nauty genbgL return G = BipartiteGraph(s[:-1], format='graph6', partition=partition) yield G From 2e55874bfbb8d07436155e73adc59b0d64c35f1c Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 5 Sep 2024 10:07:16 +0200 Subject: [PATCH 128/193] graphs: slice_decomposition.pyx: Typo in comments --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index d3d4bfee49c..d478bd1cdfe 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -1057,7 +1057,7 @@ cdef class SliceDecomposition(SageObject): r"tail anchor=center,head anchor=north," r"nodes={draw,rectangle,inner xsep=0.2em},edges={thick}]") lines.append("{") - bo, bc = "{", "}" # to write { and } if f-strings + bo, bc = "{", "}" # to write { and } in f-strings # Create the nodes and leaves of the slice decomposition tree for i in range(len(self.sigma)): l = self.xslice_len[i] From 7f6cc5efee76a8e8d8fb7aa5654fba4f0f5b167f Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Thu, 5 Sep 2024 10:09:46 +0200 Subject: [PATCH 129/193] graphs: slice_decomposition: fix a bug with latex repr (missing $) --- src/sage/graphs/graph_decompositions/slice_decomposition.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index d478bd1cdfe..6e6104cd9ae 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -1062,8 +1062,8 @@ cdef class SliceDecomposition(SageObject): for i in range(len(self.sigma)): l = self.xslice_len[i] label = r"\ ".join(sigma_latex[i:i+l]) - lines.append(f" v{i}[as={bo}{label}{bc}];") - lines.append(f" l{i}[draw=none,as={bo}{sigma_latex[i]}{bc}];") + lines.append(f" v{i}[as={bo}${label}${bc}];") + lines.append(f" l{i}[draw=none,as={bo}${sigma_latex[i]}${bc}];") j = i + 1 slices[i].append(f"l{i}") while j < i + l: From ea9163555fb370deeab51624a9484a7e34862cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 5 Sep 2024 10:24:12 +0200 Subject: [PATCH 130/193] fix doctest --- src/doc/en/thematic_tutorials/coercion_and_categories.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 8da15b32efa..8db70ca5e3e 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -133,7 +133,6 @@ This base class provides a lot more methods than a general parent:: 'is_commutative', 'is_field', 'is_integrally_closed', - 'is_noetherian', 'is_prime_field', 'is_subring', 'krull_dimension', From 5734c373a5d6449aed47f6611e204165fe5f5f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 5 Sep 2024 14:11:29 +0200 Subject: [PATCH 131/193] pep8 fixes in schemes/toric --- src/sage/schemes/toric/ideal.py | 6 +- src/sage/schemes/toric/library.py | 168 +++++++++++----------- src/sage/schemes/toric/sheaf/klyachko.py | 6 +- src/sage/schemes/toric/toric_subscheme.py | 25 ++-- 4 files changed, 102 insertions(+), 103 deletions(-) diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index 41b69d05aca..1d9a100c0b5 100755 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -354,8 +354,8 @@ def _naive_ideal(self, ring): x = ring.gens() binomials = [] for row in self.ker().matrix().rows(): - xpos = prod(x[i]**max( row[i],0) for i in range(0,len(x))) - xneg = prod(x[i]**max(-row[i],0) for i in range(0,len(x))) + xpos = prod(x[i]**max(row[i], 0) for i in range(0, len(x))) + xneg = prod(x[i]**max(-row[i], 0) for i in range(0, len(x))) binomials.append(xpos - xneg) return ring.ideal(binomials) @@ -445,6 +445,6 @@ def _ideal_HostenSturmfels(self): J = self._naive_ideal(ring) if J.is_zero(): return J - for i in range(0,self.nvariables()): + for i in range(0, self.nvariables()): J = self._ideal_quotient_by_variable(ring, J, i) return J diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index 1e1ff4d38d8..6f833c5c5d2 100755 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -58,44 +58,44 @@ # The combinatorial data of the toric varieties is stored separately here # since we might want to use it later on to do the reverse lookup. toric_varieties_rays_cones = { - 'dP6':[ + 'dP6': [ [(0, 1), (-1, 0), (-1, -1), (0, -1), (1, 0), (1, 1)], - [[0,1],[1,2],[2,3],[3,4],[4,5],[5,0]] ], - 'dP7':[ + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]], + 'dP7': [ [(0, 1), (-1, 0), (-1, -1), (0, -1), (1, 0)], - [[0,1],[1,2],[2,3],[3,4],[4,0]] ], - 'dP8':[ - [(1,1), (0, 1), (-1, -1), (1, 0)], - [[0,1],[1,2],[2,3],[3,0]] + [[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]]], + 'dP8': [ + [(1, 1), (0, 1), (-1, -1), (1, 0)], + [[0, 1], [1, 2], [2, 3], [3, 0]] ], - 'P1xP1':[ + 'P1xP1': [ [(1, 0), (-1, 0), (0, 1), (0, -1)], - [[0,2],[2,1],[1,3],[3,0]] ], - 'P1xP1_Z2':[ + [[0, 2], [2, 1], [1, 3], [3, 0]]], + 'P1xP1_Z2': [ [(1, 1), (-1, -1), (-1, 1), (1, -1)], - [[0,2],[2,1],[1,3],[3,0]] ], - 'P1':[ + [[0, 2], [2, 1], [1, 3], [3, 0]]], + 'P1': [ [(1,), (-1,)], - [[0],[1]] ], - 'P2':[ - [(1,0), (0, 1), (-1, -1)], - [[0,1],[1,2],[2,0]] ], - 'A1':[ + [[0], [1]]], + 'P2': [ + [(1, 0), (0, 1), (-1, -1)], + [[0, 1], [1, 2], [2, 0]]], + 'A1': [ [(1,)], - [[0]] ], - 'A2':[ + [[0]]], + 'A2': [ [(1, 0), (0, 1)], - [[0,1]] ], - 'A2_Z2':[ + [[0, 1]]], + 'A2_Z2': [ [(1, 0), (1, 2)], - [[0,1]] ], - 'P1xA1':[ + [[0, 1]]], + 'P1xA1': [ [(1, 0), (-1, 0), (0, 1)], - [[0,2],[2,1]] ], - 'Conifold':[ + [[0, 2], [2, 1]]], + 'Conifold': [ [(0, 0, 1), (0, 1, 1), (1, 0, 1), (1, 1, 1)], - [[0,1,2,3]] ], - 'dP6xdP6':[ + [[0, 1, 2, 3]]], + 'dP6xdP6': [ [(0, 1, 0, 0), (-1, 0, 0, 0), (-1, -1, 0, 0), (0, -1, 0, 0), (1, 0, 0, 0), (1, 1, 0, 0), (0, 0, 0, 1), (0, 0, -1, 0), (0, 0, -1, -1), @@ -108,74 +108,74 @@ [3, 4, 8, 9], [3, 4, 9, 10], [3, 4, 10, 11], [3, 4, 6, 11], [4, 5, 6, 7], [4, 5, 7, 8], [4, 5, 8, 9], [4, 5, 9, 10], [4, 5, 10, 11], [4, 5, 6, 11], [0, 5, 6, 7], [0, 5, 7, 8], - [0, 5, 8, 9], [0, 5, 9, 10], [0, 5, 10, 11], [0, 5, 6, 11]] ], - 'Cube_face_fan':[ + [0, 5, 8, 9], [0, 5, 9, 10], [0, 5, 10, 11], [0, 5, 6, 11]]], + 'Cube_face_fan': [ [(1, 1, 1), (1, -1, 1), (-1, 1, 1), (-1, -1, 1), (-1, -1, -1), (-1, 1, -1), (1, -1, -1), (1, 1, -1)], - [[0,1,2,3], [4,5,6,7], [0,1,7,6], [4,5,3,2], [0,2,5,7], [4,6,1,3]] ], - 'Cube_sublattice':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'Cube_sublattice': [ [(1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 1, 1), (-1, 0, 0), (0, -1, 0), (0, 0, -1), (1, -1, -1)], - [[0,1,2,3],[4,5,6,7],[0,1,7,6],[4,5,3,2],[0,2,5,7],[4,6,1,3]] ], - 'Cube_nonpolyhedral':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'Cube_nonpolyhedral': [ [(1, 2, 3), (1, -1, 1), (-1, 1, 1), (-1, -1, 1), (-1, -1, -1), (-1, 1, -1), (1, -1, -1), (1, 1, -1)], - [[0,1,2,3],[4,5,6,7],[0,1,7,6],[4,5,3,2],[0,2,5,7],[4,6,1,3]] ], - 'BCdlOG':[ + [[0, 1, 2, 3], [4, 5, 6, 7], [0, 1, 7, 6], [4, 5, 3, 2], [0, 2, 5, 7], [4, 6, 1, 3]]], + 'BCdlOG': [ [(-1, 0, 0, 2, 3), # 0 - ( 0,-1, 0, 2, 3), # 1 - ( 0, 0,-1, 2, 3), # 2 - ( 0, 0,-1, 1, 2), # 3 - ( 0, 0, 0,-1, 0), # 4 - ( 0, 0, 0, 0,-1), # 5 - ( 0, 0, 0, 2, 3), # 6 - ( 0, 0, 1, 2, 3), # 7 - ( 0, 0, 2, 2, 3), # 8 - ( 0, 0, 1, 1, 1), # 9 - ( 0, 1, 2, 2, 3), # 10 - ( 0, 1, 3, 2, 3), # 11 - ( 1, 0, 4, 2, 3)], # 12 - [ [0,6,7,1,4], [0,6,10,2,4], [0,6,1,2,4], [0,9,7,1,5], [0,6,7,1,5], - [0,6,10,2,5], [0,6,1,2,5], [0,9,1,4,5], [0,6,10,4,11],[0,6,7,4,11], - [0,6,10,5,11], [0,9,7,5,11], [0,6,7,5,11], [0,9,4,5,11], [0,10,4,5,11], - [0,9,7,1,8], [0,9,1,4,8], [0,7,1,4,8], [0,9,7,11,8], [0,9,4,11,8], - [0,7,4,11,8], [0,10,2,4,3], [0,1,2,4,3], [0,10,2,5,3], [0,1,2,5,3], - [0,10,4,5,3], [0,1,4,5,3], [12,6,7,1,4], [12,6,10,2,4],[12,6,1,2,4], - [12,9,7,1,5], [12,6,7,1,5], [12,6,10,2,5], [12,6,1,2,5], [12,9,1,4,5], - [12,6,10,4,11],[12,6,7,4,11], [12,6,10,5,11],[12,9,7,5,11],[12,6,7,5,11], - [12,9,4,5,11], [12,10,4,5,11],[12,9,7,1,8], [12,9,1,4,8], [12,7,1,4,8], - [12,9,7,11,8], [12,9,4,11,8], [12,7,4,11,8], [12,10,2,4,3],[12,1,2,4,3], - [12,10,2,5,3], [12,1,2,5,3], [12,10,4,5,3], [12,1,4,5,3] ] ], - 'BCdlOG_base':[ + (0, -1, 0, 2, 3), # 1 + (0, 0, -1, 2, 3), # 2 + (0, 0, -1, 1, 2), # 3 + (0, 0, 0, -1, 0), # 4 + (0, 0, 0, 0, -1), # 5 + (0, 0, 0, 2, 3), # 6 + (0, 0, 1, 2, 3), # 7 + (0, 0, 2, 2, 3), # 8 + (0, 0, 1, 1, 1), # 9 + (0, 1, 2, 2, 3), # 10 + (0, 1, 3, 2, 3), # 11 + (1, 0, 4, 2, 3)], # 12 + [[0, 6, 7, 1, 4], [0, 6, 10, 2, 4], [0, 6, 1, 2, 4], [0, 9, 7, 1, 5], [0, 6, 7, 1, 5], + [0, 6, 10, 2, 5], [0, 6, 1, 2, 5], [0, 9, 1, 4, 5], [0, 6, 10, 4, 11], [0, 6, 7, 4, 11], + [0, 6, 10, 5, 11], [0, 9, 7, 5, 11], [0, 6, 7, 5, 11], [0, 9, 4, 5, 11], [0, 10, 4, 5, 11], + [0, 9, 7, 1, 8], [0, 9, 1, 4, 8], [0, 7, 1, 4, 8], [0, 9, 7, 11, 8], [0, 9, 4, 11, 8], + [0, 7, 4, 11, 8], [0, 10, 2, 4, 3], [0, 1, 2, 4, 3], [0, 10, 2, 5, 3], [0, 1, 2, 5, 3], + [0, 10, 4, 5, 3], [0, 1, 4, 5, 3], [12, 6, 7, 1, 4], [12, 6, 10, 2, 4], [12, 6, 1, 2, 4], + [12, 9, 7, 1, 5], [12, 6, 7, 1, 5], [12, 6, 10, 2, 5], [12, 6, 1, 2, 5], [12, 9, 1, 4, 5], + [12, 6, 10, 4, 11], [12, 6, 7, 4, 11], [12, 6, 10, 5, 11], [12, 9, 7, 5, 11], [12, 6, 7, 5, 11], + [12, 9, 4, 5, 11], [12, 10, 4, 5, 11], [12, 9, 7, 1, 8], [12, 9, 1, 4, 8], [12, 7, 1, 4, 8], + [12, 9, 7, 11, 8], [12, 9, 4, 11, 8], [12, 7, 4, 11, 8], [12, 10, 2, 4, 3], [12, 1, 2, 4, 3], + [12, 10, 2, 5, 3], [12, 1, 2, 5, 3], [12, 10, 4, 5, 3], [12, 1, 4, 5, 3]]], + 'BCdlOG_base': [ [(-1, 0, 0), - ( 0,-1, 0), - ( 0, 0,-1), - ( 0, 0, 1), - ( 0, 1, 2), - ( 0, 1, 3), - ( 1, 0, 4)], - [[0,4,2],[0,4,5],[0,5,3],[0,1,3],[0,1,2], - [6,4,2],[6,4,5],[6,5,3],[6,1,3],[6,1,2]] ], - 'P2_112':[ - [(1,0), (0, 1), (-1, -2)], - [[0,1],[1,2],[2,0]] ], - 'P2_123':[ - [(1,0), (0, 1), (-2, -3)], - [[0,1],[1,2],[2,0]] ], - 'P4_11169':[ + (0, -1, 0), + (0, 0, -1), + (0, 0, 1), + (0, 1, 2), + (0, 1, 3), + (1, 0, 4)], + [[0, 4, 2], [0, 4, 5], [0, 5, 3], [0, 1, 3], [0, 1, 2], + [6, 4, 2], [6, 4, 5], [6, 5, 3], [6, 1, 3], [6, 1, 2]]], + 'P2_112': [ + [(1, 0), (0, 1), (-1, -2)], + [[0, 1], [1, 2], [2, 0]]], + 'P2_123': [ + [(1, 0), (0, 1), (-2, -3)], + [[0, 1], [1, 2], [2, 0]]], + 'P4_11169': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1)], - [[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4],[1,2,3,4]] ], - 'P4_11169_resolved':[ + [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]], + 'P4_11169_resolved': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1), (-3, -2, 0, 0)], [[0, 1, 2, 3], [0, 1, 3, 4], [0, 1, 2, 4], [1, 3, 4, 5], [0, 3, 4, 5], - [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]] ], - 'P4_11133':[ + [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]]], + 'P4_11133': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-3, -3, -1, -1)], - [[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4],[1,2,3,4]] ], - 'P4_11133_resolved':[ + [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4], [1, 2, 3, 4]]], + 'P4_11133_resolved': [ [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-3, -3, -1, -1), (-1, -1, 0, 0)], [[0, 1, 2, 3], [0, 1, 3, 4], [0, 1, 2, 4], [1, 3, 4, 5], [0, 3, 4, 5], - [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]] ] + [1, 2, 4, 5], [0, 2, 4, 5], [1, 2, 3, 5], [0, 2, 3, 5]]] } @@ -264,7 +264,7 @@ def _make_CPRFanoToricVariety(self, name, coordinate_names, base_ring): polytope = LatticePolytope(rays, lattice=ToricLattice(len(rays[0]))) points = [tuple(_) for _ in polytope.points()] ray2point = [points.index(r) for r in rays] - charts = [ [ray2point[i] for i in c] for c in cones ] + charts = [[ray2point[i] for i in c] for c in cones] self.__dict__[dict_key] = \ CPRFanoToricVariety(Delta_polar=polytope, coordinate_points=ray2point, @@ -868,7 +868,7 @@ def Cube_nonpolyhedral(self, names='z+', base_ring=QQ): """ return self._make_ToricVariety('Cube_nonpolyhedral', names, base_ring) - def Cube_deformation(self,k, names=None, base_ring=QQ): + def Cube_deformation(self, k, names=None, base_ring=QQ): r""" Construct, for each `k\in\ZZ_{\geq 0}`, a toric variety with `\ZZ_k`-torsion in the Chow group. @@ -1268,7 +1268,7 @@ def WP(self, *q, **kw): rays = rays + [v] w_c = w[:i] + w[i + 1:] cones = cones + [tuple(w_c)] - fan = Fan(cones,rays) + fan = Fan(cones, rays) return ToricVariety(fan, coordinate_names=names, base_ring=base_ring) def torus(self, n, names='z+', base_ring=QQ): diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py index 94f465860b7..a2e80dd7fa6 100755 --- a/src/sage/schemes/toric/sheaf/klyachko.py +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -605,7 +605,7 @@ def cohomology_complex(self, m): C = fan.complex() CV = [] F = self.base_ring() - for dim in range(1,fan.dim()+1): + for dim in range(1, fan.dim()+1): codim = fan.dim() - dim d_C = C.differential(codim) d_V = [] @@ -616,7 +616,7 @@ def cohomology_complex(self, m): sigma = fan(dim-1)[i] if sigma.is_face_of(tau): pr = self.E_quotient_projection(sigma, tau, m) - d = d_C[i,j] * pr.matrix().transpose() + d = d_C[i, j] * pr.matrix().transpose() else: E_sigma = self.E_quotient(sigma, m) E_tau = self.E_quotient(tau, m) @@ -695,7 +695,7 @@ def cohomology(self, degree=None, weight=None, dim=False): except KeyError: HH[d] = FreeModule(self.base_ring(), 0) if dim: - HH = vector(ZZ, [HH[i].rank() for i in range(space_dim+1) ]) + HH = vector(ZZ, [HH[i].rank() for i in range(space_dim+1)]) return HH def __richcmp__(self, other, op): diff --git a/src/sage/schemes/toric/toric_subscheme.py b/src/sage/schemes/toric/toric_subscheme.py index 33806674d28..ca8ec2278ac 100755 --- a/src/sage/schemes/toric/toric_subscheme.py +++ b/src/sage/schemes/toric/toric_subscheme.py @@ -328,14 +328,14 @@ def pullback_polynomial(p): result = R.zero() for coefficient, monomial in p: exponent = monomial.exponents()[0] - exponent = [ exponent[i] for i in cone.ambient_ray_indices() ] - exponent = vector(ZZ,exponent) + exponent = [exponent[i] for i in cone.ambient_ray_indices()] + exponent = vector(ZZ, exponent) m = n_rho_matrix.solve_right(exponent) assert all(x in ZZ for x in m), \ - 'The polynomial '+str(p)+' does not define a ZZ-divisor!' + f'The polynomial {p} does not define a ZZ-divisor!' m_coeffs = dualcone.Hilbert_coefficients(m) result += coefficient * prod(R.gen(i)**m_coeffs[i] - for i in range(0,R.ngens())) + for i in range(0, R.ngens())) return result # construct the affine algebraic scheme to use as patch @@ -353,7 +353,7 @@ def pullback_polynomial(p): if cone.is_smooth(): x = ambient.coordinate_ring().gens() phi = [] - for i in range(0,fan.nrays()): + for i in range(0, fan.nrays()): if i in cone.ambient_ray_indices(): phi.append(pullback_polynomial(x[i])) else: @@ -371,11 +371,10 @@ def pullback_polynomial(p): # it remains to find the preimage of point # map m to the monomial x^{D_m}, see reference. F = ambient.coordinate_ring().fraction_field() - image = [] - for m in dualcone.Hilbert_basis(): - x_Dm = prod([ F.gen(i)**(m*n) for i,n in enumerate(fan.rays()) ]) - image.append(x_Dm) - patch._embedding_center = tuple( f(list(point)) for f in image ) + image = [prod([F.gen(i)**(m * n) + for i, n in enumerate(fan.rays())]) + for m in dualcone.Hilbert_basis()] + patch._embedding_center = tuple(f(list(point)) for f in image) return patch def _best_affine_patch(self, point): @@ -487,7 +486,7 @@ def neighborhood(self, point): phi_reduced = [S(t) for t in phi] patch._embedding_center = patch(point_preimage) - patch._embedding_morphism = patch.hom(phi_reduced,self) + patch._embedding_morphism = patch.hom(phi_reduced, self) return patch def dimension(self): @@ -513,7 +512,7 @@ def dimension(self): if '_dimension' in self.__dict__: return self._dimension npatches = self.ambient_space().fan().ngenerating_cones() - dims = [ self.affine_patch(i).dimension() for i in range(0,npatches) ] + dims = [self.affine_patch(i).dimension() for i in range(0, npatches)] self._dimension = max(dims) return self._dimension @@ -582,7 +581,7 @@ def is_smooth(self, point=None): if '_smooth' in self.__dict__: return self._smooth npatches = self.ambient_space().fan().ngenerating_cones() - self._smooth = all(self.affine_patch(i).is_smooth() for i in range(0,npatches)) + self._smooth = all(self.affine_patch(i).is_smooth() for i in range(0, npatches)) return self._smooth def is_nondegenerate(self): From 629c6f778e76e27199b669e5d30c23555827f525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 5 Sep 2024 14:13:40 +0200 Subject: [PATCH 132/193] fix some range(0,?) --- src/sage/schemes/toric/toric_subscheme.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/toric/toric_subscheme.py b/src/sage/schemes/toric/toric_subscheme.py index ca8ec2278ac..908ef979984 100755 --- a/src/sage/schemes/toric/toric_subscheme.py +++ b/src/sage/schemes/toric/toric_subscheme.py @@ -335,7 +335,7 @@ def pullback_polynomial(p): f'The polynomial {p} does not define a ZZ-divisor!' m_coeffs = dualcone.Hilbert_coefficients(m) result += coefficient * prod(R.gen(i)**m_coeffs[i] - for i in range(0, R.ngens())) + for i in range(R.ngens())) return result # construct the affine algebraic scheme to use as patch @@ -353,7 +353,7 @@ def pullback_polynomial(p): if cone.is_smooth(): x = ambient.coordinate_ring().gens() phi = [] - for i in range(0, fan.nrays()): + for i in range(fan.nrays()): if i in cone.ambient_ray_indices(): phi.append(pullback_polynomial(x[i])) else: @@ -512,7 +512,7 @@ def dimension(self): if '_dimension' in self.__dict__: return self._dimension npatches = self.ambient_space().fan().ngenerating_cones() - dims = [self.affine_patch(i).dimension() for i in range(0, npatches)] + dims = [self.affine_patch(i).dimension() for i in range(npatches)] self._dimension = max(dims) return self._dimension @@ -581,7 +581,8 @@ def is_smooth(self, point=None): if '_smooth' in self.__dict__: return self._smooth npatches = self.ambient_space().fan().ngenerating_cones() - self._smooth = all(self.affine_patch(i).is_smooth() for i in range(0, npatches)) + self._smooth = all(self.affine_patch(i).is_smooth() + for i in range(npatches)) return self._smooth def is_nondegenerate(self): @@ -691,7 +692,7 @@ def restrict(cone): enumerate(SR.subs(divide).gens())]) return ideal, Jac_patch + SR_patch - for dim in range(0, fan.dim() + 1): + for dim in range(fan.dim() + 1): for cone in fan(dim): ideal1, ideal2 = restrict(cone) if ideal1.is_zero() or ideal2.dimension() != -1: From d91d631b1bdcd70de65da9e00e402515c932119c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 5 Sep 2024 17:34:50 +0200 Subject: [PATCH 133/193] fix more suggested range(0,?) --- src/sage/schemes/toric/ideal.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index 1d9a100c0b5..93a28beafe8 100755 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -354,8 +354,8 @@ def _naive_ideal(self, ring): x = ring.gens() binomials = [] for row in self.ker().matrix().rows(): - xpos = prod(x[i]**max(row[i], 0) for i in range(0, len(x))) - xneg = prod(x[i]**max(-row[i], 0) for i in range(0, len(x))) + xpos = prod(x[i]**max(row[i], 0) for i in range(len(x))) + xneg = prod(x[i]**max(-row[i], 0) for i in range(len(x))) binomials.append(xpos - xneg) return ring.ideal(binomials) @@ -445,6 +445,6 @@ def _ideal_HostenSturmfels(self): J = self._naive_ideal(ring) if J.is_zero(): return J - for i in range(0, self.nvariables()): + for i in range(self.nvariables()): J = self._ideal_quotient_by_variable(ring, J, i) return J From 9f97aef390fb5873bfda02dad109aa95060b53e2 Mon Sep 17 00:00:00 2001 From: Vincent Macri Date: Thu, 5 Sep 2024 11:17:18 -0600 Subject: [PATCH 134/193] Formatting --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3d69721d33b..9bd62047f12 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -46,8 +46,8 @@ body: - **OS**: Ubuntu 20.04 - Sage Version: 9.2 value: | - - **OS**: - - **Sage Version**: + - **OS**: + - **Sage Version**: render: markdown validations: required: true From 19a3c33565c8f7e8a8263fc4e71d8c1ead905a4b Mon Sep 17 00:00:00 2001 From: Vincent Macri Date: Thu, 5 Sep 2024 11:18:51 -0600 Subject: [PATCH 135/193] Report formatting --- .github/ISSUE_TEMPLATE/failure_building_from_source.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/failure_building_from_source.yml b/.github/ISSUE_TEMPLATE/failure_building_from_source.yml index 2575f5c9a10..59becde362e 100644 --- a/.github/ISSUE_TEMPLATE/failure_building_from_source.yml +++ b/.github/ISSUE_TEMPLATE/failure_building_from_source.yml @@ -17,9 +17,8 @@ body: - **OS**: Ubuntu 20.04 - Sage Version: 9.2 value: | - - **OS**: + - **OS**: - **Sage Version**: - render: markdown validations: required: true - type: textarea From 65461610db8ffd9a4fc24a71e50ebd416e0e2cb5 Mon Sep 17 00:00:00 2001 From: Vincent Macri Date: Thu, 5 Sep 2024 11:25:40 -0600 Subject: [PATCH 136/193] Formatting --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9bd62047f12..c15714b3e22 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -48,7 +48,6 @@ body: value: | - **OS**: - **Sage Version**: - render: markdown validations: required: true - type: checkboxes From db24a9cb1b7f631c4e9a762e288ee3d3070f4720 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Thu, 5 Sep 2024 12:17:31 -0700 Subject: [PATCH 137/193] cleanup code --- src/doc/common/static/custom-tabs.css | 50 --------------------------- src/sage_docbuild/conf.py | 10 ------ 2 files changed, 60 deletions(-) diff --git a/src/doc/common/static/custom-tabs.css b/src/doc/common/static/custom-tabs.css index 64decc394fa..b38f14b1e3d 100644 --- a/src/doc/common/static/custom-tabs.css +++ b/src/doc/common/static/custom-tabs.css @@ -11,53 +11,3 @@ p.with-sage-tab { margin-bottom:0.25rem } - -/* -p.with-sage-tab { - --sage-tab-width: 2.5em; - --python-tab-width: 0em; - --sage-live-tab-width: 0em; - margin-bottom:-3em; - max-width:calc(100% - var(--sage-tab-width) - var(--python-tab-width) - var(--sage-live-tab-width)); -} - -p.with-python-tab { - --python-tab-width: calc(3.5em + var(--tabs--margin-x)); -} - -p.with-sage-live-tab { - --sage-live-tab-width: calc(5em + var(--tabs--margin-x)); -} - -p.example.with-sage-tab { - margin-bottom:-1em; -} - -.tab-set { - flex-direction: row-reverse; -} - -.tab-set > label { - margin-left:0; - margin-right: var(--tabs--margin-x); -} - -.tab-set > label:last-of-type { - margin-right: 0; -} - -.tab-set > input:nth-of-type(1), -.tab-set > label:nth-of-type(1) { - order:32; -} - -.tab-set > input:nth-of-type(2), -.tab-set > label:nth-of-type(2) { - order:16; -} - -.tab-set > input:nth-of-type(3), -.tab-set > label:nth-of-type(3) { - order:8; -} -*/ diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 841279f6b39..500347dcc90 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -952,16 +952,6 @@ class SagecodeTransform(SphinxTransform): default_priority = 170 def apply(self): - for node in self.document.findall(nodes.paragraph): - if isinstance(node.children[0], nodes.Text) and node.children[0].astext().strip() in ['EXAMPLE:', 'EXAMPLES:']: - text = node.children[0].astext() - parent = node.parent - index = parent.index(node) - parent.remove(node) - para = nodes.paragraph(classes=["example"]) - para += nodes.Text(text) - parent.insert(index, para) - if self.app.builder.tags.has('html') or self.app.builder.tags.has('inventory'): for node in self.document.findall(nodes.literal_block): if node.get('language') is None and node.astext().startswith('sage:'): From b4710aca9a04c62b07c117e2a516e03593465f9b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 18:09:38 -0700 Subject: [PATCH 138/193] build/pkgs/primecount: Update to 7.14 --- build/pkgs/primecount/checksums.ini | 4 ++-- build/pkgs/primecount/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/primecount/checksums.ini b/build/pkgs/primecount/checksums.ini index c3f0b9ce77d..4e9965b9965 100644 --- a/build/pkgs/primecount/checksums.ini +++ b/build/pkgs/primecount/checksums.ini @@ -1,4 +1,4 @@ tarball=primecount-VERSION.tar.gz -sha1=3854ef6c7f454086f31aa80d68f628c5b685d702 -sha256=e9a1fa2c41b9a7b84f2bead21b53cc9f7e2a5a0a34ddd818431a4e789aa44230 +sha1=dac5db8fb6aadd8b96fcb190073becd420a2cc31 +sha256=d867ac18cc52c0f7014682169988a76f39e4cd56f8ce78fb56e064499b1d66bb upstream_url=https://github.com/kimwalisch/primecount/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primecount/package-version.txt b/build/pkgs/primecount/package-version.txt index 38abeb202c0..9ad4d4f295e 100644 --- a/build/pkgs/primecount/package-version.txt +++ b/build/pkgs/primecount/package-version.txt @@ -1 +1 @@ -7.6 +7.14 From fdf55bc3f793489d7f3d480e36356e1572a824b1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 18:09:52 -0700 Subject: [PATCH 139/193] build/pkgs/primesieve: Update to 12.4 --- build/pkgs/primesieve/checksums.ini | 4 ++-- build/pkgs/primesieve/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/primesieve/checksums.ini b/build/pkgs/primesieve/checksums.ini index faae4fa7982..5bfc159aae0 100644 --- a/build/pkgs/primesieve/checksums.ini +++ b/build/pkgs/primesieve/checksums.ini @@ -1,4 +1,4 @@ tarball=primesieve-VERSION.tar.gz -sha1=cb0a7c49b37b51980fc610d3041b9591c67a460c -sha256=b29a7ec855764ce7474d00be03e1d83209bd097faa3778382dfb73a06866097e +sha1=19941abdd52bc44a8714ead8da4af0a7ddd92ec5 +sha256=eb7081adebe8030e93b3675c74ac603438d10a36792246b274c79f11d8a987ce upstream_url=https://github.com/kimwalisch/primesieve/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primesieve/package-version.txt b/build/pkgs/primesieve/package-version.txt index 2dbc24b32d3..800fd35ee86 100644 --- a/build/pkgs/primesieve/package-version.txt +++ b/build/pkgs/primesieve/package-version.txt @@ -1 +1 @@ -11.0 +12.4 From a2b24882d872e1696a7ea0f709f14990b98ed133 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 18:15:47 -0700 Subject: [PATCH 140/193] build/pkgs/nauty: Update to 2.8.9 --- build/pkgs/nauty/checksums.ini | 4 ++-- build/pkgs/nauty/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/nauty/checksums.ini b/build/pkgs/nauty/checksums.ini index 9663569c7c4..41ff91d3186 100644 --- a/build/pkgs/nauty/checksums.ini +++ b/build/pkgs/nauty/checksums.ini @@ -1,4 +1,4 @@ tarball=nauty${VERSION}.tar.gz -sha1=672e9fc9dfd07201af37ee65807a9b493331ed92 -sha256=159d2156810a6bb240410cd61eb641add85088d9f15c888cdaa37b8681f929ce +sha1=23504eeae95a1a8a9abfd47029b4ff9da886471f +sha256=c97ab42bf48796a86a598bce3e9269047ca2b32c14fc23e07208a244fe52c4ee upstream_url=https://pallini.di.uniroma1.it/nauty${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}.tar.gz diff --git a/build/pkgs/nauty/package-version.txt b/build/pkgs/nauty/package-version.txt index b8635c72de3..d578041c4b8 100644 --- a/build/pkgs/nauty/package-version.txt +++ b/build/pkgs/nauty/package-version.txt @@ -1 +1 @@ -2.8.8.p0 +2.8.9 From ff2ed7fb907e785de08bc3b8b41d01e218962d72 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 20:29:29 -0700 Subject: [PATCH 141/193] build/pkgs/nauty/spkg-install.in: Now uses libtool; use --enable-static --disable-shared --- build/pkgs/nauty/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/nauty/spkg-install.in b/build/pkgs/nauty/spkg-install.in index a2557c3cd8e..409026d7d5a 100644 --- a/build/pkgs/nauty/spkg-install.in +++ b/build/pkgs/nauty/spkg-install.in @@ -8,7 +8,7 @@ fi # Nauty doesn't have an install target; passing a prefix to configure is # useless (but harmless) -sdh_configure CC="$CC -fPIC" $NAUTY_CONFIGURE +sdh_configure CC="$CC -fPIC" $NAUTY_CONFIGURE --enable-static --disable-shared sdh_make # No install target so we resort to manual copy From 96a7a393ba131c0d86cc8ae8a157033a08ff457e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 20:58:33 -0700 Subject: [PATCH 142/193] build/pkgs/nauty/spkg-install.in: New executable 'uniqg' --- build/pkgs/nauty/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/nauty/spkg-install.in b/build/pkgs/nauty/spkg-install.in index 409026d7d5a..8f83cf3b841 100644 --- a/build/pkgs/nauty/spkg-install.in +++ b/build/pkgs/nauty/spkg-install.in @@ -18,7 +18,7 @@ countg countneg cubhamg deledgeg delptg dimacs2g directg dreadnaut dretodot dretog edgetransg genbg genbgL geng gengL genposetg genquarticg genrang genspecialg gentourng gentreeg genktreeg hamheuristic labelg linegraphg listg multig nbrhoodg newedgeg pickg planarg productg ranlabg ransubg shortg showg -subdivideg twohamg underlyingg vcolg watercluster2 NRswitchg" +subdivideg twohamg underlyingg uniqg vcolg watercluster2 NRswitchg" sdh_install $PROGRAMS "$SAGE_LOCAL/bin" sdh_install nauty.h "$SAGE_LOCAL/include/nauty" From 0da99b04674922f401ce0e1ce35e27b11124aedb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Aug 2024 22:25:04 -0700 Subject: [PATCH 143/193] src/sage/graphs/generators/families.py: Relax a doctest for nauty 2.8.9 --- src/sage/graphs/generators/families.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index e6e5312b8a8..aa10d15d4d8 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -3710,7 +3710,7 @@ def nauty_gentreeg(options='', debug=False): ... ValueError: wrong format of parameter options sage: list(graphs.nauty_gentreeg("3 -x", debug=True)) - ['>E Usage: ...gentreeg [-D#] [-Z#:#] [-ulps] [-q] n [res/mod] ... + ['>E Usage: ...gentreeg [-D#] [-Z#:#] [-ulps] [-q] n... [res/mod] ... sage: list(graphs.nauty_gentreeg("3", debug=True)) ['>A ...gentreeg ...\n', Graph on 3 vertices] """ From fec9419ea1151999d9ffbc4c1205d478d230b1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 6 Sep 2024 10:18:15 +1200 Subject: [PATCH 144/193] more py3.9 corrections --- src/sage_docbuild/ext/sage_autodoc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index d874ba0b59c..7351a475331 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1578,7 +1578,7 @@ def can_document_member( cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: return isinstance(member, type) or ( - isattr and isinstance(member, NewType | TypeVar)) + isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) def import_object(self, raiseerror: bool = False) -> bool: ret = super().import_object(raiseerror) @@ -1651,7 +1651,7 @@ def import_object(self, raiseerror: bool = False) -> bool: # ------------------------------------------------------------------- else: self.doc_as_attr = True - if isinstance(self.object, NewType | TypeVar): + if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): modname = getattr(self.object, '__module__', self.modname) if modname != self.modname and self.modname.startswith(modname): bases = self.modname[len(modname):].strip('.').split('.') @@ -1660,7 +1660,7 @@ def import_object(self, raiseerror: bool = False) -> bool: return ret def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]: - if isinstance(self.object, NewType | TypeVar): + if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): # Suppress signature return None, None, None @@ -1845,14 +1845,14 @@ def add_directive_header(self, sig: str) -> None: self.directivetype = 'attribute' super().add_directive_header(sig) - if isinstance(self.object, NewType | TypeVar): + if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): return if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(' :final:', sourcename) canonical_fullname = self.get_canonical_fullname() - if (not self.doc_as_attr and not isinstance(self.object, NewType) + if (not self.doc_as_attr and not inspect.isNewType(self.object) and canonical_fullname and self.fullname != canonical_fullname): self.add_line(' :canonical: %s' % canonical_fullname, sourcename) @@ -1989,7 +1989,7 @@ def get_variable_comment(self) -> list[str] | None: return None def add_content(self, more_content: StringList | None) -> None: - if isinstance(self.object, NewType): + if inspect.isNewType(self.object): if self.config.autodoc_typehints_format == "short": supertype = restify(self.object.__supertype__, "smart") else: From 66e01fa58af3a087fde985879cd2c39a1bfaad10 Mon Sep 17 00:00:00 2001 From: Aram Dermenjian Date: Thu, 5 Sep 2024 16:11:44 -0700 Subject: [PATCH 145/193] Remove extra blank line --- src/sage_docbuild/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 30d3f97279a..2038a30ccef 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -1053,7 +1053,6 @@ def apply(self): prev_node['classes'].append('with-sage-live-tab') - class Ignore(SphinxDirective): has_content = True From 34937183c0f29295456d7a54c67eeb2465a75002 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 23 Aug 2024 18:39:11 -0700 Subject: [PATCH 146/193] build/pkgs/python3: Update to 3.12.5 --- build/pkgs/python3/checksums.ini | 4 ++-- build/pkgs/python3/package-version.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 73dddf24722..3bac47d26a6 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,4 +1,4 @@ tarball=Python-VERSION.tar.xz -sha1=c221421f3ba734daaf013dbdc7b48aa725cea18e -sha256=f6d419a6d8743ab26700801b4908d26d97e8b986e14f95de31b32de2b0e79554 +sha1=d9b83c17a717e1cbd3ab6bd14cfe3e508e6d87b2 +sha256=fa8a2e12c5e620b09f53e65bcd87550d2e5a1e2e04bf8ba991dcc55113876397 upstream_url=https://www.python.org/ftp/python/VERSION/Python-VERSION.tar.xz diff --git a/build/pkgs/python3/package-version.txt b/build/pkgs/python3/package-version.txt index 455808f8e19..d9506ceba51 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.12.4 +3.12.5 From f003cc9d22a05fa467548450d15c0d6cbec1f242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Fri, 6 Sep 2024 12:33:38 +1200 Subject: [PATCH 147/193] py3.9 compatibility for zip --- src/sage_docbuild/ext/sage_autodoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 7351a475331..0cc04e776d8 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -671,7 +671,7 @@ def add_content(self, more_content: StringList | None) -> None: # add additional content (e.g. from document), if present if more_content: - for line, src in zip(more_content.data, more_content.items, strict=True): + for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]: @@ -1042,7 +1042,7 @@ def add_content(self, more_content: StringList | None) -> None: super().add_content(None) self.indent = old_indent if more_content: - for line, src in zip(more_content.data, more_content.items, strict=True): + for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) @classmethod From e3021364b1bfe8da490766dc431d1b294c66706f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 5 Sep 2024 20:35:28 -0700 Subject: [PATCH 148/193] src/sage_docbuild/conf.py: Remove outdated setting of sphinx_source_suffix --- src/sage_docbuild/conf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 0f0333338e3..be1b479fb66 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -187,9 +187,6 @@ def sphinx_plot(graphics, **kwds): # Add any paths that contain templates here, relative to this directory. templates_path = [os.path.join(SAGE_DOC_SRC, 'common', 'templates'), 'templates'] -# The suffix of source filenames. -source_suffix = '.rst' - # The master toctree document. master_doc = 'index' From ea94761bf9e1fb2ff7495930bfb309fd444ee82b Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Fri, 6 Sep 2024 15:56:19 +0800 Subject: [PATCH 149/193] Require weaker Sphinx dependency for sagelib --- src/setup.cfg.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup.cfg.m4 b/src/setup.cfg.m4 index 76aa08f8833..969793209c8 100644 --- a/src/setup.cfg.m4 +++ b/src/setup.cfg.m4 @@ -33,7 +33,7 @@ dnl From Makefile.in: SAGERUNTIME SPKG_INSTALL_REQUIRES_ipython SPKG_INSTALL_REQUIRES_pexpect dnl From Makefile.in: DOC_DEPENDENCIES - SPKG_INSTALL_REQUIRES_sphinx + sphinx >=5.2, <9 SPKG_INSTALL_REQUIRES_networkx SPKG_INSTALL_REQUIRES_scipy SPKG_INSTALL_REQUIRES_sympy From d5bd34e2df1f75c7c857ee1423bd659ae0586ea6 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Tue, 9 Jul 2024 11:43:49 +0200 Subject: [PATCH 150/193] Implement algorithm 2.2 from BS2007 for EllipticCurve_with_prime_order() --- src/doc/en/reference/references/index.rst | 3 + src/sage/schemes/elliptic_curves/all.py | 2 +- .../elliptic_curves/ell_finite_field.py | 114 ++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index d7fe85422ba..35bc023c2db 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1326,6 +1326,9 @@ REFERENCES: no. 1 (2003): 97-111, http://www.moi.math.bas.bg/moiuser/~iliya/pdf_site/gf5srev.pdf. +.. [BS2007] \R. Bröker and P. Stevenhagen. *Constructing elliptic curves of + prime order*. [math.NT] (2007), :arXiv:`0712.2022`. + .. [BS2010] \P. Baseilhac and K. Shigechi. *A new current algebra and the reflection equation*. Lett. Math. Phys. **92** (2010), pp. 47-65. :arxiv:`0906.1482`. diff --git a/src/sage/schemes/elliptic_curves/all.py b/src/sage/schemes/elliptic_curves/all.py index 84f7b0d5a50..82df6f45a96 100644 --- a/src/sage/schemes/elliptic_curves/all.py +++ b/src/sage/schemes/elliptic_curves/all.py @@ -27,7 +27,7 @@ lazy_import('sage.schemes.elliptic_curves.jacobian', 'Jacobian') lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'special_supersingular_curve') - +lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'EllipticCurve_with_prime_order') lazy_import('sage.schemes.elliptic_curves.ell_rational_field', ['cremona_curves', 'cremona_optimal_curves']) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index fcd23e7243f..e81011494b7 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -15,6 +15,8 @@ - Lorenz Panny, John Cremona (2023-02): ``.twists()`` - Lorenz Panny (2023): ``special_supersingular_curve()`` + +- Martin Grenouilloux (2024): ``EllipticCurve_with_prime_order()`` """ # **************************************************************************** @@ -2786,3 +2788,115 @@ def find_q(m, m4_fac, D): yield Et except ValueError: pass + +def EllipticCurve_with_prime_order(N): + r""" + Given a prime number ``N``, find another prime number `p` and construct an + elliptic curve `E` defined over `\mathbb F_p` such that + `\#E(\mathbb F_p) = N`. + + INPUT: + + - ``N`` -- integer; the order for which we seek an elliptic curve. Must be a + prime number. + + OUTPUT: an elliptic curve `E/\mathbb F_p` of order ``N`` + + EXAMPLES:: + + sage: N = next_prime(int(b'sagemath'.hex(), 16)) + sage: E = EllipticCurve_with_prime_order(N) + sage: E + Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + + 1841350074072114366 over Finite Field of size 8314040074357871443 + sage: E.order() == N + True + + The execution time largely depends on the input:: + + sage: N = 125577861263605878504082476745517446213 + sage: E = EllipticCurve_with_prime_order(N) # Takes ~1 second. + sage: E.order() == N + True + + TESTS:: + + sage: N = next_prime(123456789) + sage: E = EllipticCurve_with_prime_order(N) + sage: E.order() == N + True + + sage: for N in prime_range(3, 100): + ....: E = EllipticCurve_with_prime_order(N) + ....: assert E.order() == N + + sage: N = 123456789 + sage: E = EllipticCurve_with_prime_order(N) + Traceback (most recent call last): + ... + ValueError: input order is not prime + + sage: E = EllipticCurve_with_prime_order(0) + Traceback (most recent call last): + ... + ValueError: input order is not prime + + ..NOTE:: + + Depending on the input, this function may run forever. + + ALGORITHM: [BS2007]_, Algorithm 2.2 + """ + from sage.arith.misc import is_prime, next_prime + from sage.combinat.subset import powerset + from sage.functions.other import floor + from sage.misc.functional import symbolic_prod as product + from sage.quadratic_forms.binary_qf import BinaryQF + from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial + + if not is_prime(N): + raise ValueError("input order is not prime") + + # The algorithm consists of multiple ‘search rounds’ for a suitable + # discriminant `D`, `r` defines the number of rounds. We expect this + # algorithm to terminate after a number of rounds that is polynomial in + # loglog N. + r = 0 + S = [] + # First prime. Iterating over the primes by chunks of size log(`N`). + p = next_prime(0) + + while True: + while p < (r + 1) * floor(N.log()): + S.append(p) + p = next_prime(p) + r += 1 + + # Every possible products of distinct elements of `S`. + # There probably is a more optimal way to compute all possible products + # of elements of S than using a powerset. Here many multiplications are + # done multiple times. + for e in powerset(S): + D = product(e) + if -D % 8 != 5: + continue + + Q = BinaryQF([1, 0, D]) + sol = Q.solve_integer(4 * N) + if sol is None: + continue + + x, _ = sol + p1 = N + 1 - x + p2 = N + 1 + x + for p_i in [p1, p2]: + if is_prime(p_i): + H = hilbert_class_polynomial(-D) + for j0, _ in H.roots(ring=GF(p_i)): + E = EllipticCurve(j=j0) + if E.order() == N: + return E + else: + for Et in E.twists(): + if Et.order() == N: + return Et From 1767c5da1abfb71f35a988afcc04adae0a5681e0 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Tue, 9 Jul 2024 11:48:10 +0200 Subject: [PATCH 151/193] Changed $ sign for backticks in EllipticCurve_with_order() docstring --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index e81011494b7..624efe45ecf 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2725,8 +2725,8 @@ def EllipticCurve_with_order(m, *, D=None): sage: all(E.order() == 21 for E in Es) True - Indeed, we can verify that this is correct. Hasse's bounds tell us that $p \leq 50$ - (approximately), and the rest can be checked via bruteforce:: + Indeed, we can verify that this is correct. Hasse's bounds tell us that + `p \leq 50` (approximately), and the rest can be checked via bruteforce:: sage: for p in prime_range(50): ....: for j in range(p): From f26730ba9986b3cd77f9b385056be8d46ded8ba6 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Tue, 9 Jul 2024 12:26:07 +0200 Subject: [PATCH 152/193] Docstring "NOTE::" block format --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 624efe45ecf..94005b9bcb2 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2841,7 +2841,7 @@ def EllipticCurve_with_prime_order(N): ... ValueError: input order is not prime - ..NOTE:: + .. NOTE:: Depending on the input, this function may run forever. From 1d8a783fbcc5f88be265a16af21706c3c5ea8888 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Wed, 10 Jul 2024 11:12:06 +0200 Subject: [PATCH 153/193] Fixed to odd primes iteration and optimized their generation using prime_range() --- .../elliptic_curves/ell_finite_field.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 94005b9bcb2..650209d2f4f 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2821,14 +2821,19 @@ def EllipticCurve_with_prime_order(N): TESTS:: - sage: N = next_prime(123456789) + sage: for N in prime_range(3, 100): + ....: E = EllipticCurve_with_prime_order(N) + ....: assert E.order() == N + + sage: N = 15175980689839334471 sage: E = EllipticCurve_with_prime_order(N) sage: E.order() == N True - sage: for N in prime_range(3, 100): - ....: E = EllipticCurve_with_prime_order(N) - ....: assert E.order() == N + sage: N = next_prime(123456789) + sage: E = EllipticCurve_with_prime_order(N) + sage: E.order() == N + True sage: N = 123456789 sage: E = EllipticCurve_with_prime_order(N) @@ -2841,17 +2846,23 @@ def EllipticCurve_with_prime_order(N): ... ValueError: input order is not prime + sage: E = EllipticCurve_with_prime_order(-7) + Traceback (most recent call last): + ... + ValueError: input order is not prime + .. NOTE:: Depending on the input, this function may run forever. ALGORITHM: [BS2007]_, Algorithm 2.2 """ - from sage.arith.misc import is_prime, next_prime + from sage.arith.misc import is_prime from sage.combinat.subset import powerset - from sage.functions.other import floor + from sage.functions.other import ceil from sage.misc.functional import symbolic_prod as product from sage.quadratic_forms.binary_qf import BinaryQF + from sage.rings.fast_arith import prime_range from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial if not is_prime(N): @@ -2862,15 +2873,10 @@ def EllipticCurve_with_prime_order(N): # algorithm to terminate after a number of rounds that is polynomial in # loglog N. r = 0 - S = [] - # First prime. Iterating over the primes by chunks of size log(`N`). - p = next_prime(0) while True: - while p < (r + 1) * floor(N.log()): - S.append(p) - p = next_prime(p) - r += 1 + # Iterating over the odd primes by chunks of size log(`N`). + S = prime_range(3, ceil((r + 1) * N.log())) # Every possible products of distinct elements of `S`. # There probably is a more optimal way to compute all possible products @@ -2900,3 +2906,7 @@ def EllipticCurve_with_prime_order(N): for Et in E.twists(): if Et.order() == N: return Et + + # At this point, no discriminant has been found, moving to next round + # and extending the prime list. + r += 1 From dd08f6c384925ce5dbbe907568502314c2f559f1 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Wed, 10 Jul 2024 21:02:37 +0200 Subject: [PATCH 154/193] Better wording for NOTE block in docstring --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 650209d2f4f..5778d90c108 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2853,7 +2853,11 @@ def EllipticCurve_with_prime_order(N): .. NOTE:: - Depending on the input, this function may run forever. + Depending on the input, this function may run for a *very* long time. + This algorithm consists of multiple "search rounds" for a suitable + discriminant `D`. We expect this algorithm to terminate after a number + of rounds that is polynomial in `loglog N`. In practice (cf. Section 5), + this number is usually 1. ALGORITHM: [BS2007]_, Algorithm 2.2 """ From 95be4d80f5d45fa56847cf8ec1df268d5c7f9091 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 11 Jul 2024 10:29:37 +0200 Subject: [PATCH 155/193] Dynamic and precise bounds for the prime table at each round --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 5778d90c108..c7d6ce1c9b0 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2877,10 +2877,13 @@ def EllipticCurve_with_prime_order(N): # algorithm to terminate after a number of rounds that is polynomial in # loglog N. r = 0 + prime_start = 3 + prime_end = ceil((r + 1) * N.log()) + S = [] while True: # Iterating over the odd primes by chunks of size log(`N`). - S = prime_range(3, ceil((r + 1) * N.log())) + S.extend(prime_range(prime_start, prime_end)) # Every possible products of distinct elements of `S`. # There probably is a more optimal way to compute all possible products @@ -2914,3 +2917,6 @@ def EllipticCurve_with_prime_order(N): # At this point, no discriminant has been found, moving to next round # and extending the prime list. r += 1 + + prime_start = prime_end + prime_end = ceil((r + 1) * N.log()) From c84d6e661c9c7e11f3ba55d23af9befd8d003713 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 11 Jul 2024 17:48:48 +0200 Subject: [PATCH 156/193] Support python native int type --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index c7d6ce1c9b0..28cbd302631 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2864,7 +2864,7 @@ def EllipticCurve_with_prime_order(N): from sage.arith.misc import is_prime from sage.combinat.subset import powerset from sage.functions.other import ceil - from sage.misc.functional import symbolic_prod as product + from sage.misc.functional import symbolic_prod as product, log from sage.quadratic_forms.binary_qf import BinaryQF from sage.rings.fast_arith import prime_range from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial @@ -2878,7 +2878,7 @@ def EllipticCurve_with_prime_order(N): # loglog N. r = 0 prime_start = 3 - prime_end = ceil((r + 1) * N.log()) + prime_end = ceil((r + 1) * log(N)) S = [] while True: @@ -2919,4 +2919,4 @@ def EllipticCurve_with_prime_order(N): r += 1 prime_start = prime_end - prime_end = ceil((r + 1) * N.log()) + prime_end = ceil((r + 1) * log(N)) From 4e78e2cacaaa59f866a3f8e8e0e1577f3aac1cf3 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 11 Jul 2024 20:09:36 +0200 Subject: [PATCH 157/193] Improved latex formatting in NOTE docstring --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 28cbd302631..7e31389ce80 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2856,8 +2856,8 @@ def EllipticCurve_with_prime_order(N): Depending on the input, this function may run for a *very* long time. This algorithm consists of multiple "search rounds" for a suitable discriminant `D`. We expect this algorithm to terminate after a number - of rounds that is polynomial in `loglog N`. In practice (cf. Section 5), - this number is usually 1. + of rounds that is polynomial in `\log\log N`. In practice (cf. Section + 5), this number is usually 1. ALGORITHM: [BS2007]_, Algorithm 2.2 """ From f2b9164d8a89ab5c808b0f6f85d7cd056a6c12ed Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Thu, 15 Aug 2024 17:01:57 +0200 Subject: [PATCH 158/193] Blazingly fast optimization by filtering primes with kronecker symbol --- .../elliptic_curves/ell_finite_field.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 7e31389ce80..776b56ffa22 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2812,10 +2812,17 @@ def EllipticCurve_with_prime_order(N): sage: E.order() == N True - The execution time largely depends on the input:: + It works for large primes:: - sage: N = 125577861263605878504082476745517446213 - sage: E = EllipticCurve_with_prime_order(N) # Takes ~1 second. + sage: N = 0x6cbc824032974516623e732462f4b74b56c4ffbd984380d9 + sage: E = EllipticCurve_with_prime_order(N) + sage: E.order() == N + True + + But the execution time largely depends on the input:: + + sage: N = 200396817641911230625970463749415493753 + sage: E = EllipticCurve_with_prime_order(N) sage: E.order() == N True @@ -2861,7 +2868,7 @@ def EllipticCurve_with_prime_order(N): ALGORITHM: [BS2007]_, Algorithm 2.2 """ - from sage.arith.misc import is_prime + from sage.arith.misc import is_prime, kronecker from sage.combinat.subset import powerset from sage.functions.other import ceil from sage.misc.functional import symbolic_prod as product, log @@ -2883,7 +2890,8 @@ def EllipticCurve_with_prime_order(N): while True: # Iterating over the odd primes by chunks of size log(`N`). - S.extend(prime_range(prime_start, prime_end)) + S.extend(p for p in prime_range(prime_start, prime_end) + if kronecker(N, p) == 1) # Every possible products of distinct elements of `S`. # There probably is a more optimal way to compute all possible products From 2e37e99e43ccd18ee6101f3a41ed3bc2a7930474 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Wed, 4 Sep 2024 15:22:04 +0200 Subject: [PATCH 159/193] Perfect compliance with algorithm from paper --- .../elliptic_curves/ell_finite_field.py | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 776b56ffa22..8afb1ddc840 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2868,7 +2868,7 @@ def EllipticCurve_with_prime_order(N): ALGORITHM: [BS2007]_, Algorithm 2.2 """ - from sage.arith.misc import is_prime, kronecker + from sage.arith.misc import is_prime, legendre_symbol from sage.combinat.subset import powerset from sage.functions.other import ceil from sage.misc.functional import symbolic_prod as product, log @@ -2879,7 +2879,7 @@ def EllipticCurve_with_prime_order(N): if not is_prime(N): raise ValueError("input order is not prime") - # The algorithm consists of multiple ‘search rounds’ for a suitable + # The algorithm consists of multiple "search rounds" for a suitable # discriminant `D`, `r` defines the number of rounds. We expect this # algorithm to terminate after a number of rounds that is polynomial in # loglog N. @@ -2890,8 +2890,10 @@ def EllipticCurve_with_prime_order(N): while True: # Iterating over the odd primes by chunks of size log(`N`). - S.extend(p for p in prime_range(prime_start, prime_end) - if kronecker(N, p) == 1) + for p in prime_range(prime_start, prime_end): + if legendre_symbol(N, p) == 1: + # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. + S.append(-p if p >> 1 & 1 else p) # Every possible products of distinct elements of `S`. # There probably is a more optimal way to compute all possible products @@ -2899,32 +2901,31 @@ def EllipticCurve_with_prime_order(N): # done multiple times. for e in powerset(S): D = product(e) - if -D % 8 != 5: + if D % 8 != 5 or D >= 0 or D >= prime_start^2: continue - Q = BinaryQF([1, 0, D]) - sol = Q.solve_integer(4 * N) + Q = BinaryQF([1, 0, -D]) + sol = Q.solve_integer(4 * N, algorithm='cornacchia') if sol is None: continue x, _ = sol - p1 = N + 1 - x - p2 = N + 1 + x - for p_i in [p1, p2]: + for p_i in [N + 1 - x, N + 1 + x]: if is_prime(p_i): - H = hilbert_class_polynomial(-D) - for j0, _ in H.roots(ring=GF(p_i)): + H = hilbert_class_polynomial(D) + for j0 in H.roots(ring=GF(p_i), multiplicities=False): E = EllipticCurve(j=j0) - if E.order() == N: - return E - else: - for Et in E.twists(): - if Et.order() == N: - return Et + # `E.twists()` also contains E. + for Et in E.twists(): + if Et.order() == N: + return Et # At this point, no discriminant has been found, moving to next round # and extending the prime list. r += 1 - prime_start = prime_end + # For small `N`, the value `(r+1)log(N)` can be less than 3. When that's + # the case, we don't need to worry about prime duplicates in `S` since + # it will be empty. + prime_start = max(3, prime_end) prime_end = ceil((r + 1) * log(N)) From 9085cc91a0fbdae6e57cdcd0f4e9ab4fbc9df131 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Fri, 6 Sep 2024 13:39:12 +0200 Subject: [PATCH 160/193] Changing to iterator output --- .../elliptic_curves/ell_finite_field.py | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8afb1ddc840..b85336ff843 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2800,60 +2800,71 @@ def EllipticCurve_with_prime_order(N): - ``N`` -- integer; the order for which we seek an elliptic curve. Must be a prime number. - OUTPUT: an elliptic curve `E/\mathbb F_p` of order ``N`` + OUTPUT: an iterator of elliptic curves `E/\mathbb F_p` of order ``N`` EXAMPLES:: sage: N = next_prime(int(b'sagemath'.hex(), 16)) - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) sage: E Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + 1841350074072114366 over Finite Field of size 8314040074357871443 sage: E.order() == N True + You can directly iterate over the function call; here only on the first 10 + curves:: + + sage: N = 54675917 + sage: for _, E in zip(range(10), EllipticCurve_with_prime_order(N)): + ....: assert E.order() == N + It works for large primes:: sage: N = 0x6cbc824032974516623e732462f4b74b56c4ffbd984380d9 - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) sage: E.order() == N True But the execution time largely depends on the input:: sage: N = 200396817641911230625970463749415493753 - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) sage: E.order() == N True TESTS:: sage: for N in prime_range(3, 100): - ....: E = EllipticCurve_with_prime_order(N) + ....: E = next(EllipticCurve_with_prime_order(N)) + ....: assert E.order() == N + + sage: N = 113 + sage: for _, E in zip(range(30), EllipticCurve_with_prime_order(N)): ....: assert E.order() == N sage: N = 15175980689839334471 - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) sage: E.order() == N True sage: N = next_prime(123456789) - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) sage: E.order() == N True sage: N = 123456789 - sage: E = EllipticCurve_with_prime_order(N) + sage: E = next(EllipticCurve_with_prime_order(N)) Traceback (most recent call last): ... ValueError: input order is not prime - sage: E = EllipticCurve_with_prime_order(0) + sage: E = next(EllipticCurve_with_prime_order(0)) Traceback (most recent call last): ... ValueError: input order is not prime - sage: E = EllipticCurve_with_prime_order(-7) + sage: E = next(EllipticCurve_with_prime_order(-7)) Traceback (most recent call last): ... ValueError: input order is not prime @@ -2901,7 +2912,7 @@ def EllipticCurve_with_prime_order(N): # done multiple times. for e in powerset(S): D = product(e) - if D % 8 != 5 or D >= 0 or D >= prime_start^2: + if D % 8 != 5 or D >= 0: continue Q = BinaryQF([1, 0, -D]) @@ -2918,7 +2929,7 @@ def EllipticCurve_with_prime_order(N): # `E.twists()` also contains E. for Et in E.twists(): if Et.order() == N: - return Et + yield Et # At this point, no discriminant has been found, moving to next round # and extending the prime list. From 4fcf282c32854ee7f4d44aa0f4bf20eea868422e Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 7 Sep 2024 00:23:01 +0200 Subject: [PATCH 161/193] Fix hypellfrob.pyx calling Python inside sig_on set_ntl_matrix_modn_dense uses isinstance so there is a chance for Python garbage collection to run. Presumably you get the SystemError: calling remove_from_pari_stack() inside sig_on() if that happens. Fixes #33304 --- src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx index 4fa3280f6f9..2b14032ffc9 100755 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx @@ -127,8 +127,10 @@ def interval_products(M0, M1, target): cdef long dim = M0.nrows() sig_on() c.restore_c() + sig_off() set_ntl_matrix_modn_dense(mm0, c, M0) set_ntl_matrix_modn_dense(mm1, c, M1) + sig_on() for t in target: targ.push_back(ntl_ZZ(t).x) numintervals = len(target)/2 From 0d26f3044a2856a84d7e7cae3986f9b6fbc471d1 Mon Sep 17 00:00:00 2001 From: Cyril Bouvier Date: Sat, 7 Sep 2024 12:00:19 +0200 Subject: [PATCH 162/193] graphs: slice decomposition: replace doctest for __hash__ --- .../graph_decompositions/slice_decomposition.pyx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx index 6e6104cd9ae..39a85485dfa 100644 --- a/src/sage/graphs/graph_decompositions/slice_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/slice_decomposition.pyx @@ -459,14 +459,10 @@ cdef class SliceDecomposition(SageObject): TESTS:: sage: P3 = graphs.PathGraph(3) - sage: hash(P3.slice_decomposition(initial_vertex=0)) - -7313201005658437102 - sage: hash(P3.slice_decomposition(initial_vertex=2)) - 1181676064626878036 - sage: hash(graphs.CompleteGraph(3).slice_decomposition()) - 6162668211142297415 - sage: hash(Graph([(0,1), (0,2)]).slice_decomposition()) - 2898184589667302557 + sage: SD1 = P3.slice_decomposition(initial_vertex=0) + sage: SD2 = P3.slice_decomposition(initial_vertex=2) + sage: len({SD1: 1, SD2: 2}) # indirect doctest + 2 """ return hash((tuple(self.sigma_inv.items()), tuple(self.lex_label.items()), From 94f80bb8ed5d9a0e9bec288617d084eda60cba4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 7 Sep 2024 18:49:53 +0200 Subject: [PATCH 163/193] fixing most ruff PERF4 warnings in combinat --- src/sage/combinat/composition_tableau.py | 5 +--- src/sage/combinat/diagram.py | 9 +++---- src/sage/combinat/diagram_algebras.py | 10 ++++---- src/sage/combinat/dlx.py | 5 +--- src/sage/combinat/finite_state_machine.py | 6 ++--- src/sage/combinat/free_dendriform_algebra.py | 10 ++++---- src/sage/combinat/free_prelie_algebra.py | 10 ++++---- src/sage/combinat/gelfand_tsetlin_patterns.py | 24 +++++++------------ src/sage/combinat/graph_path.py | 6 +---- src/sage/combinat/integer_vector.py | 10 ++++---- src/sage/combinat/knutson_tao_puzzles.py | 9 +++---- src/sage/combinat/misc.py | 8 ++----- src/sage/combinat/parallelogram_polyomino.py | 14 ++++------- src/sage/combinat/partition_algebra.py | 13 ++++------ src/sage/combinat/ribbon_shaped_tableau.py | 11 ++++----- src/sage/combinat/rsk.py | 6 ++--- src/sage/combinat/shifted_primed_tableau.py | 8 ++++--- src/sage/combinat/skew_partition.py | 21 +++++++--------- src/sage/combinat/skew_tableau.py | 23 ++++++++---------- src/sage/combinat/specht_module.py | 18 +++++--------- src/sage/combinat/symmetric_group_algebra.py | 12 ++++------ src/sage/combinat/tableau.py | 22 ++++++++--------- src/sage/combinat/tableau_tuple.py | 11 ++++----- 23 files changed, 103 insertions(+), 168 deletions(-) diff --git a/src/sage/combinat/composition_tableau.py b/src/sage/combinat/composition_tableau.py index e90fe55f6a5..1e05da84c4e 100644 --- a/src/sage/combinat/composition_tableau.py +++ b/src/sage/combinat/composition_tableau.py @@ -211,10 +211,7 @@ def descent_set(self): sage: CompositionTableau([[1],[3,2],[4,4]]).descent_set() [1, 3] """ - cols = {} - for row in self: - for col, i in enumerate(row): - cols[i] = col + cols = {i: col for row in self for col, i in enumerate(row)} return sorted(i for i in cols if i + 1 in cols and cols[i + 1] >= cols[i]) def descent_composition(self): diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index f5b8f9ad864..cc3c1dfe0b6 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -336,12 +336,9 @@ def _latex_(self): lr = r'\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}' - array = [] - for i in range(self._n_rows): - row = [] - for j in range(self._n_cols): - row.append("\\phantom{x}" if (i, j) in self else None) - array.append(row) + array = [[("\\phantom{x}" if (i, j) in self else None) + for j in range(self._n_cols)] + for i in range(self._n_rows)] def end_line(r): # give the line ending to row ``r`` diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 364a55c74c9..6410a4a93ea 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -3013,7 +3013,7 @@ def jucys_murphy_element(self, i): sage: L = [P.L(i/2) for i in range(1,2*k+1)] sage: all(x.dual() == x for x in L) True - sage: all(x * y == y * x for x in L for y in L) # long time + sage: all(x * y == y * x for x, y in Subsets(L, 2)) # long time True sage: Lsum = sum(L) sage: gens = [P.s(i) for i in range(1,k)] @@ -3045,13 +3045,13 @@ def jucys_murphy_element(self, i): The same tests for a half integer partition algebra:: - sage: k = 9/2 + sage: k = 7/2 sage: R. = QQ[] sage: P = PartitionAlgebra(k, n) sage: L = [P.L(i/2) for i in range(1,2*k+1)] sage: all(x.dual() == x for x in L) True - sage: all(x * y == y * x for x in L for y in L) # long time + sage: all(x * y == y * x for x, y in Subsets(L, 2)) # long time True sage: Lsum = sum(L) sage: gens = [P.s(i) for i in range(1,k-1/2)] @@ -5847,11 +5847,9 @@ def to_Brauer_partition(l, k=None): True """ L = to_set_partition(l, k=k) - L2 = [] paired = [] not_paired = [] - for i in L: - L2.append(list(i)) + L2 = (list(i) for i in L) for i in L2: if len(i) > 2: raise ValueError("blocks must have size at most 2, but {} has {}".format(i, len(i))) diff --git a/src/sage/combinat/dlx.py b/src/sage/combinat/dlx.py index 383dda056be..d25f7414d0d 100644 --- a/src/sage/combinat/dlx.py +++ b/src/sage/combinat/dlx.py @@ -484,10 +484,7 @@ def AllExactCovers(M): ones = [] r = 1 # damn 1-indexing for R in M.rows(): - row = [] - for i in range(len(R)): - if R[i]: - row.append(i + 1) # damn 1-indexing + row = [i for i, Ri in enumerate(R, start=1) if Ri] ones.append([r, row]) r += 1 for s in DLXMatrix(ones): diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 96d35859873..2750ea15e9c 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -9437,9 +9437,9 @@ def graph(self, edge_labels='words_in_out'): transitions = state.transitions if not transitions: isolated_vertices.append(state.label()) - for t in transitions: - graph_data.append((t.from_state.label(), t.to_state.label(), - label_fct(t))) + graph_data.extend((t.from_state.label(), t.to_state.label(), + label_fct(t)) + for t in transitions) G = DiGraph(graph_data, multiedges=True, loops=True) G.add_vertices(isolated_vertices) diff --git a/src/sage/combinat/free_dendriform_algebra.py b/src/sage/combinat/free_dendriform_algebra.py index 2acfee1b337..4be2c514778 100644 --- a/src/sage/combinat/free_dendriform_algebra.py +++ b/src/sage/combinat/free_dendriform_algebra.py @@ -939,14 +939,12 @@ def merge(self, other): return self ret = list(self.vars) cur_vars = set(ret) - for v in other.vars: - if v not in cur_vars: - ret.append(v) + ret.extend(v for v in other.vars if v not in cur_vars) return DendriformFunctor(Alphabet(ret)) - else: - return None - def _repr_(self): + return None + + def _repr_(self) -> str: """ TESTS:: diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index 1ea7d3274a1..6e7525d8b23 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -1023,14 +1023,12 @@ def merge(self, other): return self ret = list(self.vars) cur_vars = set(ret) - for v in other.vars: - if v not in cur_vars: - ret.append(v) + ret.extend(v for v in other.vars if v not in cur_vars) return PreLieFunctor(Alphabet(ret)) - else: - return None - def _repr_(self): + return None + + def _repr_(self) -> str: """ TESTS:: diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index be4e236b099..5c3c485e549 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -302,11 +302,9 @@ def boxed_entries(self) -> tuple: sage: G.boxed_entries() ((1, 0),) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] == self[i - 1][j]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij == self[i - 1][j]] return tuple(ret) @cached_method @@ -324,11 +322,9 @@ def circled_entries(self) -> tuple: sage: G.circled_entries() ((1, 1), (2, 0)) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] == self[i - 1][j + 1]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij == self[i - 1][j + 1]] return tuple(ret) @cached_method @@ -349,11 +345,9 @@ def special_entries(self) -> tuple: sage: G.special_entries() ((2, 0),) """ - ret = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i-1][j] > self[i][j] and self[i][j] > self[i-1][j+1]: - ret.append((i, j)) + ret = [(i, j) for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if self[i - 1][j] > selfij > self[i - 1][j + 1]] return tuple(ret) def number_of_boxes(self) -> int: diff --git a/src/sage/combinat/graph_path.py b/src/sage/combinat/graph_path.py index 2fb255579dd..df84b1acdf7 100644 --- a/src/sage/combinat/graph_path.py +++ b/src/sage/combinat/graph_path.py @@ -236,11 +236,7 @@ def paths_from_source_to_target(self, source, target): [[2, 3, 4], [2, 4]] """ source_paths = self.outgoing_paths(source) - paths = [] - for path in source_paths: - if path[-1] == target: - paths.append(path) - return paths + return [path for path in source_paths if path[-1] == target] def paths(self): """ diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 516d2114ffc..6848609cf5d 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -1174,19 +1174,17 @@ def _list_rec(self, n, k): EXAMPLES:: sage: IV = IntegerVectors(2,3) - sage: IV._list_rec(2,3) + sage: list(IV._list_rec(2,3)) [(2, 0, 0), (1, 1, 0), (1, 0, 1), (0, 2, 0), (0, 1, 1), (0, 0, 2)] """ - res = [] - if k == 1: - return [(n, )] + yield (n,) + return for nbar in range(n + 1): n_diff = n - nbar for rest in self._list_rec(nbar, k - 1): - res.append((n_diff,) + rest) - return res + yield (n_diff,) + rest def __iter__(self): """ diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index e1e4f746f42..f7a7c513c52 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -2064,12 +2064,9 @@ def _fill_piece(self, nw_label, ne_label, pieces) -> list[PuzzlePiece]: sage: ps._fill_piece('0', '0', ps._bottom_deltas) [0/0\0] """ - output = [] - for piece in pieces: - if (piece['north_west'] == nw_label and - piece['north_east'] == ne_label): - output.append(piece) - return output + return [piece for piece in pieces + if (piece['north_west'] == nw_label and + piece['north_east'] == ne_label)] @cached_method def _fill_strip(self, nw_labels, ne_label, pieces, final_pieces=None): diff --git a/src/sage/combinat/misc.py b/src/sage/combinat/misc.py index 404c032a8dc..d04b615be80 100644 --- a/src/sage/combinat/misc.py +++ b/src/sage/combinat/misc.py @@ -202,11 +202,7 @@ def _monomial_exponent_to_lower_factorial(me, x): sage: _monomial_exponent_to_lower_factorial(([2,2,2]),a) x^2*y^2*z^2 - x^2*y^2*z - x^2*y*z^2 - x*y^2*z^2 + x^2*y*z + x*y^2*z + x*y*z^2 - x*y*z """ - terms = [] - for i in range(len(me)): - for j in range(me[i]): - terms.append( x[i]-j ) - return prod(terms) + return prod(x[i] - j for i, mei in enumerate(me) for j in range(mei)) def umbral_operation(poly): @@ -235,7 +231,7 @@ def umbral_operation(poly): exponents = poly.exponents() coefficients = poly.coefficients() length = len(exponents) - return sum( [coefficients[i]*_monomial_exponent_to_lower_factorial(exponents[i],x) for i in range(length)] ) + return sum(coefficients[i]*_monomial_exponent_to_lower_factorial(exponents[i], x) for i in range(length)) class IterableFunctionCall: diff --git a/src/sage/combinat/parallelogram_polyomino.py b/src/sage/combinat/parallelogram_polyomino.py index ed9308e82e9..c60c72c10d2 100644 --- a/src/sage/combinat/parallelogram_polyomino.py +++ b/src/sage/combinat/parallelogram_polyomino.py @@ -1974,12 +1974,9 @@ def widths(self) -> list: sage: pp.widths() [] """ - widths = [] uw = self.upper_widths() lw = self.lower_widths() - for i in range(len(lw)): - widths.append(uw[i] - lw[i]) - return widths + return [up - lo for up, lo in zip(uw, lw)] def degree_convexity(self) -> int: r""" @@ -3387,11 +3384,10 @@ def get_BS_nodes(self): sage: pp.set_options(drawing_components=dict(tree=True)) sage: view(pp) # not tested """ - result = [] - for h in range(1, self.height()): - result.append(self._get_node_position_at_row(h)) - for w in range(1, self.width()): - result.append(self._get_node_position_at_column(w)) + result = [self._get_node_position_at_row(h) + for h in range(1, self.height())] + result.extend(self._get_node_position_at_column(w) + for w in range(1, self.width())) return result def get_right_BS_nodes(self): diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index f0d15115ac9..91e79be4cbc 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -364,9 +364,7 @@ def __iter__(self): True """ for p in Permutations(self.k): - res = [] - for i in range(self.k): - res.append(Set([i + 1, -p[i]])) + res = [Set([i, -pi]) for i, pi in enumerate(p, start=1)] yield self.element_class(self, res) @@ -433,10 +431,7 @@ def __iter__(self): {{1, -3}, {2, -2}, {4, -4}, {3, -1}}] """ for p in Permutations(self.k): - res = [] - for i in range(self.k): - res.append(Set([i + 1, -p[i]])) - + res = [Set([i, -pi]) for i, pi in enumerate(p, start=1)] res.append(Set([self.k + 1, -self.k - 1])) yield self.element_class(self, res) @@ -1941,8 +1936,8 @@ def to_set_partition(l, k=None): to_be_added -= spart sp.append(spart) - for singleton in to_be_added: - sp.append(Set([singleton])) + sp.extend(Set([singleton]) + for singleton in to_be_added) return Set(sp) diff --git a/src/sage/combinat/ribbon_shaped_tableau.py b/src/sage/combinat/ribbon_shaped_tableau.py index d33cc115396..dd7218c899c 100644 --- a/src/sage/combinat/ribbon_shaped_tableau.py +++ b/src/sage/combinat/ribbon_shaped_tableau.py @@ -350,18 +350,17 @@ def from_permutation(self, p): [[1, 2], [3]], [[1], [2], [3]]] """ - if p == []: + if not p: return self.element_class(self, []) comp = p.descents() - if comp == []: + if not comp: return self.element_class(self, [p[:]]) - r = [] - r.append([p[j] for j in range(comp[0])]) - for i in range(len(comp) - 1): - r.append([p[j] for j in range(comp[i], comp[i + 1])]) + r = [[p[j] for j in range(comp[0])]] + r.extend([p[j] for j in range(comp[i], comp[i + 1])] + for i in range(len(comp) - 1)) r.append([p[j] for j in range(comp[-1], len(p))]) r.reverse() return self.element_class(self, r) diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index 5e276d5ebcf..1fa0ba62b4e 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -2957,12 +2957,10 @@ def _backward_format_output(self, obj1, obj2, output): if j == 0: df.append([]) if j > 0 and obj1[j] < obj1[j-1]: - for _ in range(obj1[j-1]-obj1[j]): - df.append([]) + df.extend([] for _ in range(obj1[j-1]-obj1[j])) df[-1].append(obj2[j]) if obj1: - for a in range(obj1[-1]-1): - df.append([]) + df.extend([] for a in range(obj1[-1]-1)) # If biword is empty, return a decreasing factorization with 1 factor else: df.append([]) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 37bbd402c14..6d889425d28 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -2716,9 +2716,11 @@ def _add_strip(sub_tab, full_tab, length): if sub_tab and len(sub_tab) < len(full_tab): plat_list.append(min(sub_tab[-1] + primed_strip[-2] - 1, full_tab[len(sub_tab)])) - for row in reversed(range(1, len(sub_tab))): - plat_list.append(min(sub_tab[row-1]+primed_strip[row-1]-1, full_tab[row]) - - sub_tab[row] - primed_strip[row]) + plat_list.extend( + min(sub_tab[row-1] + primed_strip[row-1] - 1, full_tab[row]) + - sub_tab[row] - primed_strip[row] + for row in reversed(range(1, len(sub_tab)))) + if sub_tab: plat_list.append(full_tab[0] - sub_tab[0] - primed_strip[0]) else: diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index bee333dc9a5..aad38f9afbf 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1032,12 +1032,9 @@ def cells(self): """ outer = self.outer() inner = self.inner()[:] - inner += [0]*(len(outer)-len(inner)) - res = [] - for i in range(len(outer)): - for j in range(inner[i], outer[i]): - res.append( (i,j) ) - return res + inner += [0] * (len(outer) - len(inner)) + return [(i, j) for i, outi in enumerate(outer) + for j in range(inner[i], outi)] def to_list(self): """ @@ -1148,15 +1145,13 @@ def rows_intersection_set(self): sage: skp.rows_intersection_set() == cells True """ - res = [] outer = self.outer() inner = self.inner() - inner += [0] * int(len(outer)-len(inner)) + inner += [0] * (len(outer) - len(inner)) - for i in range(len(outer)): - for j in range(outer[i]): - if outer[i] != inner[i]: - res.append((i,j)) + res = [(i, j) for i, outi in enumerate(outer) + for j in range(outi) + if outi != inner[i]] return Set(res) def columns_intersection_set(self): @@ -1238,7 +1233,7 @@ def jacobi_trudi(self): h = SymmetricFunctions(QQ).homogeneous() H = MatrixSpace(h, nn) - q = q + [0]*int(nn-len(q)) + q = q + [0] * (nn - len(q)) m = [] for i in range(1,nn+1): row = [] diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index ca57594ed71..44189013d5c 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1172,10 +1172,10 @@ def row_stabilizer(self): # tableau, by including the identity permutation on the set [1..k]. k = self.size() gens = [list(range(1, k + 1))] - for row in self: - for j in range(len(row) - 1): - if row[j] is not None: - gens.append((row[j], row[j + 1])) + gens.extend((row[j], row[j + 1]) + for row in self + for j in range(len(row) - 1) + if row[j] is not None) return PermutationGroup(gens) def column_stabilizer(self): @@ -1777,12 +1777,10 @@ def cells(self): sage: s.cells() [(0, 1), (0, 2), (1, 0), (2, 0)] """ - res = [] - for i in range(len(self)): - for j in range(len(self[i])): - if self[i][j] is not None: - res.append((i, j)) - return res + return [(i, j) + for i, selfi in enumerate(self) + for j in range(len(selfi)) + if selfi[j] is not None] def cells_containing(self, i): r""" @@ -1950,12 +1948,11 @@ def from_expr(self, expr): sage: SkewTableaux().from_expr([[1,1],[[5],[3,4],[1,2]]]) [[None, 1, 2], [None, 3, 4], [5]] """ - skp = [] outer = expr[1] inner = expr[0] + [0] * (len(outer) - len(expr[0])) - for i in range(len(outer)): - skp.append([None] * (inner[i]) + outer[-(i + 1)]) + skp = [[None] * (inner[i]) + outer[-(i + 1)] + for i in range(len(outer))] return self.element_class(self, skp) diff --git a/src/sage/combinat/specht_module.py b/src/sage/combinat/specht_module.py index 0114a8f191b..1af53083f89 100644 --- a/src/sage/combinat/specht_module.py +++ b/src/sage/combinat/specht_module.py @@ -1146,18 +1146,12 @@ def _to_diagram(D): if isinstance(D, Diagram): return D if D in _Partitions: - D = _Partitions(D).cells() - elif D in SkewPartitions(): - D = SkewPartitions()(D).cells() - elif D in IntegerVectors(): - cells = [] - for i, row in enumerate(D): - for j in range(row): - cells.append((i, j)) - D = cells - else: - D = [tuple(cell) for cell in D] - return D + return _Partitions(D).cells() + if D in SkewPartitions(): + return SkewPartitions()(D).cells() + if D in IntegerVectors(): + return [(i, j) for i, row in enumerate(D) for j in range(row)] + return [tuple(cell) for cell in D] def specht_module_spanning_set(D, SGA=None): diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index b9ea545cd60..47820fc23a5 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -1043,10 +1043,8 @@ def central_orthogonal_idempotents(self): - :meth:`central_orthogonal_idempotent` """ - out = [] - for key in sorted(self._blocks_dictionary, reverse=True): - out.append(self.central_orthogonal_idempotent(key)) - return out + return [self.central_orthogonal_idempotent(key) + for key in sorted(self._blocks_dictionary, reverse=True)] def central_orthogonal_idempotent(self, la, block=True): r""" @@ -2016,9 +2014,9 @@ def seminormal_basis(self, mult='l2r'): basis = [] for part in Partitions_n(self.n): stp = StandardTableaux_shape(part) - for t1 in stp: - for t2 in stp: - basis.append(self.epsilon_ik(t1, t2, mult=mult)) + basis.extend(self.epsilon_ik(t1, t2, mult=mult) + for t1 in stp + for t2 in stp) return basis def dft(self, form=None, mult='l2r'): diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 19f9033fcea..80e3391b279 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1148,12 +1148,10 @@ def descents(self): sage: Tableau( [[1,2,3],[4,5]] ).descents() [(1, 0), (1, 1)] """ - descents = [] - for i in range(1, len(self)): - for j in range(len(self[i])): - if self[i][j] > self[i-1][j]: - descents.append((i, j)) - return descents + return [(i, j) + for i in range(1, len(self)) + for j, selfij in enumerate(self[i]) + if selfij > self[i-1][j]] def major_index(self): """ @@ -1214,15 +1212,15 @@ def inversions(self): for j, entry in enumerate(row): # c is in position (i,j) # find the d that satisfy condition 1 - for k in range(j+1, len(row)): - if entry > row[k]: - inversions.append(((i, j), (i, k))) + inversions.extend(((i, j), (i, k)) + for k in range(j + 1, len(row)) + if entry > row[k]) # find the d that satisfy condition 2 if i == 0: continue - for k in range(j): - if entry > previous_row[k]: - inversions.append(((i, j), (i-1, k))) + inversions.extend(((i, j), (i - 1, k)) + for k in range(j) + if entry > previous_row[k]) previous_row = row return inversions diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index a24ac5ca809..fd847c4ad1e 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -1084,10 +1084,8 @@ def row_stabilizer(self): # tableau, by including the identity permutation on the set [1..n]. n = max(self.entries()) gens = [list(range(1, n + 1))] - for t in self: - for i in range(len(t)): - for j in range(len(t[i]) - 1): - gens.append((t[i][j], t[i][j + 1])) + gens.extend((ti[j], ti[j + 1]) for t in self + for ti in t for j in range(len(ti) - 1)) return PermutationGroup(gens) def column_stabilizer(self): @@ -2644,11 +2642,10 @@ def an_element(self): ([[1, 2]], [], []) """ if self.size() == 0: - return self.element_class(self, [[] for _ in range(self.level())]) + return self.element_class(self, [[]] * self.level()) tab = [[list(range(1, self.size() + 1))]] - for _ in range(self.level() - 1): - tab.append([]) + tab.extend([] for _ in range(self.level() - 1)) return self.element_class(self, tab) From 7b7de1f7541e18e22b82aeff0daa5712ce6dc1ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 7 Sep 2024 19:54:50 +0200 Subject: [PATCH 164/193] fix something in quadratic forms --- .../quadratic_form__local_field_invariants.py | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py index fb869fc7551..aa2478af088 100644 --- a/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py +++ b/src/sage/quadratic_forms/quadratic_form__local_field_invariants.py @@ -17,7 +17,7 @@ # **************************************************************************** ########################################################################### -# TO DO: Add routines for hasse invariants at all places, anisotropic +# TO DO: Add routines for Hasse invariants at all places, anisotropic # places, is_semi_definite, and support for number fields. ########################################################################### @@ -165,7 +165,19 @@ def rational_diagonal_form(self, return_matrix=False): sage: T[0,0] = 13 Traceback (most recent call last): ... - ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). + ValueError: matrix is immutable; please change a copy instead + (i.e., use copy(M) to change a copy of M). + + Test for a singular form:: + + sage: m = matrix(GF(11), [[1,5,0,0], [5,1,9,0], [0,9,1,5], [0,0,5,1]]) + sage: qf = QuadraticForm(m) + sage: Q, T = qf.rational_diagonal_form(return_matrix=True) + sage: T + [ 1 6 5 10] + [ 0 1 10 9] + [ 0 0 1 2] + [ 0 0 0 1] """ Q, T = self._rational_diagonal_form_and_transformation() T.set_immutable() @@ -173,19 +185,17 @@ def rational_diagonal_form(self, return_matrix=False): # Quadratic forms do not support immutability, so we need to make # a copy to be safe. Q = deepcopy(Q) - - if return_matrix: - return Q, T - else: - return Q + return (Q, T) if return_matrix else Q @cached_method def _rational_diagonal_form_and_transformation(self): """ Return a diagonal form equivalent to the given quadratic from and - the corresponding transformation matrix. This is over the fraction - field of the base ring of the given quadratic form. + the corresponding transformation matrix. + + This is over the fraction field of the base ring of the given + quadratic form. OUTPUT: a tuple `(D,T)` where @@ -238,13 +248,13 @@ def _rational_diagonal_form_and_transformation(self): D = MS() for i in range(n): D[i, i] = R[i, i] - Q = Q.parent()(D) + newQ = Q.parent()(D) # Transformation matrix (inverted) T = MS(R.sage()) for i in range(n): T[i, i] = K.one() try: - return Q, ~T + return newQ, ~T except ZeroDivisionError: # Singular case is not fully supported by PARI pass @@ -276,7 +286,8 @@ def _rational_diagonal_form_and_transformation(self): temp = MS(1) for j in range(i + 1, n): if Q[i, j] != 0: - temp[i, j] = -Q[i, j] / (Q[i, i] * 2) # This should only occur when Q[i,i] != 0, which the above step guarantees. + temp[i, j] = -Q[i, j] / (Q[i, i] * 2) + # This should only occur when Q[i,i] != 0, which the above step guarantees. Q = Q(temp) T = T * temp From 50dced7f1a5090e1b1616d7013c3e0c854c3cac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Sep 2024 10:19:21 +0200 Subject: [PATCH 165/193] interface to new nauty generator for Hasse diagrams --- src/sage/graphs/digraph_generators.py | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 02590ac8f7c..081235de342 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -28,6 +28,7 @@ :meth:`~DiGraphGenerators.ImaseItoh` | Return the digraph of Imase and Itoh of order `n` and degree `d`. :meth:`~DiGraphGenerators.Kautz` | Return the Kautz digraph of degree `d` and diameter `D`. :meth:`~DiGraphGenerators.nauty_directg` | Return an iterator yielding digraphs using nauty's ``directg`` program. + :meth:`~DiGraphGenerators.nauty_posetg` | Return an iterator yielding Hasse diagrams of posets using nauty's ``genposetg`` program. :meth:`~DiGraphGenerators.Paley` | Return a Paley digraph on `q` vertices. :meth:`~DiGraphGenerators.Path` | Return a directed path on `n` vertices. :meth:`~DiGraphGenerators.RandomDirectedAcyclicGraph` | Return a random (weighted) directed acyclic graph of order `n`. @@ -758,6 +759,64 @@ def nauty_directg(self, graphs, options='', debug=False): if line and line[0] == '&': yield DiGraph(line[1:], format='dig6') + def nauty_posetg(self, options='', debug=False): + r""" + Return a generator which creates all posets using ``nauty``. + + Here a poset is seen through its Hasse diagram, which is + an acyclic and transitively reduced digraph. + + INPUT: + + - ``options`` -- string (default: ``""``); a string passed to + ``genposetg`` as if it was run at a system command line. + At a minimum, you *must* pass the number of vertices you desire + and a choice between ``o`` and ``t``` for the output order. + + - ``debug`` -- boolean (default: ``False``); if ``True`` the first line + of ``genposetg``'s output to standard error is captured and the first + call to the generator's ``next()`` function will return this line as a + string. A line leading with ">A" indicates a successful initiation of + the program with some information on the arguments, while a line + beginning with ">E" indicates an error with the input. + + The possible options, obtained as output of ``genposetg --help``:: + + n: the number of vertices + o: digraph6 output in arbitrary order + t: digraph6 output in topological order + q: supresses statistics except for the final count + + EXAMPLES:: + + sage: gen = digraphs.nauty_posetg("5 o q") + sage: len(list(gen)) + 63 + + This coincides with :oeis:`A000112`. + """ + import shlex + from sage.features.nauty import NautyExecutable + geng_path = NautyExecutable("genposetg").absolute_filename() + sp = subprocess.Popen(shlex.quote(geng_path) + f" {options}", shell=True, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True, + encoding='latin-1') + msg = sp.stderr.readline() + if debug: + yield msg + elif msg.startswith('>E'): + raise ValueError('wrong format of parameter option') + gen = sp.stdout + while True: + try: + s = next(gen) + except StopIteration: + # Exhausted list of graphs from nauty genposetg + return + G = DiGraph(s[1:-1], format='dig6') + yield G + def Complete(self, n, loops=False): r""" Return the complete digraph on `n` vertices. From f6b9fa165f66f402d09ab10670d922a84bd613c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Sep 2024 10:29:39 +0200 Subject: [PATCH 166/193] fix docstring --- src/sage/graphs/digraph_generators.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 081235de342..efe45f2dcbf 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -771,7 +771,7 @@ def nauty_posetg(self, options='', debug=False): - ``options`` -- string (default: ``""``); a string passed to ``genposetg`` as if it was run at a system command line. At a minimum, you *must* pass the number of vertices you desire - and a choice between ``o`` and ``t``` for the output order. + and a choice between ``o`` and ``t``` for the output order. - ``debug`` -- boolean (default: ``False``); if ``True`` the first line of ``genposetg``'s output to standard error is captured and the first @@ -782,10 +782,10 @@ def nauty_posetg(self, options='', debug=False): The possible options, obtained as output of ``genposetg --help``:: - n: the number of vertices - o: digraph6 output in arbitrary order - t: digraph6 output in topological order - q: supresses statistics except for the final count + n: the number of vertices + o: digraph6 output in arbitrary order + t: digraph6 output in topological order + q: supresses statistics except for the final count EXAMPLES:: From a4411905719792fd8a73d204f4b3d1c064e69899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Sep 2024 11:04:32 +0200 Subject: [PATCH 167/193] fix suggestions + cut one long line --- src/sage/graphs/digraph_generators.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index efe45f2dcbf..d3d19959399 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -782,10 +782,9 @@ def nauty_posetg(self, options='', debug=False): The possible options, obtained as output of ``genposetg --help``:: - n: the number of vertices + n: the number of vertices, between 0 and 16 o: digraph6 output in arbitrary order t: digraph6 output in topological order - q: supresses statistics except for the final count EXAMPLES:: @@ -1545,7 +1544,9 @@ def RandomDirectedGNM(self, n, m, loops=False): sage: D.num_verts() 10 sage: D.loops() - [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), (8, 8, None), (9, 9, None)] + [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None), + (4, 4, None), (5, 5, None), (6, 6, None), (7, 7, None), + (8, 8, None), (9, 9, None)] TESTS:: From 43fabb091cc85b0f6f49d3b77a25ae70d210024b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Sep 2024 11:16:56 +0200 Subject: [PATCH 168/193] simplify the doctest --- src/sage/graphs/digraph_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index d3d19959399..d4dfbdaddea 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -788,7 +788,7 @@ def nauty_posetg(self, options='', debug=False): EXAMPLES:: - sage: gen = digraphs.nauty_posetg("5 o q") + sage: gen = digraphs.nauty_posetg("5 o") sage: len(list(gen)) 63 From e94f42f5aae52dd70debc8da4d54fc5a9cee4438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 8 Sep 2024 12:13:49 +0200 Subject: [PATCH 169/193] minor mistake in doc --- src/sage/graphs/digraph_generators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index d4dfbdaddea..cbea708d998 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -771,7 +771,7 @@ def nauty_posetg(self, options='', debug=False): - ``options`` -- string (default: ``""``); a string passed to ``genposetg`` as if it was run at a system command line. At a minimum, you *must* pass the number of vertices you desire - and a choice between ``o`` and ``t``` for the output order. + and a choice between ``o`` and ``t`` for the output order. - ``debug`` -- boolean (default: ``False``); if ``True`` the first line of ``genposetg``'s output to standard error is captured and the first From dda7e9306ee64a41470000bc649b6e1e0af20d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 9 Sep 2024 18:01:56 +0200 Subject: [PATCH 170/193] pathlib in simplicial sets examples --- src/sage/topology/simplicial_set_examples.py | 45 ++++++++++---------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/sage/topology/simplicial_set_examples.py b/src/sage/topology/simplicial_set_examples.py index 498284e9816..2df0d9a1bf0 100644 --- a/src/sage/topology/simplicial_set_examples.py +++ b/src/sage/topology/simplicial_set_examples.py @@ -30,7 +30,7 @@ # **************************************************************************** import re -import os +from pathlib import Path from sage.env import SAGE_ENV from sage.misc.cachefunc import cached_method, cached_function @@ -48,6 +48,8 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.categories.simplicial_sets', 'SimplicialSets') +kenzo_path = Path(SAGE_ENV['SAGE_EXTCODE']) / 'kenzo' + # ###################################################################### # The nerve of a finite monoid, used in sage.categories.finite_monoid. @@ -96,7 +98,7 @@ def __init__(self, monoid): # of monoid elements). Omit the base point. self._simplex_data = () - def __eq__(self, other): + def __eq__(self, other) -> bool: """ Return ``True`` if ``self`` and ``other`` are equal. @@ -119,7 +121,7 @@ def __eq__(self, other): and self._monoid == other._monoid and self.base_point() == other.base_point()) - def __ne__(self, other): + def __ne__(self, other) -> bool: """ Return the negation of `__eq__`. @@ -214,13 +216,13 @@ def n_skeleton(self, n): face_dict[(g,)] = x start = 1 - for d in range(start+1, n+1): + for d in range(start + 1, n + 1): for g in monoid: if g == one: continue new_faces = {} for t in face_dict.keys(): - if len(t) != d-1: + if len(t) != d - 1: continue # chain: chain of group elements to multiply, # as a tuple. @@ -235,8 +237,8 @@ def n_skeleton(self, n): # Compute faces of x. faces = [face_dict[chain[1:]]] - for i in range(d-1): - product = chain[i] * chain[i+1] + for i in range(d - 1): + product = chain[i] * chain[i + 1] if product == one: # Degenerate. if d == 2: @@ -294,11 +296,11 @@ def Sphere(n): w_0 = AbstractSimplex(0, name='w_0') return SimplicialSet_finite({v_0: None, w_0: None}, base_point=v_0, name='S^0') - degens = range(n-2, -1, -1) + degens = range(n - 2, -1, -1) degen_v = v_0.apply_degeneracies(*degens) sigma = AbstractSimplex(n, name='sigma_{}'.format(n), latex_name='\\sigma_{}'.format(n)) - return SimplicialSet_finite({sigma: [degen_v] * (n+1)}, base_point=v_0, + return SimplicialSet_finite({sigma: [degen_v] * (n + 1)}, base_point=v_0, name='S^{}'.format(n), latex_name='S^{{{}}}'.format(n)) @@ -616,22 +618,20 @@ def ComplexProjectiveSpace(n): latex_name='CP^{2}') return K if n == 3: - file = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'CP3.txt') + file = kenzo_path / 'CP3.txt' data = simplicial_data_from_kenzo_output(file) - v = [_ for _ in data.keys() if _.dimension() == 0][0] - K = SimplicialSet_finite(data, base_point=v, name='CP^3', - latex_name='CP^{3}') - return K + v = [sigma for sigma in data if sigma.dimension() == 0][0] + return SimplicialSet_finite(data, base_point=v, name='CP^3', + latex_name='CP^{3}') if n == 4: - file = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'CP4.txt') + file = kenzo_path / 'CP4.txt' data = simplicial_data_from_kenzo_output(file) - v = [_ for _ in data.keys() if _.dimension() == 0][0] - K = SimplicialSet_finite(data, base_point=v, name='CP^4', - latex_name='CP^{4}') - return K + v = [sigma for sigma in data if sigma.dimension() == 0][0] + return SimplicialSet_finite(data, base_point=v, name='CP^4', + latex_name='CP^{4}') -def simplicial_data_from_kenzo_output(filename): +def simplicial_data_from_kenzo_output(filename) -> dict: """ Return data to construct a simplicial set, given Kenzo output. @@ -649,7 +649,8 @@ def simplicial_data_from_kenzo_output(filename): sage: from sage.topology.simplicial_set_examples import simplicial_data_from_kenzo_output sage: from sage.topology.simplicial_set import SimplicialSet - sage: sphere = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'S4.txt') + sage: from pathlib import Path + sage: sphere = Path(SAGE_ENV['SAGE_EXTCODE']) / 'kenzo' /'S4.txt' sage: S4 = SimplicialSet(simplicial_data_from_kenzo_output(sphere)) # needs pyparsing sage: S4.homology(reduced=False) # needs pyparsing {0: Z, 1: 0, 2: 0, 3: 0, 4: Z} @@ -667,7 +668,7 @@ def simplicial_data_from_kenzo_output(filename): dim_idx = data.find('Dimension = {}:'.format(dim), start) while dim_idx != -1: start = dim_idx + len('Dimension = {}:'.format(dim)) - new_dim_idx = data.find('Dimension = {}:'.format(dim+1), start) + new_dim_idx = data.find('Dimension = {}:'.format(dim + 1), start) if new_dim_idx == -1: end = len(data) else: From 36cb2af4441200df4d12110b7eee76f1e5a583c9 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 9 Sep 2024 20:32:05 +0100 Subject: [PATCH 171/193] factor out `has_order` --- src/sage/schemes/elliptic_curves/all.py | 2 +- .../elliptic_curves/ell_finite_field.py | 117 ++++++++++++++---- 2 files changed, 92 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/all.py b/src/sage/schemes/elliptic_curves/all.py index 82df6f45a96..df527315527 100644 --- a/src/sage/schemes/elliptic_curves/all.py +++ b/src/sage/schemes/elliptic_curves/all.py @@ -27,10 +27,10 @@ lazy_import('sage.schemes.elliptic_curves.jacobian', 'Jacobian') lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'special_supersingular_curve') -lazy_import('sage.schemes.elliptic_curves.ell_finite_field', 'EllipticCurve_with_prime_order') lazy_import('sage.schemes.elliptic_curves.ell_rational_field', ['cremona_curves', 'cremona_optimal_curves']) +from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_with_prime_order from sage.schemes.elliptic_curves.cm import (cm_orders, cm_j_invariants, cm_j_invariants_and_orders, diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index b85336ff843..4d4e01a0bff 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1343,6 +1343,87 @@ def is_ordinary(self, proof=True): """ return not is_j_supersingular(self.j_invariant(), proof=proof) + def has_order(self, value, num_checks=8): + r""" + Return ``True`` if the curve has order ``value``. + + INPUT: + + - ``value`` -- integer in the Hasse-Weil range for this curve + + - ``num_checks``-- integer (default: `8`); the number of times to check + whether ``value`` times a random point on this curve equals the + identity + + .. NOTE:: + + Since the method is probabilistic, there is a possibility for the + method to yield false positives (i.e. returning ``True`` even when + the result is ``False``). Even worse, it is possible for this to + happen even when ``num_checks`` is increased arbitrarily. See below + for an example and :issue:`38617` for an open discussion. + + EXAMPLES: + + For curves over small finite fields, the order is computed and compared + directly:: + + sage: E = EllipticCurve(GF(7), [0, 1]) + sage: E.order() + 12 + sage: E.has_order(12, num_checks=0) + True + sage: E.has_order(11, num_checks=0) + False + sage: E.has_order(13, num_checks=0) + False + + This tests the method on a random curve:: + + sage: # long time (10s) + sage: p = random_prime(2**128, lbound=2**127) + sage: K = GF((p, 2), name="a") + sage: E = EllipticCurve(K, [K.random_element() for _ in range(2)]) + sage: N = E.order() + sage: E.has_order(N, num_checks=20) + True + sage: E.has_order(N + 1) + False + + This demonstrates the bug mentioned in the NOTE above. The return value + should be ``False`` after :issue:`38617` is fixed:: + + sage: E = EllipticCurve(GF(127), [0, 1]) + sage: E.order() + 108 + sage: E.has_order(126) + True + + AUTHORS: + + - Mariah Lenox (2011-02-16): Initial implementation + + - Gareth Ma (2024-01-21): Fix bug for small curves + """ + # This method does *not* use the cached value of `_order` even when available + q = self.base_field().order() + a, b = Hasse_bounds(q, 1) + if not a <= value <= b: + return False + + # For really small values, the random tests are too weak to detect wrong orders + # So we go with computing directly instead. + if q <= 100: + return self.order() == value + + # Is value * random == identity? + for _ in range(num_checks): + G = self.random_point() + if not (value * G).is_zero(): + return False + + return True + def set_order(self, value, *, check=True, num_checks=8): r""" Set the value of ``self._order`` to ``value``. @@ -1357,7 +1438,7 @@ def set_order(self, value, *, check=True, num_checks=8): - ``check``-- boolean (default: ``True``); whether or not to run sanity checks on the input - - ``num_checks``-- integer (default: 8); if ``check`` is + - ``num_checks``-- integer (default: `8`); if ``check`` is ``True``, the number of times to check whether ``value`` times a random point on this curve equals the identity @@ -1403,11 +1484,11 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(0) Traceback (most recent call last): ... - ValueError: Value 0 illegal (not an integer in the Hasse range) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 0 sage: E.set_order(1000) Traceback (most recent call last): ... - ValueError: Value 1000 illegal (not an integer in the Hasse range) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 1000 It is also very likely an error to pass a value which is not the actual order of this curve. How unlikely is determined by @@ -1418,7 +1499,7 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(947) Traceback (most recent call last): ... - ValueError: Value 947 illegal (multiple of random point not the identity) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 1009 does not have order 947 For curves over small finite fields, the order is cheap to compute, so it is computed directly and compared:: @@ -1427,7 +1508,7 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(11) Traceback (most recent call last): ... - ValueError: Value 11 illegal (correct order is 12) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 11 TESTS: @@ -1438,7 +1519,7 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(3) Traceback (most recent call last): ... - ValueError: Value 3 illegal (correct order is 1) + ValueError: Elliptic Curve defined by y^2 + y = x^3 + x + 1 over Finite Field of size 2 does not have order 3 :: @@ -1446,7 +1527,7 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.set_order(4, num_checks=0) Traceback (most recent call last): ... - ValueError: Value 4 illegal (correct order is 12) + ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 4 sage: E.order() 12 @@ -1461,24 +1542,8 @@ def set_order(self, value, *, check=True, num_checks=8): """ value = Integer(value) - if check: - # Is value in the Hasse range? - q = self.base_field().order() - a,b = Hasse_bounds(q,1) - if not a <= value <= b: - raise ValueError(f"Value {value} illegal (not an integer in the Hasse range)") - - # For really small values, the random tests are too weak to detect wrong orders - # So we go with computing directly instead. - if q <= 100: - if self.order() != value: - raise ValueError(f"Value {value} illegal (correct order is {self.order()})") - - # Is value*random == identity? - for _ in range(num_checks): - G = self.random_point() - if value * G != self(0): - raise ValueError(f"Value {value} illegal (multiple of random point not the identity)") + if check and not self.has_order(value, num_checks=num_checks): + raise ValueError(f"{self} does not have order {value}") # TODO: It might help some of PARI's algorithms if we # could copy this over to the .pari_curve() as well. @@ -2678,7 +2743,7 @@ def EllipticCurve_with_order(m, *, D=None): Return an iterator for elliptic curves over finite fields with the given order. The curves are computed using the Complex Multiplication (CM) method. - A `:sage:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case + A :sage:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case the algorithm is more efficient. If ``D`` is specified, it is used as the discriminant. From 57cf88b019371c355d920f8118dac322f057f48e Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 9 Sep 2024 21:50:34 +0100 Subject: [PATCH 172/193] add extensive docs and gray code optimisation --- .../elliptic_curves/ell_finite_field.py | 138 ++++++++++++------ 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 4d4e01a0bff..8095c755041 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1416,9 +1416,17 @@ def has_order(self, value, num_checks=8): if q <= 100: return self.order() == value + # This might be slow + # if value.is_prime(): + # num_checks = 1 + # Is value * random == identity? for _ in range(num_checks): - G = self.random_point() + while True: + G = self.random_point() + if not G.is_zero(): + break + if not (value * G).is_zero(): return False @@ -2887,17 +2895,50 @@ def EllipticCurve_with_prime_order(N): It works for large primes:: sage: N = 0x6cbc824032974516623e732462f4b74b56c4ffbd984380d9 - sage: E = next(EllipticCurve_with_prime_order(N)) + sage: E = next(EllipticCurve_with_prime_order(N)); E + Elliptic Curve defined by y^2 = x^3 + 2666207849820848272386538889427721639173508298483739490459*x + + 77986137112576 over Finite Field of size 2666207849820848272386538889427721639173508298487130585243 sage: E.order() == N True - But the execution time largely depends on the input:: + The execution time largely depends on the input, specifically the smallest + discriminant ``D`` for which we can apply CM method on. Here it takes + slightly longer, though still within `1` second:: sage: N = 200396817641911230625970463749415493753 - sage: E = next(EllipticCurve_with_prime_order(N)) + sage: E = next(EllipticCurve_with_prime_order(N)); E sage: E.order() == N True + Note that the iterator does *not* return all curves with the given order:: + + sage: any(E.base_ring() is GF(7) for E in EllipticCurve_with_prime_order(7)) + False + sage: EllipticCurve(GF(7), [0, 5]).order() + 7 + + However, experimentally it returns many of them. Here it returns all of them:: + + sage: N = 23 + sage: curves = list(EllipticCurve_with_prime_order(N)); curves + [Elliptic Curve defined by y^2 = x^3 + 20*x + 20 over Finite Field of size 23, + Elliptic Curve defined by y^2 = x^3 + 10*x + 16 over Finite Field of size 23, + Elliptic Curve defined by y^2 = x^3 + 14*x + 14 over Finite Field of size 17, + Elliptic Curve defined by y^2 = x^3 + 16*x + 25 over Finite Field of size 31, + Elliptic Curve defined by y^2 = x^3 + 13*x + 6 over Finite Field of size 19, + Elliptic Curve defined by y^2 = x^3 + 24*x + 3 over Finite Field of size 29] + sage: import itertools + sage: # These are the only primes, by the Weil-Hasse bound + sage: for q in prime_range(17, 35): + ....: K = GF(q) + ....: for u in itertools.product(range(q), repeat=2): + ....: try: E = EllipticCurve(GF(q), u) + ....: except ArithmeticError: continue + ....: if E.order() == N: + ....: assert any(E.is_isomorphic(E_) for E_ in curves) + + + TESTS:: sage: for N in prime_range(3, 100): @@ -2944,42 +2985,63 @@ def EllipticCurve_with_prime_order(N): ALGORITHM: [BS2007]_, Algorithm 2.2 """ + import itertools + from sage.misc.verbose import verbose + from sage.combinat import gray_codes + from sage.sets.primes import Primes from sage.arith.misc import is_prime, legendre_symbol - from sage.combinat.subset import powerset - from sage.functions.other import ceil - from sage.misc.functional import symbolic_prod as product, log from sage.quadratic_forms.binary_qf import BinaryQF - from sage.rings.fast_arith import prime_range from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial if not is_prime(N): raise ValueError("input order is not prime") - # The algorithm consists of multiple "search rounds" for a suitable - # discriminant `D`, `r` defines the number of rounds. We expect this - # algorithm to terminate after a number of rounds that is polynomial in - # loglog N. - r = 0 - prime_start = 3 - prime_end = ceil((r + 1) * log(N)) + if N < 1000: + # Log how long this algorithm will take + from sage.rings.fast_arith import prime_range + num = sum(1 for p in prime_range(3, 4 * N) if legendre_symbol(N, p) == 1) + verbose(f"Total work to enumerate curves with order {N}: 2^{num}", level=2) + + # The algorithm considers smooth discriminants `D`, sorted by their largest + # prime factor. We expect this algorithm to terminate after a number of + # rounds that is polynomial in loglog N. S = [] - while True: - # Iterating over the odd primes by chunks of size log(`N`). - for p in prime_range(prime_start, prime_end): - if legendre_symbol(N, p) == 1: - # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. - S.append(-p if p >> 1 & 1 else p) - - # Every possible products of distinct elements of `S`. - # There probably is a more optimal way to compute all possible products - # of elements of S than using a powerset. Here many multiplications are - # done multiple times. - for e in powerset(S): - D = product(e) - if D % 8 != 5 or D >= 0: + for p in Primes(): + if p == 2: + continue + + if legendre_symbol(N, p) != 1: + continue + + verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) + + # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. + p_star = -p if p >> 1 & 1 else p + + # later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, we need + # p = |p_star| <= |-D| <= 4N + if p > 4 * N: + break + + # Although this is O(2^n) instead of O(n2^n), it misses out on the optimisation where + # when D becomes too large (see -D > 4 * N below), one can cut the branch entirely + # so perhaps this is slower + D = p_star + for idx, dx in itertools.chain([(None, 1)], gray_codes.product([2] * len(S))): + assert abs(dx) == 1, f"hmm, gray codes shouldn't do that. file a bug report" + + if idx is not None: + if dx == 1: + D *= S[idx] + else: + D //= S[idx] + + if D % 8 != 5 or D >= 0 or -D > 4 * N: continue + verbose(f"Testing {D=}", level=3) + Q = BinaryQF([1, 0, -D]) sol = Q.solve_integer(4 * N, algorithm='cornacchia') if sol is None: @@ -2989,19 +3051,13 @@ def EllipticCurve_with_prime_order(N): for p_i in [N + 1 - x, N + 1 + x]: if is_prime(p_i): H = hilbert_class_polynomial(D) - for j0 in H.roots(ring=GF(p_i), multiplicities=False): - E = EllipticCurve(j=j0) + K = GF(p_i) + for j0 in H.roots(ring=K, multiplicities=False): + E = EllipticCurve(K, j=j0) # `E.twists()` also contains E. for Et in E.twists(): - if Et.order() == N: + # num_checks=1 is sufficient for prime order + if Et.has_order(N, num_checks=1): yield Et - # At this point, no discriminant has been found, moving to next round - # and extending the prime list. - r += 1 - - # For small `N`, the value `(r+1)log(N)` can be less than 3. When that's - # the case, we don't need to worry about prime duplicates in `S` since - # it will be empty. - prime_start = max(3, prime_end) - prime_end = ceil((r + 1) * log(N)) + S.append(p_star) From 57f5d169486831d7213efc4208cae970d919486b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 10 Sep 2024 09:24:12 +1200 Subject: [PATCH 173/193] add try blocks to support both sphinx8 and py3.9 --- src/sage_docbuild/ext/sage_autodoc.py | 47 +++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 0cc04e776d8..87e4e69d7bd 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -33,6 +33,8 @@ - Kwankyu Lee (2024-02-14): rebased on Sphinx 7.2.6 - François Bissey (2024-08-24): rebased on Sphinx 8.0.2 + +- François Bissey (2024-09-10): Tweaks to support python 3.9 (and older sphinx) as well """ from __future__ import annotations @@ -1577,8 +1579,14 @@ def __init__(self, *args: Any) -> None: def can_document_member( cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: - return isinstance(member, type) or ( - isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) + # support both sphinx 8 and py3.9/older sphinx + try: + result_bool = isinstance(member, type) or ( + isattr and isinstance(member, NewType | TypeVar)) + except: + result_bool = isinstance(member, type) or ( + isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) + return result_bool def import_object(self, raiseerror: bool = False) -> bool: ret = super().import_object(raiseerror) @@ -1651,7 +1659,12 @@ def import_object(self, raiseerror: bool = False) -> bool: # ------------------------------------------------------------------- else: self.doc_as_attr = True - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: modname = getattr(self.object, '__module__', self.modname) if modname != self.modname and self.modname.startswith(modname): bases = self.modname[len(modname):].strip('.').split('.') @@ -1660,7 +1673,12 @@ def import_object(self, raiseerror: bool = False) -> bool: return ret def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]: - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: # Suppress signature return None, None, None @@ -1845,14 +1863,24 @@ def add_directive_header(self, sig: str) -> None: self.directivetype = 'attribute' super().add_directive_header(sig) - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + # support both sphinx 8 and py3.9/older sphinx + try: + test_bool = isinstance(self.object, NewType | TypeVar) + except: + test_bool = inspect.isNewType(self.object) or isinstance(self.object, TypeVar) + if test_bool: return if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: self.add_line(' :final:', sourcename) canonical_fullname = self.get_canonical_fullname() - if (not self.doc_as_attr and not inspect.isNewType(self.object) + # support both sphinx 8 and py3.9/older sphinx + try: + newtype_test = isinstance(self.object, NewType) + except: + newtype_test = inspect.isNewType(self.object) + if (not self.doc_as_attr and not newtype_test and canonical_fullname and self.fullname != canonical_fullname): self.add_line(' :canonical: %s' % canonical_fullname, sourcename) @@ -1989,7 +2017,12 @@ def get_variable_comment(self) -> list[str] | None: return None def add_content(self, more_content: StringList | None) -> None: - if inspect.isNewType(self.object): + # support both sphinx 8 and py3.9/older sphinx + try: + newtype_test = isinstance(self.object, NewType) + except: + newtype_test = inspect.isNewType(self.object) + if newtype_test: if self.config.autodoc_typehints_format == "short": supertype = restify(self.object.__supertype__, "smart") else: From bae2c8488b1ea6daf24cbbf5a79d2ad8376797a5 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Mon, 9 Sep 2024 22:49:12 +0100 Subject: [PATCH 174/193] use recursive implementation instead of gray code --- .../elliptic_curves/ell_finite_field.py | 113 +++++++++++++----- 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8095c755041..212093cdca0 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2862,7 +2862,7 @@ def find_q(m, m4_fac, D): except ValueError: pass -def EllipticCurve_with_prime_order(N): +def EllipticCurve_with_prime_order(N, max_D=10**8): r""" Given a prime number ``N``, find another prime number `p` and construct an elliptic curve `E` defined over `\mathbb F_p` such that @@ -2877,15 +2877,24 @@ def EllipticCurve_with_prime_order(N): EXAMPLES:: - sage: N = next_prime(int(b'sagemath'.hex(), 16)) + sage: N = next_prime(int.from_bytes(b'sagemath', 'big')) sage: E = next(EllipticCurve_with_prime_order(N)) sage: E - Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + - 1841350074072114366 over Finite Field of size 8314040074357871443 + Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + 1841350074072114366 + over Finite Field of size 8314040074357871443 sage: E.order() == N True - You can directly iterate over the function call; here only on the first 10 + The returned curves are sometimes random because + :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.twists` is + not deterministic. However, it's always isomorphic:: + + sage: E = next(EllipticCurve_with_prime_order(23)); E # random + Elliptic Curve defined by y^2 = x^3 + 12*x + 11 over Finite Field of size 17 + sage: E.is_isomorphic(EllipticCurve(GF(17), [3, 5])) + True + + You can directly iterate over the iterator; here only on the first 10 curves:: sage: N = 54675917 @@ -2920,13 +2929,12 @@ def EllipticCurve_with_prime_order(N): However, experimentally it returns many of them. Here it returns all of them:: sage: N = 23 + sage: set_random_seed(1337) # as the function returns random twists of curves sage: curves = list(EllipticCurve_with_prime_order(N)); curves - [Elliptic Curve defined by y^2 = x^3 + 20*x + 20 over Finite Field of size 23, - Elliptic Curve defined by y^2 = x^3 + 10*x + 16 over Finite Field of size 23, - Elliptic Curve defined by y^2 = x^3 + 14*x + 14 over Finite Field of size 17, - Elliptic Curve defined by y^2 = x^3 + 16*x + 25 over Finite Field of size 31, - Elliptic Curve defined by y^2 = x^3 + 13*x + 6 over Finite Field of size 19, - Elliptic Curve defined by y^2 = x^3 + 24*x + 3 over Finite Field of size 29] + [Elliptic Curve defined by y^2 = x^3 + 3*x + 5 over Finite Field of size 17, + Elliptic Curve defined by y^2 = x^3 + 19*x + 14 over Finite Field of size 31, + Elliptic Curve defined by y^2 = x^3 + 2*x + 9 over Finite Field of size 19, + Elliptic Curve defined by y^2 = x^3 + 7*x + 18 over Finite Field of size 29] sage: import itertools sage: # These are the only primes, by the Weil-Hasse bound sage: for q in prime_range(17, 35): @@ -2937,7 +2945,43 @@ def EllipticCurve_with_prime_order(N): ....: if E.order() == N: ....: assert any(E.is_isomorphic(E_) for E_ in curves) - + The algorithm is efficient for small ``N`` due to the low number of + suitable discriminants (see the ``recur`` internal function of the code for + details). The following runs in less than a second:: + + sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) + 402 + + There is different verbose data for level `2` and `3`. Note that if you try + to compute the 5th curve in the iterator, the algorithm takes a long time + to run, as it is trying to compute a large Hilbert class polynomial, as + noted in the verbose log:: + + sage: set_random_seed(1337) # as the function returns random twists of curves + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + Elliptic Curve defined by y^2 = x^3 + 703734957*x + 232615553 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 278352808*x + 442354703 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 22138181*x + 343211325 over Finite Field of size 999969307 + sage: from sage.misc.verbose import set_verbose + sage: set_verbose(2) + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 276281332*x + 798205466 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 515293837*x + 588191363 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 684893162*x + 679980762 over Finite Field of size 999969307 + sage: set_verbose(3) + sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): + ....: print(E) + verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 1th valid prime 19 + verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 2th valid prime 23 + verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 3th valid prime 29 + verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 + verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 620755650*x + 816246309 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 651416562*x + 875284469 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 149283707*x + 72132991 over Finite Field of size 999969307 TESTS:: @@ -2986,6 +3030,7 @@ def EllipticCurve_with_prime_order(N): ALGORITHM: [BS2007]_, Algorithm 2.2 """ import itertools + from sage.misc.misc_c import prod from sage.misc.verbose import verbose from sage.combinat import gray_codes from sage.sets.primes import Primes @@ -2996,17 +3041,33 @@ def EllipticCurve_with_prime_order(N): if not is_prime(N): raise ValueError("input order is not prime") - if N < 1000: - # Log how long this algorithm will take - from sage.rings.fast_arith import prime_range - num = sum(1 for p in prime_range(3, 4 * N) if legendre_symbol(N, p) == 1) - verbose(f"Total work to enumerate curves with order {N}: 2^{num}", level=2) + # if N < 1000: + # # Log how long this algorithm will take + # from sage.rings.fast_arith import prime_range + # num = sum(1 for p in prime_range(3, 4 * N) if legendre_symbol(N, p) == 1) + # verbose(f"Total work to enumerate curves with order {N}: 2^{num}", level=2) # The algorithm considers smooth discriminants `D`, sorted by their largest # prime factor. We expect this algorithm to terminate after a number of # rounds that is polynomial in loglog N. S = [] + def recur(idx, bound): + """ + This function returns all subsets of S[idx:] (at the time of running) + with product of absolute value <= bound. It's extremely quick since S + is basically prime_range(N) while bound is basically 4 * N. + """ + if idx >= len(S): + return + yield [] + for nxt in range(idx + 1, len(S)): + if abs(S[nxt]) <= bound: + for r in recur(nxt, bound // abs(S[nxt])): + yield [S[nxt]] + r + else: + break + for p in Primes(): if p == 2: continue @@ -3024,20 +3085,11 @@ def EllipticCurve_with_prime_order(N): if p > 4 * N: break - # Although this is O(2^n) instead of O(n2^n), it misses out on the optimisation where - # when D becomes too large (see -D > 4 * N below), one can cut the branch entirely - # so perhaps this is slower - D = p_star - for idx, dx in itertools.chain([(None, 1)], gray_codes.product([2] * len(S))): - assert abs(dx) == 1, f"hmm, gray codes shouldn't do that. file a bug report" - - if idx is not None: - if dx == 1: - D *= S[idx] - else: - D //= S[idx] + for e in recur(0, 4 * N // p): + D = p_star * prod(e) + assert abs(D) <= 4 * N - if D % 8 != 5 or D >= 0 or -D > 4 * N: + if D % 8 != 5 or D >= 0: continue verbose(f"Testing {D=}", level=3) @@ -3050,6 +3102,7 @@ def EllipticCurve_with_prime_order(N): x, _ = sol for p_i in [N + 1 - x, N + 1 + x]: if is_prime(p_i): + verbose(f"Computing the Hilbert class polynomial H_{D}", level=2) H = hilbert_class_polynomial(D) K = GF(p_i) for j0 in H.roots(ring=K, multiplicities=False): From bf706c5d390ca52d1720e099fde78e0a17dd9695 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 10 Sep 2024 00:55:16 +0100 Subject: [PATCH 175/193] fix docs --- .../elliptic_curves/ell_finite_field.py | 175 ++++++++++-------- 1 file changed, 95 insertions(+), 80 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 212093cdca0..9d2666ac989 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2862,7 +2862,7 @@ def find_q(m, m4_fac, D): except ValueError: pass -def EllipticCurve_with_prime_order(N, max_D=10**8): +def EllipticCurve_with_prime_order(N): r""" Given a prime number ``N``, find another prime number `p` and construct an elliptic curve `E` defined over `\mathbb F_p` such that @@ -2875,6 +2875,16 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): OUTPUT: an iterator of elliptic curves `E/\mathbb F_p` of order ``N`` + .. NOTE:: + + Depending on the input, this function may run for a *very* long time. + This algorithm consists of multiple "search rounds" for a suitable + discriminant `D`. We expect this algorithm to terminate after a number + of rounds that is polynomial in `\log\log N`. In practice (cf. Section + 5), this number is usually 1. + + ALGORITHM: [BS2007]_, Algorithm 2.2 + EXAMPLES:: sage: N = next_prime(int.from_bytes(b'sagemath', 'big')) @@ -2882,7 +2892,7 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): sage: E Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + 1841350074072114366 over Finite Field of size 8314040074357871443 - sage: E.order() == N + sage: E.has_order(N) True The returned curves are sometimes random because @@ -2890,7 +2900,7 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): not deterministic. However, it's always isomorphic:: sage: E = next(EllipticCurve_with_prime_order(23)); E # random - Elliptic Curve defined by y^2 = x^3 + 12*x + 11 over Finite Field of size 17 + Elliptic Curve defined by y^2 = x^3 + 12*x + 6 over Finite Field of size 17 sage: E.is_isomorphic(EllipticCurve(GF(17), [3, 5])) True @@ -2899,7 +2909,7 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): sage: N = 54675917 sage: for _, E in zip(range(10), EllipticCurve_with_prime_order(N)): - ....: assert E.order() == N + ....: assert E.has_order(N) It works for large primes:: @@ -2907,16 +2917,19 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): sage: E = next(EllipticCurve_with_prime_order(N)); E Elliptic Curve defined by y^2 = x^3 + 2666207849820848272386538889427721639173508298483739490459*x + 77986137112576 over Finite Field of size 2666207849820848272386538889427721639173508298487130585243 - sage: E.order() == N + sage: E.has_order(N) True - The execution time largely depends on the input, specifically the smallest - discriminant ``D`` for which we can apply CM method on. Here it takes - slightly longer, though still within `1` second:: + :: - sage: N = 200396817641911230625970463749415493753 - sage: E = next(EllipticCurve_with_prime_order(N)); E - sage: E.order() == N + sage: N = next_prime(2^256) + sage: E = next(EllipticCurve_with_prime_order(N)); E # random + Elliptic Curve defined by y^2 = x^3 + 6056521267553273205988520276135607487700943205131813669424576873701361709521*x + + 86942739955486781674010637133214195706465136689012129911736706024465988573567 over Finite Field of size + 115792089237316195423570985008687907853847329310253429036565151476471048389761 + sage: E.j_invariant() + 111836223967433630316209796253554285080540088646141285337487360944738698436350 + sage: E.has_order(N) True Note that the iterator does *not* return all curves with the given order:: @@ -2930,11 +2943,13 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): sage: N = 23 sage: set_random_seed(1337) # as the function returns random twists of curves - sage: curves = list(EllipticCurve_with_prime_order(N)); curves + sage: curves = list(EllipticCurve_with_prime_order(N)); curves # random [Elliptic Curve defined by y^2 = x^3 + 3*x + 5 over Finite Field of size 17, Elliptic Curve defined by y^2 = x^3 + 19*x + 14 over Finite Field of size 31, Elliptic Curve defined by y^2 = x^3 + 2*x + 9 over Finite Field of size 19, - Elliptic Curve defined by y^2 = x^3 + 7*x + 18 over Finite Field of size 29] + Elliptic Curve defined by y^2 = x^3 + 7*x + 18 over Finite Field of size 29, + Elliptic Curve defined by y^2 = x^3 + 20*x + 20 over Finite Field of size 23, + Elliptic Curve defined by y^2 = x^3 + 10*x + 16 over Finite Field of size 23] sage: import itertools sage: # These are the only primes, by the Weil-Hasse bound sage: for q in prime_range(17, 35): @@ -2942,7 +2957,7 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): ....: for u in itertools.product(range(q), repeat=2): ....: try: E = EllipticCurve(GF(q), u) ....: except ArithmeticError: continue - ....: if E.order() == N: + ....: if E.has_order(N): ....: assert any(E.is_isomorphic(E_) for E_ in curves) The algorithm is efficient for small ``N`` due to the low number of @@ -2950,57 +2965,63 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): details). The following runs in less than a second:: sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) - 402 + 534 - There is different verbose data for level `2` and `3`. Note that if you try - to compute the 5th curve in the iterator, the algorithm takes a long time - to run, as it is trying to compute a large Hilbert class polynomial, as - noted in the verbose log:: + There is different verbose data for level `2` to `4`, though level `3` + rarely logs anything (it logs when a new prime `p` is added to the + smoothness bound):: + sage: from sage.misc.verbose import set_verbose sage: set_random_seed(1337) # as the function returns random twists of curves sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - Elliptic Curve defined by y^2 = x^3 + 703734957*x + 232615553 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 278352808*x + 442354703 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 22138181*x + 343211325 over Finite Field of size 999969307 - sage: from sage.misc.verbose import set_verbose + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 + Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 sage: set_verbose(2) + sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) + verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 - Elliptic Curve defined by y^2 = x^3 + 276281332*x + 798205466 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 515293837*x + 588191363 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 684893162*x + 679980762 over Finite Field of size 999969307 - sage: set_verbose(3) + Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 + sage: set_verbose(4) + sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 1th valid prime 19 - verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 2th valid prime 23 - verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Considering 3th valid prime 29 - verbose 3 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 + verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 + ... + verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 + verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 + verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 + ... + verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 - Elliptic Curve defined by y^2 = x^3 + 620755650*x + 816246309 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 651416562*x + 875284469 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 149283707*x + 72132991 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 TESTS:: + sage: set_verbose(0) sage: for N in prime_range(3, 100): ....: E = next(EllipticCurve_with_prime_order(N)) - ....: assert E.order() == N + ....: assert E.has_order(N) sage: N = 113 sage: for _, E in zip(range(30), EllipticCurve_with_prime_order(N)): - ....: assert E.order() == N + ....: assert E.has_order(N) sage: N = 15175980689839334471 sage: E = next(EllipticCurve_with_prime_order(N)) - sage: E.order() == N + sage: E.has_order(N) True sage: N = next_prime(123456789) sage: E = next(EllipticCurve_with_prime_order(N)) - sage: E.order() == N + sage: E.has_order(N) True sage: N = 123456789 @@ -3018,25 +3039,14 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): Traceback (most recent call last): ... ValueError: input order is not prime - - .. NOTE:: - - Depending on the input, this function may run for a *very* long time. - This algorithm consists of multiple "search rounds" for a suitable - discriminant `D`. We expect this algorithm to terminate after a number - of rounds that is polynomial in `\log\log N`. In practice (cf. Section - 5), this number is usually 1. - - ALGORITHM: [BS2007]_, Algorithm 2.2 """ import itertools - from sage.misc.misc_c import prod - from sage.misc.verbose import verbose - from sage.combinat import gray_codes - from sage.sets.primes import Primes from sage.arith.misc import is_prime, legendre_symbol + from sage.misc.verbose import verbose from sage.quadratic_forms.binary_qf import BinaryQF + from sage.rings.fast_arith import prime_range from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial + from sage.sets.primes import Primes if not is_prime(N): raise ValueError("input order is not prime") @@ -3050,49 +3060,54 @@ def EllipticCurve_with_prime_order(N, max_D=10**8): # The algorithm considers smooth discriminants `D`, sorted by their largest # prime factor. We expect this algorithm to terminate after a number of # rounds that is polynomial in loglog N. - S = [] + # We start with small primes directly to accelerate the search + S = [(-p if p >> 1 & 1 else p) for p in prime_range(3, min(1000, 4 * N)) + if legendre_symbol(N, p) == 1] - def recur(idx, bound): + def recur(bound): """ - This function returns all subsets of S[idx:] (at the time of running) - with product of absolute value <= bound. It's extremely quick since S - is basically prime_range(N) while bound is basically 4 * N. + This function returns an iterator of all numbers with absolute value + not exceeding ``bound`` expressable as product of distinct elements in + ``S`` in ascending order. """ - if idx >= len(S): - return - yield [] - for nxt in range(idx + 1, len(S)): - if abs(S[nxt]) <= bound: - for r in recur(nxt, bound // abs(S[nxt])): - yield [S[nxt]] + r - else: - break + import heapq + hq = [(1, 1, -1)] + while len(hq): + abs_n, n, idx = heapq.heappop(hq) + yield n + for nxt in range(idx + 1, len(S)): + if abs_n * abs(S[nxt]) <= bound: + heapq.heappush(hq, (abs_n * abs(S[nxt]), n * S[nxt], nxt)) + else: + break - for p in Primes(): - if p == 2: - continue + for p in itertools.chain([1], Primes()): + # We add p = 1 to process the small primes + if p != 1: + if p < 1000: + continue - if legendre_symbol(N, p) != 1: - continue + if legendre_symbol(N, p) != 1: + continue - verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) + # later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, we need + # p = |p_star| <= |-D| <= 4N + if p > 4 * N: + break + + verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. p_star = -p if p >> 1 & 1 else p - # later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, we need - # p = |p_star| <= |-D| <= 4N - if p > 4 * N: - break - - for e in recur(0, 4 * N // p): - D = p_star * prod(e) + for e in recur(4 * N // p): + D = p_star * e assert abs(D) <= 4 * N if D % 8 != 5 or D >= 0: continue - verbose(f"Testing {D=}", level=3) + verbose(f"Testing {D=}", level=4) Q = BinaryQF([1, 0, -D]) sol = Q.solve_integer(4 * N, algorithm='cornacchia') From 94c4866d83e7035cb3352b2eeece13e46932afbf Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 10 Sep 2024 11:59:06 +0100 Subject: [PATCH 176/193] remove bithack --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 9d2666ac989..49779804c1d 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -3084,7 +3084,7 @@ def recur(bound): for p in itertools.chain([1], Primes()): # We add p = 1 to process the small primes if p != 1: - if p < 1000: + if p < S[-1]: continue if legendre_symbol(N, p) != 1: @@ -3098,7 +3098,7 @@ def recur(bound): verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. - p_star = -p if p >> 1 & 1 else p + p_star = -p if p % 4 == 3 else p for e in recur(4 * N // p): D = p_star * e From 459433c7a92eab7fe6420bccfccfe154553d974f Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 10 Sep 2024 12:10:28 +0100 Subject: [PATCH 177/193] add note about magic constant 1000 --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 49779804c1d..889ff1aa6e1 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -3061,6 +3061,8 @@ def EllipticCurve_with_prime_order(N): # prime factor. We expect this algorithm to terminate after a number of # rounds that is polynomial in loglog N. # We start with small primes directly to accelerate the search + # Note: 1000 is a magic constant, it's just fast enough to compute without + # sacrificing much speed S = [(-p if p >> 1 & 1 else p) for p in prime_range(3, min(1000, 4 * N)) if legendre_symbol(N, p) == 1] From 5fb4bc4ef2bb5090448056ec32b3df3518adeec0 Mon Sep 17 00:00:00 2001 From: Martin Grenouilloux Date: Tue, 10 Sep 2024 16:20:09 +0200 Subject: [PATCH 178/193] Coding style, naming and skip p=2 --- .../elliptic_curves/ell_finite_field.py | 96 ++++++++++--------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 889ff1aa6e1..e1d960d9fc1 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1405,14 +1405,15 @@ def has_order(self, value, num_checks=8): - Gareth Ma (2024-01-21): Fix bug for small curves """ - # This method does *not* use the cached value of `_order` even when available + # This method does *not* use the cached value of `_order` even when + # available. q = self.base_field().order() a, b = Hasse_bounds(q, 1) if not a <= value <= b: return False - # For really small values, the random tests are too weak to detect wrong orders - # So we go with computing directly instead. + # For really small values, the random tests are too weak to detect wrong + # orders So we go with computing directly instead. if q <= 100: return self.order() == value @@ -1498,10 +1499,9 @@ def set_order(self, value, *, check=True, num_checks=8): ... ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 7 does not have order 1000 - It is also very likely an error to pass a value which is not - the actual order of this curve. How unlikely is determined by - ``num_checks``, the factorization of the actual order, and the - actual group structure:: + It is also very likely an error to pass a value which is not the actual + order of this curve. How unlikely is determined by ``num_checks``, the + factorization of the actual order, and the actual group structure:: sage: E = EllipticCurve(GF(1009), [0, 1]) # This curve has order 948 sage: E.set_order(947) @@ -1509,8 +1509,8 @@ def set_order(self, value, *, check=True, num_checks=8): ... ValueError: Elliptic Curve defined by y^2 = x^3 + 1 over Finite Field of size 1009 does not have order 947 - For curves over small finite fields, the order is cheap to compute, so it is computed - directly and compared:: + For curves over small finite fields, the order is cheap to compute, so + it is computed directly and compared:: sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: E.set_order(11) @@ -1520,8 +1520,8 @@ def set_order(self, value, *, check=True, num_checks=8): TESTS: - The previous version's random tests are not strong enough. In particular, the following used - to work:: + The previous version's random tests are not strong enough. In particular, + the following used to work:: sage: E = EllipticCurve(GF(2), [0, 0, 1, 1, 1]) # This curve has order 1 sage: E.set_order(3) @@ -1539,8 +1539,8 @@ def set_order(self, value, *, check=True, num_checks=8): sage: E.order() 12 - .. TODO:: Add provable correctness check by computing the abelian group structure and - comparing. + .. TODO:: Add provable correctness check by computing the abelian group + structure and comparing. AUTHORS: @@ -2896,10 +2896,10 @@ def EllipticCurve_with_prime_order(N): True The returned curves are sometimes random because - :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.twists` is - not deterministic. However, it's always isomorphic:: + :meth:`~sage.schemes.elliptic_curves.ell_finite_field.EllipticCurve_finite_field.twists` + is not deterministic. However, it's always isomorphic:: - sage: E = next(EllipticCurve_with_prime_order(23)); E # random + sage: E = next(EllipticCurve_with_prime_order(23)); E # random Elliptic Curve defined by y^2 = x^3 + 12*x + 6 over Finite Field of size 17 sage: E.is_isomorphic(EllipticCurve(GF(17), [3, 5])) True @@ -2939,7 +2939,8 @@ def EllipticCurve_with_prime_order(N): sage: EllipticCurve(GF(7), [0, 5]).order() 7 - However, experimentally it returns many of them. Here it returns all of them:: + However, experimentally it returns many of them. Here it returns all of + them:: sage: N = 23 sage: set_random_seed(1337) # as the function returns random twists of curves @@ -2960,12 +2961,12 @@ def EllipticCurve_with_prime_order(N): ....: if E.has_order(N): ....: assert any(E.is_isomorphic(E_) for E_ in curves) - The algorithm is efficient for small ``N`` due to the low number of - suitable discriminants (see the ``recur`` internal function of the code for - details). The following runs in less than a second:: + The algorithm is efficient for small ``N`` due to the low number of suitable + discriminants (see the ``abs_products_under`` internal function of the code + for details). The following runs in less than a second:: sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) - 534 + 945 There is different verbose data for level `2` to `4`, though level `3` rarely logs anything (it logs when a new prime `p` is added to the @@ -3024,21 +3025,26 @@ def EllipticCurve_with_prime_order(N): sage: E.has_order(N) True + sage: E = next(EllipticCurve_with_prime_order(2)) + Traceback (most recent call last): + ... + ValueError: input order is not an odd prime + sage: N = 123456789 sage: E = next(EllipticCurve_with_prime_order(N)) Traceback (most recent call last): ... - ValueError: input order is not prime + ValueError: input order is not an odd prime sage: E = next(EllipticCurve_with_prime_order(0)) Traceback (most recent call last): ... - ValueError: input order is not prime + ValueError: input order is not an odd prime sage: E = next(EllipticCurve_with_prime_order(-7)) Traceback (most recent call last): ... - ValueError: input order is not prime + ValueError: input order is not an odd prime """ import itertools from sage.arith.misc import is_prime, legendre_symbol @@ -3048,29 +3054,23 @@ def EllipticCurve_with_prime_order(N): from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial from sage.sets.primes import Primes - if not is_prime(N): - raise ValueError("input order is not prime") - - # if N < 1000: - # # Log how long this algorithm will take - # from sage.rings.fast_arith import prime_range - # num = sum(1 for p in prime_range(3, 4 * N) if legendre_symbol(N, p) == 1) - # verbose(f"Total work to enumerate curves with order {N}: 2^{num}", level=2) + if not is_prime(N) or N < 3: + raise ValueError("input order is not an odd prime") # The algorithm considers smooth discriminants `D`, sorted by their largest # prime factor. We expect this algorithm to terminate after a number of # rounds that is polynomial in loglog N. - # We start with small primes directly to accelerate the search + # We start with small primes directly to accelerate the search. # Note: 1000 is a magic constant, it's just fast enough to compute without - # sacrificing much speed + # sacrificing much speed. S = [(-p if p >> 1 & 1 else p) for p in prime_range(3, min(1000, 4 * N)) if legendre_symbol(N, p) == 1] - def recur(bound): + def abs_products_under(bound): """ - This function returns an iterator of all numbers with absolute value - not exceeding ``bound`` expressable as product of distinct elements in - ``S`` in ascending order. + This function returns an iterator of all numbers with absolute value not + exceeding ``bound`` expressable as product of distinct elements in ``S`` + in ascending order. """ import heapq hq = [(1, 1, -1)] @@ -3083,8 +3083,9 @@ def recur(bound): else: break + # We add p = 1 to process the small primes. for p in itertools.chain([1], Primes()): - # We add p = 1 to process the small primes + if p == 2: continue if p != 1: if p < S[-1]: continue @@ -3092,17 +3093,18 @@ def recur(bound): if legendre_symbol(N, p) != 1: continue - # later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, we need - # p = |p_star| <= |-D| <= 4N + # Later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, + # we need p = |p_star| <= |-D| <= 4N. This is a stopping condition + # for the algorithm. if p > 4 * N: break verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) - # Equivalent to p* = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. - p_star = -p if p % 4 == 3 else p + # Equivalent to p_star = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. + p_star = -p if p >> 1 & 1 else p - for e in recur(4 * N // p): + for e in abs_products_under(4 * N // p): D = p_star * e assert abs(D) <= 4 * N @@ -3119,15 +3121,17 @@ def recur(bound): x, _ = sol for p_i in [N + 1 - x, N + 1 + x]: if is_prime(p_i): - verbose(f"Computing the Hilbert class polynomial H_{D}", level=2) + verbose(f"Computing the Hilbert class polynomial H_{D}", + level=2) H = hilbert_class_polynomial(D) K = GF(p_i) for j0 in H.roots(ring=K, multiplicities=False): E = EllipticCurve(K, j=j0) # `E.twists()` also contains E. for Et in E.twists(): - # num_checks=1 is sufficient for prime order + # `num_checks=1` is sufficient for prime order. if Et.has_order(N, num_checks=1): yield Et + # Extending our prime list and continuing onto the next round. S.append(p_star) From d7d681adc3e8f24cf042f234739f6472d9c67e1d Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Tue, 10 Sep 2024 16:07:49 +0100 Subject: [PATCH 179/193] fix things --- .../elliptic_curves/ell_finite_field.py | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index e1d960d9fc1..59835b784d9 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -16,7 +16,7 @@ - Lorenz Panny (2023): ``special_supersingular_curve()`` -- Martin Grenouilloux (2024): ``EllipticCurve_with_prime_order()`` +- Martin Grenouilloux, Gareth Ma (2024-09): ``EllipticCurve_with_prime_order()`` """ # **************************************************************************** @@ -2923,7 +2923,7 @@ def EllipticCurve_with_prime_order(N): :: sage: N = next_prime(2^256) - sage: E = next(EllipticCurve_with_prime_order(N)); E # random + sage: E = next(EllipticCurve_with_prime_order(N)); E # random Elliptic Curve defined by y^2 = x^3 + 6056521267553273205988520276135607487700943205131813669424576873701361709521*x + 86942739955486781674010637133214195706465136689012129911736706024465988573567 over Finite Field of size 115792089237316195423570985008687907853847329310253429036565151476471048389761 @@ -2966,7 +2966,9 @@ def EllipticCurve_with_prime_order(N): for details). The following runs in less than a second:: sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) - 945 + 534 + sage: len(list(EllipticCurve_with_prime_order(next_prime(50000)))) # long time (6s) + 3841 There is different verbose data for level `2` to `4`, though level `3` rarely logs anything (it logs when a new prime `p` is added to the @@ -3006,6 +3008,11 @@ def EllipticCurve_with_prime_order(N): TESTS:: + sage: list(EllipticCurve_with_prime_order(2)) + [Elliptic Curve defined by y^2 + x*y + y = x^3 + 1 over Finite Field of size 2, + Elliptic Curve defined by y^2 = x^3 + 2*x^2 + 2 over Finite Field of size 3, + Elliptic Curve defined by y^2 = x^3 + 2*x over Finite Field of size 5] + sage: set_verbose(0) sage: for N in prime_range(3, 100): ....: E = next(EllipticCurve_with_prime_order(N)) @@ -3025,26 +3032,25 @@ def EllipticCurve_with_prime_order(N): sage: E.has_order(N) True - sage: E = next(EllipticCurve_with_prime_order(2)) - Traceback (most recent call last): - ... - ValueError: input order is not an odd prime - sage: N = 123456789 sage: E = next(EllipticCurve_with_prime_order(N)) Traceback (most recent call last): ... - ValueError: input order is not an odd prime + ValueError: input order is not a prime sage: E = next(EllipticCurve_with_prime_order(0)) Traceback (most recent call last): ... - ValueError: input order is not an odd prime + ValueError: input order is not a prime sage: E = next(EllipticCurve_with_prime_order(-7)) Traceback (most recent call last): ... - ValueError: input order is not an odd prime + ValueError: input order is not a prime + + AUTHORS: + + - Martin Grenouilloux, Gareth Ma (2024-09): initial implementation """ import itertools from sage.arith.misc import is_prime, legendre_symbol @@ -3054,16 +3060,22 @@ def EllipticCurve_with_prime_order(N): from sage.schemes.elliptic_curves.cm import hilbert_class_polynomial from sage.sets.primes import Primes - if not is_prime(N) or N < 3: - raise ValueError("input order is not an odd prime") + if not is_prime(N): + raise ValueError("input order is not a prime") - # The algorithm considers smooth discriminants `D`, sorted by their largest - # prime factor. We expect this algorithm to terminate after a number of - # rounds that is polynomial in loglog N. - # We start with small primes directly to accelerate the search. - # Note: 1000 is a magic constant, it's just fast enough to compute without + if N == 2: + yield from [ + EllipticCurve(GF(2), [1, 0, 1, 0, 1]), + EllipticCurve(GF(3), [0, 2, 0, 0, 2]), + EllipticCurve(GF(5), [2, 0]) + ] + return + + # We start with small primes directly to accelerate the search. Note that + # 1000 is a magic constant, it's just fast enough to compute without # sacrificing much speed. - S = [(-p if p >> 1 & 1 else p) for p in prime_range(3, min(1000, 4 * N)) + # The if-then-else term is (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. + S = [(-p if p % 4 == 3 else p) for p in prime_range(3, min(1000, 4 * N)) if legendre_symbol(N, p) == 1] def abs_products_under(bound): @@ -3085,26 +3097,26 @@ def abs_products_under(bound): # We add p = 1 to process the small primes. for p in itertools.chain([1], Primes()): - if p == 2: continue if p != 1: - if p < S[-1]: + if p < abs(S[-1]): continue if legendre_symbol(N, p) != 1: continue - # Later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no solution, - # we need p = |p_star| <= |-D| <= 4N. This is a stopping condition - # for the algorithm. + # Later we need x^2 + (-D)y^2 = 4N, and since y = 0 has no + # solution, we need p = |p_star| <= |-D| <= 4N. This is a stopping + # condition for the algorithm. if p > 4 * N: break verbose(f"Considering {len(S) + 1}th valid prime {p}", level=3) - # Equivalent to p_star = (-1)^((p - 1) / 2) * p in [BS2007]_ page 5. - p_star = -p if p >> 1 & 1 else p + p_star = -p if p % 4 == 3 else p for e in abs_products_under(4 * N // p): + # According to the paper, the expected minimum D to work is + # O(log(N)^2) D = p_star * e assert abs(D) <= 4 * N @@ -3133,5 +3145,6 @@ def abs_products_under(bound): if Et.has_order(N, num_checks=1): yield Et - # Extending our prime list and continuing onto the next round. - S.append(p_star) + if p != 1: + # Extending our prime list and continuing onto the next round. + S.append(p_star) From 04f9253c0d08771c079a4e737bc7acd21d263873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 11 Sep 2024 07:36:01 +0200 Subject: [PATCH 180/193] add typing annotation in temporary_file --- src/sage/misc/temporary_file.py | 35 +++++++++++++++------------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index ff8c7a751ac..40f8237f65e 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -23,12 +23,10 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import io +import atexit import os import tempfile -import atexit - # Until tmp_dir() and tmp_filename() are removed, we use this directory # as the parent for all temporary files & directories created by them. # This lets us clean up after those two functions when sage exits normally @@ -41,7 +39,7 @@ # temporary directory ################################################################# -def tmp_dir(name='dir_', ext=''): +def tmp_dir(name='dir_', ext='') -> str: r""" Create and return a temporary directory in ``$HOME/.sage/temp/hostname/pid/`` @@ -84,7 +82,7 @@ def tmp_dir(name='dir_', ext=''): # temporary filename ################################################################# -def tmp_filename(name='tmp_', ext=''): +def tmp_filename(name='tmp_', ext='') -> str: r""" Create and return a temporary file in ``$HOME/.sage/temp/hostname/pid/`` @@ -163,8 +161,8 @@ class atomic_write: mode bits of the file were changed manually). (Not to be confused with the file opening mode.) - - ``binary`` -- boolean (default: ``True`` on Python 2, ``False`` on Python - 3); the underlying file is opened in binary mode. If ``False`` then it is + - ``binary`` -- boolean (default: ``False``); + the underlying file is opened in binary mode. If ``False`` then it is opened in text mode and an encoding with which to write the file may be supplied. @@ -299,7 +297,7 @@ class atomic_write: False """ def __init__(self, target_filename, append=False, mode=0o666, - binary=None, **kwargs): + binary=False, **kwargs) -> None: """ TESTS:: @@ -320,13 +318,11 @@ def __init__(self, target_filename, append=False, mode=0o666, os.umask(umask) self.mode = mode & (~umask) - # 'binary' mode is the default on Python 2, whereas 'text' mode is the - # default on Python 3--this reflects consistent handling of the default - # str type on the two platforms - self.binary = False if binary is None else binary + # 'text' mode is the default on Python 3 + self.binary = binary self.kwargs = kwargs - def __enter__(self): + def __enter__(self) -> str: """ Create and return a temporary file in ``self.tmpdir`` (normally the same directory as the target file). @@ -372,7 +368,7 @@ def __enter__(self): return self.tempfile - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb) -> None: """ If the ``with`` block was successful, move the temporary file to the target file. Otherwise, delete the temporary file. @@ -457,7 +453,7 @@ class atomic_dir: ....: h.read() 'Second' """ - def __init__(self, target_directory): + def __init__(self, target_directory) -> None: r""" TESTS:: @@ -473,7 +469,7 @@ def __init__(self, target_directory): self.target = os.path.realpath(target_directory) self.tmpdir = os.path.dirname(self.target) - def __enter__(self): + def __enter__(self) -> str: r""" Create and return a temporary directory in ``self.tmpdir`` (normally the same directory as the target file). @@ -492,7 +488,7 @@ def __enter__(self): self.tempname = os.path.abspath(tdir.name) return tdir - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, exc_type, exc_val, exc_tb) -> None: """ If the ``with`` block was successful, move the temporary directory to the target directory. Otherwise, delete the temporary directory. @@ -518,7 +514,8 @@ def __exit__(self, exc_type, exc_val, exc_tb): try: os.rename(self.tempname, self.target) except OSError: - # Race: Another thread or process must have created the directory + # Race: Another thread or process must have created + # the directory pass else: # Failure: delete temporary file @@ -528,7 +525,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): _spyx_tmp = None -def spyx_tmp(): +def spyx_tmp() -> str: r""" The temporary directory used to store pyx files. From 8e754fb017897a5553fe5c1c52677bf3ecfce184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 11 Sep 2024 08:16:39 +0200 Subject: [PATCH 181/193] fix typing --- src/sage/misc/temporary_file.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/temporary_file.py b/src/sage/misc/temporary_file.py index 40f8237f65e..998260be8eb 100644 --- a/src/sage/misc/temporary_file.py +++ b/src/sage/misc/temporary_file.py @@ -26,6 +26,7 @@ import atexit import os import tempfile +from typing import IO # Until tmp_dir() and tmp_filename() are removed, we use this directory # as the parent for all temporary files & directories created by them. @@ -322,7 +323,7 @@ def __init__(self, target_filename, append=False, mode=0o666, self.binary = binary self.kwargs = kwargs - def __enter__(self) -> str: + def __enter__(self) -> IO: """ Create and return a temporary file in ``self.tmpdir`` (normally the same directory as the target file). @@ -469,7 +470,7 @@ def __init__(self, target_directory) -> None: self.target = os.path.realpath(target_directory) self.tmpdir = os.path.dirname(self.target) - def __enter__(self) -> str: + def __enter__(self): r""" Create and return a temporary directory in ``self.tmpdir`` (normally the same directory as the target file). From 7fc7e9b1a94fd9938607db5bef149d8d76432a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 11 Sep 2024 08:19:23 +0200 Subject: [PATCH 182/193] another typing fix elsewhere in misc --- src/sage/misc/replace_dot_all.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/replace_dot_all.py b/src/sage/misc/replace_dot_all.py index 16ac2124377..ea51a9b3159 100644 --- a/src/sage/misc/replace_dot_all.py +++ b/src/sage/misc/replace_dot_all.py @@ -456,8 +456,8 @@ def walkdir_replace_dot_all(dir, file_regex=r'.*[.](py|pyx|pxi)$', package_regex finally: # Print report also when interrupted if verbosity: - log_messages = sorted(log_messages.rstrip().split('\n')) - for i, message in enumerate(log_messages, start=1): + log_messages_split = sorted(log_messages.rstrip().split('\n')) + for i, message in enumerate(log_messages_split, start=1): # add index to each line print(f'{i}. {message.rstrip()}') report = 'REPORT:\n' From 2854e94fcdc447f22b44d4ccc27e0deba906cc0d Mon Sep 17 00:00:00 2001 From: grhkm21 <83517584+grhkm21@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:48:16 +0100 Subject: [PATCH 183/193] Apply suggestions from code review Co-authored-by: Vincent Macri --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 59835b784d9..b249a62885f 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2873,7 +2873,7 @@ def EllipticCurve_with_prime_order(N): - ``N`` -- integer; the order for which we seek an elliptic curve. Must be a prime number. - OUTPUT: an iterator of elliptic curves `E/\mathbb F_p` of order ``N`` + OUTPUT: an iterator of (some) elliptic curves `E/\mathbb F_p` of order ``N`` .. NOTE:: @@ -2883,11 +2883,11 @@ def EllipticCurve_with_prime_order(N): of rounds that is polynomial in `\log\log N`. In practice (cf. Section 5), this number is usually 1. - ALGORITHM: [BS2007]_, Algorithm 2.2 + ALGORITHM: Based on [BS2007]_, Algorithm 2.2 EXAMPLES:: - sage: N = next_prime(int.from_bytes(b'sagemath', 'big')) + sage: N = 8314040072427107567 sage: E = next(EllipticCurve_with_prime_order(N)) sage: E Elliptic Curve defined by y^2 = x^3 + 4757897140353078952*x + 1841350074072114366 @@ -2913,7 +2913,7 @@ def EllipticCurve_with_prime_order(N): It works for large primes:: - sage: N = 0x6cbc824032974516623e732462f4b74b56c4ffbd984380d9 + sage: N = 2666207849820848272386538889527600954292544013630953455833 sage: E = next(EllipticCurve_with_prime_order(N)); E Elliptic Curve defined by y^2 = x^3 + 2666207849820848272386538889427721639173508298483739490459*x + 77986137112576 over Finite Field of size 2666207849820848272386538889427721639173508298487130585243 From 4b9e197ec179539e2fdf17b73eda45d120d474ff Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 21:23:18 +0100 Subject: [PATCH 184/193] apply review changes (explain ALGORITHM changes) --- .../elliptic_curves/ell_finite_field.py | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 59835b784d9..43366a793d7 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1414,7 +1414,9 @@ def has_order(self, value, num_checks=8): # For really small values, the random tests are too weak to detect wrong # orders So we go with computing directly instead. - if q <= 100: + # In #38341, the bound has been increased to a large value (2^64), but + # it should be decreased (to ~100) after bug #38617 is fixed. + if q <= 2**64: return self.order() == value # This might be slow @@ -2875,15 +2877,35 @@ def EllipticCurve_with_prime_order(N): OUTPUT: an iterator of elliptic curves `E/\mathbb F_p` of order ``N`` - .. NOTE:: - - Depending on the input, this function may run for a *very* long time. - This algorithm consists of multiple "search rounds" for a suitable - discriminant `D`. We expect this algorithm to terminate after a number - of rounds that is polynomial in `\log\log N`. In practice (cf. Section - 5), this number is usually 1. + ALGORITHM: - ALGORITHM: [BS2007]_, Algorithm 2.2 + Our algorithm is based on [BS2007]_, Algorithm 2.2, but we deviate for + several key steps. Firstly, the authors in the paper perform the search for + a suitable `D` *incrementally*, by enlarging the table `S` by `log(N)`-size + interval of primes `p` and testing all products of distinct primes `p` (or + rather `p^*`). We find this difficult to implement without testing + duplicate `D`s, so we instead enlarge the table one prime at a time + (effectively replacing `[r\log(N), (r + 1)\log(N)]` in the paper by `[r, + r]`). To compensate for the speed loss, we begin the algorithm by + prefilling `S` with the primes below `1000` (satisfying quadratic + reciprocity properties). The constant `1000` is determined experimentally + to be fast for many purposes, and for most `N` we tested we are able to + find a suitable small `D` without increasing the size of `S`. + + The paper also doesn't specify how to enumerate such `D`s, which recall + should be product of distinct values in the table `S`. We implement this + with a priority queue (min heap), which also allows us to search for the + suitable `D`s in increasing (absolute value) order. This is suitable for + the algorithm because smaller `D` means the Hilbert class polynomial is + computed quicker. + + Finally, to avoid repeatedly testing the same `D`s, we require the latest + prime to be added to the table to be included as a factor of `D` (see code + for more explanation). As we need to find integers `x, y` such that `x^2 + + (-D)y^2 = 4N` with `D < 0` and `N` prime, we actually need `|D| \leq 4N`, + so we terminate the algorithm when the primes in the table are larger than + that bound. This makes the iterator return all curves it can find in finite + time :) EXAMPLES:: @@ -2963,7 +2985,7 @@ def EllipticCurve_with_prime_order(N): The algorithm is efficient for small ``N`` due to the low number of suitable discriminants (see the ``abs_products_under`` internal function of the code - for details). The following runs in less than a second:: + for details):: sage: len(list(EllipticCurve_with_prime_order(next_prime(5000)))) 534 From 7941d8b458b674eadf0f173cf8f226977f8bd00c Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 21:27:59 +0100 Subject: [PATCH 185/193] linter --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 43366a793d7..e2cba07647a 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2753,7 +2753,7 @@ def EllipticCurve_with_order(m, *, D=None): Return an iterator for elliptic curves over finite fields with the given order. The curves are computed using the Complex Multiplication (CM) method. - A :sage:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case + A :class:`~sage.structure.factorization.Factorization` can be passed for ``m``, in which case the algorithm is more efficient. If ``D`` is specified, it is used as the discriminant. From 4840e9a2f09048b482e84c2f20deb2be49126cd5 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 21:58:09 +0100 Subject: [PATCH 186/193] update test to use larger valued curves --- .../elliptic_curves/ell_finite_field.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 8ddbdfdb8c2..47c630650a8 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1390,13 +1390,16 @@ def has_order(self, value, num_checks=8): sage: E.has_order(N + 1) False - This demonstrates the bug mentioned in the NOTE above. The return value - should be ``False`` after :issue:`38617` is fixed:: + This demonstrates the bug mentioned in the NOTE above. The last return + value should be ``False`` after :issue:`38617` is fixed:: - sage: E = EllipticCurve(GF(127), [0, 1]) - sage: E.order() - 108 - sage: E.has_order(126) + sage: E = EllipticCurve(GF(5443568676570036275321323), [0, 13]) + sage: N = 2333145661241 + sage: E.order() == N^2 + True + sage: E.has_order(N^2) + True + sage: E.has_order(N^2 + N) True AUTHORS: @@ -2837,7 +2840,7 @@ def find_q(m, m4_fac, D): m_val = m if D is None: - Ds = (D for D in range(-4 * m_val, 0) if D % 4 in [0, 1]) + Ds = (D for D in range(-1, -4 * m_val - 1, -1) if D % 4 in [0, 1]) else: assert D < 0 and D % 4 in [0, 1] Ds = [D] @@ -2849,20 +2852,16 @@ def find_q(m, m4_fac, D): continue H = hilbert_class_polynomial(D) - K = GF(q) - roots = H.roots(ring=K) - for j0, _ in roots: + for j0 in H.roots(ring=GF(q), multiplicities=False): E = EllipticCurve(j=j0) for Et in E.twists(): if any(Et.is_isomorphic(E) for E in seen): continue - try: - # This tests whether the curve has given order - Et.set_order(m_val) + # This tests whether the curve has given order + if Et.has_order(m_val): + Et.set_order(m_val, check=False) seen.add(Et) yield Et - except ValueError: - pass def EllipticCurve_with_prime_order(N): r""" @@ -3168,6 +3167,7 @@ def abs_products_under(bound): for Et in E.twists(): # `num_checks=1` is sufficient for prime order. if Et.has_order(N, num_checks=1): + Et.set_order(N, check=False) yield Et if p != 1: From e258c18eac18b7231549e0a3a99c054f93c0dda8 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 22:20:47 +0100 Subject: [PATCH 187/193] fix tests --- .../elliptic_curves/ell_finite_field.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 47c630650a8..841f90f9931 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -3003,32 +3003,32 @@ def EllipticCurve_with_prime_order(N): sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 - Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 sage: set_verbose(2) sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 - verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 - Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 + verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 sage: set_verbose(4) sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 + verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 ... - verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 - verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 + verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 - verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 + verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 ... - verbose 4 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 - verbose 2 (2865: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 - Elliptic Curve defined by y^2 = x^3 + 665393686*x + 948152000 over Finite Field of size 999969307 - Elliptic Curve defined by y^2 = x^3 + 572311614*x + 178583984 over Finite Field of size 999969307 + verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 + verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 + Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 TESTS:: From c9e33ea7d9a3959ee3066044ebf334e8c4f36805 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 22:29:07 +0100 Subject: [PATCH 188/193] apply review changes --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 841f90f9931..50b0fb4727c 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1436,6 +1436,7 @@ def has_order(self, value, num_checks=8): if not (value * G).is_zero(): return False + self._order = value return True def set_order(self, value, *, check=True, num_checks=8): @@ -2859,7 +2860,6 @@ def find_q(m, m4_fac, D): continue # This tests whether the curve has given order if Et.has_order(m_val): - Et.set_order(m_val, check=False) seen.add(Et) yield Et @@ -2891,7 +2891,6 @@ def EllipticCurve_with_prime_order(N): to be fast for many purposes, and for most `N` we tested we are able to find a suitable small `D` without increasing the size of `S`. -<<<<<<< HEAD The paper also doesn't specify how to enumerate such `D`s, which recall should be product of distinct values in the table `S`. We implement this with a priority queue (min heap), which also allows us to search for the @@ -2905,7 +2904,7 @@ def EllipticCurve_with_prime_order(N): (-D)y^2 = 4N` with `D < 0` and `N` prime, we actually need `|D| \leq 4N`, so we terminate the algorithm when the primes in the table are larger than that bound. This makes the iterator return all curves it can find in finite - time :) + time. ALGORITHM: Based on [BS2007]_, Algorithm 2.2 @@ -3167,7 +3166,6 @@ def abs_products_under(bound): for Et in E.twists(): # `num_checks=1` is sufficient for prime order. if Et.has_order(N, num_checks=1): - Et.set_order(N, check=False) yield Et if p != 1: From 41cb6e912e3c0b668aaabc41b39d43e18bcc5ba3 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 22:48:18 +0100 Subject: [PATCH 189/193] use _order in has_order when possible --- .../elliptic_curves/ell_finite_field.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 50b0fb4727c..87fa0ec67b8 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1399,6 +1399,7 @@ def has_order(self, value, num_checks=8): True sage: E.has_order(N^2) True + sage: del E._order sage: E.has_order(N^2 + N) True @@ -1419,7 +1420,7 @@ def has_order(self, value, num_checks=8): # orders So we go with computing directly instead. # In #38341, the bound has been increased to a large value (2^64), but # it should be decreased (to ~100) after bug #38617 is fixed. - if q <= 2**64: + if q <= 2**64 or hasattr(self, "_order"): return self.order() == value # This might be slow @@ -3008,24 +3009,24 @@ def EllipticCurve_with_prime_order(N): sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 - verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 sage: set_verbose(4) sage: set_random_seed(1337) sage: for _, E in zip(range(3), EllipticCurve_with_prime_order(10^9 + 7)): ....: print(E) - verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-19 ... - verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 - verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-163 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-163 Elliptic Curve defined by y^2 = x^3 + 265977778*x + 120868502 over Finite Field of size 1000041437 - verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-179 ... - verbose 4 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 - verbose 2 (2866: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 + verbose 4 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Testing D=-667 + verbose 2 (...: ell_finite_field.py, EllipticCurve_with_prime_order) Computing the Hilbert class polynomial H_-667 Elliptic Curve defined by y^2 = x^3 + 689795416*x + 188156157 over Finite Field of size 999969307 Elliptic Curve defined by y^2 = x^3 + 999178436*x + 900579394 over Finite Field of size 999969307 From 228a2bf301601baef9cbec59650f1deb96593c65 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 22:50:18 +0100 Subject: [PATCH 190/193] revert --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 87fa0ec67b8..b877f605c77 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1437,7 +1437,9 @@ def has_order(self, value, num_checks=8): if not (value * G).is_zero(): return False - self._order = value + # TODO: uncomment this and remove the line in `set_order` after 38617 is fixed. + # self._order = value + return True def set_order(self, value, *, check=True, num_checks=8): From 163191fdb7f63e62d19bb70ca56a5662c476a26d Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Wed, 11 Sep 2024 22:54:10 +0100 Subject: [PATCH 191/193] apply review changes --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index b877f605c77..b1f77da41e8 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -2863,6 +2863,8 @@ def find_q(m, m4_fac, D): continue # This tests whether the curve has given order if Et.has_order(m_val): + # TODO: remove after 38617 + Et.set_order(m_val, check=False) seen.add(Et) yield Et @@ -3167,8 +3169,10 @@ def abs_products_under(bound): E = EllipticCurve(K, j=j0) # `E.twists()` also contains E. for Et in E.twists(): - # `num_checks=1` is sufficient for prime order. + # `num_checks=1` is sufficient for prime order if Et.has_order(N, num_checks=1): + # TODO: remove after 38617 + Et.set_order(N, check=False) yield Et if p != 1: From b60e81f323703f3d641cf5d78c519fd8701f1449 Mon Sep 17 00:00:00 2001 From: Gareth Ma Date: Thu, 12 Sep 2024 18:32:04 +0100 Subject: [PATCH 192/193] apply review changes --- .../schemes/elliptic_curves/ell_finite_field.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index b1f77da41e8..65b49cfe1bd 100755 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1353,7 +1353,9 @@ def has_order(self, value, num_checks=8): - ``num_checks``-- integer (default: `8`); the number of times to check whether ``value`` times a random point on this curve equals the - identity + identity. If ``value`` is a prime and the curve is over a field of + order at least `5`, it is sufficient to pass in ``num_checks=1`` - + see the examples below. .. NOTE:: @@ -1403,14 +1405,20 @@ def has_order(self, value, num_checks=8): sage: E.has_order(N^2 + N) True + Due to the nature of the algorithm (testing multiple of points) and the Hasse-Weil bound, we see that for testing prime orders, ``num_checks=1`` is sufficient:: + + sage: p = random_prime(1000) + sage: E = EllipticCurve(GF(p), j=randrange(p)) + sage: q = random_prime(p + 20, lbound=p - 20) + sage: E.has_order(q, num_checks=20) == E.has_order(q, num_checks=1) + True + AUTHORS: - Mariah Lenox (2011-02-16): Initial implementation - Gareth Ma (2024-01-21): Fix bug for small curves """ - # This method does *not* use the cached value of `_order` even when - # available. q = self.base_field().order() a, b = Hasse_bounds(q, 1) if not a <= value <= b: @@ -2980,7 +2988,7 @@ def EllipticCurve_with_prime_order(N): Elliptic Curve defined by y^2 = x^3 + 20*x + 20 over Finite Field of size 23, Elliptic Curve defined by y^2 = x^3 + 10*x + 16 over Finite Field of size 23] sage: import itertools - sage: # These are the only primes, by the Weil-Hasse bound + sage: # These are the only primes, by the Hasse-Weil bound sage: for q in prime_range(17, 35): ....: K = GF(q) ....: for u in itertools.product(range(q), repeat=2): From a880b3f2d993363b70cf51c64cf391ba45e96f18 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sun, 15 Sep 2024 21:10:31 +0200 Subject: [PATCH 193/193] Updated SageMath version to 10.5.beta4 --- CITATION.cff | 4 ++-- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 4 ++-- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/version_requirements.txt | 2 +- build/pkgs/sage_docbuild/version_requirements.txt | 2 +- build/pkgs/sage_setup/version_requirements.txt | 2 +- build/pkgs/sage_sws2rst/version_requirements.txt | 2 +- build/pkgs/sagelib/version_requirements.txt | 2 +- build/pkgs/sagemath_bliss/version_requirements.txt | 2 +- build/pkgs/sagemath_categories/version_requirements.txt | 2 +- build/pkgs/sagemath_coxeter3/version_requirements.txt | 2 +- build/pkgs/sagemath_environment/version_requirements.txt | 2 +- build/pkgs/sagemath_mcqd/version_requirements.txt | 2 +- build/pkgs/sagemath_meataxe/version_requirements.txt | 2 +- build/pkgs/sagemath_objects/version_requirements.txt | 2 +- build/pkgs/sagemath_repl/version_requirements.txt | 2 +- build/pkgs/sagemath_sirocco/version_requirements.txt | 2 +- build/pkgs/sagemath_tdlib/version_requirements.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_conda/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-bliss/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-coxeter3/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-mcqd/VERSION.txt | 2 +- pkgs/sagemath-meataxe/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- pkgs/sagemath-sirocco/VERSION.txt | 2 +- pkgs/sagemath-tdlib/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 38 files changed, 44 insertions(+), 44 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 983e656e9b4..0576a00c2be 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.5.beta3 +version: 10.5.beta4 doi: 10.5281/zenodo.8042260 -date-released: 2024-09-03 +date-released: 2024-09-15 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/VERSION.txt b/VERSION.txt index e6ed441beea..76a54840a85 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.5.beta3, Release Date: 2024-09-03 +SageMath version 10.5.beta4, Release Date: 2024-09-15 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 0656d302cbd..3f4c1eecd6d 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=b08e077178bfe0d44d6028e67233ab008dd8dd05 -sha256=49ef0dab4287764522f7290d51ebbfe38492b2695201d87dd8452020a3b4d9d6 +sha1=d2f8c0bc6c40a0e3e8f7cb0deebee5e7bc7da501 +sha256=cde422cec1dc104f4f4b1369167a17cc77519186180bfc14322d078881581c50 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index e790ec6325f..d3929445739 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -448f7e212e38ad2bd0ed6be7ae8cad1cb3615913 +27fce1faa78ef19b8c43287016f0acbdf0fa169a diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 1c2fc34d235..f9a29f411e0 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.5b3 +sage-conf ~= 10.5b4 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index 42b2834c11f..8c69b2c724f 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.5b3 +sage-docbuild ~= 10.5b4 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index 1cc1b467e16..1dcac493c5d 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.5b3 +sage-setup ~= 10.5b4 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index 4fc39dc2f32..da184780274 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.5b3 +sage-sws2rst ~= 10.5b4 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index 70c35b74cca..6e3bb64c65c 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.5b3 +sagemath-standard ~= 10.5b4 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index 21f92c61b22..c6fb0e82600 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.5b3 +sagemath-bliss ~= 10.5b4 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index f78f1f84751..66dbe5766e0 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.5b3 +sagemath-categories ~= 10.5b4 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index f1942993ae7..a955c0b9902 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.5b3 +sagemath-coxeter3 ~= 10.5b4 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index 813d6d79688..480e8285f3b 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.5b3 +sagemath-environment ~= 10.5b4 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index e1297c038d3..ad9c0830686 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.5b3 +sagemath-mcqd ~= 10.5b4 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index 867597deb1e..0782881aee0 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.5b3 +sagemath-meataxe ~= 10.5b4 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 83940689774..a652cfa09f5 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.5b3 +sagemath-objects ~= 10.5b4 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index a4f097b1b6e..e73e1ae8031 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.5b3 +sagemath-repl ~= 10.5b4 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index ba5126542f8..d6d6f3820de 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.5b3 +sagemath-sirocco ~= 10.5b4 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 02cbd8f2d3f..e0801e2814c 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.5b3 +sagemath-tdlib ~= 10.5b4 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/src/VERSION.txt b/src/VERSION.txt index 674fddc2fcd..da4a0151f8b 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.5.beta3 +10.5.beta4 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 0ee1864e8ee..1c3e104e271 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='10.5.beta3' -SAGE_RELEASE_DATE='2024-09-03' -SAGE_VERSION_BANNER='SageMath version 10.5.beta3, Release Date: 2024-09-03' +SAGE_VERSION='10.5.beta4' +SAGE_RELEASE_DATE='2024-09-15' +SAGE_VERSION_BANNER='SageMath version 10.5.beta4, Release Date: 2024-09-15' diff --git a/src/sage/version.py b/src/sage/version.py index bf2f786a4bf..a8d3c7fd130 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 = '10.5.beta3' -date = '2024-09-03' -banner = 'SageMath version 10.5.beta3, Release Date: 2024-09-03' +version = '10.5.beta4' +date = '2024-09-15' +banner = 'SageMath version 10.5.beta4, Release Date: 2024-09-15'