From 08db50cfdeae1aabe6062798af5cb321aa156f11 Mon Sep 17 00:00:00 2001
From: Peter Bruin
Date: Thu, 18 Jan 2024 13:49:03 +0100
Subject: [PATCH 001/518] Move some code from Brandt modules to quaternion
algebras
---
.../algebras/quatalg/quaternion_algebra.py | 148 +++++++++++++++++-
src/sage/modular/quatalg/brandt.py | 103 ++++--------
2 files changed, 172 insertions(+), 79 deletions(-)
diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py
index 5a615b43978..ef16d1e491c 100644
--- a/src/sage/algebras/quatalg/quaternion_algebra.py
+++ b/src/sage/algebras/quatalg/quaternion_algebra.py
@@ -52,6 +52,7 @@
from sage.rings.rational_field import is_RationalField, QQ
from sage.rings.infinity import infinity
from sage.rings.number_field.number_field_base import NumberField
+from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.power_series_ring import PowerSeriesRing
from sage.structure.category_object import normalize_names
from sage.structure.parent import Parent
@@ -898,6 +899,76 @@ def maximal_order(self, take_shortcuts=True):
e_new = basis_for_quaternion_lattice(list(R.basis()) + e_new_gens, reverse=True)
return self.quaternion_order(e_new)
+ def order_with_level(self, level):
+ """
+ Return an order in this quaternion algebra with given level.
+
+ INPUT:
+
+ - ``level`` -- positive integer
+
+ Currently this is only implemented when the base field is the
+ rational numbers and the level is divisible by at most one
+ power of a prime that ramifies in this quaternion algebra.
+
+ EXAMPLES::
+
+ sage: A. = QuaternionAlgebra(5)
+ sage: level = 2 * 5 * 17
+ sage: O = A.order_with_level(level); O
+ Order of Quaternion Algebra (-2, -5) with base ring Rational Field with basis (1/2 + 1/2*j + 7/2*k, 1/2*i + 19/2*k, j + 7*k, 17*k)
+
+ Check that the order has the right index in the maximal order::
+
+ sage: L = O.free_module()
+ sage: N = A.maximal_order().free_module()
+ sage: L.index_in(N) == level / 5
+ True
+ """
+ if self.base_ring() is not QQ:
+ raise NotImplementedError("base field must be rational numbers")
+
+ if len(self.ramified_primes()) > 1:
+ raise NotImplementedError("currently this algorithm only works when the quaternion algebra is only ramified at one finite prime")
+
+ # The algorithm we use is similar to that in Magma (by David Kohel).
+ level = abs(level)
+ N = self.discriminant()
+ N1 = gcd(level, N)
+ M1 = level // N1
+
+ O = self.maximal_order()
+ # if N1 != 1:
+ # # we do not know why magma does the following, so we do not do it.
+ # for p in self.ramified_primes():
+ # if not (level % p**2):
+ # raise NotImplementedError("Currently sage can only compute orders whose level is divisible by at most one power of any prime that ramifies in the quaternion algebra")
+
+ # P = O._left_ideal_basis([N1] + [x * y - y * x
+ # for x in self.basis()
+ # for y in self.basis()])
+ # O = self.quaternion_order(P)
+
+ fact = factor(M1)
+ B = O.basis()
+
+ for (p, r) in fact:
+ a = int(-p) // 2
+ for v in GF(p)**4:
+ x = sum([int(v[i] + a) * B[i] for i in range(4)])
+ D = x.reduced_trace()**2 - 4 * x.reduced_norm()
+ # x = O.random_element((-p/2).floor(), (p/2).ceil())
+ if kronecker_symbol(D, p) == 1:
+ break
+ X = PolynomialRing(GF(p), 'x').gen()
+ a = ZZ((X**2 - ZZ(x.reduced_trace()) * X + ZZ(x.reduced_norm())).roots()[0][0])
+ I = O._left_ideal_basis([p**r, (x - a)**r])
+ O = O._right_order_from_ideal_basis(I)
+ # right_order returns the RightOrder of I inside O, so we
+ # do not need to do another intersection
+
+ return O
+
def invariants(self):
"""
Return the structural invariants `a`, `b` of this quaternion
@@ -1758,6 +1829,75 @@ def discriminant(self):
return (MatrixSpace(QQ, 4, 4)(L)).determinant().sqrt()
+ def _left_ideal_basis(self, gens):
+ """
+ Return a basis for the left ideal of ``self`` with given generators.
+
+ INPUT:
+
+ - ``gens`` -- list of elements of ``self``
+
+ OUTPUT:
+
+ A list of four elements of ``self``.
+
+ EXAMPLES::
+
+ sage: A. = QuaternionAlgebra(-17, -3)
+ sage: A.maximal_order()._left_ideal_basis([i + j, i - j, 2*k, A(3)])
+ [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k]
+ sage: A.maximal_order()._left_ideal_basis([3*(i + j), 3*(i - j), 6*k, A(3)])
+ [3/2 + 1/2*j + 2*k, 3/2*i + 3/2*k, j + k, 3*k]
+ """
+ return basis_for_quaternion_lattice([b * g for b in self.basis() for g in gens], reverse=False)
+
+ def _right_order_from_ideal_basis(self, basis):
+ """
+ Given a basis for a left ideal `I`, return the right order in
+ ``self`` of elements `x` such that `I x` is contained in `I`.
+
+ INPUT:
+
+ - ``basis`` -- basis for an ideal `I`
+
+ EXAMPLES:
+
+ sage: A. = QuaternionAlgebra(17)
+ sage: O = A.maximal_order()
+ sage: basis = O._left_ideal_basis([1]); basis
+ [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k]
+ sage: O._right_order_from_ideal_basis(basis)
+ Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
+
+ sage: basis = O._left_ideal_basis([i*j - j]); basis
+ [17 + 17/3*i + 4/3*k, 34/3*i + 2/3*k, j + k, 2*k]
+ sage: O._right_order_from_ideal_basis(basis)
+ Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
+ """
+ # Compute matrix of multiplication by each element of the basis.
+ B = self.basis()
+ Z = self.quaternion_algebra()
+ M = MatrixSpace(QQ, 4)
+
+ # I = matrix with rows the given basis for I
+ I = M([list(f) for f in basis])
+
+ # psi = matrix of right multiplication on each basis element
+ psi = [M([list(f * x) for x in Z.basis()]) for f in basis]
+
+ # invert them
+ psi_inv = [x**(-1) for x in psi]
+
+ # apply the four inverses to I
+ W = [I * x for x in psi_inv]
+
+ # The right order is the intersection of the row span of the W with the row span of B.
+ X = M([list(b) for b in B]).row_module(ZZ)
+ for A in W:
+ X = X.intersection(A.row_module(ZZ))
+ C = [Z(list(b)) for b in X.basis()]
+ return Z.quaternion_order(C)
+
def left_ideal(self, gens, check=True, *, is_basis=False):
r"""
Return the left ideal of this order generated by the given generators.
@@ -2155,14 +2295,16 @@ def quaternion_algebra(self):
def _compute_order(self, side='left'):
r"""
Used internally to compute either the left or right order
- associated to an ideal in a quaternion algebra. If
- action='right', compute the left order, and if action='left'
- compute the right order.
+ associated to an ideal in a quaternion algebra.
INPUT:
- ``side`` -- 'left' or 'right'
+ OUTPUT:
+
+ The left order if side='left'; the right order if side='right'.
+
EXAMPLES::
sage: R. = QuaternionAlgebra(-1,-11)
diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py
index 1c74d953f6d..05f201d915c 100644
--- a/src/sage/modular/quatalg/brandt.py
+++ b/src/sage/modular/quatalg/brandt.py
@@ -202,7 +202,7 @@
# https://www.gnu.org/licenses/
# ****************************************************************************
-from sage.arith.misc import gcd, factor, prime_divisors, kronecker, next_prime
+from sage.arith.misc import gcd, kronecker, next_prime, prime_divisors
from sage.matrix.constructor import matrix
from sage.matrix.matrix_space import MatrixSpace
from sage.misc.cachefunc import cached_method
@@ -216,7 +216,6 @@
from sage.rings.finite_rings.finite_field_constructor import GF
from sage.rings.integer import Integer
from sage.rings.integer_ring import ZZ
-from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.power_series_ring import PowerSeriesRing
from sage.rings.rational_field import QQ
from sage.rings.ring import CommutativeRing
@@ -370,13 +369,18 @@ def maximal_order(A):
EXAMPLES::
sage: A = BrandtModule(17).quaternion_algebra()
+
sage: sage.modular.quatalg.brandt.maximal_order(A)
+ doctest:...: DeprecationWarning: The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras
+ See https://github.com/sagemath/sage/issues/37089 for details.
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
sage: A = QuaternionAlgebra(17,names='i,j,k')
sage: A.maximal_order()
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37089, "The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras")
return A.maximal_order()
@@ -397,11 +401,15 @@ def basis_for_left_ideal(R, gens):
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)])
+ doctest:...: DeprecationWarning: The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras
+ See https://github.com/sagemath/sage/issues/37089 for details.
[1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k]
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)])
[3/2 + 1/2*i + k, i + 2*k, 3/2*j + 3/2*k, 3*k]
"""
- return basis_for_quaternion_lattice([b * g for b in R.basis() for g in gens], reverse=False)
+ from sage.misc.superseded import deprecation
+ deprecation(37089, "The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras")
+ return R._left_ideal_basis(gens)
def right_order(R, basis):
@@ -422,40 +430,22 @@ def right_order(R, basis):
We do a consistency check with the ideal equal to a maximal order::
- sage: B = BrandtModule(17); basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), B.maximal_order().basis())
+ sage: B = BrandtModule(17); basis = B.maximal_order()._left_ideal_basis([1])
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
+ doctest:...: DeprecationWarning: The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras
+ See https://github.com/sagemath/sage/issues/37089 for details.
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
sage: basis
[1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k]
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
- sage: basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i*j-j])
+ sage: basis = B.maximal_order()._left_ideal_basis([i*j - j])
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
"""
- # Compute matrix of multiplication by each element of the basis.
- B = R.basis()
- Z = R.quaternion_algebra()
- M = MatrixSpace(QQ, 4)
-
- # I = matrix with rows the given basis for I
- I = M([list(f) for f in basis])
-
- # psi = matrix of right multiplication on each basis element
- psi = [M([list(f * x) for x in Z.basis()]) for f in basis]
-
- # invert them
- psi_inv = [x**(-1) for x in psi]
-
- # apply the four inverses to I
- W = [I * x for x in psi_inv]
-
- # The right order is the intersection of the row span of the W with the row span of B.
- X = M([list(b) for b in B]).row_module(ZZ)
- for A in W:
- X = X.intersection(A.row_module(ZZ))
- C = [Z(list(b)) for b in X.basis()]
- return Z.quaternion_order(C)
+ from sage.misc.superseded import deprecation
+ deprecation(37089, "The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras")
+ return R._right_order_from_ideal_basis(basis)
def quaternion_order_with_given_level(A, level):
@@ -476,56 +466,17 @@ def quaternion_order_with_given_level(A, level):
sage: A. = QuaternionAlgebra(5)
sage: level = 2 * 5 * 17
sage: O = quaternion_order_with_given_level(A, level)
- sage: M = maximal_order(A)
+ doctest:...: DeprecationWarning: The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras
+ See https://github.com/sagemath/sage/issues/37089 for details.
+ sage: M = A.maximal_order()
sage: L = O.free_module()
sage: N = M.free_module()
sage: L.index_in(N) == level/5 #check that the order has the right index in the maximal order
True
"""
- if A.base_ring() is not QQ:
- raise NotImplementedError("base field must be rational numbers")
-
- if len(A.ramified_primes()) > 1:
- raise NotImplementedError("Currently this algorithm only works when the quaternion algebra is only ramified at one finite prime.")
-
- # (The algorithm we use is similar to that in Magma (by David Kohel).)
- # in the following magma code, M denotes the level
- level = abs(level)
- N = A.discriminant()
- N1 = gcd(level, N)
- M1 = level // N1
-
- O = maximal_order(A)
- # if N1 != 1:
- # # we do not know why magma does the following, so we do not do it.
- # for p in A.ramified_primes():
- # if not (level % p**2):
- # raise NotImplementedError("Currently sage can only compute orders whose level is divisible by at most one power of any prime that ramifies in the quaternion algebra")
-
- # P = basis_for_left_ideal(O, [N1] + [x * y - y * x
- # for x in A.basis()
- # for y in A.basis()])
- # O = A.quaternion_order(P)
-
- fact = factor(M1)
- B = O.basis()
-
- for (p, r) in fact:
- a = int(-p) // 2
- for v in GF(p)**4:
- x = sum([int(v[i] + a) * B[i] for i in range(4)])
- D = x.reduced_trace()**2 - 4 * x.reduced_norm()
- # x = O.random_element((-p/2).floor(), (p/2).ceil())
- if kronecker(D, p) == 1:
- break
- X = PolynomialRing(GF(p), 'x').gen()
- a = ZZ((X**2 - ZZ(x.reduced_trace()) * X + ZZ(x.reduced_norm())).roots()[0][0])
- I = basis_for_left_ideal(O, [p**r, (x - a)**r])
- O = right_order(O, I)
- # right_order returns the RightOrder of I inside O, so we
- # do not need to do another intersection
-
- return O
+ from sage.misc.superseded import deprecation
+ deprecation(37089, "The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras")
+ return A.order_with_level(level)
class BrandtSubmodule(HeckeSubmodule):
@@ -809,7 +760,6 @@ def quaternion_algebra(self):
"""
return QuaternionAlgebra(self.N())
- @cached_method
def maximal_order(self):
"""
Return a maximal order in the quaternion algebra associated to this Brandt module.
@@ -821,7 +771,7 @@ def maximal_order(self):
sage: BrandtModule(17).maximal_order() is BrandtModule(17).maximal_order()
True
"""
- return maximal_order(self.quaternion_algebra())
+ return self.quaternion_algebra().maximal_order()
@cached_method
def order_of_level_N(self):
@@ -838,7 +788,7 @@ def order_of_level_N(self):
sage: BrandtModule(7,3*17).order_of_level_N()
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j + 35*k, 1/2*i + 65/2*k, j + 19*k, 51*k)
"""
- return quaternion_order_with_given_level(self.quaternion_algebra(), self.level())
+ return self.quaternion_algebra().order_with_level(self.level())
def cyclic_submodules(self, I, p):
"""
@@ -1328,6 +1278,7 @@ def right_ideals(self, B=None):
sage: prod(not Is[i].is_equivalent(Is[j]) for i in range(n) for j in range(i))
1
"""
+ # TODO: move this code to orders, along with cyclic_submodules()
p = self._smallest_good_prime()
R = self.order_of_level_N()
I = R.unit_ideal()
From e974bd133efd06c94d7d59184356b11635acf058 Mon Sep 17 00:00:00 2001
From: Peter Bruin
Date: Thu, 18 Jan 2024 13:57:56 +0100
Subject: [PATCH 002/518] correct pull request number
---
src/sage/modular/quatalg/brandt.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py
index 05f201d915c..8a0dcb2dbbc 100644
--- a/src/sage/modular/quatalg/brandt.py
+++ b/src/sage/modular/quatalg/brandt.py
@@ -372,7 +372,7 @@ def maximal_order(A):
sage: sage.modular.quatalg.brandt.maximal_order(A)
doctest:...: DeprecationWarning: The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras
- See https://github.com/sagemath/sage/issues/37089 for details.
+ See https://github.com/sagemath/sage/issues/37090 for details.
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
sage: A = QuaternionAlgebra(17,names='i,j,k')
@@ -380,7 +380,7 @@ def maximal_order(A):
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k)
"""
from sage.misc.superseded import deprecation
- deprecation(37089, "The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras")
+ deprecation(37090, "The function maximal_order() is deprecated, use the maximal_order() method of quaternion algebras")
return A.maximal_order()
@@ -402,13 +402,13 @@ def basis_for_left_ideal(R, gens):
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)])
doctest:...: DeprecationWarning: The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras
- See https://github.com/sagemath/sage/issues/37089 for details.
+ See https://github.com/sagemath/sage/issues/37090 for details.
[1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k]
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)])
[3/2 + 1/2*i + k, i + 2*k, 3/2*j + 3/2*k, 3*k]
"""
from sage.misc.superseded import deprecation
- deprecation(37089, "The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras")
+ deprecation(37090, "The function basis_for_left_ideal() is deprecated, use the _left_ideal_basis() method of quaternion algebras")
return R._left_ideal_basis(gens)
@@ -433,7 +433,7 @@ def right_order(R, basis):
sage: B = BrandtModule(17); basis = B.maximal_order()._left_ideal_basis([1])
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
doctest:...: DeprecationWarning: The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras
- See https://github.com/sagemath/sage/issues/37089 for details.
+ See https://github.com/sagemath/sage/issues/37090 for details.
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
sage: basis
[1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k]
@@ -444,7 +444,7 @@ def right_order(R, basis):
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k)
"""
from sage.misc.superseded import deprecation
- deprecation(37089, "The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras")
+ deprecation(37090, "The function right_order() is deprecated, use the _right_order_from_ideal_basis() method of quaternion algebras")
return R._right_order_from_ideal_basis(basis)
@@ -467,7 +467,7 @@ def quaternion_order_with_given_level(A, level):
sage: level = 2 * 5 * 17
sage: O = quaternion_order_with_given_level(A, level)
doctest:...: DeprecationWarning: The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras
- See https://github.com/sagemath/sage/issues/37089 for details.
+ See https://github.com/sagemath/sage/issues/37090 for details.
sage: M = A.maximal_order()
sage: L = O.free_module()
sage: N = M.free_module()
@@ -475,7 +475,7 @@ def quaternion_order_with_given_level(A, level):
True
"""
from sage.misc.superseded import deprecation
- deprecation(37089, "The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras")
+ deprecation(37090, "The function quaternion_order_with_given_level() is deprecated, use the order_with_level() method of quaternion algebras")
return A.order_with_level(level)
From 8b381646ff533207daf250ef0b87ba6fbf5e1b5f Mon Sep 17 00:00:00 2001
From: Lorenz Panny
Date: Thu, 18 Jan 2024 23:05:30 +0100
Subject: [PATCH 003/518] throw ValueError instead of TypeError when logarithm
doesn't exist
...because attempting to run this with unhashable elements throws the
same exception, which can cause confusion in higher-level algorithms.
---
.../additive_abelian/additive_abelian_wrapper.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
index 2a7496cb215..9cb8d958c61 100644
--- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
+++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
@@ -365,7 +365,7 @@ def discrete_log(self, x, gens=None):
sage: G.discrete_log(V([6, 4]))
Traceback (most recent call last):
...
- TypeError: Not in group
+ ValueError: not in group
::
@@ -617,7 +617,7 @@ def _base(j, k, c):
if key in tab:
return tab[key] + vector(y)
- raise TypeError('Not in group')
+ raise ValueError('not in group')
def _rec(j, k, c):
@@ -744,7 +744,7 @@ def _expand_basis_pgroup(p, alphas, vals, beta, h, rel):
beta_q *= p
try:
e = _discrete_log_pgroup(p, vals, alphas, -beta_q)
- except TypeError:
+ except ValueError:
continue
# step 6
_expand_basis_pgroup(p, alphas, vals, beta, h, list(e) + [p**v])
@@ -815,7 +815,7 @@ def basis_from_generators(gens, ords=None):
beta, ord_beta = pgens.pop()
try:
dlog = _discrete_log_pgroup(p, vals, alphas, beta)
- except TypeError:
+ except ValueError:
pass
else:
continue
@@ -828,7 +828,7 @@ def basis_from_generators(gens, ords=None):
# assert beta_q == beta * p**v
try:
e = _discrete_log_pgroup(p, vals, alphas, -beta_q)
- except TypeError:
+ except ValueError:
continue
_expand_basis_pgroup(p, alphas, vals, beta, val_beta, list(e) + [p**v])
# assert all(a.order() == p**v for a,v in zip(alphas, vals))
From 7ffd84827154df5d9dcc3e691dfb0bc7a7975f92 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Fri, 26 Jan 2024 17:13:28 +0000
Subject: [PATCH 004/518] fix mod 0 polyring
---
src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi | 13 +++++----
.../rings/polynomial/polynomial_template.pxi | 28 ++++++++++++++++++-
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
index 36c87c75efa..4cf76fce96e 100644
--- a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
+++ b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
@@ -367,12 +367,15 @@ cdef inline int celement_pow(ZZ_pEX_c* res, ZZ_pEX_c* x, long e, ZZ_pEX_c *modul
sig_off()
else:
if ZZ_pEX_deg(modulus[0]) == 1:
- ZZ_pEX_rem(y, x[0], modulus[0])
- sig_on()
- ZZ_pEX_power(res[0], y, e)
- sig_off()
- return 0
+ ZZ_pEX_rem(y, x[0], modulus[0])
+ sig_on()
+ ZZ_pEX_power(res[0], y, e)
+ sig_off()
+ return 0
+ # This was causing a crash, but it's fixed "upstream"
+ # sig_on()
ZZ_pEX_Modulus_build(mod, modulus[0])
+ # sig_off()
if ZZ_pEX_deg(x[0]) < ZZ_pEX_deg(modulus[0]):
sig_on()
ZZ_pEX_PowerMod_pre(res[0], x[0], e, mod)
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index 7c53f107180..61143f071db 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -569,7 +569,6 @@ cdef class Polynomial_template(Polynomial):
"""
EXAMPLES::
- sage: P. = GF(2)[]
sage: P. = GF(2)[]
sage: x^1000
x^1000
@@ -583,6 +582,28 @@ cdef class Polynomial_template(Polynomial):
x^9 + x^8 + x^7 + x^5 + x^3
sage: pow(f, 2, h)
x^9 + x^8 + x^7 + x^5 + x^3
+
+ TESTS:
+
+ Ensure modulo `0` and modulo `1` does not crash (:issue:`37169`)::
+
+ sage: R. = GF(2)[]
+ sage: pow(x + 1, 2, R.zero())
+ Traceback (most recent call last):
+ ...
+ ZeroDivisionError: modulus must be nonzero
+ sage: pow(x + 1, 2, R.one())
+ 0
+
+ ::
+
+ sage: R. = GF(2^8)[]
+ sage: pow(x + 1, 2, R.zero())
+ Traceback (most recent call last):
+ ...
+ ZeroDivisionError: modulus must be nonzero
+ sage: pow(x + 1, 2, R.one())
+ 0
"""
if not isinstance(self, Polynomial_template):
raise NotImplementedError("%s^%s not defined."%(ee,self))
@@ -615,6 +636,11 @@ cdef class Polynomial_template(Polynomial):
else:
if parent is not (modulus)._parent and parent != (modulus)._parent:
modulus = parent.coerce(modulus)
+ # I tried using `celement_is_zero` but it errors because `modulus` is a Python object
+ if modulus.is_zero():
+ raise ZeroDivisionError("modulus must be nonzero")
+ if modulus.is_one():
+ return parent.zero()
celement_pow(&r.x, &(self).x, e, &(modulus).x, (self)._cparent)
#assert(r._parent(pari(self)**ee) == r)
From 320ebbe066bcd4c5463c4771e9bad50f96f23d93 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Fri, 26 Jan 2024 19:10:04 +0000
Subject: [PATCH 005/518] fix ring SIGABRT
---
src/sage/rings/polynomial/polynomial_zmod_flint.pyx | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
index 3a66198d568..815b9758176 100644
--- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
+++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
@@ -554,7 +554,11 @@ cdef class Polynomial_zmod_flint(Polynomial_template):
while nmod_poly_length(&t1.x) != 0 and n_deg < nmod_poly_degree(&t1.x):
q = self._new()
r1 = self._new()
+
+ sig_on()
nmod_poly_divrem(&q.x, &r1.x, &s1.x, &t1.x)
+ sig_off()
+
r0 = s0 - q*t0
s0 = t0
s1 = t1
From 5ead46930bf50dc9c9d7c3dc02bea354a254fc84 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Fri, 26 Jan 2024 19:14:34 +0000
Subject: [PATCH 006/518] Revert "throw ValueError instead of TypeError when
logarithm doesn't exist"
This reverts commit 8b381646ff533207daf250ef0b87ba6fbf5e1b5f, which is
from another PR.
---
.../additive_abelian/additive_abelian_wrapper.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
index 9cb8d958c61..2a7496cb215 100644
--- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
+++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py
@@ -365,7 +365,7 @@ def discrete_log(self, x, gens=None):
sage: G.discrete_log(V([6, 4]))
Traceback (most recent call last):
...
- ValueError: not in group
+ TypeError: Not in group
::
@@ -617,7 +617,7 @@ def _base(j, k, c):
if key in tab:
return tab[key] + vector(y)
- raise ValueError('not in group')
+ raise TypeError('Not in group')
def _rec(j, k, c):
@@ -744,7 +744,7 @@ def _expand_basis_pgroup(p, alphas, vals, beta, h, rel):
beta_q *= p
try:
e = _discrete_log_pgroup(p, vals, alphas, -beta_q)
- except ValueError:
+ except TypeError:
continue
# step 6
_expand_basis_pgroup(p, alphas, vals, beta, h, list(e) + [p**v])
@@ -815,7 +815,7 @@ def basis_from_generators(gens, ords=None):
beta, ord_beta = pgens.pop()
try:
dlog = _discrete_log_pgroup(p, vals, alphas, beta)
- except ValueError:
+ except TypeError:
pass
else:
continue
@@ -828,7 +828,7 @@ def basis_from_generators(gens, ords=None):
# assert beta_q == beta * p**v
try:
e = _discrete_log_pgroup(p, vals, alphas, -beta_q)
- except ValueError:
+ except TypeError:
continue
_expand_basis_pgroup(p, alphas, vals, beta, val_beta, list(e) + [p**v])
# assert all(a.order() == p**v for a,v in zip(alphas, vals))
From 7a72ca50ad57bc920d2675d1d1d2e74ecc5bf356 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Sat, 10 Feb 2024 15:38:21 +0000
Subject: [PATCH 007/518] =?UTF-8?q?apply=20review=20changes=20=F0=9F=93=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi | 1 -
src/sage/rings/polynomial/polynomial_template.pxi | 5 ++---
src/sage/rings/polynomial/polynomial_zmod_flint.pyx | 10 ++++++++++
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
index 4cf76fce96e..bce8452d84a 100644
--- a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
+++ b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
@@ -372,7 +372,6 @@ cdef inline int celement_pow(ZZ_pEX_c* res, ZZ_pEX_c* x, long e, ZZ_pEX_c *modul
ZZ_pEX_power(res[0], y, e)
sig_off()
return 0
- # This was causing a crash, but it's fixed "upstream"
# sig_on()
ZZ_pEX_Modulus_build(mod, modulus[0])
# sig_off()
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index 61143f071db..ae58bcfb925 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -636,10 +636,9 @@ cdef class Polynomial_template(Polynomial):
else:
if parent is not (modulus)._parent and parent != (modulus)._parent:
modulus = parent.coerce(modulus)
- # I tried using `celement_is_zero` but it errors because `modulus` is a Python object
- if modulus.is_zero():
+ if celement_is_zero(&(modulus).x, (self)._cparent):
raise ZeroDivisionError("modulus must be nonzero")
- if modulus.is_one():
+ if celement_is_one(&(modulus).x, (self)._cparent):
return parent.zero()
celement_pow(&r.x, &(self).x, e, &(modulus).x, (self)._cparent)
diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
index 815b9758176..aff24b6dfe8 100644
--- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
+++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
@@ -532,6 +532,16 @@ cdef class Polynomial_zmod_flint(Polynomial_template):
(3*x^4 + 2*x^3 + x^2 + 2*x, x^4 + 3*x^3 + x^2 + x)
sage: (p*d % x^9) == n
True
+
+ Check that :issue:`37169` is fixed - it does not throw an error::
+
+ sage: R. = Zmod(4)[]
+ sage: _. = R.quotient_ring(x^2 - 1)
+ sage: c = 2 * z + 1
+ sage: c * Zmod(2).zero()
+ Traceback (most recent call last):
+ ...
+ RuntimeError: Aborted
"""
if n_deg < 0 or d_deg < 0:
raise ValueError("The degree bounds n_deg and d_deg should be positive.")
From 542bfdd47e5fa0675bcea9b80bdbbea11d63c696 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Tue, 13 Feb 2024 11:35:53 +0000
Subject: [PATCH 008/518] fix crashes + normalise code
---
src/sage/libs/flint/nmod_poly_linkage.pxi | 13 ++++++----
.../rings/polynomial/polynomial_template.pxi | 24 +++++++++++++++++--
.../polynomial/polynomial_zmod_flint.pyx | 7 ++++--
3 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi
index 303cb6472fe..eb181588704 100644
--- a/src/sage/libs/flint/nmod_poly_linkage.pxi
+++ b/src/sage/libs/flint/nmod_poly_linkage.pxi
@@ -578,15 +578,18 @@ cdef inline int celement_gcd(nmod_poly_t res, nmod_poly_t a, nmod_poly_t b, unsi
sage: (G//d)*d == G
True
"""
+ cdef unsigned long leadcoeff, modulus, leadcoeff_res
if celement_is_zero(b, n):
nmod_poly_set(res, a)
return 0
- nmod_poly_gcd(res, a, b)
- cdef unsigned long leadcoeff = nmod_poly_get_coeff_ui(res, nmod_poly_degree(res))
- cdef unsigned long modulus = nmod_poly_modulus(res)
- if n_gcd(modulus,leadcoeff) == 1:
- nmod_poly_make_monic(res, res)
+ # A check that the leading coefficients are invertible is *not* sufficient
+ try:
+ sig_on()
+ nmod_poly_gcd(res, a, b)
+ sig_off()
+ except RuntimeError:
+ raise ValueError("non-invertible elements encountered during GCD")
cdef inline int celement_xgcd(nmod_poly_t res, nmod_poly_t s, nmod_poly_t t, nmod_poly_t a, nmod_poly_t b, unsigned long n) except -2:
"""
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index ae58bcfb925..d3bc6cf6660 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -352,10 +352,30 @@ cdef class Polynomial_template(Polynomial):
x + 1
sage: f.gcd(x^2)
x
+
+ TESTS:
+
+ Ensure non-invertible elements does not crash Sage::
+
+ sage: R. = Zmod(4)[]
+ sage: f = R(2 * x)
+ sage: f.gcd(f)
+ 2*x
+
+ ::
+
+ sage: f = x^2 + 3 * x + 1
+ sage: g = x^2 + x + 1
+ sage: f.gcd(g)
+ Traceback (most recent call last):
+ ...
+ ValueError: non-invertible elements encountered during GCD
"""
- if(celement_is_zero(&self.x, (self)._cparent)):
+ if celement_is_zero(&self.x, (self)._cparent):
return other
- if(celement_is_zero(&other.x, (self)._cparent)):
+ if celement_is_zero(&other.x, (self)._cparent):
+ return self
+ if celement_equal(&self.x, &other.x, (self)._cparent):
return self
cdef type T = type(self)
diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
index aff24b6dfe8..17f1e1878af 100644
--- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
+++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx
@@ -757,9 +757,12 @@ cdef class Polynomial_zmod_flint(Polynomial_template):
...
ValueError: leading coefficient must be invertible
"""
- if self.base_ring().characteristic().gcd(
- self.leading_coefficient().lift()) != 1:
+ cdef unsigned long leadcoeff, modulus
+ leadcoeff = nmod_poly_get_coeff_ui(&self.x, nmod_poly_degree(&self.x))
+ modulus = nmod_poly_modulus(&self.x)
+ if leadcoeff > 1 and n_gcd(modulus, leadcoeff) != 1:
raise ValueError("leading coefficient must be invertible")
+
cdef Polynomial_zmod_flint res = self._new()
nmod_poly_make_monic(&res.x, &self.x)
return res
From 256b3a747d4187069f4b4cfdbeb3e3addffb0040 Mon Sep 17 00:00:00 2001
From: grhkm21 <83517584+grhkm21@users.noreply.github.com>
Date: Tue, 13 Feb 2024 11:48:42 +0000
Subject: [PATCH 009/518] review
ye
Co-authored-by: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com>
---
src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi | 2 --
src/sage/rings/polynomial/polynomial_template.pxi | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
index bce8452d84a..31805daaaf3 100644
--- a/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
+++ b/src/sage/libs/ntl/ntl_ZZ_pEX_linkage.pxi
@@ -372,9 +372,7 @@ cdef inline int celement_pow(ZZ_pEX_c* res, ZZ_pEX_c* x, long e, ZZ_pEX_c *modul
ZZ_pEX_power(res[0], y, e)
sig_off()
return 0
- # sig_on()
ZZ_pEX_Modulus_build(mod, modulus[0])
- # sig_off()
if ZZ_pEX_deg(x[0]) < ZZ_pEX_deg(modulus[0]):
sig_on()
ZZ_pEX_PowerMod_pre(res[0], x[0], e, mod)
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index d3bc6cf6660..c7b97c75a9b 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -355,7 +355,7 @@ cdef class Polynomial_template(Polynomial):
TESTS:
- Ensure non-invertible elements does not crash Sage::
+ Ensure non-invertible elements do not crash Sage::
sage: R. = Zmod(4)[]
sage: f = R(2 * x)
From 059c73346efcb9f6819e1af160c412e879d308f3 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Tue, 13 Feb 2024 12:41:11 +0000
Subject: [PATCH 010/518] link issue #37317
---
src/sage/rings/polynomial/polynomial_template.pxi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index c7b97c75a9b..863f642754d 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -355,7 +355,7 @@ cdef class Polynomial_template(Polynomial):
TESTS:
- Ensure non-invertible elements do not crash Sage::
+ Ensure non-invertible elements does not crash Sage (:issue:`37317`)::
sage: R. = Zmod(4)[]
sage: f = R(2 * x)
From 2e4020a3c691d39b117816b5be2c7905bcd858a0 Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Wed, 14 Feb 2024 13:46:24 +0200
Subject: [PATCH 011/518] Add class FlatsMatroid
---
src/sage/matroids/constructor.py | 17 +-
src/sage/matroids/flats_matroid.pxd | 20 ++
src/sage/matroids/flats_matroid.pyx | 524 ++++++++++++++++++++++++++++
src/sage/matroids/unpickling.pyx | 46 +++
4 files changed, 605 insertions(+), 2 deletions(-)
create mode 100644 src/sage/matroids/flats_matroid.pxd
create mode 100644 src/sage/matroids/flats_matroid.pyx
diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py
index 137f4c28230..ef14148947b 100644
--- a/src/sage/matroids/constructor.py
+++ b/src/sage/matroids/constructor.py
@@ -113,6 +113,7 @@
import sage.matroids.basis_exchange_matroid
from .rank_matroid import RankMatroid
from .circuits_matroid import CircuitsMatroid
+from .flats_matroid import FlatsMatroid
from .circuit_closures_matroid import CircuitClosuresMatroid
from .basis_matroid import BasisMatroid
from .linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid
@@ -180,6 +181,7 @@ def Matroid(groundset=None, data=None, **kwds):
- ``circuits`` -- The list of circuits of the matroid.
- ``nonspanning_circuits`` -- The list of nonspanning_circuits of the
matroid.
+ - ``flats`` -- The dictionary of flats indexed by their rank.
- ``graph`` -- A graph, whose edges form the elements of the matroid.
- ``matrix`` -- A matrix representation of the matroid.
- ``reduced_matrix`` -- A reduced representation of the matroid: if
@@ -698,8 +700,9 @@ def Matroid(groundset=None, data=None, **kwds):
key = None
if data is None:
for k in ['bases', 'independent_sets', 'circuits',
- 'nonspanning_circuits', 'graph', 'matrix', 'reduced_matrix',
- 'rank_function', 'revlex', 'circuit_closures', 'matroid']:
+ 'nonspanning_circuits', 'flats', 'graph', 'matrix',
+ 'reduced_matrix', 'rank_function', 'revlex',
+ 'circuit_closures', 'matroid']:
if k in kwds:
data = kwds.pop(k)
key = k
@@ -791,6 +794,16 @@ def Matroid(groundset=None, data=None, **kwds):
nsc_defined=True
)
+ # Flats
+ elif key == 'flats':
+ # Determine groundset
+ if groundset is None:
+ groundset = set()
+ for i in data:
+ for F in data[i]:
+ groundset.update(F)
+ M = FlatsMatroid(groundset=groundset, flats=data)
+
# Graphs:
elif key == 'graph':
from sage.graphs.graph import Graph
diff --git a/src/sage/matroids/flats_matroid.pxd b/src/sage/matroids/flats_matroid.pxd
new file mode 100644
index 00000000000..f3ceb787f30
--- /dev/null
+++ b/src/sage/matroids/flats_matroid.pxd
@@ -0,0 +1,20 @@
+from sage.matroids.matroid cimport Matroid
+
+cdef class FlatsMatroid(Matroid):
+ cdef frozenset _groundset # _E
+ cdef int _matroid_rank # _R
+ cdef dict _F # flats
+ cpdef groundset(self) noexcept
+ cpdef _rank(self, X) noexcept
+ cpdef full_rank(self) noexcept
+ cpdef _is_independent(self, F) noexcept
+
+ # enumeration
+ cpdef flats(self, k) noexcept
+ cpdef whitney_numbers2(self) noexcept
+
+ # isomorphism
+ cpdef _is_isomorphic(self, other, certificate=*) noexcept
+
+ # verification
+ cpdef is_valid(self) noexcept
diff --git a/src/sage/matroids/flats_matroid.pyx b/src/sage/matroids/flats_matroid.pyx
new file mode 100644
index 00000000000..546526ee300
--- /dev/null
+++ b/src/sage/matroids/flats_matroid.pyx
@@ -0,0 +1,524 @@
+r"""
+Flats matroids
+
+Matroids are characterized by a set of flats, which are sets invariant under
+closure. The ``FlatsMatroid`` class implements matroids using this information
+as data.
+
+A ``FlatsMatroid`` can be created from another matroid or from a dictionary of
+flats. For a full description of allowed inputs, see
+:class:`below `. It is
+recommended to use the :func:`Matroid() `
+function for a more flexible way of constructing a ``FlatsMatroid`` and other
+classes of matroids. For direct access to the ``FlatsMatroid`` constructor,
+run::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+
+AUTHORS:
+
+- Giorgos Mousa (2024-01-01): initial version
+"""
+
+# ****************************************************************************
+# Copyright (C) 2024 Giorgos Mousa
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# https://www.gnu.org/licenses/
+# ****************************************************************************
+
+from sage.structure.richcmp cimport rich_to_bool, richcmp
+from sage.matroids.matroid cimport Matroid
+from sage.matroids.set_system cimport SetSystem
+from sage.matroids.utilities import setprint_s
+from cpython.object cimport Py_EQ, Py_NE
+
+
+cdef class FlatsMatroid(Matroid):
+ r"""
+ INPUT:
+
+ - ``M`` -- a matroid (default: ``None``)
+ - ``groundset`` -- a list (default: ``None``); the groundset of the matroid
+ - ``flats`` -- a dictionary (default: ``None``); the lists of `k`-flats of
+ the matroid, indexed by their rank `k`
+
+ .. NOTE::
+
+ For a more flexible means of input, use the ``Matroid()`` function.
+ """
+
+ # necessary (__init__, groundset, _rank)
+
+ def __init__(self, M=None, groundset=None, flats=None):
+ """
+ Initialization of the matroid. See class docstring for full
+ documentation.
+
+ TESTS::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Fano())
+ sage: TestSuite(M).run()
+ """
+ self._F = {}
+ if M is not None:
+ self._groundset = frozenset(M.groundset())
+ for i in range(len(M.groundset()) + 1):
+ for F in M.flats(i):
+ try:
+ self._F[i].add(frozenset(F))
+ except KeyError:
+ self._F[i] = set()
+ self._F[i].add(frozenset(F))
+ else:
+ self._groundset = frozenset(groundset)
+ for i in flats:
+ for F in flats[i]:
+ try:
+ self._F[i].add(frozenset(F))
+ except KeyError:
+ self._F[i] = set()
+ self._F[i].add(frozenset(F))
+ self._matroid_rank = self.rank(self._groundset)
+
+ cpdef groundset(self) noexcept:
+ """
+ Return the groundset of the matroid.
+
+ The groundset is the set of elements that comprise the matroid.
+
+ OUTPUT: a set
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Theta(2))
+ sage: sorted(M.groundset())
+ ['x0', 'x1', 'y0', 'y1']
+ """
+ return self._groundset
+
+ cpdef _rank(self, X) noexcept:
+ """
+ Return the rank of a set ``X``.
+
+ This method does no checking on ``X``, and ``X`` may be assumed to have
+ the same interface as ``frozenset``.
+
+ INPUT:
+
+ - ``X`` -- an object with Python's ``frozenset`` interface
+
+ OUTPUT: an integer; the rank of ``X`` in the matroid
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Theta(3))
+ sage: M._rank(['x1', 'y0', 'y2'])
+ 2
+ """
+ cdef frozenset XX = frozenset(X)
+ cdef int min = len(self._groundset)
+ for i in self._F:
+ if i < min:
+ for f in self._F[i]:
+ if f >= XX:
+ min = i
+ break
+ return min
+
+ # optional
+
+ cpdef full_rank(self) noexcept:
+ r"""
+ Return the rank of the matroid.
+
+ The *rank* of the matroid is the size of the largest independent
+ subset of the groundset.
+
+ OUTPUT: an integer; the rank of the matroid
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Theta(7))
+ sage: M.full_rank()
+ 7
+ """
+ return self._matroid_rank
+
+ cpdef _is_isomorphic(self, other, certificate=False) noexcept:
+ """
+ Test if ``self`` is isomorphic to ``other``.
+
+ INPUT:
+
+ - ``other`` -- a matroid
+ - ``certificate`` -- boolean (default: ``False``)
+
+
+ OUTPUT: boolean, and, if ``certificate=True``, a dictionary giving the
+ isomorphism or ``None``
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = matroids.catalog.NonDesargues()
+ sage: N = FlatsMatroid(M)
+ sage: N._is_isomorphic(M)
+ True
+ sage: N._is_isomorphic(matroids.catalog.R9())
+ False
+
+ .. NOTE::
+
+ Internal version that does no input checking.
+ """
+ if certificate:
+ return self._is_isomorphic(other), self._isomorphism(other)
+ N = FlatsMatroid(other)
+ flats_self = frozenset([F for i in self._F for F in self._F[i]])
+ flats_other = frozenset([F for i in N._F for F in N._F[i]])
+ SS = SetSystem(list(self._groundset), flats_self)
+ OS = SetSystem(list(N._groundset), flats_other)
+ return SS._isomorphism(OS) is not None
+
+ # representation
+
+ def _repr_(self):
+ """
+ Return a string representation of the matroid.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Uniform(6, 6)); M
+ Matroid of rank 6 on 6 elements with 64 flats
+ """
+ flats_num = sum(1 for i in self._F for F in self._F[i])
+ return Matroid._repr_(self) + " with " + str(flats_num) + " flats"
+
+ # comparison
+
+ def __hash__(self):
+ r"""
+ Return an invariant of the matroid.
+
+ This function is called when matroids are added to a set. It is very
+ desirable to override it so it can distinguish matroids on the same
+ groundset, which is a very typical use case!
+
+ .. WARNING::
+
+ This method is linked to __richcmp__ (in Cython) and __cmp__ or
+ __eq__/__ne__ (in Python). If you override one, you should
+ (and in Cython: MUST) override the other!
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Vamos())
+ sage: N = FlatsMatroid(matroids.catalog.Vamos())
+ sage: hash(M) == hash(N)
+ True
+ sage: O = FlatsMatroid(matroids.catalog.NonVamos())
+ sage: hash(M) == hash(O)
+ False
+ """
+ flats = frozenset([F for i in self._F for F in self._F[i]])
+ return hash(tuple([self._groundset, flats]))
+
+ def __richcmp__(left, right, int op):
+ r"""
+ Compare two matroids.
+
+ We take a very restricted view on equality: the objects need to be of
+ the exact same type (so no subclassing) and the internal data need to
+ be the same. For FlatsMatroids, this means that the groundsets and the
+ dictionaries of flats of the two matroids are equal.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Pappus())
+ sage: N = FlatsMatroid(matroids.catalog.NonPappus())
+ sage: M == N
+ False
+ sage: N = Matroid(M.bases())
+ sage: M == N
+ False
+ """
+ cdef FlatsMatroid lt, rt
+ if op not in [Py_EQ, Py_NE]:
+ return NotImplemented
+ if type(left) is not type(right):
+ return NotImplemented
+ lt = left
+ rt = right
+ if lt.groundset() != rt.groundset():
+ return rich_to_bool(op, 1)
+ if lt.full_rank() != rt.full_rank():
+ return rich_to_bool(op, 1)
+ return richcmp(lt._F, rt._F, op)
+
+ # copying, loading, saving
+
+ def __copy__(self):
+ """
+ Create a shallow copy.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Vamos())
+ sage: N = copy(M) # indirect doctest
+ sage: M == N
+ True
+ sage: M.groundset() is N.groundset()
+ True
+ """
+ N = FlatsMatroid(groundset=[], flats={})
+ N._groundset = self._groundset
+ N._F = self._F
+ N._matroid_rank = self._matroid_rank
+ N.rename(self.get_custom_name())
+ return N
+
+ def __deepcopy__(self, memo=None):
+ """
+ Create a deep copy.
+
+ .. NOTE::
+
+ Since matroids are immutable, a shallow copy normally suffices.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Vamos())
+ sage: N = deepcopy(M) # indirect doctest
+ sage: M == N
+ True
+ sage: M.groundset() is N.groundset()
+ False
+ """
+ if memo is None:
+ memo = {}
+ from copy import deepcopy
+ # Since matroids are immutable, N cannot reference itself in correct code, so no need to worry about the recursion.
+ N = FlatsMatroid(groundset=deepcopy(self._groundset, memo), flats=deepcopy(self._F, memo))
+ N.rename(deepcopy(self.get_custom_name(), memo))
+ return N
+
+ def __reduce__(self):
+ """
+ Save the matroid for later reloading.
+
+ OUTPUT:
+
+ A tuple ``(unpickle, (version, data))``, where ``unpickle`` is the
+ name of a function that, when called with ``(version, data)``,
+ produces a matroid isomorphic to ``self``. ``version`` is an integer
+ (currently 0) and ``data`` is a tuple ``(E, F, name)`` where ``E`` is
+ the groundset, ``F`` is the dictionary of flats, and ``name`` is a
+ custom name.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Vamos())
+ sage: M == loads(dumps(M)) # indirect doctest
+ True
+ sage: M.reset_name()
+ sage: loads(dumps(M))
+ Matroid of rank 4 on 8 elements with 79 flats
+ """
+ import sage.matroids.unpickling
+ data = (self._groundset, self._F, self.get_custom_name())
+ version = 0
+ return sage.matroids.unpickling.unpickle_flats_matroid, (version, data)
+
+ # enumeration
+
+ cpdef flats(self, k) noexcept:
+ r"""
+ Return the flats of the matroid of specified rank.
+
+ INPUT:
+
+ - ``k`` -- an integer
+
+ OUTPUT: a SetSystem
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Uniform(3, 4))
+ sage: sorted(M.flats(2), key=str)
+ [frozenset({0, 1}),
+ frozenset({0, 2}),
+ frozenset({0, 3}),
+ frozenset({1, 2}),
+ frozenset({1, 3}),
+ frozenset({2, 3})]
+ """
+ if k in self._F:
+ return SetSystem(list(self._groundset), self._F[k])
+ return SetSystem(list(self._groundset))
+
+ def flats_iterator(self, k):
+ r"""
+ Return an iterator over the flats of the matroid of specified rank.
+
+ INPUT:
+
+ - ``k`` -- an integer
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.Uniform(3, 4))
+ sage: sorted([list(F) for F in M.flats_iterator(2)])
+ [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
+ """
+ if k in self._F:
+ for F in self._F[k]:
+ yield F
+
+ cpdef whitney_numbers2(self) noexcept:
+ r"""
+ Return the Whitney numbers of the second kind of the matroid.
+
+ The Whitney numbers of the second kind are here encoded as a vector
+ `(W_0, ..., W_r)`, where `W_i` is the number of flats of rank `i`, and
+ `r` is the rank of the matroid.
+
+ OUTPUT: a list of integers
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.XY13())
+ sage: M.whitney_numbers2()
+ [1, 13, 78, 250, 394, 191, 1]
+ """
+ cdef list W = []
+ cdef int i
+ for i in sorted(self._F):
+ W.append(len(self._F[i]))
+ return W
+
+ # verification
+
+ cpdef is_valid(self) noexcept:
+ r"""
+ Test if ``self`` obeys the matroid axioms.
+
+ For a matroid defined by its flats, we check the flat axioms.
+
+ OUTPUT: boolean
+
+ EXAMPLES::
+
+ sage: M = Matroid(flats={0: [[]], 1: [[0], [1]], 2: [[0, 1]]})
+ sage: M.is_valid()
+ True
+ sage: M = Matroid(flats={0: [''], 1: ['a', 'b'], 2: ['ab']})
+ sage: M.is_valid()
+ True
+ sage: M = Matroid(flats={0: [[]], 1: [[0], [1]]}) # missing groundset
+ sage: M.is_valid()
+ False
+ sage: M = Matroid(flats={0: [''],
+ ....: 1: ['0','1','2','3','4','5','6','7','8','9','a','b','c'],
+ ....: 2: ['45','46','47','4c','56','57','5c','67','6c','7c',
+ ....: '048','149','24a','34b','059','15a','25b','358',
+ ....: '06a','16b','268','369','07b','178','279','37a',
+ ....: '0123c','89abc'],
+ ....: 3: ['0123456789abc']})
+ sage: M.is_valid()
+ True
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.NonVamos())
+ sage: M.is_valid()
+ True
+
+ TESTS::
+
+ sage: Matroid(flats={0: [], 1: [[0], [1]], 2: [[0, 1]]}).is_valid() # missing an intersection
+ False
+ sage: Matroid(flats={0: [[]], 2: [[0], [1]], 3: [[0, 1]]}).is_valid() # invalid ranks
+ False
+ sage: Matroid(flats={0: [[]], 1: [[0], [1]], 2: [[0], [0, 1]]}).is_valid() # duplicates
+ False
+ sage: Matroid(flats={0: [[]], 1: [[0], [1], [0, 1]]}).is_valid()
+ False
+ sage: Matroid(flats={0: [[]], 1: [[0, 1], [2]], 2: [[0], [1], [0, 1, 2]]}).is_valid()
+ False
+ sage: M = Matroid(flats={0: [''], # missing an extention of flat ['5'] by '6'
+ ....: 1: ['0','1','2','3','4','5','6','7','8','9','a','b','c'],
+ ....: 2: ['45','46','47','4c','57','5c','67','6c','7c',
+ ....: '048','149','24a','34b','059','15a','25b','358',
+ ....: '06a','16b','268','369','07b','178','279','37a',
+ ....: '0123c','89abc'],
+ ....: 3: ['0123456789abc']})
+ sage: M.is_valid()
+ False
+ """
+ cdef int i, j, k
+ cdef frozenset F1, F2, F3, I12
+ cdef list ranks, cover, flats_lst
+ cdef bint flag
+
+ # check flats dictionary for invalid ranks and repeated flats
+ ranks = sorted(self._F)
+ if ranks != list(range(len(ranks))):
+ return False
+ flats_lst = [F for i in self._F for F in self._F[i]]
+ if len(flats_lst) != len(set(flats_lst)):
+ return False
+
+ # the groundset must be a flat
+ flag = False
+ for i in self._F:
+ for F1 in self._F[i]:
+ if F1 == self._groundset:
+ flag = True
+ break
+ if not flag:
+ return False
+
+ # a single element extension of a flat must be a subset of exactly one flat
+ for i in ranks[:-1]:
+ for F1 in self._F[i]:
+ cover = []
+ for F2 in self._F[i+1]:
+ if F2 >= F1:
+ cover.extend(F1 ^ F2)
+ if len(cover) != len(F1 ^ self._groundset) or set(cover) != F1 ^ self._groundset:
+ return False
+
+ # the intersection of two flats must be a flat
+ for i in ranks:
+ for j in ranks[i:]:
+ for F1 in self._F[i]:
+ for F2 in self._F[j]:
+ flag = False
+ I12 = F1 & F2
+ for k in self._F:
+ if k <= i:
+ for F3 in self._F[k]:
+ if F3 == I12:
+ flag = True
+ break
+ if flag:
+ break
+ if not flag:
+ return False
+
+ return True
diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx
index 2cd946d7503..da9ef0a5e5f 100644
--- a/src/sage/matroids/unpickling.pyx
+++ b/src/sage/matroids/unpickling.pyx
@@ -14,6 +14,7 @@ Python terminology) functions for Sage's matroids.
AUTHORS:
- Rudi Pendavingh, Stefan van Zwam (2013-07-01): initial version
+- Giorgos Mousa (2024-01-01): add CircuitsMatroid and FlatsMatroid
"""
# ****************************************************************************
# Copyright (C) 2013 Rudi Pendavingh
@@ -32,6 +33,7 @@ from sage.rings.rational cimport Rational
from sage.matroids.basis_matroid cimport BasisMatroid
from sage.matroids.circuits_matroid cimport CircuitsMatroid
from sage.matroids.circuit_closures_matroid cimport CircuitClosuresMatroid
+from sage.matroids.flats_matroid cimport FlatsMatroid
from sage.matroids.dual_matroid import DualMatroid
from sage.matroids.graphic_matroid import GraphicMatroid
from sage.matroids.lean_matrix cimport GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, PlusMinusOneMatrix, RationalMatrix
@@ -173,6 +175,50 @@ def unpickle_circuit_closures_matroid(version, data):
return M
+#############################################################################
+# FlatsMatroid
+#############################################################################
+
+def unpickle_flats_matroid(version, data):
+ """
+ Unpickle a FlatsMatroid.
+
+ *Pickling* is Python's term for the loading and saving of objects.
+ Functions like these serve to reconstruct a saved object. This all happens
+ transparently through the ``load`` and ``save`` commands, and you should
+ never have to call this function directly.
+
+ INPUT:
+
+ - ``version`` -- an integer, expected to be 0
+ - ``data`` -- a tuple ``(E, F, name)`` in which ``E`` is the groundset of
+ the matroid, ``F`` is the dictionary of flats, and ``name`` is a custom
+ name.
+
+ OUTPUT:
+
+ A matroid.
+
+ .. WARNING::
+
+ Users should never call this function directly.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = FlatsMatroid(matroids.catalog.Vamos())
+ sage: M == loads(dumps(M)) # indirect doctest
+ True
+ """
+ cdef FlatsMatroid M
+ if version != 0:
+ raise TypeError("object was created with newer version of Sage. Please upgrade.")
+ M = FlatsMatroid(groundset=data[0], flats=data[1])
+ if data[2] is not None:
+ M.rename(data[2])
+ return M
+
+
#############################################################################
# DualMatroid
#############################################################################
From c68aec538497b99364b4588b2c246dc5627cbdbf Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Thu, 15 Feb 2024 12:45:09 +0200
Subject: [PATCH 012/518] Few optimizations
---
src/sage/matroids/flats_matroid.pyx | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/src/sage/matroids/flats_matroid.pyx b/src/sage/matroids/flats_matroid.pyx
index 546526ee300..1456a952d89 100644
--- a/src/sage/matroids/flats_matroid.pyx
+++ b/src/sage/matroids/flats_matroid.pyx
@@ -76,14 +76,14 @@ cdef class FlatsMatroid(Matroid):
self._F[i].add(frozenset(F))
else:
self._groundset = frozenset(groundset)
- for i in flats:
+ for i in sorted(flats):
for F in flats[i]:
try:
self._F[i].add(frozenset(F))
except KeyError:
self._F[i] = set()
self._F[i].add(frozenset(F))
- self._matroid_rank = self.rank(self._groundset)
+ self._matroid_rank = max([0] + list(self._F))
cpdef groundset(self) noexcept:
"""
@@ -121,16 +121,20 @@ cdef class FlatsMatroid(Matroid):
sage: M = FlatsMatroid(matroids.Theta(3))
sage: M._rank(['x1', 'y0', 'y2'])
2
+
+ TESTS::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: M = matroids.catalog.NonDesargues()
+ sage: F = FlatsMatroid(M)
+ sage: for S in powerset(M.groundset()):
+ ....: assert M.rank(S) == F.rank(S)
"""
cdef frozenset XX = frozenset(X)
- cdef int min = len(self._groundset)
- for i in self._F:
- if i < min:
+ for i in range(self.rank() + 1):
for f in self._F[i]:
if f >= XX:
- min = i
- break
- return min
+ return i
# optional
@@ -409,7 +413,7 @@ cdef class FlatsMatroid(Matroid):
"""
cdef list W = []
cdef int i
- for i in sorted(self._F):
+ for i in self._F:
W.append(len(self._F[i]))
return W
@@ -476,7 +480,7 @@ cdef class FlatsMatroid(Matroid):
cdef bint flag
# check flats dictionary for invalid ranks and repeated flats
- ranks = sorted(self._F)
+ ranks = list(self._F)
if ranks != list(range(len(ranks))):
return False
flats_lst = [F for i in self._F for F in self._F[i]]
From a092c7ef9c62edaa28a0f6ce8a825279249eccc3 Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Thu, 15 Feb 2024 14:16:46 +0200
Subject: [PATCH 013/518] Add iterators, Whitney numbers, characteristic
polynomial, etc
---
src/sage/matroids/basis_exchange_matroid.pxd | 4 +-
src/sage/matroids/basis_exchange_matroid.pyx | 33 +-
src/sage/matroids/basis_matroid.pyx | 4 +-
src/sage/matroids/database_matroids.py | 2 +-
src/sage/matroids/matroid.pxd | 3 +
src/sage/matroids/matroid.pyx | 1324 ++++++++++--------
src/sage/matroids/union_matroid.pyx | 2 +-
7 files changed, 770 insertions(+), 602 deletions(-)
diff --git a/src/sage/matroids/basis_exchange_matroid.pxd b/src/sage/matroids/basis_exchange_matroid.pxd
index 5877f62fc09..151af953299 100644
--- a/src/sage/matroids/basis_exchange_matroid.pxd
+++ b/src/sage/matroids/basis_exchange_matroid.pxd
@@ -61,8 +61,8 @@ cdef class BasisExchangeMatroid(Matroid):
cpdef _augment(self, X, Y) noexcept
cpdef _is_independent(self, F) noexcept
- cpdef f_vector(self) noexcept
- cdef _f_vector_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long rnk) noexcept
+ cpdef whitney_numbers2(self) noexcept
+ cdef _whitney_numbers2_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long rnk) noexcept
cpdef flats(self, R) noexcept
cdef _flats_rec(self, SetSystem Rflats, long R, bitset_t* flats, bitset_t* todo, long elt, long rnk) noexcept
cpdef coflats(self, R) noexcept
diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx
index 4839fd6c9d9..f336b1d77b8 100644
--- a/src/sage/matroids/basis_exchange_matroid.pyx
+++ b/src/sage/matroids/basis_exchange_matroid.pyx
@@ -1232,27 +1232,26 @@ cdef class BasisExchangeMatroid(Matroid):
# enumeration
- cpdef f_vector(self) noexcept:
+ cpdef whitney_numbers2(self) noexcept:
r"""
- Return the `f`-vector of the matroid.
+ Return the Whitney numbers of the second kind of the matroid.
- The `f`-*vector* is a vector `(f_0, ..., f_r)`, where `f_i` is the
- number of flats of rank `i`, and `r` is the rank of the matroid.
+ The Whitney numbers of the second kind are here encoded as a vector
+ `(W_0, ..., W_r)`, where `W_i` is the number of flats of rank `i`, and
+ `r` is the rank of the matroid.
- OUTPUT:
-
- List of integers.
+ OUTPUT: a list of integers
EXAMPLES::
sage: M = matroids.catalog.S8()
- sage: M.f_vector()
+ sage: M.whitney_numbers2()
[1, 8, 22, 14, 1]
"""
cdef bitset_t *flats
cdef bitset_t *todo
if self._matroid_rank == 0:
- return [0]
+ return [1]
flats = sig_malloc((self.full_rank() + 1) * sizeof(bitset_t))
todo = sig_malloc((self.full_rank() + 1) * sizeof(bitset_t))
@@ -1264,7 +1263,7 @@ cdef class BasisExchangeMatroid(Matroid):
bitset_clear(todo[0])
self.__closure(flats[0], todo[0])
bitset_complement(todo[0], flats[0])
- self._f_vector_rec(f_vec, flats, todo, 0, 0)
+ self._whitney_numbers2_rec(f_vec, flats, todo, 0, 0)
for i in range(self.full_rank() + 1):
bitset_free(flats[i])
bitset_free(todo[i])
@@ -1272,9 +1271,9 @@ cdef class BasisExchangeMatroid(Matroid):
sig_free(todo)
return f_vec
- cdef _f_vector_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long i) noexcept:
+ cdef _whitney_numbers2_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long i) noexcept:
"""
- Recursion for the f_vector method.
+ Recursion for the whitney_numbers2 method.
"""
cdef long e
f_vec[i] += 1
@@ -1287,7 +1286,7 @@ cdef class BasisExchangeMatroid(Matroid):
bitset_difference(todo[i + 1], flats[i + 1], flats[i])
if bitset_first(todo[i + 1]) == e:
bitset_copy(todo[i + 1], todo[i])
- self._f_vector_rec(f_vec, flats, todo, e + 1, i + 1)
+ self._whitney_numbers2_rec(f_vec, flats, todo, e + 1, i + 1)
e = bitset_next(todo[i], e)
cpdef flats(self, r) noexcept:
@@ -1311,7 +1310,7 @@ cdef class BasisExchangeMatroid(Matroid):
EXAMPLES::
sage: M = matroids.catalog.S8()
- sage: M.f_vector()
+ sage: M.whitney_numbers2()
[1, 8, 22, 14, 1]
sage: len(M.flats(2))
22
@@ -1386,7 +1385,7 @@ cdef class BasisExchangeMatroid(Matroid):
EXAMPLES::
sage: M = matroids.catalog.S8().dual()
- sage: M.f_vector()
+ sage: M.whitney_numbers2()
[1, 8, 22, 14, 1]
sage: len(M.coflats(2))
22
@@ -1957,8 +1956,8 @@ cdef class BasisExchangeMatroid(Matroid):
else:
k = min(self.full_rank() - 1, 2)
fie, f_vec = self._flat_element_inv(k)
- self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie)]), f_vec]))
- self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie)])
+ self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie, key=str)]), f_vec]))
+ self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie, key=str)])
return self._weak_invariant_var
cpdef _weak_partition(self) noexcept:
diff --git a/src/sage/matroids/basis_matroid.pyx b/src/sage/matroids/basis_matroid.pyx
index 137edc21d94..63f5c72a02d 100644
--- a/src/sage/matroids/basis_matroid.pyx
+++ b/src/sage/matroids/basis_matroid.pyx
@@ -428,9 +428,9 @@ cdef class BasisMatroid(BasisExchangeMatroid):
sage: M = Matroid(bases=matroids.catalog.N2().bases())
sage: M.truncation()
Matroid of rank 5 on 12 elements with 702 bases
- sage: M.f_vector()
+ sage: M.whitney_numbers2()
[1, 12, 66, 190, 258, 99, 1]
- sage: M.truncation().f_vector()
+ sage: M.truncation().whitney_numbers2()
[1, 12, 66, 190, 258, 1]
"""
if self.full_rank() == 0:
diff --git a/src/sage/matroids/database_matroids.py b/src/sage/matroids/database_matroids.py
index 0a75ce22283..57fecb91514 100644
--- a/src/sage/matroids/database_matroids.py
+++ b/src/sage/matroids/database_matroids.py
@@ -541,7 +541,7 @@ def P7():
sage: M = matroids.catalog.P7(); M
P7: Ternary matroid of rank 3 on 7 elements, type 1+
- sage: M.f_vector()
+ sage: M.whitney_numbers2()
[1, 7, 11, 1]
sage: M.has_minor(matroids.CompleteGraphic(4))
False
diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd
index 7d2bc60078d..dae3e7d4368 100644
--- a/src/sage/matroids/matroid.pxd
+++ b/src/sage/matroids/matroid.pxd
@@ -124,6 +124,8 @@ cdef class Matroid(SageObject):
cpdef coflats(self, r) noexcept
cpdef hyperplanes(self) noexcept
cpdef f_vector(self) noexcept
+ cpdef whitney_numbers(self) noexcept
+ cpdef whitney_numbers2(self) noexcept
cpdef broken_circuits(self, ordering=*) noexcept
cpdef no_broken_circuits_sets(self, ordering=*) noexcept
@@ -213,6 +215,7 @@ cdef class Matroid(SageObject):
cpdef _internal(self, B) noexcept
cpdef _external(self, B) noexcept
cpdef tutte_polynomial(self, x=*, y=*) noexcept
+ cpdef characteristic_polynomial(self, l=*) noexcept
cpdef flat_cover(self, solver=*, verbose=*, integrality_tolerance=*) noexcept
# misc
diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx
index 2338c144190..caf6b6df3ee 100644
--- a/src/sage/matroids/matroid.pyx
+++ b/src/sage/matroids/matroid.pyx
@@ -19,7 +19,7 @@ methods (organized by category). Note that most subclasses (notably
:mod:`LinearMatroids `) will implement
additional functionality (e.g. linear extensions).
-- Ground set:
+- Groundset:
- :meth:`groundset() `
- :meth:`size() `
@@ -71,6 +71,8 @@ additional functionality (e.g. linear extensions).
- :meth:`coflats() `
- :meth:`hyperplanes() `
- :meth:`f_vector() `
+ - :meth:`whitney_numbers() `
+ - :meth:`whitney_numbers2() `
- :meth:`broken_circuits() `
- :meth:`no_broken_circuits_sets() `
@@ -128,6 +130,7 @@ additional functionality (e.g. linear extensions).
- Invariants
- :meth:`tutte_polynomial() `
+ - :meth:`characteristic_polynomial() `
- :meth:`flat_cover() `
- Visualization
@@ -174,9 +177,9 @@ A subclass should always override the underscored method, if available, and as
a rule leave the regular method alone.
These underscored methods are not documented in the reference manual. To see
-them, within Sage you can create a matroid ``M`` and type ``M._``. Then
-``M._rank?`` followed by ```` will bring up the documentation string of
-the ``_rank()`` method.
+them, within Sage you can create a matroid ``M`` and type ``M._`` followed by
+:kbd:`Tab`. Then ``M._rank?`` followed by :kbd:`Tab` will bring up the
+documentation string of the ``_rank()`` method.
Creating new Matroid subclasses
===============================
@@ -315,9 +318,10 @@ REFERENCES
AUTHORS:
-- Michael Welsh (2013-04-03): Changed flats() to use SetSystem
-- Michael Welsh (2013-04-01): Added is_3connected(), using naive algorithm
- Rudi Pendavingh, Stefan van Zwam (2013-04-01): initial version
+- Michael Welsh (2013-04-01): Added is_3connected(), using naive algorithm
+- Michael Welsh (2013-04-03): Changed flats() to use SetSystem
+- Giorgos Mousa (2024-02-15): Add Whitney numbers, characteristic polynomial
Methods
=======
@@ -458,7 +462,6 @@ cdef class Matroid(SageObject):
A subclass should always override the underscored method, if
available, and as a rule leave the regular method alone.
-
"""
# virtual methods
@@ -469,9 +472,7 @@ cdef class Matroid(SageObject):
The groundset is the set of elements that comprise the matroid.
- OUTPUT:
-
- A set.
+ OUTPUT: a set
.. NOTE::
@@ -499,9 +500,7 @@ cdef class Matroid(SageObject):
- ``X`` -- an object with Python's ``frozenset`` interface.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
.. NOTE::
@@ -531,9 +530,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- ``frozenset`` instance containing a subset of the groundset.
+ OUTPUT: ``frozenset`` instance containing a subset of the groundset
EXAMPLES::
@@ -603,9 +600,7 @@ cdef class Matroid(SageObject):
- ``B`` -- a basis of the matroid.
- ``e`` -- an element not in ``B``.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
EXAMPLES::
@@ -624,9 +619,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- ``frozenset`` instance containing a subset of the groundset.
+ OUTPUT: ``frozenset`` instance containing a subset of the groundset
EXAMPLES::
@@ -652,9 +645,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -673,9 +664,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- ``frozenset`` instance containing a subset of the groundset.
+ OUTPUT: ``frozenset`` instance containing a subset of the groundset
EXAMPLES::
@@ -745,9 +734,7 @@ cdef class Matroid(SageObject):
- ``B`` -- a basis of the matroid.
- ``e`` -- an element of ``B``.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
EXAMPLES::
@@ -766,9 +753,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- ``frozenset`` instance containing a subset of the groundset.
+ OUTPUT: ``frozenset`` instance containing a subset of the groundset
EXAMPLES::
@@ -799,9 +784,7 @@ cdef class Matroid(SageObject):
- ``Y`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``, and disjoint from ``X``.
- OUTPUT:
-
- ``frozenset`` instance containing a subset of the groundset.
+ OUTPUT: ``frozenset`` instance containing a subset of the groundset
EXAMPLES::
@@ -834,9 +817,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -863,9 +844,7 @@ cdef class Matroid(SageObject):
i.e. ``len(X) == self.full_rank()``. Otherwise its behavior is
undefined.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -892,9 +871,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -926,9 +903,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -957,9 +932,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -980,9 +953,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. WARNING::
@@ -1010,9 +981,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -1044,9 +1013,7 @@ cdef class Matroid(SageObject):
- ``X`` -- An object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -1086,9 +1053,7 @@ cdef class Matroid(SageObject):
- ``deletions`` is coindependent
- ``contractions`` and ``deletions`` are disjoint.
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
EXAMPLES::
@@ -1107,19 +1072,18 @@ cdef class Matroid(SageObject):
cpdef _has_minor(self, N, bint certificate=False) noexcept:
"""
- Test if matroid has the specified minor,
- and optionally return frozensets ``X`` and ``Y`` so that ``N`` is isomorphic to ``self.minor(X, Y)``.
+ Test if matroid has the specified minor, and optionally return
+ frozensets ``X`` and ``Y`` so that ``N`` is isomorphic to
+ ``self.minor(X, Y)``.
INPUT:
- ``N`` -- An instance of a ``Matroid`` object,
- - ``certificate`` -- Boolean (Default: ``False``) If ``True``, returns
+ - ``certificate`` -- boolean (Default: ``False``) If ``True``, returns
``True, (X, Y, dic) where ``N`` is isomorphic to ``self.minor(X, Y)``,
and ``dic`` is an isomorphism between ``N`` and ``self.minor(X, Y)``.
- OUTPUT:
-
- boolean or tuple.
+ OUTPUT: boolean or tuple
EXAMPLES::
@@ -1172,9 +1136,7 @@ cdef class Matroid(SageObject):
- ``F`` -- a subset of the groundset, assumed to be a closed set of
rank `r(M) - 2`.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -1201,9 +1163,7 @@ cdef class Matroid(SageObject):
- ``hyperplanes`` -- the set of hyperplanes of a linear subclass of
``self``.
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
EXAMPLES::
@@ -1259,9 +1219,7 @@ cdef class Matroid(SageObject):
"""
Return the size of the groundset.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -1346,9 +1304,7 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -1373,9 +1329,7 @@ cdef class Matroid(SageObject):
The *rank* of the matroid is the size of the largest independent
subset of the groundset.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -1399,9 +1353,7 @@ cdef class Matroid(SageObject):
The output of this method can change in between calls.
- OUTPUT:
-
- Set of elements.
+ OUTPUT: a set of elements
EXAMPLES::
@@ -1426,9 +1378,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Subset of ``X``.
+ OUTPUT: a subset of ``X``
EXAMPLES::
@@ -1456,9 +1406,7 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- OUTPUT:
-
- Set of elements.
+ OUTPUT: a set of elements
- If ``X`` is not ``None``, the output is a circuit contained in ``X``
if such a circuit exists. Otherwise an error is raised.
@@ -1500,9 +1448,7 @@ cdef class Matroid(SageObject):
- ``B`` -- a basis of the matroid.
- ``e`` -- an element not in ``B``.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
.. SEEALSO::
@@ -1534,9 +1480,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Set of elements containing ``X``.
+ OUTPUT: a superset of ``X``
EXAMPLES::
@@ -1611,9 +1555,7 @@ cdef class Matroid(SageObject):
- ``Y`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- OUTPUT:
-
- A subset of `Y - X`.
+ OUTPUT: a subset of `Y - X`
EXAMPLES::
@@ -1652,9 +1594,7 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
.. SEEALSO::
@@ -1682,9 +1622,7 @@ cdef class Matroid(SageObject):
The *corank* of the matroid equals the rank of the dual matroid. It is
given by ``M.size() - M.full_rank()``.
- OUTPUT:
-
- Integer.
+ OUTPUT: an integer
.. SEEALSO::
@@ -1710,9 +1648,7 @@ cdef class Matroid(SageObject):
Output can change between calls.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
.. SEEALSO::
@@ -1746,9 +1682,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- A subset of ``X``.
+ OUTPUT: a subset of ``X``
.. SEEALSO::
@@ -1783,9 +1717,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- A set of elements containing ``X``.
+ OUTPUT: a superset of ``X``
.. SEEALSO::
@@ -1816,9 +1748,7 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
- If ``X`` is not ``None``, the output is a cocircuit contained in
``X`` if such a cocircuit exists. Otherwise an error is raised.
@@ -1868,9 +1798,7 @@ cdef class Matroid(SageObject):
- ``B`` -- a basis of the matroid.
- ``e`` -- an element of ``B``.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
.. SEEALSO::
@@ -1898,9 +1826,7 @@ cdef class Matroid(SageObject):
A *loop* is an element `u` of the groundset such that the
one-element set `\{ u \}` is dependent.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
EXAMPLES::
@@ -1920,9 +1846,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -1946,9 +1870,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -1972,9 +1894,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -2004,9 +1924,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2038,9 +1956,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- ``k`` -- a positive integer
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2091,9 +2007,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -2117,9 +2031,7 @@ cdef class Matroid(SageObject):
one-element set `\{ u \}` is a cocircuit. In other words, a coloop
is a loop of the dual of the matroid.
- OUTPUT:
-
- A set of elements.
+ OUTPUT: a set of elements
.. SEEALSO::
@@ -2146,9 +2058,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2179,9 +2089,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2213,9 +2121,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2250,9 +2156,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2283,9 +2187,7 @@ cdef class Matroid(SageObject):
- ``X`` -- a subset (or any iterable) of the groundset
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -2322,9 +2224,7 @@ cdef class Matroid(SageObject):
Certain subclasses may check other axioms instead.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -2339,13 +2239,21 @@ cdef class Matroid(SageObject):
....: 3: [[1, 2, 3, 4, 5], [1, 2, 3, 6, 7], [1, 4, 5, 6, 7]]})
sage: M.is_valid()
False
+
+ TESTS::
+
+ sage: def r(X):
+ ....: return -1
+ sage: M = Matroid(groundset=[0,1,2], rank_function=r)
+ sage: M.is_valid()
+ False
"""
E = list(self.groundset())
for i in range(len(E) + 1):
for X in combinations(E, i):
XX = frozenset(X)
rX = self._rank(XX)
- if rX > i:
+ if rX > i or rX < 0:
return False
for j in range(i, len(E) + 1):
for Y in combinations(E, j):
@@ -2362,11 +2270,9 @@ cdef class Matroid(SageObject):
cpdef circuits(self) noexcept:
"""
- Return the list of circuits of the matroid.
-
- OUTPUT:
+ Return the circuits of the matroid.
- An iterable containing all circuits.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2383,21 +2289,52 @@ cdef class Matroid(SageObject):
['c', 'f', 'g'], ['d', 'e', 'f']]
"""
C = set()
- for B in self.bases():
- C.update([self._circuit(B.union(set([e])))
- for e in self.groundset().difference(B)])
- return list(C)
+ B_ext = set()
+ for B in self.bases_iterator():
+ for e in B ^ self.groundset():
+ B_ext.add(B | set([e]))
+ for S in B_ext:
+ C.add(self._circuit(S))
+ return SetSystem(list(self.groundset()), C)
+
+ def circuits_iterator(self, k=None):
+ """
+ Return an iterator over the circuits of the matroid.
+
+ .. SEEALSO::
+
+ :meth:`M.circuit() `
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Fano()
+ sage: sorted([sorted(C) for C in M.circuits_iterator()])
+ [['a', 'b', 'c', 'g'], ['a', 'b', 'd', 'e'], ['a', 'b', 'f'],
+ ['a', 'c', 'd', 'f'], ['a', 'c', 'e'], ['a', 'd', 'g'],
+ ['a', 'e', 'f', 'g'], ['b', 'c', 'd'], ['b', 'c', 'e', 'f'],
+ ['b', 'd', 'f', 'g'], ['b', 'e', 'g'], ['c', 'd', 'e', 'g'],
+ ['c', 'f', 'g'], ['d', 'e', 'f']]
+ """
+ if k and k < self.rank() + 2:
+ for X in combinations(self.groundset(), k):
+ X = frozenset(X)
+ if self._is_circuit(X):
+ yield X
+ elif not k:
+ for k in range(self.rank() + 2):
+ for X in combinations(self.groundset(), k):
+ X = frozenset(X)
+ if self._is_circuit(X):
+ yield X
cpdef nonspanning_circuits(self) noexcept:
"""
- Return the list of nonspanning circuits of the matroid.
+ Return the nonspanning circuits of the matroid.
A *nonspanning circuit* is a circuit whose rank is strictly smaller
than the rank of the matroid.
- OUTPUT:
-
- An iterable containing all nonspanning circuits.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2412,19 +2349,44 @@ cdef class Matroid(SageObject):
['b', 'c', 'd'], ['b', 'e', 'g'], ['c', 'f', 'g'],
['d', 'e', 'f']]
"""
- C = set()
- for N in self.nonbases():
+ cdef SetSystem C
+ C = SetSystem(list(self.groundset()))
+ for N in self.nonbases_iterator():
if self._rank(N) == self.full_rank() - 1:
- C.add(self._circuit(N))
- return list(C)
+ C.append(self._circuit(N))
+ return C
- cpdef cocircuits(self) noexcept:
+ def nonspanning_circuits_iterator(self):
"""
- Return the list of cocircuits of the matroid.
+ Return an iterator over the nonspanning circuits of the matroid.
- OUTPUT:
+ A *nonspanning circuit* is a circuit whose rank is strictly smaller
+ than the rank of the matroid.
+
+ .. SEEALSO::
- An iterable containing all cocircuits.
+ :meth:`M.circuit() `,
+ :meth:`M.rank() `
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Fano()
+ sage: sorted([sorted(C) for C in M.nonspanning_circuits_iterator()])
+ [['a', 'b', 'f'], ['a', 'c', 'e'], ['a', 'd', 'g'],
+ ['b', 'c', 'd'], ['b', 'e', 'g'], ['c', 'f', 'g'],
+ ['d', 'e', 'f']]
+ """
+ for k in range(self.rank() + 1):
+ for X in combinations(self.groundset(), k):
+ X = frozenset(X)
+ if self._is_circuit(X):
+ yield X
+
+ cpdef cocircuits(self) noexcept:
+ """
+ Return the cocircuits of the matroid.
+
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2439,20 +2401,39 @@ cdef class Matroid(SageObject):
['c', 'd', 'e', 'g']]
"""
C = set()
- for B in self.bases():
+ for B in self.bases_iterator():
+ C.update([self._cocircuit(self.groundset().difference(B).union(set([e]))) for e in B])
+ return list(C)
+
+ def cocircuits_iterator(self):
+ """
+ Return an iterator over the cocircuits of the matroid.
+
+ .. SEEALSO::
+
+ :meth:`M.cocircuit() `
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Fano()
+ sage: sorted([sorted(C) for C in M.cocircuits_iterator()])
+ [['a', 'b', 'c', 'g'], ['a', 'b', 'd', 'e'], ['a', 'c', 'd', 'f'],
+ ['a', 'e', 'f', 'g'], ['b', 'c', 'e', 'f'], ['b', 'd', 'f', 'g'],
+ ['c', 'd', 'e', 'g']]
+ """
+ C = set()
+ for B in self.bases_iterator():
C.update([self._cocircuit(self.groundset().difference(B).union(set([e]))) for e in B])
return list(C)
cpdef noncospanning_cocircuits(self) noexcept:
"""
- Return the list of noncospanning cocircuits of the matroid.
+ Return the noncospanning cocircuits of the matroid.
A *noncospanning cocircuit* is a cocircuit whose corank is strictly
smaller than the corank of the matroid.
- OUTPUT:
-
- An iterable containing all nonspanning circuits.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2471,14 +2452,12 @@ cdef class Matroid(SageObject):
cpdef circuit_closures(self) noexcept:
"""
- Return the list of closures of circuits of the matroid.
+ Return the closures of circuits of the matroid.
A *circuit closure* is a closed set containing a circuit.
- OUTPUT:
-
- A dictionary containing the circuit closures of the matroid, indexed
- by their ranks.
+ OUTPUT: a dictionary containing the circuit closures of the matroid,
+ indexed by their ranks
.. SEEALSO::
@@ -2501,21 +2480,19 @@ cdef class Matroid(SageObject):
[['a', 'b', 'c', 'd', 'e', 'f', 'g']]
"""
CC = [set([]) for r in range(self.rank() + 1)]
- for C in self.circuits():
+ for C in self.circuits_iterator():
CC[len(C) - 1].add(self.closure(C))
return {r: CC[r] for r in range(self.rank() + 1) if CC[r]}
cpdef nonspanning_circuit_closures(self) noexcept:
"""
- Return the list of closures of nonspanning circuits of the matroid.
+ Return the closures of nonspanning circuits of the matroid.
A *nonspanning circuit closure* is a closed set containing a
nonspanning circuit.
- OUTPUT:
-
- A dictionary containing the nonspanning circuit closures of the
- matroid, indexed by their ranks.
+ OUTPUT: a dictionary containing the nonspanning circuit closures of the
+ matroid, indexed by their ranks
.. SEEALSO::
@@ -2534,20 +2511,18 @@ cdef class Matroid(SageObject):
KeyError: 3
"""
CC = [set([]) for r in range(self.rank() + 1)]
- for C in self.nonspanning_circuits():
+ for C in self.nonspanning_circuits_iterator():
CC[len(C) - 1].add(self.closure(C))
return {r: CC[r] for r in range(self.rank() + 1) if CC[r]}
cpdef nonbases(self) noexcept:
r"""
- Return the list of nonbases of the matroid.
+ Return the nonbases of the matroid.
A *nonbasis* is a set with cardinality ``self.full_rank()`` that is
not a basis.
- OUTPUT:
-
- An iterable containing the nonbases of the matroid.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2572,6 +2547,33 @@ cdef class Matroid(SageObject):
res.append(X)
return res
+ def nonbases_iterator(self):
+ r"""
+ Return an iterator over the nonbases of the matroid.
+
+ A *nonbasis* is a set with cardinality ``self.full_rank()`` that is
+ not a basis.
+
+ .. SEEALSO::
+
+ :meth:`M.basis() `
+
+ EXAMPLES::
+
+ sage: M = matroids.Uniform(2, 4)
+ sage: list(M.nonbases_iterator())
+ []
+ sage: [sorted(X) for X in matroids.catalog.P6().nonbases_iterator()]
+ [['a', 'b', 'c']]
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``self.full_rank()``
+ """
+ for X in combinations(self.groundset(), self.full_rank()):
+ if self._rank(X) < len(X):
+ yield frozenset(X)
+
cpdef dependent_r_sets(self, long r) noexcept:
r"""
Return the list of dependent subsets of fixed size.
@@ -2580,10 +2582,6 @@ cdef class Matroid(SageObject):
- ``r`` -- a nonnegative integer.
- OUTPUT:
-
- An iterable containing all dependent subsets of size ``r``.
-
EXAMPLES::
sage: M = matroids.catalog.Vamos()
@@ -2605,15 +2603,40 @@ cdef class Matroid(SageObject):
res.append(X)
return res
+ def dependent_r_sets_iterator(self, long r):
+ r"""
+ Return an iterator over the dependent subsets of fixed size.
+
+ INPUT:
+
+ - ``r`` -- a nonnegative integer.
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Vamos()
+ sage: list(M.dependent_r_sets_iterator(3))
+ []
+ sage: sorted([sorted(X) for X in
+ ....: matroids.catalog.Vamos().dependent_r_sets_iterator(4)])
+ [['a', 'b', 'c', 'd'], ['a', 'b', 'e', 'f'], ['a', 'b', 'g', 'h'],
+ ['c', 'd', 'e', 'f'], ['e', 'f', 'g', 'h']]
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``r``
+ """
+ for X in combinations(self.groundset(), r):
+ X = frozenset(X)
+ if self._rank(X) < len(X):
+ yield X
+
cpdef bases(self) noexcept:
r"""
- Return the list of bases of the matroid.
+ Return the bases of the matroid.
A *basis* is a maximal independent set.
- OUTPUT:
-
- An iterable containing all bases of the matroid.
+ OUTPUT: a SetSystem
EXAMPLES::
@@ -2636,13 +2659,33 @@ cdef class Matroid(SageObject):
res.append(X)
return res
- cpdef independent_sets(self) noexcept:
+ def bases_iterator(self):
r"""
- Return the list of independent subsets of the matroid.
+ Return an iterator over the bases of the matroid.
- OUTPUT:
+ A *basis* is a maximal independent set.
+
+ EXAMPLES::
- An iterable containing all independent subsets of the matroid.
+ sage: M = matroids.Uniform(2, 4)
+ sage: sorted([sorted(X) for X in M.bases_iterator()])
+ [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``self.full_rank()``
+
+ .. SEEALSO::
+
+ :meth:`M.independent_r_sets() `
+ """
+ for X in combinations(self.groundset(), self.full_rank()):
+ if self._rank(frozenset(X)) == len(X):
+ yield frozenset(X)
+
+ cpdef independent_sets(self) noexcept:
+ r"""
+ Return the list of independent subsets of the matroid.
EXAMPLES::
@@ -2672,12 +2715,50 @@ cdef class Matroid(SageObject):
r -= 1
elif T[r]:
I[r+1] = I[r].union([T[r].pop()])
- res.append(I[r+1])
+ res.append(I[r+1])
+ T[r+1] = T[r] - self._closure(I[r+1])
+ r += 1
+ else:
+ r -= 1
+ return res
+
+ def independent_sets_iterator(self):
+ r"""
+ Return an iterator over the independent subsets of the matroid.
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Pappus()
+ sage: I = list(M.independent_sets_iterator())
+ sage: len(I)
+ 121
+
+ .. SEEALSO::
+
+ :meth:`M.independent_r_sets() `
+ """
+ cdef int r
+ cdef int full_rank = self.full_rank()
+ cdef list T = [set() for r in range(full_rank)]
+ cdef list I = [frozenset()] * (full_rank+1)
+ r = 0
+
+ yield frozenset()
+ T[0] = set(self.groundset()) - self.closure([])
+ while r >= 0:
+ if r + 1 == full_rank:
+ for x in T[r]:
+ I[r+1] = I[r].union([x])
+ yield I[r+1]
+ T[r] = set()
+ r -= 1
+ elif T[r]:
+ I[r+1] = I[r].union([T[r].pop()])
+ yield I[r+1]
T[r+1] = T[r] - self._closure(I[r+1])
r += 1
else:
r -= 1
- return res
cpdef independent_r_sets(self, long r) noexcept:
r"""
@@ -2687,11 +2768,6 @@ cdef class Matroid(SageObject):
- ``r`` -- a nonnegative integer.
- OUTPUT:
-
- An iterable containing all independent subsets of the matroid of
- cardinality ``r``.
-
EXAMPLES::
sage: M = matroids.catalog.Pappus()
@@ -2719,6 +2795,40 @@ cdef class Matroid(SageObject):
res.append(X)
return res
+ def independent_r_sets_iterator(self, r):
+ r"""
+ Return an iterator over the size-``r`` independent subsets of the
+ matroid.
+
+ INPUT:
+
+ - ``r`` -- a nonnegative integer.
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Pappus()
+ sage: list(M.independent_r_sets_iterator(4))
+ []
+ sage: S = list(M.independent_r_sets_iterator(3))
+ sage: len(S)
+ 75
+ sage: frozenset({'a', 'c', 'e'}) in S
+ True
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``r``
+
+ .. SEEALSO::
+
+ :meth:`M.independent_sets() `
+ :meth:`M.bases() `
+ """
+ for X in combinations(self.groundset(), r):
+ X = frozenset(X)
+ if self._rank(X) == len(X):
+ yield X
+
cpdef _extend_flags(self, flags) noexcept:
r"""
Recursion for the ``self._flags(r)`` method.
@@ -2793,9 +2903,7 @@ cdef class Matroid(SageObject):
- ``r`` -- A natural number.
- OUTPUT:
-
- An iterable containing all flats of rank ``r``.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2809,8 +2917,7 @@ cdef class Matroid(SageObject):
['b', 'c', 'd'], ['b', 'e', 'g'], ['c', 'f', 'g'],
['d', 'e', 'f']]
"""
- return SetSystem(list(self.groundset()),
- subsets=[f[0] for f in self._flags(r)])
+ return SetSystem(list(self.groundset()), subsets=[f[0] for f in self._flags(r)])
cpdef coflats(self, r) noexcept:
r"""
@@ -2822,9 +2929,7 @@ cdef class Matroid(SageObject):
- ``r`` -- a nonnegative integer.
- OUTPUT:
-
- An iterable containing all coflats of corank ``r``.
+ OUTPUT: a SetSystem
.. SEEALSO::
@@ -2862,9 +2967,7 @@ cdef class Matroid(SageObject):
A *hyperplane* is a flat of rank ``self.full_rank() - 1``. A *flat* is
a closed set.
- OUTPUT:
-
- An iterable containing all hyperplanes of the matroid.
+ OUTPUT: an iterable containing all hyperplanes of the matroid
.. SEEALSO::
@@ -2882,32 +2985,90 @@ cdef class Matroid(SageObject):
r"""
Return the `f`-vector of the matroid.
- The `f`-*vector* is a vector `(f_0, ..., f_r)`, where `f_i` is the
- number of flats of rank `i`, and `r` is the rank of the matroid.
+ The `f`-*vector* is a vector `(f_0, \dots, f_r)`, where `f_i` is the
+ number of independent sets of rank `i`, and `r` is the rank of the
+ matroid.
- OUTPUT:
+ OUTPUT: a list of integers
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.Vamos()
+ sage: M.f_vector()
+ [1, 8, 28, 56, 65]
+
+ TESTS::
- List of integers.
+ sage: for M in matroids.AllMatroids(5): # optional - matroid_database
+ ....: assert M.f_vector() == SimplicialComplex(M.bases()).f_vector()
+ """
+ cdef list f = []
+ cdef int i, sum
+ for i in range(self.full_rank() + 1):
+ sum = 0
+ for _ in self.independent_r_sets_iterator(i):
+ sum += 1
+ f.append(sum)
+ return f
+
+ cpdef whitney_numbers(self) noexcept:
+ r"""
+ Return the Whitney numbers of the first kind of the matroid.
+
+ The Whitney numbers of the first kind -- here encoded as a vector
+ `(w_0=1, ..., w_r)` -- are numbers of alternating sign, where `w_i` is
+ the value of the coefficient of the `(r-i)`-th degree term of the
+ matroid's characteristic polynomial. Moreover, `|w_i|` is the number of
+ `i`-faces of the broken circuit complex of the matroid.
+
+ OUTPUT: a list of integers
EXAMPLES::
sage: M = matroids.catalog.BetsyRoss()
- sage: M.f_vector()
+ sage: M.whitney_numbers()
+ [1, -11, 35, -25]
+
+ TESTS::
+
+ sage: M = Matroid(groundset=[0,1,2], circuits=[[0]])
+ sage: M.whitney_numbers()
+ []
+ """
+ cdef list abs_w = [0] * (self.rank()+1)
+ for S in self.no_broken_circuits_sets_iterator():
+ abs_w[len(S)] += 1
+ return [(-1)**i * abs_w[i] for i in range(len(abs_w)) if abs_w[i] != 0]
+
+ cpdef whitney_numbers2(self) noexcept:
+ r"""
+ Return the Whitney numbers of the second kind of the matroid.
+
+ The Whitney numbers of the second kind are here encoded as a vector
+ `(W_0, ..., W_r)`, where `W_i` is the number of flats of rank `i`, and
+ `r` is the rank of the matroid.
+
+ OUTPUT: a list of integers
+
+ EXAMPLES::
+
+ sage: M = matroids.catalog.BetsyRoss()
+ sage: M.whitney_numbers2()
[1, 11, 20, 1]
"""
loops = self._closure(set())
flags = [[loops, set(), self.groundset() - loops]]
- f_vec = [1]
+ W = [1]
for r in range(self.full_rank()):
flags = self._extend_flags(flags)
- f_vec.append(len(flags))
- return f_vec
+ W.append(len(flags))
+ return W
cpdef broken_circuits(self, ordering=None) noexcept:
r"""
Return the list of broken circuits of ``self``.
- Let `M` be a matroid with ground set `E`, and let `<` be a total
+ Let `M` be a matroid with groundset `E`, and let `<` be a total
ordering on `E`. A *broken circuit* for `M` means a subset `B` of
`E` such that there exists a `u \in E` for which `B \cup \{ u \}`
is a circuit of `M` and `u < b` for all `b \in B`.
@@ -2931,17 +3092,17 @@ cdef class Matroid(SageObject):
[[1, 2], [1, 4], [2, 3, 4]]
"""
if ordering is None:
- ordering = sorted(self.groundset())
+ ordering = sorted(self.groundset(), key=str)
else:
orderset = frozenset(ordering)
if len(orderset) != len(self.groundset()) or orderset != self.groundset():
- raise ValueError("not an ordering of the ground set")
+ raise ValueError("not an ordering of the groundset")
ordering = list(ordering)
ret = set()
- for C in self.circuits():
+ for C in self.circuits_iterator():
for k in ordering:
if k in C:
- ret.add(C.difference([k]))
+ ret.add(frozenset(C).difference([k]))
break
return frozenset(ret)
@@ -2949,7 +3110,7 @@ cdef class Matroid(SageObject):
r"""
Return the no broken circuits (NBC) sets of ``self``.
- An NBC set is a subset `A` of the ground set under some total
+ An NBC set is a subset `A` of the groundset under some total
ordering `<` such that `A` contains no broken circuit.
INPUT:
@@ -2990,10 +3151,12 @@ cdef class Matroid(SageObject):
minimal-removal convention, while the implementation is not
modified from the published algorithm.
"""
- cdef list rev_order
+ if len(self.loops()) > 0:
+ return []
+ cdef list rev_order
if ordering is None:
- rev_order = sorted(self.groundset(), reverse=True)
+ rev_order = sorted(self.groundset(), key=str, reverse=True)
else:
if frozenset(ordering) != self.groundset():
raise ValueError("not an ordering of the groundset")
@@ -3006,8 +3169,7 @@ cdef class Matroid(SageObject):
cdef dict reverse_dict = {value: key for key, value in enumerate(rev_order)}
cdef list H, Ht, temp, B = [frozenset()]
- cdef frozenset loops = self.loops()
- cdef list next_level = [[val] for val in rev_order if val not in loops]
+ cdef list next_level = [[val] for val in rev_order]
cdef list cur_level
cdef Py_ssize_t i = 0
cdef Py_ssize_t tp
@@ -3032,6 +3194,68 @@ cdef class Matroid(SageObject):
next_level.extend(Ht)
return B
+ def no_broken_circuits_sets_iterator(self, ordering=None):
+ r"""
+ Return an iterator over no broken circuits (NBC) sets of ``self``.
+
+ An NBC set is a subset `A` of the groundset under some total
+ ordering `<` such that `A` contains no broken circuit.
+
+ INPUT:
+
+ - ``ordering`` -- a total ordering of the groundset given as a list
+
+ EXAMPLES::
+
+ sage: M = Matroid(circuits=[[1,2,3], [3,4,5], [1,2,4,5]])
+ sage: SimplicialComplex(list(M.no_broken_circuits_sets_iterator()))
+ Simplicial complex with vertex set (1, 2, 3, 4, 5)
+ and facets {(1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5)}
+ sage: SimplicialComplex(list(M.no_broken_circuits_sets_iterator([5,4,3,2,1])))
+ Simplicial complex with vertex set (1, 2, 3, 4, 5)
+ and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)}
+
+ ::
+
+ sage: M = Matroid(circuits=[[1,2,3], [1,4,5], [2,3,4,5]])
+ sage: SimplicialComplex(list(M.no_broken_circuits_sets_iterator([5,4,3,2,1])))
+ Simplicial complex with vertex set (1, 2, 3, 4, 5)
+ and facets {(1, 3, 5), (2, 3, 5), (2, 4, 5), (3, 4, 5)}
+ """
+ if len(self.loops()) == 0:
+
+ if ordering is None:
+ rev_order = sorted(self.groundset(), key=str, reverse=True)
+ else:
+ if frozenset(ordering) != self.groundset():
+ raise ValueError("not an ordering of the groundset")
+ rev_order = list(reversed(ordering))
+
+ Tmax = len(rev_order)
+ reverse_dict = {value: key for key, value in enumerate(rev_order)}
+
+ yield frozenset()
+ next_level = [[val] for val in rev_order]
+ i = 0
+ level = -1
+ while next_level:
+ cur_level = next_level
+ next_level = []
+ level += 1
+ for H in cur_level:
+ tp = ( reverse_dict[H[level]]) + 1
+ is_indep = True
+ Ht = [None] * (Tmax-tp)
+ for i in range(tp, Tmax):
+ temp = H + [rev_order[i]]
+ if not self._is_independent(frozenset(temp)):
+ is_indep = False
+ break
+ Ht[i-tp] = temp
+ if is_indep:
+ yield frozenset(H)
+ next_level.extend(Ht)
+
def orlik_solomon_algebra(self, R, ordering=None, **kwargs):
"""
Return the Orlik-Solomon algebra of ``self``.
@@ -3039,13 +3263,13 @@ cdef class Matroid(SageObject):
INPUT:
- ``R`` -- the base ring
- - ``ordering`` -- (optional) an ordering of the ground set
- - optional parameter ``invariant`` -- (optional, default: None) either
- a semigroup ``G`` whose ``__call__`` acts on the groundset, or pair
- ``(G, action)`` where ``G`` is a semigroup and ``action``
- is a function ``action(g,e)`` which takes a pair of a group
- element and a grounset element and returns the groundset
- element which is the result of ``e`` acted upon by ``g``
+ - ``ordering`` -- (optional) an ordering of the groundset
+ - ``invariant`` -- (optional, default: None) either a semigroup ``G``
+ whose ``__call__`` acts on the groundset, or pair ``(G, action)``
+ where ``G`` is a semigroup and ``action`` is a function
+ ``action(g,e)`` which takes a pair of a group element and a grounset
+ element and returns the groundset element which is the result of
+ ``e`` acted upon by ``g``
.. SEEALSO::
@@ -3133,8 +3357,7 @@ cdef class Matroid(SageObject):
n = self.size()
vector_e = FreeModule(ZZ, n).basis()
convert = {ind: i for i, ind in enumerate(self.groundset())}
- vertices = [sum(vector_e[convert[i]] for i in B)
- for B in self.bases()]
+ vertices = [sum(vector_e[convert[i]] for i in B) for B in self.bases_iterator()]
return Polyhedron(vertices)
def independence_matroid_polytope(self):
@@ -3177,8 +3400,7 @@ cdef class Matroid(SageObject):
ambient = FreeModule(ZZ, n)
vector_e = ambient.basis()
convert = {ind: i for i, ind in enumerate(self.groundset())}
- vertices = [ambient.sum(vector_e[convert[i]] for i in IS)
- for IS in self.independent_sets()]
+ vertices = [ambient.sum(vector_e[convert[i]] for i in IS) for IS in self.independent_sets_iterator()]
return Polyhedron(vertices)
# isomorphism and equality
@@ -3193,13 +3415,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid,
- - optional parameter ``certificate`` -- Boolean.
-
- OUTPUT:
+ - ``other`` -- a matroid,
+ - ``certificate`` -- boolean (default: ``False``)
- Boolean,
- and, if certificate = True, a dictionary or None
+ OUTPUT: boolean, and, if ``certificate = True``, a dictionary or
+ ``None``
EXAMPLES::
@@ -3236,12 +3456,10 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- A matroid,
- - optional parameter ``certificate`` -- Boolean.
+ - ``certificate`` -- boolean (default: ``False``).
- OUTPUT:
-
- Boolean,
- and, if ``certificate=True``, a dictionary giving the isomorphism or ``None``
+ OUTPUT: boolean, and, if ``certificate=True``, a dictionary giving the
+ isomorphism or ``None``
.. NOTE::
@@ -3265,7 +3483,7 @@ cdef class Matroid(SageObject):
return self._is_isomorphic(other), self._isomorphism(other)
if self is other:
return True
- return (self.full_rank() == other.full_rank() and self.nonbases()._isomorphism(other.nonbases()) is not None)
+ return (self.full_rank() == other.full_rank() and SetSystem(list(self.groundset()), list(self.nonbases()))._isomorphism(SetSystem(list(other.groundset()), list(other.nonbases()))) is not None)
cpdef isomorphism(self, other) noexcept:
r"""
@@ -3278,11 +3496,9 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid.
-
- OUTPUT:
+ - ``other`` -- a matroid
- A dictionary, or ``None``.
+ OUTPUT: a dictionary, or ``None``
EXAMPLES::
@@ -3314,11 +3530,9 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid.
+ - ``other`` -- a matroid
- OUTPUT:
-
- A dictionary, or ``None``
+ OUTPUT: a dictionary, or ``None``
EXAMPLES::
@@ -3335,7 +3549,7 @@ cdef class Matroid(SageObject):
if self is other:
return {e:e for e in self.groundset()}
if self.full_rank() == other.full_rank():
- return self.nonbases()._isomorphism(other.nonbases())
+ return SetSystem(list(self.groundset()), list(self.nonbases()))._isomorphism(SetSystem(list(other.groundset()), list(other.nonbases())))
else:
return None
@@ -3349,11 +3563,9 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid.
-
- OUTPUT:
+ - ``other`` -- a matroid
- Boolean.
+ OUTPUT: boolean
.. NOTE::
@@ -3435,13 +3647,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid.
- - ``morphism`` -- A map. Can be, for instance,
- a dictionary, function, or permutation.
+ - ``other`` -- a matroid
+ - ``morphism`` -- a map; can be, for instance, a dictionary, function,
+ or permutation
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -3574,13 +3784,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid instance.
+ - ``other`` -- a matroid
- ``morphism`` -- a dictionary mapping the groundset of ``self`` to
the groundset of ``other``.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
EXAMPLES::
@@ -3699,13 +3907,11 @@ cdef class Matroid(SageObject):
INPUT:
- ``contractions`` -- (default: ``None``) an element or set of
- elements to be contracted.
+ elements to be contracted
- ``deletions`` -- (default: ``None``) an element or set of elements
- to be deleted.
+ to be deleted
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. NOTE::
@@ -3820,11 +4026,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``X`` -- Either a single element of the groundset, or a collection
- of elements.
-
- OUTPUT:
+ of elements
- The matroid obtained by contracting the element(s) in ``X``.
+ OUTPUT: the matroid obtained by contracting the element(s) in ``X``
.. SEEALSO::
@@ -3899,11 +4103,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``X`` -- Either a single element of the groundset, or a collection
- of elements.
+ of elements
- OUTPUT:
-
- The matroid obtained by deleting the element(s) in ``X``.
+ OUTPUT: the matroid obtained by deleting the element(s) in ``X``
.. SEEALSO::
@@ -3950,7 +4152,7 @@ cdef class Matroid(SageObject):
r"""
Shorthand for ``self.delete(X)``.
- DEPRECATED
+ Deprecated.
EXAMPLES::
@@ -3967,7 +4169,7 @@ cdef class Matroid(SageObject):
r"""
Return the dual of the matroid.
- Let `M` be a matroid with ground set `E`. If `B` is the set of bases
+ Let `M` be a matroid with groundset `E`. If `B` is the set of bases
of `M`, then the set `\{E - b : b \in B\}` is the set of bases of
another matroid, the *dual* of `M`.
@@ -3976,10 +4178,6 @@ cdef class Matroid(SageObject):
This function wraps ``self`` in a ``DualMatroid`` object. For more
efficiency, subclasses that can, should override this method.
- OUTPUT:
-
- The dual matroid.
-
EXAMPLES::
sage: M = matroids.catalog.Pappus()
@@ -4006,9 +4204,7 @@ cdef class Matroid(SageObject):
can be obtained by adding an element freely to the span of the matroid
and then contracting that element.
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. SEEALSO::
@@ -4036,13 +4232,11 @@ cdef class Matroid(SageObject):
INPUT:
- ``N`` -- An instance of a ``Matroid`` object,
- - ``certificate`` -- Boolean (Default: ``False``) If ``True``, returns
+ - ``certificate`` -- boolean (Default: ``False``) If ``True``, returns
``True, (X, Y, dic) where ``N`` is isomorphic to ``self.minor(X, Y)``,
and ``dic`` is an isomorphism between ``N`` and ``self.minor(X, Y)``.
- OUTPUT:
-
- boolean or tuple.
+ OUTPUT: boolean or tuple
.. SEEALSO::
@@ -4096,9 +4290,7 @@ cdef class Matroid(SageObject):
where ``F`` is a flat and ``self.minor(contractions=F)`` has a
`U_{2,k}` restriction or ``(False, None)``.
- OUTPUT:
-
- Boolean or tuple.
+ OUTPUT: boolean or tuple
.. SEEALSO::
@@ -4161,9 +4353,7 @@ cdef class Matroid(SageObject):
- ``hyperlines`` -- (default: None) a set of flats of codimension 2.
The flats are assumed to be ``frozenset`` compatible.
- OUTPUT:
-
- Boolean or tuple.
+ OUTPUT: boolean or tuple
EXAMPLES::
@@ -4213,9 +4403,7 @@ cdef class Matroid(SageObject):
each of these. If not specified, the element is assumed to be in the
span of the full groundset.
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. NOTE::
@@ -4292,9 +4480,7 @@ cdef class Matroid(SageObject):
of each of these. If not specified, the element is assumed to be in
the cospan of the full groundset.
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. SEEALSO::
@@ -4363,11 +4549,9 @@ cdef class Matroid(SageObject):
INPUT:
- - ``subsets`` -- A collection of subsets of the groundset.
-
- OUTPUT:
+ - ``subsets`` -- a collection of subsets of the groundset
- A collection of subsets.
+ OUTPUT: a collection of subsets
.. SEEALSO::
@@ -4446,12 +4630,10 @@ cdef class Matroid(SageObject):
`e` that does not contain a minor `N` isomorphic to `U_{2, k}`,
where ``k > line_length``, and such that `e \in E(N)`.
- ``subsets`` -- (default: ``None``) a collection of subsets of the
- ground set. If given, restricts the output to linear subclasses such
+ groundset. If given, restricts the output to linear subclasses such
that each hyperplane contains an element of ``subsets``.
- OUTPUT:
-
- An iterable collection of linear subclasses.
+ OUTPUT: an iterable collection of linear subclasses
.. NOTE::
@@ -4513,14 +4695,12 @@ cdef class Matroid(SageObject):
- ``line_length`` -- (optional) a natural number. If given, restricts
the output to extensions that do not contain a `U_{2, k}` minor
where ``k > line_length``.
- - ``subsets`` -- (optional) a collection of subsets of the ground set.
+ - ``subsets`` -- (optional) a collection of subsets of the groundset.
If given, restricts the output to extensions where the new element
is contained in all hyperplanes that contain an element of
``subsets``.
- OUTPUT:
-
- An iterable containing matroids.
+ OUTPUT: an iterable containing matroids
.. NOTE::
@@ -4579,14 +4759,12 @@ cdef class Matroid(SageObject):
- ``coline_length`` -- (optional) a natural number. If given,
restricts the output to coextensions that do not contain a
`U_{k - 2, k}` minor where ``k > coline_length``.
- - ``subsets`` -- (optional) a collection of subsets of the ground set.
+ - ``subsets`` -- (optional) a collection of subsets of the groundset.
If given, restricts the output to extensions where the new element
is contained in all cohyperplanes that contain an element of
``subsets``.
- OUTPUT:
-
- An iterable containing matroids.
+ OUTPUT: an iterable containing matroids
.. NOTE::
@@ -4631,9 +4809,7 @@ cdef class Matroid(SageObject):
parallel class (a closed set of rank 1, that is, each pair in it forms
a circuit of length 2).
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. SEEALSO::
@@ -4669,9 +4845,7 @@ cdef class Matroid(SageObject):
element from each series class (a coclosed set of rank 1, that is,
each pair in it forms a cocircuit of length 2).
- OUTPUT:
-
- A matroid.
+ OUTPUT: a matroid
.. SEEALSO::
@@ -4703,9 +4877,7 @@ cdef class Matroid(SageObject):
A matroid is *simple* if it contains no circuits of length 1 or 2.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -4739,9 +4911,7 @@ cdef class Matroid(SageObject):
Dual method of
:meth:`M.is_simple() `.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -4775,9 +4945,7 @@ cdef class Matroid(SageObject):
deleting the complement of that subset is
:meth:`connected `.
- OUTPUT:
-
- A list of subsets.
+ OUTPUT: a list of subsets
.. SEEALSO::
@@ -4815,9 +4983,7 @@ cdef class Matroid(SageObject):
groundset with `X, Y` nonempty and `r(X) + r(Y) = r(X\cup Y)`.
A matroid is *connected* if it has no separations.
- OUTPUT:
-
- Boolean.
+ OUTPUT: boolean
.. SEEALSO::
@@ -4867,9 +5033,7 @@ cdef class Matroid(SageObject):
- ``T`` -- (optional) a subset (or any iterable) of the groundset
disjoint from ``S``
- OUTPUT:
-
- An integer.
+ OUTPUT: an integer
EXAMPLES::
@@ -4906,12 +5070,10 @@ cdef class Matroid(SageObject):
INPUT:
- - ``S`` -- a subset of the ground set
- - ``T`` -- (optional) a subset of the ground set disjoint from ``S``
-
- OUTPUT:
+ - ``S`` -- a subset of the groundset
+ - ``T`` -- (optional) a subset of the groundset disjoint from ``S``
- An integer.
+ OUTPUT: an integer
ALGORITHM:
@@ -4945,9 +5107,8 @@ cdef class Matroid(SageObject):
- ``T`` -- a subset (or any iterable) of the groundset disjoint
from ``S``
- OUTPUT:
-
- A tuple ``(I, X)`` containing a frozenset ``I`` and a frozenset ``X``.
+ OUTPUT: a tuple ``(I, X)`` containing a frozenset ``I`` and a frozenset
+ ``X``
ALGORITHM:
@@ -4991,12 +5152,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``S`` -- a subset of the ground set
- - ``T`` -- a subset of the ground set disjoint from ``S``
-
- OUTPUT:
+ - ``S`` -- a subset of the groundset
+ - ``T`` -- a subset of the groundset disjoint from ``S``
- A tuple ``(I, X)`` containing a frozenset ``I`` and a frozenset ``X``.
+ OUTPUT: a tuple ``(I, X)`` containing a frozenset ``I`` and a frozenset
+ ``X``
ALGORITHM:
@@ -5063,14 +5223,12 @@ cdef class Matroid(SageObject):
INPUT:
- - ``k`` -- a integer greater or equal to 1.
- - ``certificate`` -- (default: ``False``) a boolean; if ``True``,
+ - ``k`` -- an integer greater or equal to 1
+ - ``certificate`` -- (default: ``False``) boolean; if ``True``,
then return ``True, None`` if the matroid is k-connected,
and ``False, X`` otherwise, where ``X`` is a `= 2
+ # there is no partition A,B of the groundset such that |A|, |B| >= 2
else:
return True
# now there exist two disjoint pairs
@@ -5449,13 +5601,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``certificate`` -- (default: ``False``) a boolean; if ``True``,
+ - ``certificate`` -- boolean (default: ``False``); if ``True``,
then return ``True, None`` if the matroid is 3-connected,
and ``False,`` `X` otherwise, where `X` is a `<3`-separation
- OUTPUT:
-
- boolean, or a tuple ``(boolean, frozenset)``
+ OUTPUT: boolean, or a tuple ``(boolean, frozenset)``
ALGORITHM:
@@ -5475,7 +5625,7 @@ cdef class Matroid(SageObject):
....: groundset='abcdef')
sage: N._is_3connected_shifting() # needs sage.graphs
False
- sage: matroids.catalog.BetsyRoss()._is_3connected_shifting() # needs sage.graphs
+ sage: matroids.catalog.BetsyRoss()._is_3connected_shifting() # needs sage.graphs
True
sage: M = matroids.catalog.R6()
sage: M._is_3connected_shifting() # needs sage.graphs
@@ -5527,13 +5677,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``certificate`` -- (default: ``False``) a boolean; if ``True``,
+ - ``certificate`` -- boolean (default: ``False``); if ``True``,
then return ``True, None`` if the matroid is 4-connected,
and ``False,`` `X` otherwise, where `X` is a `<4`-separation
- OUTPUT:
-
- boolean, or a tuple ``(boolean, frozenset)``
+ OUTPUT: boolean, or a tuple ``(boolean, frozenset)``
ALGORITHM:
@@ -5639,7 +5787,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``X`` -- A basis
+ - ``X`` -- a basis
- ``P_rows`` -- a set of row indices of the first submatrix
- ``P_cols`` -- a set of column indices of the first submatrix
- ``Q_rows`` -- a set of row indices of the second submatrix
@@ -5648,8 +5796,8 @@ cdef class Matroid(SageObject):
OUTPUT:
- - `False, None` -- if there is no ``m``-separator.
- - `True, E` -- if there exist a ``m``-separator ``E``.
+ - `False, None` -- if there is no ``m``-separator
+ - `True, E` -- if there exist an ``m``-separator ``E``
EXAMPLES::
@@ -5700,7 +5848,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``X`` -- A basis
+ - ``X`` -- a basis
- ``X_1`` -- set of row indices of the first submatrix
- ``Y_2`` -- set of column indices of the first submatrix
- ``X_2`` -- set of row indices of the second submatrix
@@ -5709,8 +5857,8 @@ cdef class Matroid(SageObject):
OUTPUT:
- - ``False, None`` -- if there is no ``m``-separator.
- - ``True, E`` -- if there exist a ``m``-separator ``E``.
+ - ``False, None`` -- if there is no ``m``-separator
+ - ``True, E`` -- if there exist an ``m``-separator ``E``
EXAMPLES::
@@ -5783,13 +5931,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``certificate`` -- (default: ``False``) a boolean; if ``True``,
+ - ``certificate`` -- boolean (default: ``False``); if ``True``,
then return ``True, None`` if the matroid is 3-connected,
and ``False,`` `X` otherwise, where `X` is a `<3`-separation
- OUTPUT:
-
- boolean, or a tuple ``(boolean, frozenset)``
+ OUTPUT: boolean, or a tuple ``(boolean, frozenset)``
ALGORITHM:
@@ -5808,7 +5954,7 @@ cdef class Matroid(SageObject):
....: groundset='abcdef')
sage: N._is_3connected_BC()
False
- sage: matroids.catalog.BetsyRoss()._is_3connected_BC() # needs sage.graphs
+ sage: matroids.catalog.BetsyRoss()._is_3connected_BC() # needs sage.graphs
True
sage: M = matroids.catalog.R6()
sage: M._is_3connected_BC() # needs sage.graphs
@@ -5845,9 +5991,7 @@ cdef class Matroid(SageObject):
- ``fund_cocircuits`` -- a iterable of some fundamental cocircuits with
respect to ``basis``. It must contain all separating fundamental cocircuits.
- OUTPUT:
-
- boolean
+ OUTPUT: boolean
EXAMPLES::
@@ -5937,19 +6081,21 @@ cdef class Matroid(SageObject):
A matroid is paving if each of its circuits has size `r` or `r+1`.
- OUTPUT:
-
- boolean
+ OUTPUT: boolean
EXAMPLES::
sage: M = matroids.catalog.Vamos()
sage: M.is_paving()
True
+ sage: M = matroids.Theta(4)
+ sage: M.is_paving()
+ False
"""
- for C in self.circuits():
- if len(C) < self.rank():
- return False
+ if self.rank() >= 2:
+ for X in combinations(self.groundset(), self.rank()-1):
+ if not self._is_independent(frozenset(X)):
+ return False
return True
cpdef is_sparse_paving(self) noexcept:
@@ -5959,9 +6105,7 @@ cdef class Matroid(SageObject):
A matroid is sparse-paving if the symmetric difference of every pair
of circuits is greater than 2.
- OUTPUT:
-
- boolean
+ OUTPUT: boolean
EXAMPLES::
@@ -5975,7 +6119,7 @@ cdef class Matroid(SageObject):
if not self.is_paving():
return False
from itertools import combinations
- for (C1, C2) in combinations(self.circuits(), 2):
+ for (C1, C2) in combinations(self.circuits_iterator(), 2):
if len(C1 ^ C2) <= 2:
return False
return True
@@ -6014,7 +6158,7 @@ cdef class Matroid(SageObject):
r"""
Return a binary matroid `M` so that relative to a fixed basis `B`,
`X` is a basis of ``self`` if and only if `X` is a basis of `M`
- for all subsets `X` of the ground set such that
+ for all subsets `X` of the groundset such that
`|X \setminus B| \leq 1`.
INPUT:
@@ -6061,14 +6205,12 @@ cdef class Matroid(SageObject):
- ``randomized_tests`` -- (default: 1) an integer; the number of
times a certain necessary condition for being binary is tested,
using randomization
- - ``verify`` -- (default: ``True``), a Boolean; if ``True``,
+ - ``verify`` -- boolean (default: ``True``); if ``True``,
any output will be a binary matroid representing ``self``; if
``False``, any output will represent ``self`` if and only if the
matroid is binary
- OUTPUT:
-
- Either a BinaryMatroid, or ``None``
+ OUTPUT: either a ``BinaryMatroid``, or ``None``
ALGORITHM:
@@ -6118,9 +6260,7 @@ cdef class Matroid(SageObject):
times a certain necessary condition for being binary is tested,
using randomization
- OUTPUT:
-
- A Boolean.
+ OUTPUT: boolean
ALGORITHM:
@@ -6189,9 +6329,9 @@ cdef class Matroid(SageObject):
if basis is None:
basis = self.basis()
- basis = sorted(basis)
+ basis = sorted(basis, key=str)
bdx = {basis[i]: i for i in range(len(basis))}
- E = sorted(self.groundset())
+ E = sorted(self.groundset(), key=str)
idx = {Ei: i for i, Ei in enumerate(E)}
A = TernaryMatrix(len(basis), len(E))
for e in basis:
@@ -6246,14 +6386,14 @@ cdef class Matroid(SageObject):
- ``randomized_tests`` -- (default: 1) an integer; the number of
times a certain necessary condition for being ternary is tested,
using randomization
- - ``verify`` -- (default: ``True``), a Boolean; if ``True``,
+ - ``verify`` -- boolean (default: ``True``); if ``True``,
any output will be a ternary matroid representing ``self``; if
``False``, any output will represent ``self`` if and only if the
matroid is ternary
- OUTPUT:
-
- Either a :class:`TernaryMatroid `, or ``None``
+ OUTPUT: Either a
+ :class:`TernaryMatroid `,
+ or ``None``
ALGORITHM:
@@ -6303,9 +6443,7 @@ cdef class Matroid(SageObject):
times a certain necessary condition for being ternary is tested,
using randomization
- OUTPUT:
-
- A Boolean.
+ OUTPUT: boolean
ALGORITHM:
@@ -6376,14 +6514,12 @@ cdef class Matroid(SageObject):
INPUT:
- ``C`` -- a circuit
- - ``certificate`` -- (default: ``False``) a boolean, if ``True``
+ - ``certificate`` -- boolean (default: ``False``), if ``True``
return ``True, (x, Ax, Bx)``, where ``x`` is a chord and ``Ax`` and
``Bx`` are circuits whose union is the elements of ``C``
together with ``x``, if ``False`` return ``False, None``
- OUTPUT:
-
- - boolean or tuple
+ OUTPUT: boolean or tuple
EXAMPLES::
@@ -6428,14 +6564,12 @@ cdef class Matroid(SageObject):
INPUT:
- ``C`` -- a circuit
- - ``certificate`` -- (default: ``False``) a boolean, if ``True``
+ - ``certificate`` -- boolean (default: ``False``), if ``True``
return ``True, (x, Ax, Bx)``, where ``x`` is a chord and ``Ax`` and
``Bx`` are circuits whose union is the elements of ``C``
together with ``x``, if ``False`` return ``False, None``
- OUTPUT:
-
- - boolean or tuple
+ OUTPUT: boolean or tuple
EXAMPLES::
@@ -6478,9 +6612,7 @@ cdef class Matroid(SageObject):
``True`` return ``True, C``, where ``C`` is a non
``k1`` ``k2`` circuit
- Output:
-
- - boolean or tuple
+ OUTPUT: boolean or tuple
.. SEEALSO::
@@ -6557,11 +6689,9 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- ``weights`` -- a dictionary or function mapping the elements of
- ``X`` to nonnegative weights.
-
- OUTPUT:
+ ``X`` to nonnegative weights
- A subset of ``X``.
+ OUTPUT: a subset of ``X``
ALGORITHM:
@@ -6644,11 +6774,9 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- ``weights`` -- a dictionary or function mapping the elements of
- ``X`` to nonnegative weights.
-
- OUTPUT:
+ ``X`` to nonnegative weights
- A subset of ``X``.
+ OUTPUT: a subset of ``X``
ALGORITHM:
@@ -6732,15 +6860,14 @@ cdef class Matroid(SageObject):
The *weight* of a subset ``S`` is ``sum(weights(e) for e in S)``.
-
INPUT:
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- ``weights`` -- a dictionary or function mapping the elements of
- ``X`` to nonnegative weights.
+ ``X`` to nonnegative weights
- OUTPUT: Boolean.
+ OUTPUT: boolean
ALGORITHM:
@@ -6751,7 +6878,6 @@ cdef class Matroid(SageObject):
previously selected elements, then we check if it is independent with the
previously selected elements with higher weight.
-
EXAMPLES::
sage: from sage.matroids.advanced import setprint
@@ -6886,11 +7012,9 @@ cdef class Matroid(SageObject):
- ``X`` -- (default: the groundset) a subset (or any iterable)
of the groundset
- ``weights`` -- a dictionary or function mapping the elements of
- ``X`` to nonnegative weights.
-
- OUTPUT:
+ ``X`` to nonnegative weights
- Boolean.
+ OUTPUT: boolean
ALGORITHM:
@@ -7042,14 +7166,12 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- a second matroid with the same groundset as this
- matroid.
+ matroid
- ``weights`` -- (default: ``None``) a dictionary which specifies a
- weight for each element of the common groundset. Defaults to the
- all-1 weight function.
-
- OUTPUT:
+ weight for each element of the common groundset; defaults to the
+ all-1 weight function
- A subset of the groundset.
+ OUTPUT: a subset of the groundset
EXAMPLES::
@@ -7096,13 +7218,11 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- a second matroid with the same groundset as this
- matroid.
+ matroid
- ``weights`` -- a dictionary which must specify a weight for each
- element of the common groundset.
-
- OUTPUT:
+ element of the common groundset
- A subset of the groundset.
+ OUTPUT: a subset of the groundset
.. NOTE::
@@ -7135,12 +7255,12 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- a matroid with the same ground set as ``self``.
+ - ``other`` -- a matroid with the same groundset as ``self``
- ``weights`` -- a dictionary specifying a weight for each element of
- the common ground set ``E``.
+ the common groundset ``E``
- ``Y`` -- an extremal common independent set of ``self`` and
- ``other`` of size `k`. That is, a common independent set of maximum
- weight among common independent sets of size `k`.
+ ``other`` of size `k`; that is, a common independent set of maximum
+ weight among common independent sets of size `k`
OUTPUT:
@@ -7231,11 +7351,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- a second matroid with the same groundset as this
- matroid.
-
- OUTPUT:
+ matroid
- A subset of the groundset.
+ OUTPUT: subset of the groundset
EXAMPLES::
@@ -7263,11 +7381,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- a second matroid with the same groundset as this
- matroid.
-
- OUTPUT:
+ matroid
- A subset of the groundset.
+ OUTPUT: a subset of the groundset
.. NOTE::
@@ -7298,8 +7414,9 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- a matroid with the same ground set as ``self``.
- - ``Y`` -- an common independent set of ``self`` and ``other`` of size `k`.
+ - ``other`` -- a matroid with the same groundset as ``self``
+ - ``Y`` -- an common independent set of ``self`` and ``other`` of size
+ `k`
OUTPUT:
@@ -7433,9 +7550,7 @@ cdef class Matroid(SageObject):
Return a minimum number of disjoint independent sets that covers the
groundset.
- OUTPUT:
-
- A list of disjoint independent sets that covers the groundset.
+ OUTPUT: a list of disjoint independent sets that covers the groundset
EXAMPLES::
@@ -7508,11 +7623,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``B`` -- a basis of the matroid, assumed to have Python's
- ``frozenset`` interface.
-
- OUTPUT:
+ ``frozenset`` interface
- A subset of ``B``.
+ OUTPUT: a subset of ``B``
.. SEEALSO::
@@ -7529,7 +7642,7 @@ cdef class Matroid(SageObject):
N = self.groundset() - B
A = set()
for e in B:
- if min(self._cocircuit(N | set([e]))) == e:
+ if min(self._cocircuit(N | set([e])), key=str) == e:
A.add(e)
return A
@@ -7547,11 +7660,9 @@ cdef class Matroid(SageObject):
INPUT:
- ``B`` -- a basis of the matroid, assumed to have Python's
- ``frozenset`` interface.
-
- OUTPUT:
+ ``frozenset`` interface
- A subset of ``self.groundset() - B``.
+ OUTPUT: a subset of ``self.groundset() - B``
.. SEEALSO::
@@ -7568,7 +7679,7 @@ cdef class Matroid(SageObject):
N = self.groundset() - B
A = set()
for e in N:
- if min(self._circuit(B | set([e]))) == e:
+ if min(self._circuit(B | set([e])), key=str) == e:
A.add(e)
return A
@@ -7596,8 +7707,8 @@ cdef class Matroid(SageObject):
INPUT:
- - ``x`` -- (optional) a variable or numerical argument.
- - ``y`` -- (optional) a variable or numerical argument.
+ - ``x`` -- (optional) a variable or numerical argument
+ - ``y`` -- (optional) a variable or numerical argument
OUTPUT:
@@ -7627,15 +7738,63 @@ cdef class Matroid(SageObject):
R = ZZ['x, y']
x, y = R._first_ngens(2)
T = R(0)
- for B in self.bases():
+ for B in self.bases_iterator():
T += x ** len(self._internal(B)) * y ** len(self._external(B))
if a is not None and b is not None:
T = T(a, b)
return T
+ cpdef characteristic_polynomial(self, l=None) noexcept:
+ r"""
+ Return the characteristic polynomial of the matroid.
+
+ The *characteristic polynomial* of a matroid `M` is the polynomial
+
+ .. MATH::
+
+ \chi_M(\lambda) = \sum_{S \subseteq E} (-1)^{|S|}\lambda^{r(E)-r(S)},
+
+ where `E` is the groundset and`r` is the matroid's rank function. The
+ characteristic polynomial is also equal to
+ \sum_{i = 0}^r w_i\lambda^{r-i}, where `\{w_i\}_{i=0}^r` are the
+ Whitney numbers of the first kind.
+
+ INPUT:
+
+ - ``l`` -- a variable or numerical argument (optional)
+
+ OUTPUT: the characteristic polynomial, `\chi_M(\lambda)`, where
+ `\lambda` is substituted with any value provided as input.
+
+ EXAMPLES::
+
+ sage: M = matroids.CompleteGraphic(5)
+ sage: M.characteristic_polynomial()
+ l^4 - 10*l^3 + 35*l^2 - 50*l + 24
+ sage: M.characteristic_polynomial().factor()
+ (l - 4) * (l - 3) * (l - 2) * (l - 1)
+ sage: M.characteristic_polynomial(5)
+ 24
+
+ .. SEEALSO::
+
+ :meth:`whitney_numbers() `
+ """
+ val = l
+ R = ZZ['l']
+ l = R._first_ngens(1)[0]
+ chi = R(0)
+ cdef int r = self.rank()
+ cdef list w = self.whitney_numbers()
+ for i in range(len(w)):
+ chi += w[i] * l**(r-i)
+ if val is not None:
+ return chi(val)
+ return chi
+
cpdef flat_cover(self, solver=None, verbose=0, integrality_tolerance=1e-3) noexcept:
"""
- Return a minimum-size cover of the nonbases by non-spanning flats.
+ Return a minimum-size cover of the nonbases by nonspanning flats.
A *nonbasis* is a subset that has the size of a basis, yet is
dependent. A *flat* is a closed set.
@@ -7665,9 +7824,15 @@ cdef class Matroid(SageObject):
{'b', 'c', 'd'}, {'b', 'e', 'g'}, {'c', 'f', 'g'},
{'d', 'e', 'f'}]
"""
- NB = self.nonbases()
- if not NB:
+ flag = False
+ NB = self.nonbases_iterator()
+ for S in NB:
+ flag = True
+ break
+ if not flag: # if empty
return []
+
+ NB = self.nonbases_iterator()
FF = []
for r in range(self.full_rank()):
FF.extend(self.flats(r))
@@ -7770,7 +7935,7 @@ cdef class Matroid(SageObject):
# Create the ambient polynomial ring
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
try:
- names = ['A{}'.format(''.join(str(x) for x in sorted(F))) for F in flats]
+ names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=str))) for F in flats]
P = PolynomialRing(R, names)
except ValueError: # variables are not proper names
P = PolynomialRing(R, 'A', len(flats))
@@ -7796,22 +7961,22 @@ cdef class Matroid(SageObject):
- ``B`` -- (optional) a list containing a basis.
If internal point placement is used, these elements will be placed as vertices of a triangle.
- ``lineorders`` -- (optional) A list of lists where each of the inner lists
- specify ground set elements in a certain order which will be used to draw the
+ specify groundset elements in a certain order which will be used to draw the
corresponding line in geometric representation (if it exists).
- ``pos_method`` -- An integer specifying positioning method.
- ``0``: default positioning
- ``1``: use pos_dict if it is not ``None``
- ``2``: Force directed (Not yet implemented).
- - ``pos_dict``: A dictionary mapping ground set elements to their (x,y) positions.
- - ``save_pos``: A boolean indicating that point placements (either internal or user provided) and
+ - ``pos_dict``: A dictionary mapping groundset elements to their (x,y) positions.
+ - ``save_pos``: boolean indicating that point placements (either internal or user provided) and
line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for
reproducing the geometric representation during the same session
OUTPUT:
A sage graphics object of type that
- corresponds to the geometric representation of the matroid
+ corresponds to the geometric representation of the matroid.
EXAMPLES::
@@ -7847,27 +8012,31 @@ cdef class Matroid(SageObject):
lineorders2=matroids_plot_helpers.lineorders_union(self._cached_info['lineorders'],lineorders)
return matroids_plot_helpers.geomrep(self,B,lineorders2,pd=pos_dict, sp=save_pos)
- cpdef show(self,B=None,lineorders=None,pos_method=None,pos_dict=None,save_pos=False,lims=None) noexcept:
+ cpdef show(self, B=None, lineorders=None, pos_method=None, pos_dict=None, save_pos=False, lims=None) noexcept:
"""
Show the geometric representation of the matroid.
INPUT:
- - ``B`` -- (optional) a list containing elements of the groundset not in any particular order.
- If internal point placement is used, these elements will be placed as vertices of a triangle.
- - ``lineorders`` -- (optional) A list of lists where each of the inner lists
- specify ground set elements in a certain order which will be used to draw the
- corresponding line in geometric representation (if it exists).
- - ``pos_method`` -- An integer specifying positioning method
+ - ``B`` -- (optional) a list containing elements of the groundset not
+ in any particular order. If internal point placement is used, these
+ elements will be placed as vertices of a triangle.
+ - ``lineorders`` -- (optional) a list of lists where each of the inner
+ lists specify groundset elements in a certain order which will be
+ used to draw the corresponding line in geometric representation (if
+ it exists).
+ - ``pos_method`` -- an integer specifying the positioning method
- ``0``: default positioning
- ``1``: use pos_dict if it is not ``None``
- ``2``: Force directed (Not yet implemented).
- - ``pos_dict`` -- A dictionary mapping ground set elements to their (x,y) positions.
- - ``save_pos`` -- A boolean indicating that point placements (either internal or user provided) and
- line orders (if provided) will be cached in the matroid (``M._cached_info``) and can be used for
+ - ``pos_dict`` -- a dictionary mapping groundset elements to their
+ (x, y) positions
+ - ``save_pos`` -- boolean indicating that point placements (either
+ internal or user provided) and line orders (if provided) will be
+ cached in the matroid (``M._cached_info``) and can be used for
reproducing the geometric representation during the same session
- - ``lims`` -- A list of 4 elements ``[xmin,xmax,ymin,ymax]``
+ - ``lims`` -- a list of 4 elements ``[xmin,xmax,ymin,ymax]``
EXAMPLES::
@@ -7896,16 +8065,18 @@ cdef class Matroid(SageObject):
G.show(xmin=lims[0], xmax=lims[1], ymin=lims[2], ymax=lims[3])
return
- cpdef _fix_positions(self,pos_dict=None,lineorders=None) noexcept:
+ cpdef _fix_positions(self, pos_dict=None, lineorders=None) noexcept:
"""
Cache point positions and line orders without actually plotting
INPUT:
- - ``pos_dict`` -- (optional) A dictionary mapping ground set elements to their (x,y) positions.
- - ``lineorders`` -- (optional) A list of lists where each of the inner lists
- specify ground set elements in a certain order which will be used to draw the
- corresponding line in geometric representation (if it exists).
+ - ``pos_dict`` -- (optional) A dictionary mapping groundset elements to
+ their (x, y) positions
+ - ``lineorders`` -- (optional) a list of lists where each of the inner
+ lists specify groundset elements in a certain order which will be
+ used to draw the corresponding line in geometric representation (if
+ it exists).
EXAMPLES::
@@ -7940,16 +8111,14 @@ cdef class Matroid(SageObject):
Return the broken circuit complex of ``self``.
The broken circuit complex of a matroid with a total ordering `<`
- on the ground set is obtained from the
+ on the groundset is obtained from the
:meth:`NBC sets ` under subset inclusion.
INPUT:
- ``ordering`` -- a total ordering of the groundset given as a list
- OUTPUT:
-
- A simplicial complex of the NBC sets under inclusion.
+ OUTPUT: a simplicial complex of the NBC sets under inclusion
EXAMPLES::
@@ -7962,7 +8131,12 @@ cdef class Matroid(SageObject):
and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)}
"""
from sage.topology.simplicial_complex import SimplicialComplex
- return SimplicialComplex(self.no_broken_circuits_sets(ordering))
+ cdef int r = self.rank()
+ cdef list facets = []
+ for S in self.no_broken_circuits_sets_iterator(ordering):
+ if len(S) == r:
+ facets += [S]
+ return SimplicialComplex(facets, maximality_check=False)
cpdef automorphism_group(self) noexcept:
r"""
@@ -8007,9 +8181,7 @@ cdef class Matroid(SageObject):
maximum elements removed. The *Bergman complex* of a matroid `M` is the
order complex of `L`.
- OUTPUT:
-
- A simplicial complex as just described.
+ OUTPUT: a simplicial complex
EXAMPLES::
@@ -8028,10 +8200,10 @@ cdef class Matroid(SageObject):
r"""
Return the augmented Bergman complex of ``self``.
- Given a matroid `M` with ground set `E=\{1,2,\ldots,n\}`,
+ Given a matroid `M` with groundset `E=\{1,2,\ldots,n\}`,
the *augmented Bergman complex* can be seen as a hybrid of the complex
- of independent sets of `M` and the Bergman complex of `M`. It is defined
- as the simplicial complex on vertex set
+ of independent sets of `M` and the Bergman complex of `M`. It is
+ defined as the simplicial complex on vertex set
.. MATH::
@@ -8046,9 +8218,7 @@ cdef class Matroid(SageObject):
for which `I` is an independent set and `I\subseteq F_1\subsetneq F_2
\subsetneq\cdots\subsetneq F_\ell`.
- OUTPUT:
-
- A simplicial complex as just described.
+ OUTPUT: a simplicial complex
EXAMPLES::
@@ -8114,12 +8284,12 @@ cdef class Matroid(SageObject):
r = self.rank() - len(c)
# get candidate independent_sets
- for I in self.independent_r_sets(r):
+ for I in self.independent_r_sets_iterator(r):
if I.issubset(c[0]):
# add the facet
DM.add_face([f'L{i}' for i in I] +
- [f'R{sorted(F)}' for F in c])
+ [f'R{sorted(F, key=str)}' for F in c])
return DM
def union(self, matroids):
@@ -8127,19 +8297,17 @@ cdef class Matroid(SageObject):
Return the matroid union with another matroid or a list of matroids.
Let `(M_1, M_2, \ldots, M_k)` be a list of matroids where each `M_i`
- has ground set `E_i`. The *matroid
- union* `M` of `(M_1, M_2, \ldots, M_k)` has ground set `E = \cup E_i`.
- Moreover, a set `I \subseteq E` is independent in `M` if and only if the
- restriction of `I` to `E_i` is independent in `M_i` for every `i`.
+ has groundset `E_i`. The *matroid
+ union* `M` of `(M_1, M_2, \ldots, M_k)` has groundset `E = \cup E_i`.
+ Moreover, a set `I \subseteq E` is independent in `M` if and only if
+ the restriction of `I` to `E_i` is independent in `M_i` for every `i`.
INPUT:
- ``matroids`` - a matroid or a list of matroids
- OUTPUT:
-
- An instance of
- :class:`MatroidUnion `.
+ OUTPUT: an instance of
+ :class:`MatroidUnion `
EXAMPLES::
@@ -8170,7 +8338,7 @@ cdef class Matroid(SageObject):
matroids.
Let `(M_1, M_2, \ldots, M_k)` be a list of matroids where each `M_i`
- has ground set `E_i`. The matroid sum `(E_1,I_1),\ldots,(E_n,I_n)`
+ has groundset `E_i`. The matroid sum `(E_1,I_1),\ldots,(E_n,I_n)`
is a matroid `(E,I)` where `E= \bigsqcup_{i=1}^n E_i` and
`I= \bigsqcup_{i=1}^n I_i`.
@@ -8178,9 +8346,7 @@ cdef class Matroid(SageObject):
- ``matroids`` - a matroid or list of matroids
- OUTPUT:
-
- An instance of
+ OUTPUT: an instance of
:class:`MatroidSum `
EXAMPLES::
diff --git a/src/sage/matroids/union_matroid.pyx b/src/sage/matroids/union_matroid.pyx
index 04f7eee4402..cbcc89763f4 100644
--- a/src/sage/matroids/union_matroid.pyx
+++ b/src/sage/matroids/union_matroid.pyx
@@ -22,7 +22,7 @@ cdef class MatroidUnion(Matroid):
Matroid of rank 1 on 2 elements with 2 bases
sage: M.bases()
Iterator over a system of subsets
- sage: M.circuits()
+ sage: list(M.circuits())
[frozenset({3, 4})]
INPUT:
From bc92c7ee3ab15fe90e05504da1fc119f4764794a Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Thu, 15 Feb 2024 14:52:13 +0200
Subject: [PATCH 014/518] Set "default=-1" for rank of FlatsMatroid
This is for an invalid input with no flats
---
src/sage/matroids/flats_matroid.pyx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/matroids/flats_matroid.pyx b/src/sage/matroids/flats_matroid.pyx
index 1456a952d89..3c69920717b 100644
--- a/src/sage/matroids/flats_matroid.pyx
+++ b/src/sage/matroids/flats_matroid.pyx
@@ -83,7 +83,7 @@ cdef class FlatsMatroid(Matroid):
except KeyError:
self._F[i] = set()
self._F[i].add(frozenset(F))
- self._matroid_rank = max([0] + list(self._F))
+ self._matroid_rank = max(self._F, default=-1)
cpdef groundset(self) noexcept:
"""
From 4ffa2826ef56faec139b17cdc0b5fbef0dd0eec6 Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Fri, 16 Feb 2024 14:58:04 +0200
Subject: [PATCH 015/518] More internal iterators and cythonization
---
src/sage/matroids/matroid.pxd | 9 +++++
src/sage/matroids/matroid.pyx | 69 +++++++++++++++--------------------
2 files changed, 38 insertions(+), 40 deletions(-)
diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd
index dae3e7d4368..7ab91af018f 100644
--- a/src/sage/matroids/matroid.pxd
+++ b/src/sage/matroids/matroid.pxd
@@ -129,6 +129,10 @@ cdef class Matroid(SageObject):
cpdef broken_circuits(self, ordering=*) noexcept
cpdef no_broken_circuits_sets(self, ordering=*) noexcept
+ # polytopes
+ cpdef matroid_polytope(self) noexcept
+ cpdef independence_matroid_polytope(self) noexcept
+
# isomorphism
cpdef is_isomorphic(self, other, certificate=*) noexcept
cpdef _is_isomorphic(self, other, certificate=*) noexcept
@@ -155,6 +159,7 @@ cdef class Matroid(SageObject):
cpdef modular_cut(self, subsets) noexcept
cpdef linear_subclasses(self, line_length=*, subsets=*) noexcept
cpdef extensions(self, element=*, line_length=*, subsets=*) noexcept
+ cpdef coextensions(self, element=*, coline_length=*, subsets=*) noexcept
# connectivity
cpdef simplify(self) noexcept
@@ -222,8 +227,12 @@ cdef class Matroid(SageObject):
cpdef automorphism_group(self) noexcept
cpdef bergman_complex(self) noexcept
cpdef augmented_bergman_complex(self) noexcept
+ cpdef broken_circuit_complex(self, ordering=*) noexcept
# visualization
cpdef plot(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*) noexcept
cpdef show(self,B=*,lineorders=*,pos_method=*,pos_dict=*,save_pos=*,lims=*) noexcept
cpdef _fix_positions(self,pos_dict=*,lineorders=*) noexcept
+
+ # construction
+ cpdef direct_sum(self, matroids) noexcept
diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx
index caf6b6df3ee..42468f02761 100644
--- a/src/sage/matroids/matroid.pyx
+++ b/src/sage/matroids/matroid.pyx
@@ -147,6 +147,7 @@ additional functionality (e.g. linear extensions).
- :meth:`chow_ring() `
- :meth:`matroid_polytope() `
- :meth:`independence_matroid_polytope() `
+ - :meth:`lattice_of_flats() `
- :meth:`orlik_solomon_algebra() `
- :meth:`bergman_complex() `
- :meth:`augmented_bergman_complex() `
@@ -1113,9 +1114,9 @@ cdef class Matroid(SageObject):
if certificate:
return False, None
return False
- YY = self.dual().independent_r_sets(cd)
- for X in self.independent_r_sets(rd):
- for Y in YY:
+ D = self.dual()
+ for X in self.independent_r_sets_iterator(rd):
+ for Y in D.independent_r_sets_iterator(cd):
if X.isdisjoint(Y):
if N._is_isomorphic(self._minor(contractions=X, deletions=Y)):
if certificate:
@@ -2405,27 +2406,6 @@ cdef class Matroid(SageObject):
C.update([self._cocircuit(self.groundset().difference(B).union(set([e]))) for e in B])
return list(C)
- def cocircuits_iterator(self):
- """
- Return an iterator over the cocircuits of the matroid.
-
- .. SEEALSO::
-
- :meth:`M.cocircuit() `
-
- EXAMPLES::
-
- sage: M = matroids.catalog.Fano()
- sage: sorted([sorted(C) for C in M.cocircuits_iterator()])
- [['a', 'b', 'c', 'g'], ['a', 'b', 'd', 'e'], ['a', 'c', 'd', 'f'],
- ['a', 'e', 'f', 'g'], ['b', 'c', 'e', 'f'], ['b', 'd', 'f', 'g'],
- ['c', 'd', 'e', 'g']]
- """
- C = set()
- for B in self.bases_iterator():
- C.update([self._cocircuit(self.groundset().difference(B).union(set([e]))) for e in B])
- return list(C)
-
cpdef noncospanning_cocircuits(self) noexcept:
"""
Return the noncospanning cocircuits of the matroid.
@@ -3019,7 +2999,7 @@ cdef class Matroid(SageObject):
`(w_0=1, ..., w_r)` -- are numbers of alternating sign, where `w_i` is
the value of the coefficient of the `(r-i)`-th degree term of the
matroid's characteristic polynomial. Moreover, `|w_i|` is the number of
- `i`-faces of the broken circuit complex of the matroid.
+ `(i-1)`-dimensional faces of the broken circuit complex of the matroid.
OUTPUT: a list of integers
@@ -3318,7 +3298,7 @@ cdef class Matroid(SageObject):
# polytopes
- def matroid_polytope(self):
+ cpdef matroid_polytope(self) noexcept:
r"""
Return the matroid polytope of ``self``.
@@ -3357,10 +3337,15 @@ cdef class Matroid(SageObject):
n = self.size()
vector_e = FreeModule(ZZ, n).basis()
convert = {ind: i for i, ind in enumerate(self.groundset())}
- vertices = [sum(vector_e[convert[i]] for i in B) for B in self.bases_iterator()]
+ vertices = []
+ for B in self.bases_iterator():
+ sum = 0
+ for i in B:
+ sum += vector_e[convert[i]]
+ vertices += [sum]
return Polyhedron(vertices)
- def independence_matroid_polytope(self):
+ cpdef independence_matroid_polytope(self) noexcept:
r"""
Return the independence matroid polytope of ``self``.
@@ -3400,7 +3385,12 @@ cdef class Matroid(SageObject):
ambient = FreeModule(ZZ, n)
vector_e = ambient.basis()
convert = {ind: i for i, ind in enumerate(self.groundset())}
- vertices = [ambient.sum(vector_e[convert[i]] for i in IS) for IS in self.independent_sets_iterator()]
+ vertices = []
+ for IS in self.independent_sets_iterator():
+ lst = []
+ for i in IS:
+ lst += [vector_e[convert[i]]]
+ vertices += [ambient.sum(lst)]
return Polyhedron(vertices)
# isomorphism and equality
@@ -4256,8 +4246,7 @@ cdef class Matroid(SageObject):
sage: matroids.catalog.NonFano().has_minor(M)
True
sage: matroids.catalog.NonFano().has_minor(M, certificate=True)
- (True, (frozenset(), frozenset({'g'}),
- {0: 'b', 1: 'c', 2: 'a', 3: 'd', 4: 'e', 5: 'f'}))
+ (True, (frozenset(), frozenset({...}), {...}))
sage: M = matroids.catalog.Fano()
sage: M.has_minor(M, True)
(True,
@@ -4735,7 +4724,7 @@ cdef class Matroid(SageObject):
raise ValueError("cannot extend by element already in groundset")
return extension.MatroidExtensions(self, element, line_length=line_length, subsets=subsets) # return enumerator
- def coextensions(self, element=None, coline_length=None, subsets=None):
+ cpdef coextensions(self, element=None, coline_length=None, subsets=None) noexcept:
r"""
Return an iterable set of single-element coextensions of the matroid.
@@ -6632,12 +6621,12 @@ cdef class Matroid(SageObject):
sage: M.is_chordal(4, 5)
False
sage: M.is_chordal(4, 5, certificate=True)
- (False, frozenset({'a', 'b', 'e', 'f', 'g'}))
+ (False, frozenset({...}))
"""
cdef frozenset C
if k2 is None:
k2 = len(self.groundset()) + 1 # This is always larger than the rank
- for C in self.circuits():
+ for C in self.circuits_iterator():
if len(C) < k1 or len(C) > k2:
continue
if not self._is_circuit_chordal(C):
@@ -7754,9 +7743,9 @@ cdef class Matroid(SageObject):
\chi_M(\lambda) = \sum_{S \subseteq E} (-1)^{|S|}\lambda^{r(E)-r(S)},
- where `E` is the groundset and`r` is the matroid's rank function. The
+ where `E` is the groundset and `r` is the matroid's rank function. The
characteristic polynomial is also equal to
- \sum_{i = 0}^r w_i\lambda^{r-i}, where `\{w_i\}_{i=0}^r` are the
+ `\sum_{i = 0}^r w_i\lambda^{r-i}`, where `\{w_i\}_{i=0}^r` are the
Whitney numbers of the first kind.
INPUT:
@@ -8106,7 +8095,7 @@ cdef class Matroid(SageObject):
self._cached_info = {'plot_positions': pos_dict, 'lineorders': lineorders}
return
- def broken_circuit_complex(self, ordering=None):
+ cpdef broken_circuit_complex(self, ordering=None) noexcept:
r"""
Return the broken circuit complex of ``self``.
@@ -8171,7 +8160,7 @@ cdef class Matroid(SageObject):
[Oxl2011]_, p. 189.
"""
from sage.topology.simplicial_complex import SimplicialComplex
- return SimplicialComplex(self.bases()).automorphism_group()
+ return SimplicialComplex(self.bases(), maximality_check=False).automorphism_group()
cpdef bergman_complex(self) noexcept:
r"""
@@ -8265,7 +8254,7 @@ cdef class Matroid(SageObject):
"""
# Construct independent set complex from bases
from sage.topology.simplicial_complex import SimplicialComplex
- IM = SimplicialComplex(self.bases())
+ IM = SimplicialComplex(self.bases(), maximality_check=False)
LM = self.lattice_of_flats()
@@ -8332,7 +8321,7 @@ cdef class Matroid(SageObject):
matroids.insert(0, self)
return union_matroid.MatroidUnion(iter(matroids))
- def direct_sum(self, matroids):
+ cpdef direct_sum(self, matroids) noexcept:
r"""
Return the matroid direct sum with another matroid or list of
matroids.
From e7763d013b3cd51b86caa3c4196c265b9741f745 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Fri, 16 Feb 2024 15:00:33 +0000
Subject: [PATCH 016/518] Faster scalar multiplication for python types
---
src/sage/schemes/elliptic_curves/ell_point.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py
index bcfe8d5c1d2..bf86995daa8 100644
--- a/src/sage/schemes/elliptic_curves/ell_point.py
+++ b/src/sage/schemes/elliptic_curves/ell_point.py
@@ -3877,6 +3877,19 @@ def _acted_upon_(self, other, side):
return Q
+ def __mul__(self, other):
+ r"""
+ For all sage types, _acted_upon_ is called, but for
+ multiplication by a Python int, the slower __mul__
+ is called. Here we bind __mul__ to acted upon so that
+ the fast Pari multiplication is called when possible,
+ even when the scalar is a Python int rather than Sage
+ Integer.
+ """
+ return self._acted_upon_(other, None)
+
+ __rmul__ = __mul__
+
def log(self, base):
r"""
Return the discrete logarithm of this point to the given ``base``.
From 427ca29c02ba95c6054897597c6bba7f9a7210ff Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Fri, 16 Feb 2024 17:30:32 +0200
Subject: [PATCH 017/518] Add FlatsMatroid to doc
---
src/doc/en/reference/matroids/index.rst | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/doc/en/reference/matroids/index.rst b/src/doc/en/reference/matroids/index.rst
index 692d798d222..b52c5b3420a 100644
--- a/src/doc/en/reference/matroids/index.rst
+++ b/src/doc/en/reference/matroids/index.rst
@@ -29,6 +29,7 @@ Concrete implementations
sage/matroids/basis_matroid
sage/matroids/circuits_matroid
sage/matroids/circuit_closures_matroid
+ sage/matroids/flats_matroid
sage/matroids/linear_matroid
sage/matroids/rank_matroid
sage/matroids/graphic_matroid
From 9ea8e050534c7676680baf473d0806e5f4052298 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Mon, 19 Feb 2024 21:04:35 +0100
Subject: [PATCH 018/518] minor ruff fixes in quadratic forms (C4, UP, PERF)
---
src/sage/quadratic_forms/binary_qf.py | 26 ++---
src/sage/quadratic_forms/genera/genus.py | 97 +++++++++----------
.../quadratic_forms/genera/spinor_genus.py | 10 +-
.../quadratic_form__neighbors.py | 30 +++---
4 files changed, 81 insertions(+), 82 deletions(-)
diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py
index 555fe01b3ef..8286b639f4e 100755
--- a/src/sage/quadratic_forms/binary_qf.py
+++ b/src/sage/quadratic_forms/binary_qf.py
@@ -177,7 +177,7 @@ def _pari_init_(self):
sage: type(gp(f))
"""
- return 'Qfb(%s,%s,%s)' % (self._a, self._b, self._c)
+ return f'Qfb({self._a},{self._b},{self._c})'
@staticmethod
def principal(D):
@@ -218,7 +218,7 @@ def principal(D):
"""
D = ZZ(D)
D4 = D % 4
- if D4 not in (0,1):
+ if D4 not in (0, 1):
raise ValueError('discriminant must be congruent to 0 or 1 modulo 4')
return BinaryQF([1, D4, (D4-D)//4])
@@ -475,7 +475,7 @@ def __neg__(self):
"""
return BinaryQF([-self._a, -self._b, -self._c])
- def _repr_(self):
+ def _repr_(self) -> str:
"""
Display the quadratic form.
@@ -492,7 +492,7 @@ def _repr_(self):
"""
return repr(self.polynomial())
- def _latex_(self):
+ def _latex_(self) -> str:
"""
Return latex representation of this binary quadratic form.
@@ -637,7 +637,7 @@ def determinant(self):
det = determinant
@cached_method
- def has_fundamental_discriminant(self):
+ def has_fundamental_discriminant(self) -> bool:
"""
Return whether the discriminant `D` of this form is a
fundamental discriminant (i.e. `D` is the smallest element
@@ -659,7 +659,7 @@ def has_fundamental_discriminant(self):
"""
return self.discriminant().is_fundamental_discriminant()
- def is_primitive(self):
+ def is_primitive(self) -> bool:
r"""
Return whether the form `ax^2 + bxy + cy^2` satisfies
`\gcd(a, b, c) = 1`, i.e., is primitive.
@@ -705,7 +705,7 @@ def is_primitive(self):
"""
return self.content().is_one()
- def is_zero(self):
+ def is_zero(self) -> bool:
"""
Return whether ``self`` is identically zero.
@@ -721,7 +721,7 @@ def is_zero(self):
return self.content().is_zero()
@cached_method
- def is_weakly_reduced(self):
+ def is_weakly_reduced(self) -> bool:
r"""
Check if the form `ax^2 + bxy + cy^2` satisfies
`|b| \leq a \leq c`, i.e., is weakly reduced.
@@ -745,7 +745,7 @@ def is_weakly_reduced(self):
return (abs(self._b) <= self._a) and (self._a <= self._c)
@cached_method
- def is_reducible(self):
+ def is_reducible(self) -> bool:
r"""
Return whether this form is reducible and cache the result.
@@ -1930,9 +1930,11 @@ def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True):
b = D.sqrt()
c = ZZ.zero()
# -b/2 < a <= b/2
- for a in xsrange((-b/2).floor() + 1, (b/2).floor() + 1):
- if not primitive_only or (gcd([a, b, c]) == 1):
- form_list.append(BinaryQF(a, b, c))
+ form_list.extend(BinaryQF(a, b, c)
+ for a in xsrange((-b / 2).floor() + 1,
+ (b / 2).floor() + 1)
+ if not primitive_only or (gcd([a, b, c]) == 1))
+
# We follow the description of Buchmann/Vollmer 6.7.1. They
# enumerate all reduced forms. We only want representatives.
else:
diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py
index 8946683a988..2db66a93653 100644
--- a/src/sage/quadratic_forms/genera/genus.py
+++ b/src/sage/quadratic_forms/genera/genus.py
@@ -408,7 +408,7 @@ def LocalGenusSymbol(A, p):
return Genus_Symbol_p_adic_ring(p, symbol)
-def is_GlobalGenus(G):
+def is_GlobalGenus(G) -> bool:
r"""
Return if `G` represents the genus of a global quadratic form or lattice.
@@ -445,13 +445,13 @@ def is_GlobalGenus(G):
verbose(mesg="False in is_2_adic_genus(sym)", level=2)
return False
if (a*b).kronecker(p) != 1:
- verbose(mesg="False in (%s*%s).kronecker(%s)" % (a, b, p),
+ verbose(mesg=f"False in ({a}*{b}).kronecker({p})",
level=2)
return False
oddity -= loc.excess()
else:
if a.kronecker(p) != b:
- verbose(mesg="False in %s.kronecker(%s) != *%s" % (a, p, b),
+ verbose(mesg=f"False in {a}.kronecker({p}) != *{b}",
level=2)
return False
oddity += loc.excess()
@@ -461,7 +461,7 @@ def is_GlobalGenus(G):
return True
-def is_2_adic_genus(genus_symbol_quintuple_list):
+def is_2_adic_genus(genus_symbol_quintuple_list) -> bool:
r"""
Given a `2`-adic local symbol (as the underlying list of quintuples)
check whether it is the `2`-adic symbol of a `2`-adic form.
@@ -806,7 +806,7 @@ def basis_complement(B):
F = B.parent().base_ring()
m = B.nrows()
n = B.ncols()
- C = MatrixSpace(F, n - m, n, sparse=True)(0)
+ C = MatrixSpace(F, n - m, n, sparse=True).zero()
k = 0
l = 0
for i in range(m):
@@ -1023,7 +1023,7 @@ def split_odd(A):
return A[0, 0], MatrixSpace(ZZ, 0, A.ncols())([])
even, i = is_even_matrix(A)
R = A.parent().base_ring()
- C = MatrixSpace(R, n0 - 1, n0)(0)
+ C = MatrixSpace(R, n0 - 1, n0).zero()
u = A[i, i]
for j in range(n0-1):
if j < i:
@@ -1035,7 +1035,7 @@ def split_odd(A):
B = C*A*C.transpose()
even, j = is_even_matrix(B)
if even:
- I = A.parent()(1)
+ I = A.parent().one()
# TODO: we could manually (re)construct the kernel here...
if i == 0:
I[1, 0] = 1 - A[1, 0]*u
@@ -1045,7 +1045,7 @@ def split_odd(A):
i = 0
A = I*A*I.transpose()
u = A[i, i]
- C = MatrixSpace(R, n0-1, n0)(0)
+ C = MatrixSpace(R, n0-1, n0).zero()
for j in range(n0-1):
if j < i:
C[j, j] = 1
@@ -1355,7 +1355,7 @@ def __repr__(self):
# mark the beginning of this compartment with [
CS_string += "["
block = CS[block_index]
- block_string = "%s^%s " % (p**block[0], block[2] * block[1])
+ block_string = f"{p**block[0]}^{block[2] * block[1]} "
CS_string += block_string
if block_index in compartment_ends:
# close this compartment with ] and remove a space
@@ -1373,8 +1373,8 @@ def __repr__(self):
else:
for s in self._symbol:
- CS_string += " %s^%s" % (p**s[0], s[2] * s[1])
- rep = "Genus symbol at %s: %s" % (p, CS_string)
+ CS_string += f" {p**s[0]}^{s[2] * s[1]}"
+ rep = f"Genus symbol at {p}: {CS_string}"
return rep.rstrip()
def _latex_(self):
@@ -1409,7 +1409,7 @@ def _latex_(self):
# mark the beginning of this compartment with [
CS_string += "["
block = CS[block_index]
- block_string = "%s^{%s} " % (p**block[0], block[2] * block[1])
+ block_string = f"{p**block[0]}^{{{block[2] * block[1]}}} "
CS_string += block_string
if block_index in compartment_ends:
# close this compartment with ] and remove a space
@@ -1425,8 +1425,8 @@ def _latex_(self):
else:
for s in self._symbol:
- CS_string += " {%s}^{%s}" % (p**s[0], s[2]*s[1])
- return r"\mbox{Genus symbol at } %s\mbox{: }%s" % (p, CS_string)
+ CS_string += f" {{{p**s[0]}}}^{{{s[2]*s[1]}}}"
+ return fr"\mbox{{Genus symbol at }} {p}\mbox{{: }}{CS_string}"
def __eq__(self, other):
r"""
@@ -1587,9 +1587,8 @@ def automorphous_numbers(self):
if I.count(r) > 2:
I.remove(r)
# products of all pairs
- for r1 in I:
- for r2 in I:
- automorphs.append(r1*r2)
+ automorphs.extend(r1*r2 for r1 in I for r2 in I)
+
# supplement (i)
for block in sym:
if block[1] >= 2:
@@ -1635,7 +1634,7 @@ def automorphous_numbers(self):
for k in range(len(sym)):
s = sym[k:k+3]
if sum([b[1] for b in s if b[0] - s[0][0] < 4]) >= 3:
- automorphs += [ZZ(1), ZZ(3), ZZ(5), ZZ(7)]
+ automorphs += [ZZ.one(), ZZ(3), ZZ(5), ZZ(7)]
break
# supplement (ii)
@@ -1759,11 +1758,9 @@ def gram_matrix(self, check=True):
[---+-+-]
[0 0|0|8]
"""
- G = []
p = self._prime
symbol = self.symbol_tuple_list()
- for block in symbol:
- G.append(_gram_from_jordan_block(p, block))
+ G = [_gram_from_jordan_block(p, block) for block in symbol]
G = matrix.block_diagonal(G)
# check calculation
if check:
@@ -1816,11 +1813,11 @@ def mass(self):
# type factors
nII = ZZ.sum(fq[1] for fq in sym if fq[3] == 0)
- nI_I = ZZ(0) # the total number of pairs of adjacent constituents f_q,
+ nI_I = ZZ.zero() # the total number of pairs of adjacent constituents f_q,
# f_2q that are both of type I (odd)
for k in range(r-1):
if sym[k][3] == sym[k+1][3] == 1 and sym[k][0] + 1 == sym[k+1][0]:
- nI_I += ZZ(1)
+ nI_I += ZZ.one()
return m_p * ZZ(2)**(nI_I - nII)
def _standard_mass(self):
@@ -1844,7 +1841,7 @@ def _standard_mass(self):
D = ZZ(-1)**s * self.determinant()
epsilon = (4*D).kronecker(p)
std *= (1 - epsilon*p**(-s))
- return QQ(1) / std
+ return QQ.one() / std
def _species_list(self):
r"""
@@ -1896,7 +1893,7 @@ def _species_list(self):
if sym[k][3] == 0 or n % 2 == 1:
t = n // ZZ(2)
else:
- t = (n // ZZ(2)) - ZZ(1)
+ t = (n // ZZ(2)) - ZZ.one()
if free and (o == 0 or o == 1 or o == 7):
species = 2*t
elif free and (o == 3 or o == 5 or o == 4):
@@ -2223,7 +2220,7 @@ def scale(self):
1
"""
if self.rank() == 0:
- return ZZ(0)
+ return ZZ.zero()
return self.prime()**self._symbol[0][0]
def norm(self):
@@ -2244,7 +2241,7 @@ def norm(self):
2
"""
if self.rank() == 0:
- return ZZ(0)
+ return ZZ.zero()
p = self.prime()
if p == 2:
fq = self._symbol[0]
@@ -2263,10 +2260,10 @@ def level(self):
4
"""
if self.rank() == 0:
- return ZZ(1)
+ return ZZ.one()
return self.prime()**self._symbol[-1][0]
- def trains(self):
+ def trains(self) -> list:
r"""
Compute the indices for each of the trains in this local genus
symbol if it is associated to the prime `p=2` (and raise an
@@ -2293,7 +2290,7 @@ def trains(self):
symbol = self._symbol
return canonical_2_adic_trains(symbol)
- def compartments(self):
+ def compartments(self) -> list:
r"""
Compute the indices for each of the compartments in this local genus
symbol if it is associated to the prime `p=2` (and raise an
@@ -2393,7 +2390,7 @@ def __init__(self, signature_pair, local_symbols, representative=None, check=Tru
self._signature = signature_pair
self._local_symbols = local_symbols
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
Return a string representing the global genus symbol.
@@ -2426,12 +2423,12 @@ def __repr__(self):
rep = "Genus"
if self.dimension() <= 20:
rep += " of\n%s" % self._representative
- rep += "\nSignature: %s" % (self._signature,)
+ rep += f"\nSignature: {self._signature}"
for s in self._local_symbols:
rep += "\n" + s.__repr__()
return rep
- def _latex_(self):
+ def _latex_(self) -> str:
r"""
The Latex representation of this lattice.
@@ -2452,12 +2449,12 @@ def _latex_(self):
rep += r" of}\\ %s" % self._representative._latex_()
else:
rep += r"}"
- rep += r"\\ \mbox{Signature: } %s" % (self._signature,)
+ rep += fr"\\ \mbox{{Signature: }} {self._signature}"
for s in self._local_symbols:
rep += r"\\ " + s._latex_()
return rep
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
r"""
Determines if two global genus symbols are equal (not just equivalent!).
@@ -2507,7 +2504,7 @@ def __eq__(self, other):
return False
return True
- def __ne__(self, other):
+ def __ne__(self, other) -> bool:
r"""
Determine if two global genus symbols are unequal (not just inequivalent!).
@@ -2539,7 +2536,7 @@ def __ne__(self, other):
"""
return not self == other
- def is_even(self):
+ def is_even(self) -> bool:
r"""
Return if this genus is even.
@@ -2606,11 +2603,10 @@ def _proper_spinor_kernel(self):
sig = self.signature_pair_of_matrix()
if sig[0] * sig[1] > 1:
kernel_gens.append(A.delta(-1, prime=-1))
- for sym in syms:
- for r in sym.automorphous_numbers():
- kernel_gens.append(A.delta(r, prime=sym.prime()))
- K = A.subgroup(kernel_gens)
- return A, K
+ kernel_gens.extend(A.delta(r, prime=sym.prime())
+ for sym in syms
+ for r in sym.automorphous_numbers())
+ return A, A.subgroup(kernel_gens)
def _improper_spinor_kernel(self):
r"""
@@ -2651,7 +2647,7 @@ def _improper_spinor_kernel(self):
K = A.subgroup(K.gens() + (j,))
return A, K
- def spinor_generators(self, proper):
+ def spinor_generators(self, proper) -> list:
r"""
Return the spinor generators.
@@ -2853,8 +2849,9 @@ def discriminant_form(self):
qL = []
for gs in self._local_symbols:
p = gs._prime
- for block in gs.symbol_tuple_list():
- qL.append(_gram_from_jordan_block(p, block, True))
+ qL.extend(_gram_from_jordan_block(p, block, True)
+ for block in gs.symbol_tuple_list())
+
q = matrix.block_diagonal(qL)
return TorsionQuadraticForm(q)
@@ -3070,7 +3067,7 @@ def representatives(self, backend=None, algorithm=None):
return [self.representative()]
if n == 2:
# Binary forms are considered positive definite take care of that.
- e = ZZ(1)
+ e = ZZ.one()
if self.signature_pair()[0] == 0:
e = ZZ(-1)
d = - 4 * self.determinant()
@@ -3083,7 +3080,7 @@ def representatives(self, backend=None, algorithm=None):
if n > 2:
from sage.quadratic_forms.quadratic_form import QuadraticForm
from sage.quadratic_forms.quadratic_form__neighbors import neighbor_iteration
- e = ZZ(1)
+ e = ZZ.one()
if not self.is_even():
e = ZZ(2)
if self.signature_pair()[0] == 0:
@@ -3256,7 +3253,7 @@ def mass(self, backend='sage'):
if pos * neg != 0:
raise ValueError("the genus must be definite.")
if pos + neg == 1:
- return QQ(1) / QQ(2)
+ return QQ((1, 2))
if backend == 'sage':
mass = self._standard_mass()
for sym in self._local_symbols:
@@ -3504,10 +3501,10 @@ def M_p(species, p):
625/1152
"""
if species == 0:
- return QQ(1)
+ return QQ.one()
n = species.abs()
s = (n + 1) // ZZ(2)
- mp = ZZ(2) * ZZ.prod(ZZ(1) - p**(-2 * k) for k in range(1, s))
+ mp = ZZ(2) * ZZ.prod(ZZ.one() - p**(-2 * k) for k in range(1, s))
if n % 2 == 0:
mp *= ZZ.one() - species.sign() * p**(-s)
return QQ.one() / mp
diff --git a/src/sage/quadratic_forms/genera/spinor_genus.py b/src/sage/quadratic_forms/genera/spinor_genus.py
index f049496965d..4f318a64d44 100644
--- a/src/sage/quadratic_forms/genera/spinor_genus.py
+++ b/src/sage/quadratic_forms/genera/spinor_genus.py
@@ -61,16 +61,16 @@ def _repr_(self) -> str:
e = self.exponents()
p = self.parent()._primes
s = "[2:"
- if e[0] == 0 and e[1] == 0:
+ if e[0] == 0 == e[1]:
s += "1"
elif e[0] == 1 and e[1] == 0:
s += "3"
elif e[0] == 0 and e[1] == 1:
s += "5"
- elif e[0] == 1 and e[1] == 1:
+ elif e[0] == 1 == e[1]:
s += "7"
for k in range(1, len(p)):
- s += ", %s:%s" % (p[k], (-1)**e[k + 1])
+ s += f", {p[k]}:{(-1)**e[k + 1]}"
s += "]"
return s
@@ -116,7 +116,7 @@ def __reduce__(self):
OUTPUT:
- - a tuple ``f`` such that this element is ``f[0](*f[1])``
+ a tuple ``f`` such that this element is ``f[0](*f[1])``
EXAMPLES::
@@ -139,7 +139,7 @@ def _repr_(self) -> str:
sage: SpinorOperators((2, 3, 7))
Group of SpinorOperators at primes (2, 3, 7)
"""
- return "Group of SpinorOperators at primes %s" % (self._primes,)
+ return f"Group of SpinorOperators at primes {self._primes}"
def to_square_class(self, x, p):
r"""
diff --git a/src/sage/quadratic_forms/quadratic_form__neighbors.py b/src/sage/quadratic_forms/quadratic_form__neighbors.py
index ec853cd52ef..0486e0a4acb 100644
--- a/src/sage/quadratic_forms/quadratic_form__neighbors.py
+++ b/src/sage/quadratic_forms/quadratic_form__neighbors.py
@@ -188,7 +188,7 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
"""
p = ZZ(p)
if not p.divides(self(y)):
- raise ValueError("y=%s must be of square divisible by p=%s" % (y, p))
+ raise ValueError(f"y={y} must be of square divisible by p={p}")
if self.base_ring() not in [ZZ, QQ]:
raise NotImplementedError("the base ring of this form must be the integers or the rationals")
@@ -202,10 +202,10 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
odd = True
if G.denominator() != 1:
raise ValueError("the associated bilinear form q(x+y)-q(x)-q(y) must be integral.")
- b = y*G*y
+ b = y * G * y
if not b % p == 0:
raise ValueError("y^2 must be divisible by p=%s" % p)
- y_dual = y*G
+ y_dual = y * G
if p != 2 and b % p**2 != 0:
for k in range(n):
if y_dual[k] % p != 0:
@@ -213,8 +213,8 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
break
else:
raise ValueError("either y is not primitive or self is not maximal at %s" % p)
- z *= (2*y*G*z).inverse_mod(p)
- y = y - b*z
+ z *= (2 * y * G * z).inverse_mod(p)
+ y = y - b * z
# assert y*G*y % p^2 == 0
if p == 2:
val = b.valuation(p)
@@ -228,14 +228,14 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
break
else:
raise ValueError("either y is not primitive or self is not even, maximal at 2")
- y += 2*z
+ y += 2 * z
# assert y*G*y % 8 == 0
- y_dual = G*y
+ y_dual = G * y
Ly = y_dual.change_ring(GF(p)).column().kernel().matrix().lift()
B = Ly.stack(p * matrix.identity(n))
# the rows of B now generate L_y = { x in L | (x,y)=0 mod p}
- B = y.row().stack(p*B)
+ B = y.row().stack(p * B)
B = B.hermite_form()[:n, :] / p
# the rows of B generate ZZ * y/p + L_y
# by definition this is the p-neighbor of L at y
@@ -243,10 +243,10 @@ def find_p_neighbor_from_vec(self, p, y, return_matrix=False):
if return_matrix:
return B.T
- else:
- QF = self.parent()
- Gnew = (B*G*B.T).change_ring(R)
- return QF(Gnew)
+
+ QF = self.parent()
+ Gnew = (B * G * B.T).change_ring(R)
+ return QF(Gnew)
def neighbor_iteration(seeds, p, mass=None, max_classes=ZZ(10)**3,
@@ -346,9 +346,9 @@ def p_divisible_vectors(Q, max_neighbors):
raise ValueError("unknown algorithm")
waiting_list = list(seeds)
isom_classes = []
- mass_count = QQ(0)
- n_isom_classes = ZZ(0)
- while len(waiting_list) > 0 and mass != mass_count and n_isom_classes < max_classes:
+ mass_count = QQ.zero()
+ n_isom_classes = ZZ.zero()
+ while waiting_list and mass != mass_count and n_isom_classes < max_classes:
# find all p-neighbors of Q
Q = waiting_list.pop()
for v in p_divisible_vectors(Q, max_neighbors):
From d67041a126b3aef0a07f06a3efbb1a2363ec6e24 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Mon, 19 Feb 2024 21:19:46 +0000
Subject: [PATCH 019/518] Add a check for action after precomposing with map to
the Integers
---
src/sage/schemes/elliptic_curves/ell_point.py | 13 -------------
src/sage/structure/parent.pyx | 19 +++++++++++++++++++
2 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py
index bf86995daa8..bcfe8d5c1d2 100644
--- a/src/sage/schemes/elliptic_curves/ell_point.py
+++ b/src/sage/schemes/elliptic_curves/ell_point.py
@@ -3877,19 +3877,6 @@ def _acted_upon_(self, other, side):
return Q
- def __mul__(self, other):
- r"""
- For all sage types, _acted_upon_ is called, but for
- multiplication by a Python int, the slower __mul__
- is called. Here we bind __mul__ to acted upon so that
- the fast Pari multiplication is called when possible,
- even when the scalar is a Python int rather than Sage
- Integer.
- """
- return self._acted_upon_(other, None)
-
- __rmul__ = __mul__
-
def log(self, base):
r"""
Return the discrete logarithm of this point to the given ``base``.
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index 6ac939db3c8..86ad87b5a49 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2678,6 +2678,25 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
return action
if parent_is_integers(S) and not self.has_coerce_map_from(S):
+ # Try the above again, but first coerce integer-like type to Integer
+ # with a connecting coersion
+ R_el = _Integer(S_el) # Map integer-like to Integer
+ R = parent(R_el) # Is this the best way to get the Integer parent?
+
+ # Compute the coercsion from whatever S is to the Integer class
+ # This should always work because parent_is_integers(S) is True
+ connecting = R._internal_coerce_map_from(S)
+
+ # Now we check if there's an element action from Integers
+ action = detect_element_action(self, R, self_on_left, self_el, R_el)
+ # When this is not None, we can do the Precomposed action
+ if action is not None:
+ if self_on_left:
+ return PrecomposedAction(action, None, connecting)
+ else:
+ return PrecomposedAction(action, connecting, None)
+
+ # Otherwise, we do the most basic IntegerMulAction
from sage.structure.coerce_actions import IntegerMulAction
try:
return IntegerMulAction(S, self, not self_on_left, self_el)
From 0fb4d629be540893331a4714b7eceb853f2be90f Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Mon, 19 Feb 2024 23:28:19 +0000
Subject: [PATCH 020/518] Ensure that connecting is not None, does this help
with the CI failure?
---
src/sage/structure/parent.pyx | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index 86ad87b5a49..a30f3ff6b80 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2687,14 +2687,17 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
# This should always work because parent_is_integers(S) is True
connecting = R._internal_coerce_map_from(S)
- # Now we check if there's an element action from Integers
- action = detect_element_action(self, R, self_on_left, self_el, R_el)
- # When this is not None, we can do the Precomposed action
- if action is not None:
- if self_on_left:
- return PrecomposedAction(action, None, connecting)
- else:
- return PrecomposedAction(action, connecting, None)
+ # TODO: is this ever not None?
+ if connecting is not None:
+ # Now we check if there's an element action from Integers
+ action = detect_element_action(self, R, self_on_left, self_el, R_el)
+ # When this is not None, we can do the Precomposed action
+ if action is not None:
+ if self_on_left:
+ action = PrecomposedAction(action, None, connecting)
+ else:
+ action = PrecomposedAction(action, connecting, None)
+ return action
# Otherwise, we do the most basic IntegerMulAction
from sage.structure.coerce_actions import IntegerMulAction
From a9812a5293642f5033449a8f43d74cbaef568835 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Mon, 19 Feb 2024 23:37:37 +0000
Subject: [PATCH 021/518] whitespace
---
src/sage/structure/parent.pyx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index a30f3ff6b80..4fff67cc60b 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2694,7 +2694,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
# When this is not None, we can do the Precomposed action
if action is not None:
if self_on_left:
- action = PrecomposedAction(action, None, connecting)
+ action = PrecomposedAction(action, None, connecting)
else:
action = PrecomposedAction(action, connecting, None)
return action
From 839b6bd5e17954efd8ae3b7f9aa619d5227517c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Tue, 20 Feb 2024 08:07:09 +0100
Subject: [PATCH 022/518] fix some wrong changes
---
src/sage/quadratic_forms/genera/genus.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py
index 2db66a93653..8ab98bc4d06 100644
--- a/src/sage/quadratic_forms/genera/genus.py
+++ b/src/sage/quadratic_forms/genera/genus.py
@@ -806,7 +806,7 @@ def basis_complement(B):
F = B.parent().base_ring()
m = B.nrows()
n = B.ncols()
- C = MatrixSpace(F, n - m, n, sparse=True).zero()
+ C = MatrixSpace(F, n - m, n, sparse=True)(0)
k = 0
l = 0
for i in range(m):
@@ -1023,7 +1023,7 @@ def split_odd(A):
return A[0, 0], MatrixSpace(ZZ, 0, A.ncols())([])
even, i = is_even_matrix(A)
R = A.parent().base_ring()
- C = MatrixSpace(R, n0 - 1, n0).zero()
+ C = MatrixSpace(R, n0 - 1, n0)(0)
u = A[i, i]
for j in range(n0-1):
if j < i:
@@ -1035,7 +1035,7 @@ def split_odd(A):
B = C*A*C.transpose()
even, j = is_even_matrix(B)
if even:
- I = A.parent().one()
+ I = A.parent()(1)
# TODO: we could manually (re)construct the kernel here...
if i == 0:
I[1, 0] = 1 - A[1, 0]*u
@@ -1045,7 +1045,7 @@ def split_odd(A):
i = 0
A = I*A*I.transpose()
u = A[i, i]
- C = MatrixSpace(R, n0-1, n0).zero()
+ C = MatrixSpace(R, n0-1, n0)(0)
for j in range(n0-1):
if j < i:
C[j, j] = 1
@@ -1835,15 +1835,15 @@ def _standard_mass(self):
"""
n = self.dimension()
p = self.prime()
- s = (n + 1) // ZZ(2)
- std = 2 * QQ.prod(1-p**(-2*k) for k in range(1, s))
- if n % 2 == 0:
+ s = (n + 1) // 2
+ std = 2 * QQ.prod(1 - p**(-2 * k) for k in range(1, s))
+ if not n % 2:
D = ZZ(-1)**s * self.determinant()
- epsilon = (4*D).kronecker(p)
- std *= (1 - epsilon*p**(-s))
+ epsilon = (4 * D).kronecker(p)
+ std *= (1 - epsilon * p**(-s))
return QQ.one() / std
- def _species_list(self):
+ def _species_list(self) -> list:
r"""
Return the species list.
From 0c4e46dc8e93a5b8089d60dbafc0645b9439ae65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Tue, 20 Feb 2024 09:54:56 +0100
Subject: [PATCH 023/518] add a polytope method to hypergeometric motives
---
src/doc/en/reference/references/index.rst | 7 ++++
src/sage/modular/hypergeometric_motive.py | 50 +++++++++++++++++++++--
2 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index e68bdbc2d57..0a85c8cbc7b 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -5471,6 +5471,10 @@ REFERENCES:
Hecke algebras of type A*. J. Algebraic Combin.
**6** (1997), 59-87.
+.. [[RRV2022] David P. Roberts and Fernando Rodriguez Villegas,
+ *Hypergeometric motives*, Notices Amer. Math. Soc., **69** vol. 6 (2022).
+ :doi:`10.1090/noti2491`.
+
.. [RSS] :wikipedia:`Residual_sum_of_squares`, accessed 13th
October 2009.
@@ -5520,6 +5524,9 @@ REFERENCES:
.. [RV2007] Fernando Rodriguez Villegas. Experimental Number Theory.
Oxford Graduate Texts in Mathematics 13, 2007.
+.. [RV2019] Fernando Rodriguez Villegas. *Mixed Hodge numbers and
+ factorial ratios*. :arxiv:`1907.02722`, 2019.
+
.. [RW2008] Alexander Raichev and Mark C. Wilson. *Asymptotics of
coefficients of multivariate generating functions:
improvements for smooth points*, Electronic Journal of
diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py
index eb7dfaafe75..ad76993989c 100644
--- a/src/sage/modular/hypergeometric_motive.py
+++ b/src/sage/modular/hypergeometric_motive.py
@@ -42,6 +42,8 @@
- [Roberts2015]_
+- [RRV2022]_
+
- [BeCoMe]_
- [Watkins]_
@@ -60,27 +62,31 @@
from collections import defaultdict
from itertools import combinations
+
from sage.arith.misc import divisors, gcd, euler_phi, moebius, is_prime
from sage.arith.misc import gauss_sum, kronecker_symbol
from sage.combinat.integer_vector_weighted import WeightedIntegerVectors
from sage.functions.generalized import sgn
from sage.functions.log import log
from sage.functions.other import floor, ceil, frac
+from sage.geometry.lattice_polytope import LatticePolytope
+from sage.matrix.constructor import matrix
from sage.misc.cachefunc import cached_method
from sage.misc.functional import cyclotomic_polynomial
from sage.misc.misc_c import prod
from sage.modular.hypergeometric_misc import hgm_coeffs
-from sage.rings.fraction_field import FractionField
+from sage.modules.free_module_element import vector
+from sage.rings.finite_rings.finite_field_constructor import GF
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
+from sage.rings.fraction_field import FractionField
from sage.rings.integer_ring import ZZ
from sage.rings.padics.padic_generic_element import gauss_table
from sage.rings.polynomial.polynomial_ring import polygen, polygens
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.power_series_ring import PowerSeriesRing
from sage.rings.rational_field import QQ
-from sage.schemes.generic.spec import Spec
-from sage.rings.finite_rings.finite_field_constructor import GF
from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField
+from sage.schemes.generic.spec import Spec
def characteristic_polynomial_from_traces(traces, d, q, i, sign):
@@ -1131,6 +1137,44 @@ def canonical_scheme(self, t=None):
ideal = ring.ideal([eq0, eq1, self.M_value() * eq2_neg - t * eq2_pos])
return Spec(ring.quotient(ideal))
+ def polytope(self):
+ """
+ Return the associated lattice polytope.
+
+ This uses the matrix defined in section 3 of [RRV2022]_ and
+ section 3 of [RV2019]_.
+
+ EXAMPLES::
+
+ sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
+ sage: H = Hyp(gamma_list=[-5, -2, 3, 4])
+ sage: P = H.polytope(); P
+ 2-d lattice polytope in 2-d lattice M
+ sage: P.polyhedron().f_vector()
+ (1, 4, 4, 1)
+ sage: len(P.points())
+ 7
+
+ The Chebyshev example from [RV2019]_::
+
+ sage: H = Hyp(gamma_list=[-30, -1, 6, 10, 15])
+ sage: P = H.polytope(); P
+ 3-d lattice polytope in 3-d lattice M
+ sage: len(P.points())
+ 19
+ sage: P.polyhedron().f_vector()
+ (1, 5, 9, 6, 1)
+ """
+ l = len(self.gamma_list())
+ m = matrix(ZZ, l, 1, self.gamma_list())
+ ext_ker = m.kernel().basis_matrix().insert_row(0, vector(ZZ, [1] * l))
+ unique_relation = ext_ker.kernel().basis()[0]
+ removed = next(i for i, ci in enumerate(unique_relation)
+ if i and abs(ci) == 1)
+ mat = matrix(ZZ, [v for i, v in enumerate(ext_ker)
+ if i and i != removed])
+ return LatticePolytope(mat.transpose())
+
# --- Operations on data ---
def twist(self):
r"""
From 87cc7614878c49069ad73b9782b542aa6fe32b43 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 20 Feb 2024 14:50:24 +0000
Subject: [PATCH 024/518] Explicitly import ZZ for the coercion
---
src/sage/structure/parent.pyx | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index 4fff67cc60b..0f0ad0ae308 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2680,24 +2680,25 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
if parent_is_integers(S) and not self.has_coerce_map_from(S):
# Try the above again, but first coerce integer-like type to Integer
# with a connecting coersion
- R_el = _Integer(S_el) # Map integer-like to Integer
- R = parent(R_el) # Is this the best way to get the Integer parent?
+ # TODO: is this the best way to gain access to ZZ
+ # before I used _Integer as defined above but sometimes this was None
+ from sage.rings.integer_ring import ZZ
# Compute the coercsion from whatever S is to the Integer class
# This should always work because parent_is_integers(S) is True
- connecting = R._internal_coerce_map_from(S)
-
- # TODO: is this ever not None?
- if connecting is not None:
- # Now we check if there's an element action from Integers
- action = detect_element_action(self, R, self_on_left, self_el, R_el)
- # When this is not None, we can do the Precomposed action
- if action is not None:
- if self_on_left:
- action = PrecomposedAction(action, None, connecting)
- else:
- action = PrecomposedAction(action, connecting, None)
- return action
+ connecting = ZZ._internal_coerce_map_from(S)
+ ZZ_el = ZZ(S_el)
+
+ # Now we check if there's an element action from Integers
+ action = detect_element_action(self, ZZ, self_on_left, self_el, ZZ_el)
+
+ # When this is not None, we can do the Precomposed action
+ if action is not None:
+ if self_on_left:
+ action = PrecomposedAction(action, None, connecting)
+ else:
+ action = PrecomposedAction(action, connecting, None)
+ return action
# Otherwise, we do the most basic IntegerMulAction
from sage.structure.coerce_actions import IntegerMulAction
From 387f705df2c42b42f9b03e90d4c5dc2ea0117b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Tue, 20 Feb 2024 17:24:00 +0100
Subject: [PATCH 025/518] fix mistake in ref
---
src/doc/en/reference/references/index.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index 0a85c8cbc7b..049812bdaad 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -5471,7 +5471,7 @@ REFERENCES:
Hecke algebras of type A*. J. Algebraic Combin.
**6** (1997), 59-87.
-.. [[RRV2022] David P. Roberts and Fernando Rodriguez Villegas,
+.. [RRV2022] David P. Roberts and Fernando Rodriguez Villegas,
*Hypergeometric motives*, Notices Amer. Math. Soc., **69** vol. 6 (2022).
:doi:`10.1090/noti2491`.
From 391a72154749cd310335006d1e6c071c8a231c22 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 20 Feb 2024 17:52:29 +0000
Subject: [PATCH 026/518] Avoid the buggy case when connecting is None
---
src/sage/structure/parent.pyx | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index 0f0ad0ae308..e654c85a6a3 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2679,26 +2679,30 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
if parent_is_integers(S) and not self.has_coerce_map_from(S):
# Try the above again, but first coerce integer-like type to Integer
- # with a connecting coersion
- # TODO: is this the best way to gain access to ZZ
- # before I used _Integer as defined above but sometimes this was None
- from sage.rings.integer_ring import ZZ
-
- # Compute the coercsion from whatever S is to the Integer class
- # This should always work because parent_is_integers(S) is True
- connecting = ZZ._internal_coerce_map_from(S)
- ZZ_el = ZZ(S_el)
+ # with a connecting coercion
+ global _Integer
+ if _Integer is None:
+ from sage.rings.integer import Integer as _Integer
+ ZZ_el = _Integer(S_el)
+ ZZ = ZZ_el.parent()
# Now we check if there's an element action from Integers
action = detect_element_action(self, ZZ, self_on_left, self_el, ZZ_el)
# When this is not None, we can do the Precomposed action
if action is not None:
- if self_on_left:
- action = PrecomposedAction(action, None, connecting)
- else:
- action = PrecomposedAction(action, connecting, None)
- return action
+ # Compute the coercion from whatever S is to the Integer class
+ # This should always work because parent_is_integers(S) is True
+ # but it fails when S is gmpy2.mpz.
+ # TODO: should we also patch _internal_coerce_map_from so that
+ # there's a map from gmpy2.mpz to ZZ?
+ connecting = ZZ._internal_coerce_map_from(S)
+ if connecting is not None:
+ if self_on_left:
+ action = PrecomposedAction(action, None, connecting)
+ else:
+ action = PrecomposedAction(action, connecting, None)
+ return action
# Otherwise, we do the most basic IntegerMulAction
from sage.structure.coerce_actions import IntegerMulAction
From b481d62e87df22f672e796beff14efe4e7a0b6ce Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Tue, 20 Feb 2024 18:39:51 -0800
Subject: [PATCH 027/518] src/doc/en/installation/source.rst: Add section on
upgrading
---
src/doc/en/developer/git_basic.rst | 6 +-
src/doc/en/installation/source.rst | 140 +++++++++++++++++++++++++++++
2 files changed, 143 insertions(+), 3 deletions(-)
diff --git a/src/doc/en/developer/git_basic.rst b/src/doc/en/developer/git_basic.rst
index 524181438ee..47d8029f4a8 100644
--- a/src/doc/en/developer/git_basic.rst
+++ b/src/doc/en/developer/git_basic.rst
@@ -40,11 +40,11 @@ or the following brief instructions::
[alice@localhost ~]$ ssh-keygen
Generating public/private rsa key pair.
- Enter file in which to save the key (/home/user/.ssh/id_rsa):
+ Enter file in which to save the key (/home/alice/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
- Your identification has been saved in /home/user/.ssh/id_rsa.
- Your public key has been saved in /home/user/.ssh/id_rsa.pub.
+ Your identification has been saved in /home/alice/.ssh/id_rsa.
+ Your public key has been saved in /home/alice/.ssh/id_rsa.pub.
The key fingerprint is:
ce:32:b3:de:38:56:80:c9:11:f0:b3:88:f2:1c:89:0a alice@localhost
The key's randomart image is:
diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst
index 26454e4d6d3..9064779510e 100644
--- a/src/doc/en/installation/source.rst
+++ b/src/doc/en/installation/source.rst
@@ -1139,3 +1139,143 @@ a single copy of Sage in a multi-user computer network.
the installation by yourself::
$ sudo chown -R root SAGE_LOCAL
+
+
+Upgrading the system and upgrading Sage
+---------------------------------------
+
+Caveats when upgrading system packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When Sage has been installed from source, it will make use of various system
+packages; in particular, it will link to shared libraries provided by
+the system.
+
+The system's package manager does not keep track of the applications that
+make use of the shared libraries. Therefore indiscriminate upgrades of
+system packages can break a Sage installation.
+
+This can always be fixed by a full rebuild::
+
+ $ make distclean && make build
+
+But this time-consuming step can often be avoided by just reinstalling a
+few packages. The command ``make -j list-broken-packages`` assists with
+this::
+
+ $ make -j list-broken-packages
+ make --no-print-directory auditwheel_or_delocate-no-deps
+ ...
+ # Checking .../local/var/lib/sage/installed/bliss-0.73+debian-1+sage-2016-08-02.p0
+ ...
+ Checking shared library file '.../local/lib/libumfpack.dylib'
+ Checking shared library file '.../local/var/tmp/sage/build/suitesparse-5.10.1/src/lib/libsliplu.1.0.2.dylib'
+ Error during installcheck of 'suitesparse': .../local/var/tmp/sage/build/suitesparse-5.10.1/src/lib/libsliplu.1.0.2.dylib
+ ...
+ Uninstall broken packages by typing:
+
+ make lcalc-SAGE_LOCAL-uninstall;
+ make ratpoints-SAGE_LOCAL-uninstall;
+ make r-SAGE_LOCAL-uninstall;
+ make suitesparse-SAGE_LOCAL-uninstall;
+
+After running the suggested commands, run::
+
+ $ make build
+
+
+Upgrading Sage using a separate worktree
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you have a working installation of Sage built from source and wish to
+try out a new version, we strongly recommend to use a separate
+`git worktree `_, so that you
+can keep using your existing installation when something goes wrong.
+
+Start from the directory created when you used ``git clone``, perhaps
+``~/sage/sage/``. Let's verify that this is indeed a git repository by
+looking at the hidden ``.git`` subdirectory. It will looks like this,
+but the exact contents can vary::
+
+ [alice@localhost sage]$ ls .git
+ COMMIT_EDITMSG HEAD branches description gitk.cache
+ index logs packed-refs FETCH_HEAD ORIG_HEAD
+ config hooks info objects refs
+
+Good. Now let's see what worktrees already exist::
+
+ [alice@localhost sage]$ git worktree list
+ /home/alice/sage/sage c0ffeefe10 [master]
+
+We see just one line, the directory created when you used ``git clone``.
+We will call this the "main worktree" from now on. Next to the directory,
+you can see the abbreviated commit sha and the name of the branch that
+we're on (``master``).
+
+To try out a new version of Sage, let's fetch it first from the main
+repository::
+
+ [alice@localhost sage]$ git fetch upstream 10.3.beta8
+ From https://github.com/sagemath/sage
+ * tag 10.3.beta8 -> FETCH_HEAD
+
+Now let's create a new worktree. We need a name for it; it should
+start with ``worktree-`` but can be anything after that. Experience
+shows that worktrees are often repurposed later, and because a
+directory containing a Sage installation cannot be moved without
+breaking the installation in it, it may be a good idea to choose
+a memorable name without much meaning::
+
+ [alice@localhost sage]$ git worktree add worktree-purple FETCH_HEAD
+ Preparing worktree (detached HEAD 30b3d78fac)
+ Updating files: 100% (11191/11191), done.
+ HEAD is now at 30b3d78fac Updated SageMath version to 10.3.beta8
+
+We now have a subdirectory ``worktree-purple``. This is a
+"linked worktree"::
+
+ [alice@localhost sage]$ git worktree list
+ /home/alice/sage/sage c0ffeefe10 [master]
+ /home/alice/sage/sage/worktree-purple 30b3d78fac (detached HEAD)
+ [alice@localhost sage]$ cd worktree-purple
+ [alice@localhost worktree-purple]$ cat VERSION.txt
+ SageMath version 10.3.beta8, Release Date: 2024-02-13
+
+All worktrees created in this way share the same repository,
+so they have access to all branches::
+
+ [alice@localhost worktree-purple]$ git --no-pager branch -v
+ * (no branch) 30b3d78fac Updated SageMath version to 10.3.beta8
+ + master 2a9a4267f9 Updated SageMath version to 10.2
+
+In fact, ``.git`` here is not a directory, just a hidden
+file::
+
+ [alice@localhost worktree-purple]$ ls -l .git
+ -rw-r--r-- 1 alice staff 59 Feb 20 18:16 .git
+
+In the new worktree, we now build Sage from scratch. This
+is completely independent of and will not disrupt your
+existing working installation in the main worktree.
+
+We will refer again to the step-by-step instructions
+from the file
+`README.md `_.
+Our worktree ``worktree-purple`` is the ``SAGE_ROOT``
+for this purpose.
+
+One thing that we can share between worktrees without
+worry is the directory ``upstream``, where Sage caches
+downloaded archives of packages. To have the new worktree
+share it with the main worktree, let's create a symbolic
+link. This is an optional step that will avoid
+re-downloading files that you already have::
+
+ [alice@localhost worktree-purple]$ ln -s ../upstream/ .
+
+Now let's build Sage, starting with the step::
+
+ [alice@localhost worktree-purple]$ make configure
+
+Refer to the file `README.md `_.
+for the following steps.
From 913d8c6f38a60f96bd4be92349f018f17f5fa8c8 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Tue, 20 Feb 2024 23:46:12 -0800
Subject: [PATCH 028/518] src/doc/en/installation/source.rst: Fix section
markup
---
src/doc/en/installation/source.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst
index 9064779510e..17c4328621e 100644
--- a/src/doc/en/installation/source.rst
+++ b/src/doc/en/installation/source.rst
@@ -1145,7 +1145,7 @@ Upgrading the system and upgrading Sage
---------------------------------------
Caveats when upgrading system packages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When Sage has been installed from source, it will make use of various system
packages; in particular, it will link to shared libraries provided by
@@ -1184,8 +1184,8 @@ After running the suggested commands, run::
$ make build
-Upgrading Sage using a separate worktree
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Upgrading Sage using a separate git worktree
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When you have a working installation of Sage built from source and wish to
try out a new version, we strongly recommend to use a separate
From 81aff8c65e4682299257c1e278c8a9fa631f5617 Mon Sep 17 00:00:00 2001
From: Kwankyu Lee
Date: Wed, 21 Feb 2024 15:58:35 +0900
Subject: [PATCH 029/518] Add as_subscheme() method for algebraic points
---
src/sage/schemes/affine/affine_point.py | 55 +++++++++-----
.../schemes/projective/projective_point.py | 75 +++++++++++++------
2 files changed, 88 insertions(+), 42 deletions(-)
diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py
index dabcaa134f9..32bfb3a42cf 100644
--- a/src/sage/schemes/affine/affine_point.py
+++ b/src/sage/schemes/affine/affine_point.py
@@ -1,27 +1,19 @@
r"""
Points on affine varieties
-Scheme morphism for points on affine varieties.
-
+This module implements scheme morphism for points on affine varieties.
AUTHORS:
-- David Kohel, William Stein
-
-- Volker Braun (2011-08-08): Renamed classes, more documentation, misc
- cleanups.
-
-- Ben Hutz (2013)
+- David Kohel, William Stein (2006): initial version
+- Volker Braun (2011-08-08): renamed classes, more documentation, misc cleanups
+- Ben Hutz (2013): many improvements
"""
-# Historical note: in trac #11599, V.B. renamed
-# * _point_morphism_class -> _morphism
-# * _homset_class -> _point_homset
-
# ****************************************************************************
-# Copyright (C) 2011 Volker Braun
# Copyright (C) 2006 David Kohel
# Copyright (C) 2006 William Stein
+# Copyright (C) 2011 Volker Braun
#
# Distributed under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2 of
@@ -34,13 +26,11 @@
from sage.schemes.generic.morphism import SchemeMorphism_point, SchemeMorphism, is_SchemeMorphism
from sage.structure.sequence import Sequence
-_NumberFields = NumberFields()
-
-############################################################################
-# Rational points on schemes, which we view as morphisms determined
-# by coordinates.
-############################################################################
+# --------------------------------------------------------------------
+# Rational points on schemes, which we view as morphisms determined by
+# coordinates.
+# --------------------------------------------------------------------
class SchemeMorphism_point_affine(SchemeMorphism_point):
"""
@@ -211,7 +201,7 @@ def global_height(self, prec=None):
R = RealField(prec)
H = max([self[i].abs() for i in range(self.codomain().ambient_space().dimension_relative())])
return R(max(H,1)).log()
- if self.domain().base_ring() in _NumberFields or isinstance(self.domain().base_ring(), sage.rings.abc.Order):
+ if self.domain().base_ring() in NumberFields() or isinstance(self.domain().base_ring(), sage.rings.abc.Order):
return max([self[i].global_height(prec) for i in range(self.codomain().ambient_space().dimension_relative())])
else:
raise NotImplementedError("must be over a number field or a number field Order")
@@ -410,6 +400,31 @@ def multiplicity(self):
raise TypeError("this point must be a point on an affine subscheme")
return self.codomain().multiplicity(self)
+ def as_subscheme(self):
+ r"""
+ Return the subscheme associated with this rational point.
+
+ EXAMPLES::
+
+ sage: A2. = AffineSpace(QQ, 2)
+ sage: p1 = A2.point([0,0]).as_subscheme(); p1
+ Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
+ x, y
+ sage: p2 = A2.point([1,1]).as_subscheme(); p2
+ Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
+ x - 1, y - 1
+ sage: p1 + p2
+ Closed subscheme of Affine Space of dimension 2 over Rational Field defined by:
+ x - y, y^2 - y
+ """
+ A = self.codomain().ambient_space()
+ g = A.gens()
+ v = self._coords
+ n = len(v)
+ if n != len(g):
+ raise ValueError('not a point of the ambient space')
+ return A.subscheme([g[i] - v[i] for i in range(n)])
+
class SchemeMorphism_point_affine_finite_field(SchemeMorphism_point_affine_field):
diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py
index dea15db15e5..d90b2a440fd 100644
--- a/src/sage/schemes/projective/projective_point.py
+++ b/src/sage/schemes/projective/projective_point.py
@@ -1,29 +1,23 @@
r"""
Points on projective varieties
-Scheme morphism for points on projective varieties
-
-
+This module implements scheme morphism for points on projective varieties.
AUTHORS:
-- David Kohel, William Stein
-
-- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as
- a projective point.
-
-- Volker Braun (2011-08-08): Renamed classes, more documentation, misc
- cleanups.
-
-- Ben Hutz (June 2012) added support for projective ring;
- (March 2013) iteration functionality and new directory structure
+- David Kohel, William Stein (2006): initial version
+- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as a
+ projective point
+- Volker Braun (2011-08-08): Renamed classes, more documentation, misc cleanups
+- Ben Hutz (2012-06): added support for projective ring
+- Ben Hutz (2013-03): added iteration functionality and new directory structure
for affine/projective, height functionality
"""
# ****************************************************************************
-# Copyright (C) 2011 Volker Braun
# Copyright (C) 2006 David Kohel
# Copyright (C) 2006 William Stein
+# Copyright (C) 2011 Volker Braun
#
# 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
@@ -34,7 +28,6 @@
from sage.categories.integral_domains import IntegralDomains
from sage.categories.number_fields import NumberFields
-_NumberFields = NumberFields()
from sage.rings.fraction_field import FractionField
from sage.rings.number_field.order import is_NumberFieldOrder, Order as NumberFieldOrder
from sage.rings.qqbar import number_field_elements_from_algebraics
@@ -52,9 +45,11 @@
from sage.structure.sequence import Sequence
from sage.structure.richcmp import richcmp, op_EQ, op_NE
-#*******************************************************************
+
+# --------------------
# Projective varieties
-#*******************************************************************
+# --------------------
+
class SchemeMorphism_point_projective_ring(SchemeMorphism_point):
"""
A rational point of projective space over a ring.
@@ -752,7 +747,7 @@ def global_height(self, prec=None):
if prec is None:
prec = 53
K = self.codomain().base_ring()
- if K in _NumberFields or is_NumberFieldOrder(K):
+ if K in NumberFields() or is_NumberFieldOrder(K):
P = self
else:
try:
@@ -807,7 +802,7 @@ def local_height(self, v, prec=None):
0.693147180559945
"""
K = FractionField(self.domain().base_ring())
- if K not in _NumberFields:
+ if K not in NumberFields():
raise TypeError("must be over a number field or a number field order")
return max([K(c).local_height(v, prec=prec) for c in self])
@@ -842,7 +837,7 @@ def local_height_arch(self, i, prec=None):
3.401197381662155375413236691607
"""
K = FractionField(self.domain().base_ring())
- if K not in _NumberFields:
+ if K not in NumberFields():
raise TypeError("must be over a number field or a number field order")
if K == QQ:
return max(K(c).local_height_arch(prec=prec) for c in self)
@@ -1054,6 +1049,7 @@ def is_preperiodic(self, f, err=0.1, return_period=False):
except AttributeError:
raise TypeError("map must be a dynamical system")
+
class SchemeMorphism_point_projective_field(SchemeMorphism_point_projective_ring):
"""
A rational point of projective space over a field.
@@ -1401,6 +1397,39 @@ def multiplicity(self):
raise TypeError("this point must be a point on a projective subscheme")
return self.codomain().multiplicity(self)
+ def as_subscheme(self):
+ r"""
+ Return the subscheme associated with this rational point.
+
+ EXAMPLES::
+
+ sage: P2. = ProjectiveSpace(QQ,2)
+ sage: p1 = P2.point([0,0,1]).as_subscheme(); p1
+ Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
+ x, y
+ sage: p2 = P2.point([1,1,1]).as_subscheme(); p2
+ Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
+ x - z, y - z
+ sage: p1 + p2
+ Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
+ x - y, y^2 - y*z
+ """
+ P = self.codomain().ambient_space()
+ g = P.gens()
+ v = self._coords
+ n = len(v)
+ if n != len(g):
+ raise ValueError('not a point of the ambient space')
+ for i in range(n - 1, -1, -1):
+ if v[i]:
+ break
+ else:
+ raise ValueError('invalid homogeneous coordinates')
+ a = v[i]
+ x = g[i]
+ return P.subscheme([a*g[j] - v[j]*x for j in range(n) if j != i])
+
+
class SchemeMorphism_point_projective_finite_field(SchemeMorphism_point_projective_field):
def __hash__(self):
@@ -1438,9 +1467,11 @@ def __hash__(self):
N = self.codomain().ambient_space().dimension_relative()
return hash(sum(hash(self[i]) * p**i for i in range(N + 1)))
-#*******************************************************************
+
+# -----------------
# Abelian varieties
-#*******************************************************************
+# -----------------
+
class SchemeMorphism_point_abelian_variety_field(AdditiveGroupElement, SchemeMorphism_point_projective_field):
"""
A rational point of an abelian variety over a field.
From 9ffa23f09f6520d608960322440b88c05d970ddf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Wed, 21 Feb 2024 09:21:14 +0100
Subject: [PATCH 030/518] suggested name, full pep8, type annotation for
hypergeometric motives
---
src/sage/modular/hypergeometric_motive.py | 83 +++++++++++------------
1 file changed, 41 insertions(+), 42 deletions(-)
diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py
index ad76993989c..49e13394129 100644
--- a/src/sage/modular/hypergeometric_motive.py
+++ b/src/sage/modular/hypergeometric_motive.py
@@ -207,7 +207,7 @@ def formule(u):
yield H
-def possible_hypergeometric_data(d, weight=None):
+def possible_hypergeometric_data(d, weight=None) -> list:
"""
Return the list of possible parameters of hypergeometric motives (up to swapping).
@@ -226,7 +226,7 @@ def possible_hypergeometric_data(d, weight=None):
return list(enumerate_hypergeometric_data(d, weight))
-def cyclotomic_to_alpha(cyclo):
+def cyclotomic_to_alpha(cyclo) -> list:
"""
Convert a list of indices of cyclotomic polynomials
to a list of rational numbers.
@@ -252,14 +252,12 @@ def cyclotomic_to_alpha(cyclo):
sage: cyclotomic_to_alpha([2,3])
[1/3, 1/2, 2/3]
"""
- alpha = []
- for d in cyclo:
- for k in ZZ(d).coprime_integers(d):
- alpha.append(QQ((k, d)))
+ alpha = [QQ((k, d)) for d in cyclo
+ for k in ZZ(d).coprime_integers(d)]
return sorted(alpha)
-def alpha_to_cyclotomic(alpha):
+def alpha_to_cyclotomic(alpha) -> list:
"""
Convert from a list of rationals arguments to a list of integers.
@@ -320,10 +318,10 @@ def capital_M(n):
[1, 4, 27, 64, 3125, 432, 823543]
"""
n = ZZ(n)
- return QQ.prod(d ** (d * moebius(n / d)) for d in divisors(n))
+ return QQ.prod(d ** (d * moebius(n // d)) for d in divisors(n))
-def cyclotomic_to_gamma(cyclo_up, cyclo_down):
+def cyclotomic_to_gamma(cyclo_up, cyclo_down) -> dict:
"""
Convert a quotient of products of cyclotomic polynomials
to a quotient of products of polynomials `x^n - 1`.
@@ -398,7 +396,7 @@ def gamma_list_to_cyclotomic(galist):
sorted(d for d in resu for k in range(-resu[d])))
-class HypergeometricData():
+class HypergeometricData:
_gauss_table = {}
def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None):
@@ -450,7 +448,7 @@ def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None):
deg = sum(euler_phi(x) for x in cyclo_down)
up_deg = sum(euler_phi(x) for x in cyclo_up)
if up_deg != deg:
- msg = 'not the same degree: {} != {}'.format(up_deg, deg)
+ msg = f'not the same degree: {up_deg} != {deg}'
raise ValueError(msg)
cyclo_up.sort()
cyclo_down.sort()
@@ -489,7 +487,7 @@ def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None):
for v in cyclo_up)
# --- Internals ---
- def __repr__(self):
+ def __repr__(self) -> str:
"""
Return the string representation.
@@ -504,7 +502,7 @@ def __repr__(self):
txt = "Hypergeometric data for {} and {}"
return txt.format(list(self._alpha), list(self._beta))
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
"""
Return whether two data are equal.
@@ -521,7 +519,7 @@ def __eq__(self, other):
return (self._alpha == other._alpha and
self._beta == other._beta)
- def __ne__(self, other):
+ def __ne__(self, other) -> bool:
"""
Return whether two data are unequal.
@@ -550,7 +548,7 @@ def __hash__(self):
return hash((self._alpha, self._beta))
# --- Parameters and invariants ---
- def cyclotomic_data(self):
+ def cyclotomic_data(self) -> tuple:
"""
Return the pair of tuples of indices of cyclotomic polynomials.
@@ -562,7 +560,7 @@ def cyclotomic_data(self):
"""
return (list(self._cyclo_up), list(self._cyclo_down))
- def alpha_beta(self):
+ def alpha_beta(self) -> tuple:
"""
Return the pair of lists of rational arguments.
@@ -574,7 +572,7 @@ def alpha_beta(self):
"""
return (list(self._alpha), list(self._beta))
- def alpha(self):
+ def alpha(self) -> list:
"""
Return the first tuple of rational arguments.
@@ -586,7 +584,7 @@ def alpha(self):
"""
return list(self._alpha)
- def beta(self):
+ def beta(self) -> list:
"""
Return the second tuple of rational arguments.
@@ -598,7 +596,7 @@ def beta(self):
"""
return list(self._beta)
- def defining_polynomials(self):
+ def defining_polynomials(self) -> tuple:
"""
Return the pair of products of cyclotomic polynomials.
@@ -612,7 +610,7 @@ def defining_polynomials(self):
down = prod(cyclotomic_polynomial(d) for d in self._cyclo_down)
return (up, down)
- def gamma_array(self):
+ def gamma_array(self) -> dict:
r"""
Return the dictionary `\{v: \gamma_v\}` for the expression
@@ -630,7 +628,7 @@ def gamma_array(self):
"""
return dict(self._gamma_array)
- def gamma_list(self):
+ def gamma_list(self) -> list:
r"""
Return a list of integers describing the `x^n - 1` factors.
@@ -654,7 +652,7 @@ def gamma_list(self):
resu += [sgn(n) * v] * abs(n)
return resu
- def wild_primes(self):
+ def wild_primes(self) -> list:
r"""
Return the wild primes.
@@ -667,14 +665,15 @@ def wild_primes(self):
[2, 3, 5]
"""
gamma = self.gamma_array()
- return sorted({p for n in gamma.keys() for (p, _) in n.factor()})
+ return sorted({p for n in gamma for p, _ in n.factor()})
def zigzag(self, x, flip_beta=False):
r"""
Count ``alpha``'s at most ``x`` minus ``beta``'s at most ``x``.
This function is used to compute the weight and the Hodge numbers.
- With `flip_beta` set to True, replace each `b` in `\beta` with `1-b`.
+ With `flip_beta` set to ``True``, replace each `b` in `\beta`
+ with `1-b`.
.. SEEALSO::
@@ -771,7 +770,7 @@ def degree(self):
"""
return self._deg
- def hodge_numbers(self):
+ def hodge_numbers(self) -> list:
"""
Return the Hodge numbers.
@@ -882,7 +881,7 @@ def hodge_function(self, x):
return j
return j - (i - x) * (k - 1)
- def hodge_polygon_vertices(self):
+ def hodge_polygon_vertices(self) -> list:
"""
Return the vertices of the Hodge polygon.
@@ -948,7 +947,7 @@ def E_polynomial(self, vars=None):
gamma_plus = [g for g in gamma if g > 0]
gamma_minus = [g for g in gamma if g < 0]
- domain = set(d for g in gamma for d in divisors(g.abs()))
+ domain = {d for g in gamma for d in divisors(g.abs())}
m_plus = {d: len([1 for g in gamma_plus if not g % d])
for d in domain}
@@ -1003,7 +1002,7 @@ def M_value(self):
"""
return self._M_value
- def is_primitive(self):
+ def is_primitive(self) -> bool:
"""
Return whether this data is primitive.
@@ -1043,7 +1042,7 @@ def primitive_index(self):
"""
return gcd(self.gamma_list())
- def has_symmetry_at_one(self):
+ def has_symmetry_at_one(self) -> bool:
"""
If ``True``, the motive H(t=1) is a direct sum of two motives.
@@ -1123,8 +1122,8 @@ def canonical_scheme(self, t=None):
gamma_neg = [u for u in self.gamma_list() if u < 0]
N_pos = len(gamma_pos)
N_neg = len(gamma_neg)
- varX = ['X{}'.format(i) for i in range(N_pos)]
- varY = ['Y{}'.format(i) for i in range(N_neg)]
+ varX = [f'X{i}' for i in range(N_pos)]
+ varY = [f'Y{i}' for i in range(N_neg)]
ring = PolynomialRing(basering, varX + varY)
gens = ring.gens()
X = gens[:N_pos]
@@ -1137,7 +1136,7 @@ def canonical_scheme(self, t=None):
ideal = ring.ideal([eq0, eq1, self.M_value() * eq2_neg - t * eq2_pos])
return Spec(ring.quotient(ideal))
- def polytope(self):
+ def lattice_polytope(self):
"""
Return the associated lattice polytope.
@@ -1148,7 +1147,7 @@ def polytope(self):
sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
sage: H = Hyp(gamma_list=[-5, -2, 3, 4])
- sage: P = H.polytope(); P
+ sage: P = H.lattice_polytope(); P
2-d lattice polytope in 2-d lattice M
sage: P.polyhedron().f_vector()
(1, 4, 4, 1)
@@ -1158,7 +1157,7 @@ def polytope(self):
The Chebyshev example from [RV2019]_::
sage: H = Hyp(gamma_list=[-30, -1, 6, 10, 15])
- sage: P = H.polytope(); P
+ sage: P = H.lattice_polytope(); P
3-d lattice polytope in 3-d lattice M
sage: len(P.points())
19
@@ -1308,7 +1307,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
If left unspecified, `prec` is set to the minimum `p`-adic precision
needed to recover the Euler factor.
- If `cache_p` is True, then the function caches an intermediate
+ If `cache_p` is ``True``, then the function caches an intermediate
result which depends only on `p` and `f`. This leads to a significant
speedup when iterating over `t`.
@@ -1401,7 +1400,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
raise ValueError('p not prime')
if not all(x.denominator() % p for x in self._alpha + self._beta):
raise NotImplementedError('p is wild')
- if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0):
+ if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
raise NotImplementedError('p is tame')
if 0 in alpha:
@@ -1420,7 +1419,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
# also: D = (self.weight() + 1 - m[0]) // 2
if prec is None:
- prec = ceil((self.weight() * f) / 2 + log(2*self.degree()+1, p))
+ prec = ceil((self.weight() * f) / 2 + log(2 * self.degree() + 1, p))
use_longs = (p ** prec < 2 ** 31)
gamma = self._gamma_array
@@ -1434,10 +1433,10 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False):
else:
gtab = gauss_table(p, f, prec, use_longs)
trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs)
- sigma = trcoeffs[p-2]
+ sigma = trcoeffs[p - 2]
p_ring = sigma.parent()
- teich = p_ring.teichmuller(M/t)
- for i in range(p-3, -1, -1):
+ teich = p_ring.teichmuller(M / t)
+ for i in range(p - 3, -1, -1):
sigma = sigma * teich + trcoeffs[i]
resu = ZZ(-1) ** m[0] * sigma / (1 - q)
return IntegerModRing(p**prec)(resu).lift_centered()
@@ -1542,7 +1541,7 @@ def H_value(self, p, f, t, ring=None):
raise ValueError('p not prime')
if not all(x.denominator() % p for x in self._alpha + self._beta):
raise NotImplementedError('p is wild')
- if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0):
+ if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
raise NotImplementedError('p is tame')
if 0 in alpha:
@@ -1754,7 +1753,7 @@ def euler_factor(self, t, p, cache_p=False):
raise ValueError('p not prime')
if not all(x.denominator() % p for x in self._alpha + self._beta):
raise NotImplementedError('p is wild')
- if (t.numerator()*t.denominator() % p == 0 or (t-1) % p == 0):
+ if (t.numerator() * t.denominator() % p == 0 or (t - 1) % p == 0):
raise NotImplementedError('p is tame')
# now p is good
d = self.degree()
From 0ffd40d373e5eeac105c616c6d73ae4a30e8c510 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Wed, 21 Feb 2024 10:31:01 +0000
Subject: [PATCH 031/518] fix doctest
---
src/sage/rings/polynomial/polynomial_template.pxi | 4 +++-
src/sage/rings/ring.pyx | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index 863f642754d..076a9753b29 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -376,7 +376,9 @@ cdef class Polynomial_template(Polynomial):
if celement_is_zero(&other.x, (self)._cparent):
return self
if celement_equal(&self.x, &other.x, (self)._cparent):
- return self
+ # note: gcd(g, g) "canonicalizes" the generator i.e. make polynomials monic
+ # c.f. ring/ring.pyx:445
+ return self.monic()
cdef type T = type(self)
cdef Polynomial_template r = T.__new__(T)
diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx
index 2356eb44981..be9fa3354b2 100644
--- a/src/sage/rings/ring.pyx
+++ b/src/sage/rings/ring.pyx
@@ -441,6 +441,7 @@ cdef class Ring(ParentWithGens):
g = gens[0]
if len(gens) == 1:
try:
+ # TODO: Rewrite this properly
g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
except (AttributeError, NotImplementedError):
pass
From 8766c9bd6c3f2ae47f1d6f0115994ec1eb6b33a9 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Wed, 21 Feb 2024 09:12:45 -0800
Subject: [PATCH 032/518] src/doc/en/installation/source.rst: Fix typo
---
src/doc/en/installation/source.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst
index 17c4328621e..5fdc573f2e4 100644
--- a/src/doc/en/installation/source.rst
+++ b/src/doc/en/installation/source.rst
@@ -1277,5 +1277,5 @@ Now let's build Sage, starting with the step::
[alice@localhost worktree-purple]$ make configure
-Refer to the file `README.md `_.
+Refer to the file `README.md `_
for the following steps.
From 30fecca1981087a88eb8db2cf05e18edbb50d16f Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Wed, 21 Feb 2024 22:21:06 +0000
Subject: [PATCH 033/518] Include a doctest for precomposition
---
src/sage/structure/parent.pyx | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx
index e654c85a6a3..7a134d770e9 100644
--- a/src/sage/structure/parent.pyx
+++ b/src/sage/structure/parent.pyx
@@ -2635,6 +2635,25 @@ cdef class Parent(sage.structure.category_object.CategoryObject):
sage: print(coercion_model.get_action(E, ZZ, operator.pow)) # needs sage.schemes
None
+
+ ::
+
+ With Pull Request #37369, registered multiplication actions by
+ `ZZ` are also discovered and used when a Python ``int`` is multiplied.
+ Previously, it was only discovering the generic Integer Multiplication
+ Action that all additive groups have. As a result, optimised
+ implementations, such as the use of Pari for scalar multiplication of points
+ on elliptic curves over Finite Fields, was not used if an ``int``
+ multiplied a point, resulting in a 10x slowdown for large characteristic::
+
+ sage: E = EllipticCurve(GF(17),[1,1])
+ sage: coercion_model.discover_action(ZZ, E, operator.mul)
+ Left action by Integer Ring on Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 17
+ sage: coercion_model.discover_action(int, E, operator.mul)
+ Left action by Integer Ring on Elliptic Curve defined by y^2 = x^3 + x + 1 over Finite Field of size 17
+ with precomposition on left by Native morphism:
+ From: Set of Python objects of class 'int'
+ To: Integer Ring
"""
# G acts on S, G -> G', R -> S => G' acts on R (?)
# NO! ZZ[x,y] acts on Matrices(ZZ[x]) but ZZ[y] does not.
From 81a71ae73cc6c60101fb583b87b5ba0085e164bb Mon Sep 17 00:00:00 2001
From: nbruin
Date: Wed, 21 Feb 2024 20:20:46 -0800
Subject: [PATCH 034/518] Remove long/int relic
in py2, long and int were different types. In py3, only int remains, with long a synonym in cython.
---
src/sage/rings/integer_ring.pyx | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx
index 559de2065e2..f4da4a8c961 100644
--- a/src/sage/rings/integer_ring.pyx
+++ b/src/sage/rings/integer_ring.pyx
@@ -586,10 +586,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
sage: f(-7r)
-7
"""
- if S is long:
+ if S is int:
return sage.rings.integer.long_to_Z()
- elif S is int:
- return sage.rings.integer.int_to_Z()
elif S is bool:
return True
elif is_numpy_type(S):
From 0da20b8199f3e0090b307162faf04e40e6db5c22 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Wed, 21 Feb 2024 21:59:36 -0800
Subject: [PATCH 035/518] replace platform-specific code in
sage.parallel.ncpus.ncpus() by os.cpu_count()
---
src/sage/parallel/ncpus.py | 58 ++++----------------------------------
1 file changed, 5 insertions(+), 53 deletions(-)
diff --git a/src/sage/parallel/ncpus.py b/src/sage/parallel/ncpus.py
index dfb6c3c6734..b386d06666c 100644
--- a/src/sage/parallel/ncpus.py
+++ b/src/sage/parallel/ncpus.py
@@ -1,46 +1,19 @@
"""
CPU Detection
"""
-# Parallel Python Software: http://www.parallelpython.com
-# Copyright (c) 2005-2008, Vitalii Vanovschi
-# All rights reserved.
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-# * Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# * Neither the name of the author nor the names of its contributors
-# may be used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-# THE POSSIBILITY OF SUCH DAMAGE.
-
-######
-# This is from ParallelPython (the pp.py file).
import os
-import subprocess
def ncpus():
"""
- Detects the number of effective CPUs in the system.
+ Detects the number of available CPUs in the system.
+
+ ALGORITHM: :func:`os.cpu_count`
EXAMPLES::
- sage: sage.parallel.ncpus.ncpus() # random output -- depends on machine.
+ sage: sage.parallel.ncpus.ncpus() # random output -- depends on machine
2
"""
# Support Sage environment variable SAGE_NUM_THREADS
@@ -53,25 +26,4 @@ def ncpus():
else:
return int(n)
- # for Linux, Unix and MacOS
- if hasattr(os, "sysconf"):
- if "SC_NPROCESSORS_ONLN" in os.sysconf_names:
- # Linux and Unix
- ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
- if isinstance(ncpus, int) and ncpus > 0:
- return ncpus
- else:
- # MacOS X
- # deprecated: return int(os.popen2("sysctl -n hw.ncpu")[1].read())
- process = subprocess.Popen("sysctl -n hw.ncpu", shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, close_fds=True)
- return int(process.stdout.read())
- # for Windows
- if "NUMBER_OF_PROCESSORS" in os.environ:
- ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
- if ncpus > 0:
- return ncpus
- # return the default value
- return 1
+ return os.cpu_count() or 1
From f987fdafa3056fed3cb83e3a9d0eb56e634a17a2 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Wed, 21 Feb 2024 22:00:18 -0800
Subject: [PATCH 036/518] prefer os.sched_getaffinity() when available
---
src/sage/parallel/ncpus.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/sage/parallel/ncpus.py b/src/sage/parallel/ncpus.py
index b386d06666c..dda3cde6516 100644
--- a/src/sage/parallel/ncpus.py
+++ b/src/sage/parallel/ncpus.py
@@ -26,4 +26,9 @@ def ncpus():
else:
return int(n)
- return os.cpu_count() or 1
+ n = None
+
+ if hasattr(os, 'sched_getaffinity'):
+ n = len(os.sched_getaffinity(0))
+
+ return n or os.cpu_count() or 1
From a72cb9944e7e97b31ae458619729b92e2d29920c Mon Sep 17 00:00:00 2001
From: Marco Perin <30049458+marco-perin@users.noreply.github.com>
Date: Wed, 21 Feb 2024 22:03:43 -0800
Subject: [PATCH 037/518] Update constructor.py
In the documentation for polyhedron constructor, there was a typo where `Polyhedron(o)` was used when `Polyhedron(p)` should have been used instead ( the `o` var was from the same code piece, but from the very previous example ).
The result of the lines of code have been updated accordingly
---
src/sage/geometry/polyhedron/constructor.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py
index f18492f584f..8a86eddec66 100644
--- a/src/sage/geometry/polyhedron/constructor.py
+++ b/src/sage/geometry/polyhedron/constructor.py
@@ -505,9 +505,9 @@ def Polyhedron(vertices=None, rays=None, lines=None,
sage: p.add_constraint(x >= -1)
sage: p.add_constraint(y <= 1)
sage: p.add_constraint(y >= -1)
- sage: Polyhedron(o)
+ sage: Polyhedron(p, base_ring=ZZ)
A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
- sage: Polyhedron(o, base_ring=QQ)
+ sage: Polyhedron(p)
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices
sage: # needs sage.combinat
From 015e3e630bf5d4f251f031addfc40b70857e1c7f Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Wed, 21 Feb 2024 22:05:00 -0800
Subject: [PATCH 038/518] Reviewer edits
---
src/sage/parallel/ncpus.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sage/parallel/ncpus.py b/src/sage/parallel/ncpus.py
index dda3cde6516..24e8c0c5743 100644
--- a/src/sage/parallel/ncpus.py
+++ b/src/sage/parallel/ncpus.py
@@ -7,9 +7,9 @@
def ncpus():
"""
- Detects the number of available CPUs in the system.
+ Return the number of available CPUs in the system.
- ALGORITHM: :func:`os.cpu_count`
+ ALGORITHM: :func:`os.sched_getaffinity` or :func:`os.cpu_count`
EXAMPLES::
From b1c4b32121430754274048aa89f7e07e7afe9c64 Mon Sep 17 00:00:00 2001
From: Kwankyu Lee
Date: Thu, 22 Feb 2024 15:58:35 +0900
Subject: [PATCH 039/518] Edit hypellfrob.pyx
---
src/doc/en/reference/references/index.rst | 8 ++
.../hyperelliptic_curves/hypellfrob.pyx | 73 +++++++------------
.../hyperelliptic_curves/hypellfrob/README | 5 +-
3 files changed, 35 insertions(+), 51 deletions(-)
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index e68bdbc2d57..790aecf20f8 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -3137,6 +3137,14 @@ REFERENCES:
.. [HarPri] F. Harary and G. Prins. The block-cutpoint-tree of
a graph. Publ. Math. Debrecen 13 1966 103-107.
+.. [Harv2007] David Harvey. *Kedlaya's algorithm in larger characteristic*,
+ :arxiv:`math/0610973`.
+
+.. [BGS2007] Alin Bostan, Pierrick Gaudry, and Eric Schost, *Linear recurrences
+ with polynomial coefficients and application to integer factorization and
+ Cartier-Manin operator*, SIAM Journal on Computing 36 (2007), no. 6,
+ 1777-1806
+
.. [Hat2002] Allen Hatcher, "Algebraic Topology", Cambridge University
Press (2002).
diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
index b4144780c1d..a6b31812187 100644
--- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
+++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
@@ -9,17 +9,22 @@
# sage.doctest: needs sage.libs.ntl sage.modules sage.rings.padics
r"""
-Frobenius on Monsky-Washnitzer cohomology of a hyperelliptic curve over a
-largish prime finite field
+Frobenius on Monsky-Washnitzer cohomology of a hyperelliptic curve
-This is a wrapper for the matrix() function in hypellfrob.cpp.
+This module provides :func:`hypellfrob`, that is a wrapper for the ``matrix()``
+function in ``hypellfrob.cpp``.
-AUTHOR:
+``hypellfrob.cpp`` is a C++ program for computing the zeta function of a
+hyperelliptic curve over a largish prime finite field, based on the method
+described in the paper [Harv2007]_. More precisely, it computes the matrix of
+Frobenius on the Monsky-Washnitzer cohomology of the curve; the zeta function
+can be recovered via the characteristic polynomial of the matrix.
-- David Harvey (2007-05)
+AUTHORS:
+- David Harvey (2007-05): initial version
- David Harvey (2007-12): rewrote for ``hypellfrob`` version 2.0
-
+- Alex J. Best (2018-02): added wrapper
"""
# *****************************************************************************
@@ -68,21 +73,17 @@ def interval_products(M0, M1, target):
r"""
Given a matrix `M` with coefficients linear polynomials over `\ZZ/N\ZZ` and
a list of integers `a_0 < b_0 \le a_1 < b_1 \le \cdots \le a_n < b_n`
- compute the matrices
- ``\prod_{t = a_i + 1}^{b_i} M(t)``
- for `i = 0` to `n`.
-
- This is a wrapper for code in the ``hypellfrob`` package.
+ compute the matrices `\prod_{t = a_i + 1}^{b_i} M(t)` for `i = 0` to `n`.
INPUT:
- - ``M0``, ``M1`` -- matrices over `\ZZ/N\ZZ`, so that `M = M0 + M1*x`
+ - ``M0``, ``M1`` -- matrices over `\ZZ/N\ZZ`, so that `M = M0 + M1\times x`
- ``target`` -- a list of integers
ALGORITHM:
- Described in [Harv2007]_.
- Based on the work of Bostan-Gaudry-Schost [BGS2007]_.
+ Described in [Harv2007]_. Based on the work of Bostan-Gaudry-Schost
+ [BGS2007]_.
EXAMPLES::
@@ -116,20 +117,6 @@ def interval_products(M0, M1, target):
sage: [prod(Matrix(Integers(3^18), 1, 1, [t + 1]) for t in range(3,5))]
[[20]]
- AUTHORS:
-
- - David Harvey (2007-12): Original code
- - Alex J. Best (2018-02): Wrapper
-
- REFERENCES:
-
- .. [Harv2007] David Harvey. *Kedlaya's algorithm in larger characteristic*,
- :arxiv:`math/0610973`.
-
- .. [BGS2007] Alin Bostan, Pierrick Gaudry, and Eric Schost,
- *Linear recurrences with polynomial coefficients and application
- to integer factorization and Cartier-Manin operator*, SIAM
- Journal on Computing 36 (2007), no. 6, 1777-1806
"""
# Sage objects that wrap the NTL objects
cdef mat_ZZ_p_c mm0, mm1
@@ -175,24 +162,17 @@ def hypellfrob(p, N, Q):
INPUT:
- ``p`` -- a prime
- - ``Q`` -- a monic polynomial in `\ZZ[x]` of odd degree.
- Must have no multiple roots mod `p`.
- - ``N`` -- precision parameter; the output matrix will be correct modulo `p^N`.
-
- PRECONDITIONS:
+ - ``Q`` -- a monic polynomial in `\ZZ[x]` of odd degree; must have no
+ multiple roots mod `p`.
+ - ``N`` -- precision parameter; the output matrix will be correct modulo `p^N`
- Must have `p > (2g+1)(2N-1)`, where `g = (\deg(Q)-1)/2` is the genus
- of the curve.
+ The prime `p` should satisfy `p > (2g+1)(2N-1)`, where `g =
+ \frac{\deg(Q)-1}{2}` is the genus of the curve.
ALGORITHM:
- Described in "Kedlaya's algorithm in larger characteristic" by David
- Harvey. Running time is theoretically soft-`O(p^{1/2} N^{5/2} g^3)`.
-
- .. TODO::
-
- Remove the restriction on `p`. Probably by merging in Robert's code,
- which eventually needs a fast C++/NTL implementation.
+ Described in [Harv2007]_. Running time is theoretically soft-`O(p^{1/2}
+ N^{5/2} g^3)`.
EXAMPLES::
@@ -216,10 +196,11 @@ def hypellfrob(p, N, Q):
[ O(101) O(101) 65 + O(101) 42 + O(101)]
[ O(101) O(101) 89 + O(101) 29 + O(101)]
- AUTHORS:
+ .. TODO::
+
+ Remove the restriction on `p`. Probably by merging in Robert's code,
+ which eventually needs a fast C++/NTL implementation.
- - David Harvey (2007-05)
- - David Harvey (2007-12): updated for hypellfrob version 2.0
"""
# Sage objects that wrap the NTL objects
cdef ntl_ZZ pp
@@ -268,5 +249,3 @@ def hypellfrob(p, N, Q):
data = [[mm[j, i]._integer_() + prec for i in range(2 * g)]
for j in range(2 * g)]
return Matrix(R, data)
-
-# end of file
diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/README b/src/sage/schemes/hyperelliptic_curves/hypellfrob/README
index 710d6f0021e..d03da976b04 100644
--- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/README
+++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/README
@@ -2,10 +2,7 @@ This directory contains the main source files for hypellfrob
version 2.1.1, by David Harvey.
hypellfrob is also maintained as a separate project, see
-http://cims.nyu.edu/~harvey/hypellfrob/
-
-At that web site you'll find a standalone demo program and
-a test suite.
+https://web.maths.unsw.edu.au/~davidharvey/code/hypellfrob
hypellfrob 2.1.1 is Copyright (C) 2007, 2008 David Harvey and is
licensed under the GPL (version 2 or later).
From 33e14a90c6a4579bcf60d91e8c1c87b19a53550f Mon Sep 17 00:00:00 2001
From: nbruin
Date: Thu, 22 Feb 2024 08:58:33 -0800
Subject: [PATCH 040/518] remove int_to_Z because it's buggy
int_to_Z used to work in Py2 on the type "int" there which were integers that fit in a word. In Py3 there is no such type anymore and "int" now is what "long" used to be -- -arbitrary length integers. In Py3 int_to_Z is buggy:
sage: itoZ=sage.rings.integer.int_to_Z()
sage: itoZ(1000000000000000000000)
OverflowError
---
src/sage/rings/integer.pyx | 81 --------------------------------------
1 file changed, 81 deletions(-)
diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx
index aa7d4855bbb..d9f9deede99 100644
--- a/src/sage/rings/integer.pyx
+++ b/src/sage/rings/integer.pyx
@@ -7389,87 +7389,6 @@ def make_integer(s):
return r
-cdef class int_to_Z(Morphism):
- """
- Morphism from Python ints to Sage integers.
-
- EXAMPLES::
-
- sage: f = ZZ.coerce_map_from(int)
- sage: type(f)
-
- sage: f(5r)
- 5
- sage: type(f(5r))
-
- sage: 1 + 2r
- 3
- sage: type(1 + 2r)
-
-
- This is intended for internal use by the coercion system,
- to facilitate fast expressions mixing ints and more complex
- Python types. Note that (as with all morphisms) the input
- is forcably coerced to the domain ``int`` if it is not
- already of the correct type which may have undesirable results::
-
- sage: f.domain()
- Set of Python objects of class 'int'
- sage: f(1/3)
- 0
- sage: f(1.7)
- 1
- sage: f("10")
- 10
-
- A pool is used for small integers::
-
- sage: f(10) is f(10)
- True
- sage: f(-2) is f(-2)
- True
- """
-
- def __init__(self):
- """
- TESTS::
-
- sage: f = ZZ.coerce_map_from(int)
- sage: f.parent()
- Set of Morphisms from Set of Python objects of class 'int' to Integer Ring in Category of sets
- """
- import sage.categories.homset
- from sage.sets.pythonclass import Set_PythonType
- Morphism.__init__(self, sage.categories.homset.Hom(Set_PythonType(int), integer_ring.ZZ))
-
- cpdef Element _call_(self, a) noexcept:
- """
- Return a new integer with the same value as ``a``.
-
- TESTS::
-
- sage: f = ZZ.coerce_map_from(int)
- sage: f(100r)
- 100
- """
- if type(a) is not int:
- raise TypeError("must be a Python int object")
-
- return smallInteger(PyLong_AsLong(a))
-
- def _repr_type(self):
- """
- TESTS::
-
- sage: f = ZZ.coerce_map_from(int)
- sage: print(f)
- Native morphism:
- From: Set of Python objects of class 'int'
- To: Integer Ring
- """
- return "Native"
-
-
cdef class long_to_Z(Morphism):
"""
EXAMPLES::
From fa1905c556e570060574f9a6acf98be72c62afed Mon Sep 17 00:00:00 2001
From: nbruin
Date: Thu, 22 Feb 2024 09:00:00 -0800
Subject: [PATCH 041/518] remove forward declaration of int_to_Z
---
src/sage/rings/integer.pxd | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/sage/rings/integer.pxd b/src/sage/rings/integer.pxd
index 2f81c164943..b831f095eb1 100644
--- a/src/sage/rings/integer.pxd
+++ b/src/sage/rings/integer.pxd
@@ -42,6 +42,3 @@ cdef inline Integer _Integer_from_mpz(mpz_t e) noexcept:
cdef Integer z = Integer.__new__(Integer)
mpz_set(z.value, e)
return z
-
-cdef class int_to_Z(Morphism):
- pass
From d8298b98692e249ea0575b6f19351b9e9b79d53d Mon Sep 17 00:00:00 2001
From: nbruin
Date: Thu, 22 Feb 2024 10:48:37 -0800
Subject: [PATCH 042/518] rename long_to_Z to int_to_Z
---
src/sage/rings/integer.pyx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx
index d9f9deede99..4b837947047 100644
--- a/src/sage/rings/integer.pyx
+++ b/src/sage/rings/integer.pyx
@@ -7389,7 +7389,7 @@ def make_integer(s):
return r
-cdef class long_to_Z(Morphism):
+cdef class int_to_Z(Morphism):
"""
EXAMPLES::
From 18ace4ee14b6402f68b8819d21350130c9054c09 Mon Sep 17 00:00:00 2001
From: nbruin
Date: Thu, 22 Feb 2024 10:49:41 -0800
Subject: [PATCH 043/518] adapt call site to int_to_Z
---
src/sage/rings/integer_ring.pyx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx
index f4da4a8c961..e35559d68d8 100644
--- a/src/sage/rings/integer_ring.pyx
+++ b/src/sage/rings/integer_ring.pyx
@@ -587,7 +587,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
-7
"""
if S is int:
- return sage.rings.integer.long_to_Z()
+ return sage.rings.integer.int_to_Z()
elif S is bool:
return True
elif is_numpy_type(S):
From e69260248bb0b17f22c48fc81d4c1ed33e0a37b1 Mon Sep 17 00:00:00 2001
From: Kwankyu Lee
Date: Fri, 23 Feb 2024 04:57:29 +0900
Subject: [PATCH 044/518] Requested changes
---
.../hyperelliptic_curves/hypellfrob.pyx | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
index a6b31812187..9f9bb04d0ea 100644
--- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
+++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx
@@ -71,18 +71,18 @@ cdef extern from "hypellfrob.h":
def interval_products(M0, M1, target):
r"""
- Given a matrix `M` with coefficients linear polynomials over `\ZZ/N\ZZ` and
- a list of integers `a_0 < b_0 \le a_1 < b_1 \le \cdots \le a_n < b_n`
- compute the matrices `\prod_{t = a_i + 1}^{b_i} M(t)` for `i = 0` to `n`.
+ Given matrices `M(t)` with entries linear in `t` over `\ZZ/N\ZZ` and a list
+ of integers `a_0 < b_0 \le a_1 < b_1 \le \cdots \le a_n < b_n`, compute the
+ matrices `\prod_{t = a_i + 1}^{b_i} M(t)` for `i = 0` to `n`.
INPUT:
- - ``M0``, ``M1`` -- matrices over `\ZZ/N\ZZ`, so that `M = M0 + M1\times x`
- - ``target`` -- a list of integers
+ - ``M0``, ``M1`` -- matrices over `\ZZ/N\ZZ`, so that `M(t) = M_0 + M_1t`
+ - ``target`` -- a list of integers `a_0, b_0, \dots, a_n, b_n`
ALGORITHM:
- Described in [Harv2007]_. Based on the work of Bostan-Gaudry-Schost
+ Described in [Harv2007]_, Theorem 10. Based on the work of Bostan-Gaudry-Schost
[BGS2007]_.
EXAMPLES::
@@ -167,12 +167,12 @@ def hypellfrob(p, N, Q):
- ``N`` -- precision parameter; the output matrix will be correct modulo `p^N`
The prime `p` should satisfy `p > (2g+1)(2N-1)`, where `g =
- \frac{\deg(Q)-1}{2}` is the genus of the curve.
+ \left(\deg Q - 1\right) / 2` is the genus of the curve.
ALGORITHM:
- Described in [Harv2007]_. Running time is theoretically soft-`O(p^{1/2}
- N^{5/2} g^3)`.
+ Described in [Harv2007]_, Section 7. Running time is theoretically
+ `\widetilde{O}(p^{1/2} N^{5/2} g^3)`.
EXAMPLES::
From b3a8da4baab5370ddf4d200e16913a8cdb4aa981 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Thu, 22 Feb 2024 21:34:36 -0800
Subject: [PATCH 045/518] src/sage/misc/lazy_import.pyx: Deprecate star imports
---
src/sage/misc/lazy_import.pyx | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx
index c9b09dc1ef5..61a073557d2 100644
--- a/src/sage/misc/lazy_import.pyx
+++ b/src/sage/misc/lazy_import.pyx
@@ -1117,6 +1117,11 @@ def lazy_import(module, names, as_=None, *,
if namespace is None:
namespace = inspect.currentframe().f_locals
if "*" in names:
+ from sage.misc.superseded import deprecation_cython
+
+ deprecation_cython(37433,
+ 'lazy_import of * is deprecated; provide the names to be imported explicitly')
+
ix = names.index("*")
all = get_star_imports(module)
names[ix:ix+1] = all
From bae605552c3f061f97d28cf470598023a97b00f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Fri, 23 Feb 2024 09:54:56 +0100
Subject: [PATCH 046/518] small cleanup in monoids
---
src/sage/monoids/automatic_semigroup.py | 5 +-
src/sage/monoids/free_abelian_monoid.py | 11 +++--
.../monoids/free_abelian_monoid_element.pyx | 2 +-
src/sage/monoids/free_monoid.py | 2 +-
src/sage/monoids/free_monoid_element.py | 23 +++++----
src/sage/monoids/hecke_monoid.py | 8 ++--
src/sage/monoids/indexed_free_monoid.py | 22 ++++-----
src/sage/monoids/monoid.py | 6 ++-
src/sage/monoids/string_monoid.py | 32 +++++++------
src/sage/monoids/string_monoid_element.py | 34 ++++++++------
src/sage/monoids/string_ops.py | 47 ++++++++++---------
src/sage/monoids/trace_monoid.py | 20 ++++----
12 files changed, 110 insertions(+), 102 deletions(-)
diff --git a/src/sage/monoids/automatic_semigroup.py b/src/sage/monoids/automatic_semigroup.py
index b6e96688cf8..8c06f205acc 100644
--- a/src/sage/monoids/automatic_semigroup.py
+++ b/src/sage/monoids/automatic_semigroup.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
Automatic Semigroups
@@ -418,7 +417,7 @@ def _repr_(self):
else:
of = ""
- return "%s%s with %s generators" % (typ, of, len(self._generators))
+ return f"{typ}{of} with {len(self._generators)} generators"
def repr_element_method(self, style="ambient"):
"""
@@ -514,7 +513,7 @@ def retract(self, ambient_element, check=True):
if element not in self._elements_set:
cache = self._retract.cache
del cache[((ambient_element,), ())]
- raise ValueError("%s not in %s" % (ambient_element, self))
+ raise ValueError(f"{ambient_element} not in {self}")
return element
@cached_method
diff --git a/src/sage/monoids/free_abelian_monoid.py b/src/sage/monoids/free_abelian_monoid.py
index a4b2465386b..590a20eaa1a 100644
--- a/src/sage/monoids/free_abelian_monoid.py
+++ b/src/sage/monoids/free_abelian_monoid.py
@@ -44,15 +44,15 @@
sage: x.list()
[7, 2, 0, 1, 1]
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2005 David Kohel
#
# 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.misc.cachefunc import cached_method
from sage.structure.category_object import normalize_names
@@ -141,7 +141,7 @@ def FreeAbelianMonoid(index_set=None, names=None, **kwds):
sage: FreeAbelianMonoid(names='x,y')
Free abelian monoid on 2 generators (x, y)
"""
- if isinstance(index_set, str): # Swap args (this works if names is None as well)
+ if isinstance(index_set, str): # Swap args (this works if names is None as well)
names, index_set = index_set, names
if index_set is None and names is not None:
@@ -179,6 +179,7 @@ def is_FreeAbelianMonoid(x):
"""
return isinstance(x, FreeAbelianMonoid_class)
+
class FreeAbelianMonoid_class(Parent):
"""
Free abelian monoid on `n` generators.
@@ -204,7 +205,7 @@ def __init__(self, n, names):
def __repr__(self):
n = self.__ngens
- return "Free abelian monoid on %s generators %s" % (n,self.gens())
+ return f"Free abelian monoid on {n} generators {self.gens()}"
def __call__(self, x):
"""
diff --git a/src/sage/monoids/free_abelian_monoid_element.pyx b/src/sage/monoids/free_abelian_monoid_element.pyx
index 66bd3a15ed5..a9eec5f9065 100644
--- a/src/sage/monoids/free_abelian_monoid_element.pyx
+++ b/src/sage/monoids/free_abelian_monoid_element.pyx
@@ -321,7 +321,7 @@ cdef class FreeAbelianMonoidElement(MonoidElement):
def __pow__(self, n, modulus):
"""
- Raises self to the power of ``n``.
+ Raise ``self`` to the power of ``n``.
AUTHORS:
diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py
index 12688312b9c..c3abc1b8169 100644
--- a/src/sage/monoids/free_monoid.py
+++ b/src/sage/monoids/free_monoid.py
@@ -196,7 +196,7 @@ def __init__(self, n, names=None):
Monoid_class.__init__(self, names)
def _repr_(self):
- return "Free monoid on %s generators %s" % (self.__ngens, self.gens())
+ return f"Free monoid on {self.__ngens} generators {self.gens()}"
def _element_constructor_(self, x, check=True):
"""
diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py
index 341a88a9ebe..d2cf92c4ba8 100644
--- a/src/sage/monoids/free_monoid_element.py
+++ b/src/sage/monoids/free_monoid_element.py
@@ -9,7 +9,7 @@
pairs of integers.
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2005 David Kohel
#
# Distributed under the terms of the GNU General Public License (GPL)
@@ -22,7 +22,7 @@
# is available at:
#
# https://www.gnu.org/licenses/
-#*****************************************************************************
+# ****************************************************************************
from sage.rings.integer import Integer
from sage.structure.element import MonoidElement
@@ -123,7 +123,7 @@ def _repr_(self):
if e == 1:
s += "%s" % g
else:
- s += "%s^%s" % (g,e)
+ s += f"{g}^{e}"
if len(s) == 0:
s = "1"
return s
@@ -159,9 +159,9 @@ def _latex_(self):
g = x[int(v[i][0])]
e = v[i][1]
if e == 1:
- s += "%s " % (g,)
+ s += f"{g} "
else:
- s += "%s^{%s}" % (g, e)
+ s += f"{g}^{{{e}}}"
s = s.rstrip(" ") # strip the trailing whitespace caused by adding a space after each element name
if len(s) == 0:
s = "1"
@@ -212,7 +212,7 @@ def __call__(self, *x, **kwds):
raise ValueError("must specify as many values as generators in parent")
# I don't start with 0, because I don't want to preclude evaluation with
- #arbitrary objects (e.g. matrices) because of funny coercion.
+ # arbitrary objects (e.g. matrices) because of funny coercion.
one = P.one()
result = None
for var_index, exponent in self._element_list:
@@ -262,7 +262,7 @@ def _mul_(self, y):
z._element_list = x_elt + y_elt
else:
m = (y_elt[0][0], x_elt[k][1]+y_elt[0][1])
- z._element_list = x_elt[:k] + [ m ] + y_elt[1:]
+ z._element_list = x_elt[:k] + [m] + y_elt[1:]
return z
def __invert__(self):
@@ -334,8 +334,7 @@ def _richcmp_(self, other, op):
def _acted_upon_(self, x, self_on_left):
"""
- Currently, returns the action of the integer 1 on this
- element.
+ Return the action of the integer 1 on this element.
EXAMPLES::
@@ -373,9 +372,9 @@ def to_word(self, alph=None):
gens = self.parent().gens()
if alph is None:
alph = gens
- alph = [str(_) for _ in alph]
- W = Words(alph)
- return W(sum([ [alph[gens.index(i[0])]] * i[1] for i in list(self) ], []))
+ alph = [str(c) for c in alph]
+ W = Words(alph, infinite=False)
+ return W(sum([[alph[gens.index(i[0])]] * i[1] for i in iter(self)], []))
def to_list(self, indices=False):
r"""
diff --git a/src/sage/monoids/hecke_monoid.py b/src/sage/monoids/hecke_monoid.py
index 98f41dc4552..179446d24e6 100644
--- a/src/sage/monoids/hecke_monoid.py
+++ b/src/sage/monoids/hecke_monoid.py
@@ -1,16 +1,16 @@
-# -*- coding: utf-8 -*-
"""
Hecke Monoids
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2015 Nicolas M. Thiéry
#
# Distributed under the terms of the GNU General Public License (GPL)
-# http://www.gnu.org/licenses/
-#*****************************************************************************
+# https://www.gnu.org/licenses/
+# ****************************************************************************
from sage.misc.cachefunc import cached_function
from sage.sets.finite_set_maps import FiniteSetMaps
+
@cached_function
def HeckeMonoid(W):
r"""
diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py
index 58910533a9a..5467ab1eed7 100644
--- a/src/sage/monoids/indexed_free_monoid.py
+++ b/src/sage/monoids/indexed_free_monoid.py
@@ -111,7 +111,7 @@ def _repr_(self):
scalar_mult = P._print_options['scalar_mult']
- exp = lambda v: '^{}'.format(v) if v != 1 else ''
+ exp = lambda v: f'^{v}' if v != 1 else ''
return scalar_mult.join(P._repr_generator(g) + exp(v) for g,v in monomial)
def _ascii_art_(self):
@@ -180,7 +180,7 @@ def _latex_(self):
if scalar_mult == "*":
scalar_mult = " "
- exp = lambda v: '^{{{}}}'.format(v) if v != 1 else ''
+ exp = lambda v: f'^{{{v}}}' if v != 1 else ''
return scalar_mult.join(P._latex_generator(g) + exp(v) for g,v in monomial)
def __iter__(self):
@@ -287,7 +287,7 @@ def support(self):
sage: (a*c^3).support()
[0, 2]
"""
- supp = set(key for key, exp in self._sorted_items() if exp != 0)
+ supp = {key for key, exp in self._sorted_items() if exp != 0}
return sorted(supp)
def leading_support(self):
@@ -544,9 +544,9 @@ def __pow__(self, n):
1
"""
if not isinstance(n, (int, Integer)):
- raise TypeError("Argument n (= {}) must be an integer".format(n))
+ raise TypeError(f"Argument n (= {n}) must be an integer")
if n < 0:
- raise ValueError("Argument n (= {}) must be positive".format(n))
+ raise ValueError(f"Argument n (= {n}) must be positive")
if n == 1:
return self
if n == 0:
@@ -733,7 +733,7 @@ def _element_constructor_(self, x=None):
if x is None:
return self.one()
if x in self._indices:
- raise TypeError("unable to convert {!r}, use gen() instead".format(x))
+ raise TypeError(f"unable to convert {x!r}, use gen() instead")
return self.element_class(self, x)
def _an_element_(self):
@@ -844,7 +844,7 @@ def _repr_(self):
sage: FreeMonoid(index_set=ZZ)
Free monoid indexed by Integer Ring
"""
- return "Free monoid indexed by {}".format(self._indices)
+ return f"Free monoid indexed by {self._indices}"
Element = IndexedFreeMonoidElement
@@ -884,7 +884,7 @@ def gen(self, x):
IndexError: 0 is not in the index set
"""
if x not in self._indices:
- raise IndexError("{} is not in the index set".format(x))
+ raise IndexError(f"{x} is not in the index set")
try:
return self.element_class(self, ((self._indices(x),1),))
except (TypeError, NotImplementedError): # Backup (e.g., if it is a string)
@@ -924,7 +924,7 @@ def _repr_(self):
sage: FreeAbelianMonoid(index_set=ZZ)
Free abelian monoid indexed by Integer Ring
"""
- return "Free abelian monoid indexed by {}".format(self._indices)
+ return f"Free abelian monoid indexed by {self._indices}"
def _element_constructor_(self, x=None):
"""
@@ -951,7 +951,7 @@ def _element_constructor_(self, x=None):
1
"""
if isinstance(x, (list, tuple)):
- d = dict()
+ d = {}
for k, v in x:
if k in d:
d[k] += v
@@ -1000,7 +1000,7 @@ def gen(self, x):
IndexError: 0 is not in the index set
"""
if x not in self._indices:
- raise IndexError("{} is not in the index set".format(x))
+ raise IndexError(f"{x} is not in the index set")
try:
return self.element_class(self, {self._indices(x): 1})
except (TypeError, NotImplementedError): # Backup (e.g., if it is a string)
diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py
index 7af83b5fe4b..97c0dfa6ad3 100644
--- a/src/sage/monoids/monoid.py
+++ b/src/sage/monoids/monoid.py
@@ -5,9 +5,10 @@
from sage.structure.parent import Parent
from sage.misc.cachefunc import cached_method
-def is_Monoid(x):
+
+def is_Monoid(x) -> bool:
r"""
- Returns True if ``x`` is of type ``Monoid_class``.
+ Return ``True`` if ``x`` is of type ``Monoid_class``.
EXAMPLES::
@@ -27,6 +28,7 @@ def is_Monoid(x):
"""
return isinstance(x, Monoid_class)
+
class Monoid_class(Parent):
def __init__(self, names):
r"""
diff --git a/src/sage/monoids/string_monoid.py b/src/sage/monoids/string_monoid.py
index 55045fd7f14..7aa51ad9776 100644
--- a/src/sage/monoids/string_monoid.py
+++ b/src/sage/monoids/string_monoid.py
@@ -8,21 +8,22 @@
Sage supports a wide range of specific free string monoids.
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2007 David Kohel
#
# 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 .free_monoid import FreeMonoid
from .string_monoid_element import StringMonoidElement
from .string_ops import strip_encoding
+
class StringMonoid_class(FreeMonoid):
r"""
A free string monoid on `n` generators.
@@ -108,12 +109,13 @@ def gen(self, i=0):
n = self.ngens()
if i < 0 or not i < n:
raise IndexError(
- "Argument i (= %s) must be between 0 and %s." % (i, n-1))
+ f"Argument i (= {i}) must be between 0 and {n-1}.")
return StringMonoidElement(self, [int(i)])
-#*****************************************************************************
+
+# ****************************************************************************
# Specific global string monoids
-#*****************************************************************************
+# ****************************************************************************
class BinaryStringMonoid(StringMonoid_class):
r"""
@@ -182,7 +184,7 @@ def __call__(self, x, check=True):
sage: S.gen(1)
1
"""
- ## There should really some careful type checking here...
+ # There should really some careful type checking here...
if isinstance(x, StringMonoidElement) and x.parent() == self:
return x
elif isinstance(x, list):
@@ -276,7 +278,7 @@ def __init__(self):
sage: x[0] * x[3]^3 * x[5]^4 * x[6]
033355556
"""
- StringMonoid_class.__init__(self, 8, [ str(i) for i in range(8) ])
+ StringMonoid_class.__init__(self, 8, [str(i) for i in range(8)])
def __repr__(self):
return "Free octal string monoid"
@@ -300,7 +302,7 @@ def __call__(self, x, check=True):
sage: S([ i for i in range(8) ])
01234567
"""
- ## There should really some careful type checking here...
+ # There should really some careful type checking here...
if isinstance(x, StringMonoidElement) and x.parent() == self:
return x
elif isinstance(x, list):
@@ -344,7 +346,7 @@ def __init__(self):
0aaaf
"""
alph = '0123456789abcdef'
- StringMonoid_class.__init__(self, 16, [ alph[i] for i in range(16) ])
+ StringMonoid_class.__init__(self, 16, [alph[i] for i in range(16)])
def __repr__(self):
return "Free hexadecimal string monoid"
@@ -368,7 +370,7 @@ def __call__(self, x, check=True):
sage: S([ i for i in range(16) ])
0123456789abcdef
"""
- ## There should really some careful type checking here...
+ # There should really some careful type checking here...
if isinstance(x, StringMonoidElement) and x.parent() == self:
return x
elif isinstance(x, list):
@@ -453,7 +455,7 @@ def __init__(self):
/
"""
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
- StringMonoid_class.__init__(self, 64, [ alph[i] for i in range(64) ])
+ StringMonoid_class.__init__(self, 64, [alph[i] for i in range(64)])
def __repr__(self):
return "Free radix 64 string monoid"
@@ -480,7 +482,7 @@ def __call__(self, x, check=True):
sage: S([ i for i in range(64) ])
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
"""
- ## There should really some careful type checking here...
+ # There should really some careful type checking here...
if isinstance(x, StringMonoidElement) and x.parent() == self:
return x
elif isinstance(x, list):
@@ -560,7 +562,7 @@ def __init__(self):
"W": RR(0.023), "X": RR(0.001),
"Y": RR(0.020), "Z": RR(0.001)}
alph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- StringMonoid_class.__init__(self, 26, [ alph[i] for i in range(26) ])
+ StringMonoid_class.__init__(self, 26, [alph[i] for i in range(26)])
def __repr__(self):
return "Free alphabetic string monoid on A-Z"
@@ -584,7 +586,7 @@ def __call__(self, x, check=True):
sage: S([ i for i in range(26) ])
ABCDEFGHIJKLMNOPQRSTUVWXYZ
"""
- ## There should really some careful type checking here...
+ # There should really some careful type checking here...
if isinstance(x, StringMonoidElement) and x.parent() == self:
return x
elif isinstance(x, list):
diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py
index 185aa901a07..0299359c7f7 100644
--- a/src/sage/monoids/string_monoid_element.py
+++ b/src/sage/monoids/string_monoid_element.py
@@ -12,13 +12,13 @@
The internal representation of elements does not use the exponential
compression of FreeMonoid elements (a feature), and could be packed into words.
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2007 David Kohel
#
# Distributed under the terms of the GNU General Public License (GPL)
#
-# http://www.gnu.org/licenses/
-#*****************************************************************************
+# https://www.gnu.org/licenses/
+# ****************************************************************************
# import operator
from sage.rings.integer import Integer
@@ -32,19 +32,21 @@ def is_StringMonoidElement(x):
"""
return isinstance(x, StringMonoidElement)
+
def is_AlphabeticStringMonoidElement(x):
r"""
"""
from .string_monoid import AlphabeticStringMonoid
return isinstance(x, StringMonoidElement) and \
- isinstance(x.parent(), AlphabeticStringMonoid)
+ isinstance(x.parent(), AlphabeticStringMonoid)
+
def is_BinaryStringMonoidElement(x):
r"""
"""
from .string_monoid import BinaryStringMonoid
return isinstance(x, StringMonoidElement) and \
- isinstance(x.parent(), BinaryStringMonoid)
+ isinstance(x.parent(), BinaryStringMonoid)
def is_OctalStringMonoidElement(x):
@@ -52,7 +54,7 @@ def is_OctalStringMonoidElement(x):
"""
from .string_monoid import OctalStringMonoid
return isinstance(x, StringMonoidElement) and \
- isinstance(x.parent(), OctalStringMonoid)
+ isinstance(x.parent(), OctalStringMonoid)
def is_HexadecimalStringMonoidElement(x):
@@ -60,7 +62,7 @@ def is_HexadecimalStringMonoidElement(x):
"""
from .string_monoid import HexadecimalStringMonoid
return isinstance(x, StringMonoidElement) and \
- isinstance(x.parent(), HexadecimalStringMonoid)
+ isinstance(x.parent(), HexadecimalStringMonoid)
def is_Radix64StringMonoidElement(x):
@@ -68,7 +70,7 @@ def is_Radix64StringMonoidElement(x):
"""
from .string_monoid import Radix64StringMonoid
return isinstance(x, StringMonoidElement) and \
- isinstance(x.parent(), Radix64StringMonoid)
+ isinstance(x.parent(), Radix64StringMonoid)
class StringMonoidElement(FreeMonoidElement):
@@ -89,7 +91,7 @@ def __init__(self, S, x, check=True):
if not isinstance(b, (int, Integer)):
raise TypeError(
"x (= %s) must be a list of integers." % x)
- self._element_list = list(x) # make copy
+ self._element_list = list(x) # make copy
elif isinstance(x, str):
alphabet = list(self.parent().alphabet())
self._element_list = []
@@ -312,15 +314,15 @@ def decoding(self, padic=False):
if isinstance(S, BinaryStringMonoid):
if not n % 8 == 0:
"String %s must have even length 0 mod 8 to determine a byte character string." % str(self)
- pows = [ 2**i for i in range(8) ]
+ pows = [2**i for i in range(8)]
s = []
x = self._element_list
for k in range(n//8):
m = 8*k
if padic:
- c = chr(sum([ x[m+i]*pows[i] for i in range(8) ]))
+ c = chr(sum([x[m+i] * pows[i] for i in range(8)]))
else:
- c = chr(sum([ x[m+7-i]*pows[i] for i in range(8) ]))
+ c = chr(sum([x[m+7-i] * pows[i] for i in range(8)]))
s.append(c)
return ''.join(s)
raise TypeError(
@@ -328,7 +330,7 @@ def decoding(self, padic=False):
def coincidence_index(self, prec=0):
"""
- Returns the probability of two randomly chosen characters being equal.
+ Return the probability of two randomly chosen characters being equal.
"""
if prec == 0:
RR = RealField()
@@ -424,10 +426,12 @@ def character_count(self):
def frequency_distribution(self, length=1, prec=0):
"""
- Returns the probability space of character frequencies. The output
- of this method is different from that of the method
+ Return the probability space of character frequencies.
+
+ The output of this method is different from that of the method
:func:`characteristic_frequency()
`.
+
One can think of the characteristic frequency probability of an
element in an alphabet `A` as the expected probability of that element
occurring. Let `S` be a string encoded using elements of `A`. The
diff --git a/src/sage/monoids/string_ops.py b/src/sage/monoids/string_ops.py
index 8f822f98cc8..bdfabe79cdd 100644
--- a/src/sage/monoids/string_ops.py
+++ b/src/sage/monoids/string_ops.py
@@ -1,16 +1,17 @@
"Utility functions on strings"
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2007 David Kohel
#
# Distributed under the terms of the GNU General Public License (GPL)
#
-# http://www.gnu.org/licenses/
-#*****************************************************************************
+# https://www.gnu.org/licenses/
+# ****************************************************************************
from sage.rings.real_mpfr import RealField
from .string_monoid_element import StringMonoidElement
+
def strip_encoding(S):
"""
The upper case string of S stripped of all non-alphabetic characters.
@@ -21,7 +22,7 @@ def strip_encoding(S):
sage: strip_encoding(S)
'THECATINTHEHAT'
"""
- if not isinstance(S,str):
+ if not isinstance(S, str):
raise TypeError("Argument S (= %s) must be a string.")
X = ''
for i in range(len(S)):
@@ -30,17 +31,18 @@ def strip_encoding(S):
X += S[i].upper()
return X
+
def frequency_distribution(S, n=1, field=None):
"""
The probability space of frequencies of n-character substrings of S.
"""
- if isinstance(S,tuple):
+ if isinstance(S, tuple):
S = list(S)
- elif isinstance(S,(str,StringMonoidElement)):
- S = [ S[i:i+n] for i in range(len(S)-n+1) ]
+ elif isinstance(S, (str, StringMonoidElement)):
+ S = [S[i:i+n] for i in range(len(S)-n+1)]
if field is None:
field = RealField()
- if isinstance(S,list):
+ if isinstance(S, list):
P = {}
N = len(S)
eps = field(1)/N
@@ -51,10 +53,11 @@ def frequency_distribution(S, n=1, field=None):
else:
P[c] = eps
from sage.probability.random_variable import DiscreteProbabilitySpace
- return DiscreteProbabilitySpace(S,P,field)
+ return DiscreteProbabilitySpace(S, P, field)
raise TypeError("Argument S (= %s) must be a string, list, or tuple.")
-def coincidence_index(S,n=1):
+
+def coincidence_index(S, n=1):
"""
The coincidence index of the string S.
@@ -64,7 +67,7 @@ def coincidence_index(S,n=1):
sage: coincidence_index(S)
0.120879120879121
"""
- if not isinstance(S,str):
+ if not isinstance(S, str):
try:
S.coincidence_index(n)
except AttributeError:
@@ -79,9 +82,10 @@ def coincidence_index(S,n=1):
else:
X[c] = 1
RR = RealField()
- return RR(sum([ m*(m-1) for m in X.values() ]))/RR(N*(N-1))
+ return RR(sum([m*(m-1) for m in X.values()]))/RR(N*(N-1))
+
-def coincidence_discriminant(S,n=2):
+def coincidence_discriminant(S, n=2):
"""
Input: A tuple of strings, e.g. produced as decimation of transposition
ciphertext, or a sample plaintext.
@@ -94,24 +98,21 @@ def coincidence_discriminant(S,n=2):
sage: coincidence_discriminant([ S[i:i+2] for i in range(len(S)-1) ])
0.0827001855677322
"""
- if not isinstance(S,(list,tuple)):
+ if not isinstance(S, (list, tuple)):
raise TypeError("Argument S (= %s) must be a list or tuple" % S)
if n != 2:
raise ValueError("Argument n (= %s) is only implemented for n = 2" % n)
- truth = True
- for bool in ( isinstance(c,(str,StringMonoidElement)) for c in S ):
- truth = truth and bool
+ truth = all(isinstance(c, (str, StringMonoidElement)) for c in S)
if not truth:
raise TypeError("Argument S (= %s) must be a list of strings.")
- for bool in ( len(c) == n for c in S ):
- truth = truth and bool
+ truth = all(len(c) == n for c in S)
if not truth:
raise ValueError("Argument S (= %s) must be a list of strings of length 2" % S)
- X1 = [ frequency_distribution([ s[i] for s in S]) for i in range(2) ]
+ X1 = [frequency_distribution([s[i] for s in S]) for i in range(2)]
XX = frequency_distribution(S)
- if isinstance(S[0],StringMonoidElement):
+ if isinstance(S[0], StringMonoidElement):
M = S[0].parent()
n = M.ngens()
- return sum([ (XX(M([i,j]))-X1[0](M([i]))*X1[1](M([j])))**2 for i in range(n) for j in range(n) ])
+ return sum([(XX(M([i, j]))-X1[0](M([i]))*X1[1](M([j])))**2 for i in range(n) for j in range(n)])
AZ = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
- return sum([ (XX(AZ[i]+AZ[j])-X1[0](AZ[i])*X1[1](AZ[j]))**2 for i in range(26) for j in range(26) ])
+ return sum([(XX(AZ[i]+AZ[j])-X1[0](AZ[i])*X1[1](AZ[j]))**2 for i in range(26) for j in range(26)])
diff --git a/src/sage/monoids/trace_monoid.py b/src/sage/monoids/trace_monoid.py
index f8176a1e1f2..d13b405421b 100644
--- a/src/sage/monoids/trace_monoid.py
+++ b/src/sage/monoids/trace_monoid.py
@@ -106,7 +106,7 @@ def _repr_(self):
"""
if self == self.parent().one():
return "1"
- return "[{}]".format(self.value)
+ return f"[{self.value}]"
def _richcmp_(self, other, op):
r"""
@@ -285,7 +285,7 @@ def hasse_diagram(self, algorithm="naive"):
elif algorithm == "min":
return self.min_hasse_diagram()
raise ValueError("`alg` option must be `naive` "
- "or `min`, got `{}`.".format(algorithm))
+ f"or `min`, got `{algorithm}`.")
def min_hasse_diagram(self):
r"""
@@ -312,7 +312,7 @@ def min_hasse_diagram(self):
elements = self._flat_elements()
elements.reverse()
independence = self.parent()._independence
- reachable = dict()
+ reachable = {}
min = set()
graph = DiGraph({})
@@ -331,7 +331,7 @@ def min_hasse_diagram(self):
min.remove(j)
used.add(j)
forbidden = set(chain.from_iterable(reachable[v] for v in used))
- front = set(dest for _, dest in graph.outgoing_edges(front, labels=False))
+ front = {dest for _, dest in graph.outgoing_edges(front, labels=False)}
front = front - forbidden
min.add(i)
@@ -638,7 +638,7 @@ def _compute_dependence_stack(self, x):
d: [False, False, True, False]})
"""
independence = self._independence
- generators_set = set(e for e, _ in x)
+ generators_set = {e for e, _ in x}
stacks = dict(sorted((g, []) for g in generators_set))
for generator, times in reversed(list(x)):
stacks[generator].extend(repeat(True, times))
@@ -723,7 +723,7 @@ def _compute_foata_normal_form(self, x):
(b, a*d, a, b, a, b*c, c, a)
"""
if not x._element_list:
- return tuple()
+ return ()
generators_set, stacks = self._compute_dependence_stack(x)
independence = self._independence
@@ -818,8 +818,8 @@ def dependence_graph(self):
sage: M.dependence_graph() == Graph({a:[a,b], b:[b], c:[c,b]})
True
"""
- return Graph(set(frozenset((e1, e2)) if e1 != e2 else (e1, e2)
- for e1, e2 in self.dependence()), loops=True,
+ return Graph({frozenset((e1, e2)) if e1 != e2 else (e1, e2)
+ for e1, e2 in self.dependence()}, loops=True,
format="list_of_edges",
immutable=True)
@@ -979,7 +979,7 @@ def _repr_(self):
"""
return ("Trace monoid on {!s} generators {!s} "
"with independence relation {{{}}}").format(self.ngens(), self.gens(),
- ", ".join("{{{}, {}}}".format(x, y)
+ ", ".join(f"{{{x}, {y}}}"
for (x, y) in self._sorted_independence()))
def _latex_(self):
@@ -996,7 +996,7 @@ def _latex_(self):
return "\\langle {} \\mid {} \\rangle".format(
repr(self._free_monoid.gens())[1:-1],
",".join(
- "{0!r}{1!r}={1!r}{0!r}".format(v1, v2)
+ f"{v1!r}{v2!r}={v2!r}{v1!r}"
for v1, v2 in self._sorted_independence()
)
)
From 4a38b34aa3de396628669f3cb2bf6a3244378a4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Fri, 23 Feb 2024 15:09:33 +0100
Subject: [PATCH 047/518] a few details in cython files in symbolic
---
src/sage/symbolic/constants.py | 2 +-
src/sage/symbolic/constants_c_impl.pxi | 6 +++---
src/sage/symbolic/expression.pyx | 10 +++++-----
src/sage/symbolic/function.pyx | 14 ++++++-------
src/sage/symbolic/getitem_impl.pxi | 13 ++++++------
src/sage/symbolic/ginac/function_info.cpp | 2 +-
src/sage/symbolic/ginac/sum.cpp | 2 +-
src/sage/symbolic/pynac.pxi | 22 ++++++++++-----------
src/sage/symbolic/pynac_constant_impl.pxi | 6 +++---
src/sage/symbolic/pynac_impl.pxi | 20 +++++++++----------
src/sage/symbolic/substitution_map_impl.pxi | 15 +++++++-------
11 files changed, 55 insertions(+), 57 deletions(-)
diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py
index c87bef3471b..351cc5cecc1 100644
--- a/src/sage/symbolic/constants.py
+++ b/src/sage/symbolic/constants.py
@@ -212,7 +212,7 @@
# 2009 Mike Hansen
# Distributed under the terms of the GNU General Public License (GPL),
# version 2 or any later version. The full text of the GPL is available at:
-# http://www.gnu.org/licenses/
+# https://www.gnu.org/licenses/
###############################################################################
import math
diff --git a/src/sage/symbolic/constants_c_impl.pxi b/src/sage/symbolic/constants_c_impl.pxi
index 3818926a9a2..520c5e91a53 100644
--- a/src/sage/symbolic/constants_c_impl.pxi
+++ b/src/sage/symbolic/constants_c_impl.pxi
@@ -2,7 +2,7 @@
The constant `e`
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2008 William Stein
# Copyright (C) 2008-2009 Burcin Erocal
# Copyright (C) 2009-2012 Mike Hansen
@@ -13,8 +13,8 @@ The constant `e`
# 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/
+# ****************************************************************************
# keep exp(1) for fast access
# this is initialized in the constructor of the class E below to prevent
diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx
index ab19b46c181..c2a47f3773f 100644
--- a/src/sage/symbolic/expression.pyx
+++ b/src/sage/symbolic/expression.pyx
@@ -12377,11 +12377,11 @@ cdef class Expression(Expression_abc):
"""
from sage.symbolic.relation import solve
return solve(self, x, multiplicities=multiplicities,
- solution_dict=solution_dict,
- explicit_solutions=explicit_solutions,
- to_poly_solve=to_poly_solve,
- algorithm=algorithm,
- domain=domain)
+ solution_dict=solution_dict,
+ explicit_solutions=explicit_solutions,
+ to_poly_solve=to_poly_solve,
+ algorithm=algorithm,
+ domain=domain)
def solve_diophantine(self, x=None, solution_dict=False):
"""
diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx
index e349fe20100..3dc59574fba 100644
--- a/src/sage/symbolic/function.pyx
+++ b/src/sage/symbolic/function.pyx
@@ -652,8 +652,8 @@ cdef class Function(SageObject):
if isinstance(x, (float, complex)):
return True
if isinstance(x, Element):
- parent = (x)._parent
- return hasattr(parent, 'precision') and parent._is_numerical()
+ xparent = (x)._parent
+ return hasattr(xparent, 'precision') and xparent._is_numerical()
return False
def _interface_init_(self, I=None):
@@ -1058,8 +1058,8 @@ cdef class BuiltinFunction(Function):
return res
from sage.rings.polynomial.polynomial_ring import PolynomialRing_commutative
from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain
- if (isinstance(arg_parent, PolynomialRing_commutative)
- or isinstance(arg_parent, MPolynomialRing_polydict_domain)):
+ if isinstance(arg_parent, (PolynomialRing_commutative,
+ MPolynomialRing_polydict_domain)):
try:
return SR(res).polynomial(ring=arg_parent)
except TypeError:
@@ -1223,8 +1223,8 @@ cdef class SymbolicFunction(Function):
slist = [self._nargs, self._name, str(self._latex_name),
self._evalf_params_first]
for fname in sfunctions_funcs:
- real_fname = '_%s_'%fname
- if hasattr(self, '%s'%real_fname):
+ real_fname = '_%s_' % fname
+ if hasattr(self, '%s' % real_fname):
slist.append(hash(getattr(self, real_fname).__code__))
else:
slist.append(' ')
@@ -1398,7 +1398,7 @@ cdef class SymbolicFunction(Function):
for pickle, fname in zip(function_pickles, sfunctions_funcs):
if pickle:
- real_fname = '_%s_'%fname
+ real_fname = '_%s_' % fname
setattr(self, real_fname, unpickle_function(pickle))
SymbolicFunction.__init__(self, name, nargs, latex_name,
diff --git a/src/sage/symbolic/getitem_impl.pxi b/src/sage/symbolic/getitem_impl.pxi
index 7bbb2dfe852..c20cf1e6e31 100644
--- a/src/sage/symbolic/getitem_impl.pxi
+++ b/src/sage/symbolic/getitem_impl.pxi
@@ -1,14 +1,14 @@
"Operands"
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2011 Burcin Erocal
#
# 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.sage_object cimport SageObject
@@ -46,9 +46,10 @@ cdef inline int normalize_index(object arg, int nops, object err_msg) except -1:
if i < 0:
i = nops + i
if i >= nops or i < 0:
- raise IndexError("operand index out of range, got %s, expect between %s and %s"%(arg,-nops,nops-1))
+ raise IndexError("operand index out of range, got %s, expect between %s and %s" % (arg, -nops, nops-1))
return i
+
def normalize_index_for_doctests(arg, nops):
"""
Wrapper function to test ``normalize_index``.
@@ -165,7 +166,7 @@ cdef class OperandsWrapper(SageObject):
sage: (x^2).op
Operands of x^2
"""
- return "Operands of %s"%(self._expr)
+ return "Operands of %s" % (self._expr)
def _latex_(self):
r"""
@@ -174,7 +175,7 @@ cdef class OperandsWrapper(SageObject):
sage: latex((x^2).op)
\text{Operands wrapper for expression }x^{2}
"""
- return r"\text{Operands wrapper for expression }%s"%(self._expr._latex_())
+ return r"\text{Operands wrapper for expression }%s" % (self._expr._latex_())
def __reduce__(self):
"""
diff --git a/src/sage/symbolic/ginac/function_info.cpp b/src/sage/symbolic/ginac/function_info.cpp
index e12941fc644..4da26c06e53 100644
--- a/src/sage/symbolic/ginac/function_info.cpp
+++ b/src/sage/symbolic/ginac/function_info.cpp
@@ -1,7 +1,7 @@
// Logic for info flags of function expressions
//
// (c) 2016,2017 Ralf Stephan
-// Distributed under GPL2, see http://www.gnu.org
+// Distributed under GPL2, see https://www.gnu.org
#include
diff --git a/src/sage/symbolic/ginac/sum.cpp b/src/sage/symbolic/ginac/sum.cpp
index ede1b216f1e..d8ca1056f18 100644
--- a/src/sage/symbolic/ginac/sum.cpp
+++ b/src/sage/symbolic/ginac/sum.cpp
@@ -1,7 +1,7 @@
// Algorithms for indefinite and definite summation
//
// (c) 2016 Ralf Stephan
-// Distributed under GPL2, see http://www.gnu.org
+// Distributed under GPL2, see https://www.gnu.org
//
// Ref.: 1. W. Koepf, Algorithms for m-fold Hypergeometric Summation,
// Journal of Symbolic Computation (1995) 20, 399-417
diff --git a/src/sage/symbolic/pynac.pxi b/src/sage/symbolic/pynac.pxi
index 09f0ad032d5..fe021f891da 100644
--- a/src/sage/symbolic/pynac.pxi
+++ b/src/sage/symbolic/pynac.pxi
@@ -9,7 +9,7 @@ Check that we can externally cimport this (:trac:`18825`)::
....: ''')
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2008 William Stein
# Copyright (C) 2008 Burcin Erocal
# Copyright (C) 2017 Jeroen Demeyer
@@ -18,8 +18,8 @@ Check that we can externally cimport this (:trac:`18825`)::
# 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 cpython.object cimport PyObject
from libcpp.vector cimport vector
@@ -323,7 +323,6 @@ cdef extern from "pynac_wrap.h":
GEx unarchive_ex(GExList sym_lst, unsigned ind) except +
void printraw "printraw(std::cout); " (int t)
-
GEx g_abs "GiNaC::abs" (GEx x) except + # absolute value
GEx g_step "GiNaC::unit_step" (GEx x) except + # step function
GEx g_csgn "GiNaC::csgn" (GEx x) except + # complex sign
@@ -415,10 +414,9 @@ cdef extern from "pynac_wrap.h":
void g_foptions_assign "ASSIGN_WRAP" (GFunctionOpt, GFunctionOpt)
- GFunctionOpt g_function_options "GiNaC::function_options" \
- (char *m)
+ GFunctionOpt g_function_options "GiNaC::function_options" (char *m)
GFunctionOpt g_function_options_args "GiNaC::function_options" \
- (char *m, unsigned nargs)
+ (char *m, unsigned nargs)
unsigned g_register_new "GiNaC::function::register_new" (GFunctionOpt opt)
unsigned find_function "GiNaC::function::find_function" (char* name,
@@ -428,7 +426,7 @@ cdef extern from "pynac_wrap.h":
bint has_symbol_or_function "GiNaC::has_symbol_or_function" (GEx ex)
GFunctionOptVector g_registered_functions \
- "GiNaC::function::registered_functions" ()
+ "GiNaC::function::registered_functions" ()
# these serials allow us to map pynac function objects to
# Sage special functions for the .operator() method of expressions
@@ -456,7 +454,7 @@ cdef extern from "pynac_wrap.h":
unsigned Li2_serial "GiNaC::Li2_SERIAL::serial" # dilogarithm
unsigned Li_serial "GiNaC::Li_SERIAL::serial" # classical polylogarithm as well as multiple polylogarithm
unsigned G_serial "GiNaC::G_SERIAL::serial" # multiple polylogarithm
- #unsigned G2_serial "GiNaC::G_SERIAL::serial" # multiple polylogarithm with explicit signs for the imaginary parts
+ # unsigned G2_serial "GiNaC::G_SERIAL::serial" # multiple polylogarithm with explicit signs for the imaginary parts
unsigned S_serial "GiNaC::S_SERIAL::serial" # Nielsen's generalized polylogarithm
unsigned H_serial "GiNaC::H_SERIAL::serial" # harmonic polylogarithm
unsigned zeta1_serial "GiNaC::zeta1_SERIAL::serial" # Riemann's zeta function as well as multiple zeta value
@@ -467,7 +465,7 @@ cdef extern from "pynac_wrap.h":
unsigned lgamma_serial "GiNaC::lgamma_SERIAL::serial" # logarithm of gamma function
unsigned beta_serial "GiNaC::beta_SERIAL::serial" # beta function (tgamma(x)*tgamma(y)/tgamma(x+y))
unsigned psi_serial "GiNaC::psi_SERIAL::serial" # psi (digamma) function
- #unsigned psi2_serial "GiNaC::psi_SERIAL::serial" # derivatives of psi function (polygamma functions)
+ # unsigned psi2_serial "GiNaC::psi_SERIAL::serial" # derivatives of psi function (polygamma functions)
unsigned factorial_serial "GiNaC::factorial_SERIAL::serial" # factorial function n!
unsigned binomial_serial "GiNaC::binomial_SERIAL::serial" # binomial coefficients
unsigned Order_serial "GiNaC::Order_SERIAL::serial" # order term function in truncated power series
@@ -567,6 +565,6 @@ cdef extern from "pynac_wrap.h":
cdef extern from "ginac/order.h":
bint print_order_compare "GiNaC::print_order().compare" \
- (GEx left, GEx right) except +
+ (GEx left, GEx right) except +
bint print_order_compare_mul "GiNaC::print_order_mul().compare" \
- (GEx left, GEx right) except +
+ (GEx left, GEx right) except +
diff --git a/src/sage/symbolic/pynac_constant_impl.pxi b/src/sage/symbolic/pynac_constant_impl.pxi
index 9dd5366dcaa..442afbafbc3 100644
--- a/src/sage/symbolic/pynac_constant_impl.pxi
+++ b/src/sage/symbolic/pynac_constant_impl.pxi
@@ -2,7 +2,7 @@
Wrapper around Pynac's constants
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2008 William Stein
# Copyright (C) 2008 Burcin Erocal
# Copyright (C) 2009 Mike Hansen
@@ -11,8 +11,8 @@ Wrapper around Pynac's constants
# 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.cpython.string cimport str_to_bytes
diff --git a/src/sage/symbolic/pynac_impl.pxi b/src/sage/symbolic/pynac_impl.pxi
index b2091522c49..29f62d102fe 100644
--- a/src/sage/symbolic/pynac_impl.pxi
+++ b/src/sage/symbolic/pynac_impl.pxi
@@ -707,20 +707,20 @@ cdef stdstring* py_latex_fderivative(unsigned id, params,
nv += 1
else:
if nv == 1:
- diff_args.append(r"\partial %s"%(args[v]._latex_(),))
+ diff_args.append(r"\partial %s" % (args[v]._latex_(),))
else:
- diff_args.append(r"(\partial %s)^{%s}"%(args[v]._latex_(),nv))
+ diff_args.append(r"(\partial %s)^{%s}" % (args[v]._latex_(),nv))
v=next_v
nv=1
if nv == 1:
- diff_args.append(r"\partial %s"%(args[v]._latex_(),))
+ diff_args.append(r"\partial %s" % (args[v]._latex_(),))
else:
- diff_args.append(r"(\partial %s)^{%s}"%(args[v]._latex_(),nv))
+ diff_args.append(r"(\partial %s)^{%s}" % (args[v]._latex_(),nv))
if len(params) == 1:
- operator_string=r"\frac{\partial}{%s}"%(''.join(diff_args),)
+ operator_string=r"\frac{\partial}{%s}" % (''.join(diff_args),)
else:
- operator_string=r"\frac{\partial^{%s}}{%s}"%(len(params),''.join(diff_args))
- py_res = operator_string+py_latex_function_pystring(id,args,False)
+ operator_string=r"\frac{\partial^{%s}}{%s}" % (len(params),''.join(diff_args))
+ py_res = operator_string+py_latex_function_pystring(id, args, False)
else:
ostr = ''.join([r'\mathrm{D}_{',
', '.join(repr(int(x)) for x in params), '}'])
@@ -886,7 +886,7 @@ cdef py_binomial_int(int n, unsigned int k) noexcept:
cdef bint sign
if n < 0:
n = -n + (k-1)
- sign = k%2
+ sign = k % 2
else:
sign = 0
cdef Integer ans = PY_NEW(Integer)
@@ -903,7 +903,7 @@ cdef py_binomial(n, k) noexcept:
cdef bint sign
if n < 0:
n = k-n-1
- sign = k%2
+ sign = k % 2
else:
sign = 0
# Convert n and k to unsigned ints.
@@ -931,7 +931,7 @@ def test_binomial(n, k):
OUTPUT:
- integer
+ integer
EXAMPLES::
diff --git a/src/sage/symbolic/substitution_map_impl.pxi b/src/sage/symbolic/substitution_map_impl.pxi
index c1dc3be748b..79d8d4491cf 100644
--- a/src/sage/symbolic/substitution_map_impl.pxi
+++ b/src/sage/symbolic/substitution_map_impl.pxi
@@ -7,15 +7,15 @@ Pynac's ``subs()`` methods and pass a wrapper for the substitution map
back to Python.
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2013 Volker Braun
#
# 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.sage_object cimport SageObject
@@ -26,7 +26,7 @@ cdef class SubstitutionMap(SageObject):
cpdef Expression apply_to(self, Expression expr, unsigned options) noexcept:
"""
- Apply the substitution to a symbolic expression
+ Apply the substitution to a symbolic expression.
EXAMPLES::
@@ -40,7 +40,7 @@ cdef class SubstitutionMap(SageObject):
def _repr_(self):
"""
- Return the string representation
+ Return the string representation.
EXAMPLES::
@@ -53,7 +53,7 @@ cdef class SubstitutionMap(SageObject):
cdef SubstitutionMap new_SubstitutionMap_from_GExMap(const GExMap& smap) noexcept:
"""
- Wrap a Pynac object into a Python object
+ Wrap a Pynac object into a Python object.
INPUT:
@@ -77,7 +77,7 @@ cdef SubstitutionMap new_SubstitutionMap_from_GExMap(const GExMap& smap) noexcep
cpdef SubstitutionMap make_map(subs_dict) noexcept:
"""
- Construct a new substitution map
+ Construct a new substitution map.
OUTPUT:
@@ -94,4 +94,3 @@ cpdef SubstitutionMap make_map(subs_dict) noexcept:
smap.insert(make_pair((k)._gobj,
(v)._gobj))
return new_SubstitutionMap_from_GExMap(smap)
-
From dec62ed3ece663ed25c78bd02e36e398781d8283 Mon Sep 17 00:00:00 2001
From: Marc Mezzarobba
Date: Tue, 20 Feb 2024 17:09:24 +0100
Subject: [PATCH 048/518] remove most references to Arb as an independent
library
---
COPYING.txt | 1 -
build/bin/sage-package | 1 -
build/sage_bootstrap/app.py | 10 ++--
build/sage_bootstrap/cmdline.py | 5 +-
src/doc/en/developer/portability_testing.rst | 17 +++---
.../en/reference/rings_numerical/index.rst | 2 +-
src/sage/arith/misc.py | 6 ++-
src/sage/features/sagemath.py | 2 +-
src/sage/matrix/matrix_complex_ball_dense.pxd | 2 +-
src/sage/matrix/matrix_complex_ball_dense.pyx | 26 ++++-----
src/sage/misc/package.py | 2 -
src/sage/numerical/gauss_legendre.pyx | 4 +-
src/sage/rings/complex_arb.pxd | 2 +-
src/sage/rings/complex_arb.pyx | 53 +++++++++----------
.../number_field_element_quadratic.pxd | 2 +-
.../number_field_element_quadratic.pyx | 4 +-
.../polynomial/polynomial_complex_arb.pxd | 2 +-
.../polynomial/polynomial_complex_arb.pyx | 18 +++----
.../polynomial_integer_dense_flint.pyx | 2 +-
.../polynomial/polynomial_rational_flint.pyx | 10 ++--
src/sage/rings/real_arb.pxd | 2 +-
src/sage/rings/real_arb.pyx | 33 ++++++------
src/sage/schemes/elliptic_curves/cm.py | 3 +-
23 files changed, 102 insertions(+), 107 deletions(-)
diff --git a/COPYING.txt b/COPYING.txt
index d5c28eacead..90ef02345b8 100644
--- a/COPYING.txt
+++ b/COPYING.txt
@@ -31,7 +31,6 @@ the licenses of the components of Sage are included below as well.
SOFTWARE LICENSE
-----------------------------------------------------------------------
-arb GPLv2+
boehm_gc MIT-like license (see below)
backports_ssl_match_hostname Python License
boost_cropped Boost Software License (see below)
diff --git a/build/bin/sage-package b/build/bin/sage-package
index 02a07140875..f4e4201f9d7 100755
--- a/build/bin/sage-package
+++ b/build/bin/sage-package
@@ -15,7 +15,6 @@
#
# $ sage-package list
# 4ti2
-# arb
# autotools
# [...]
# zlib
diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py
index 488b9b9edbd..9d88b6d960e 100644
--- a/build/sage_bootstrap/app.py
+++ b/build/sage_bootstrap/app.py
@@ -57,8 +57,8 @@ def list_cls(self, *package_classes, **filters):
$ sage --package list
4ti2
- arb
- autotools
+ _bootstrap
+ _develop
[...]
zlib
@@ -66,9 +66,9 @@ def list_cls(self, *package_classes, **filters):
perl_term_readline_gnu
$ sage -package list --has-file=spkg-configure.m4 --has-file=distros/debian.txt
- arb
- boost_cropped
- brial
+ 4ti2
+ _develop
+ _prereq
[...]
zlib
"""
diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py
index f11c26adb28..ca37455c678 100644
--- a/build/sage_bootstrap/cmdline.py
+++ b/build/sage_bootstrap/cmdline.py
@@ -70,14 +70,11 @@
$ sage --package list
4ti2
- arb
- autotools
[...]
zlib
$ sage --package list :standard:
- arb
- backports_ssl_match_hostname
+ _prereq
[...]
zlib
"""
diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst
index d6460e835d8..b230378c5ee 100644
--- a/src/doc/en/developer/portability_testing.rst
+++ b/src/doc/en/developer/portability_testing.rst
@@ -243,21 +243,22 @@ At the end of the ``./configure`` run, Sage issued a message like the
following::
configure: notice: the following SPKGs did not find equivalent system packages:
- arb boost_cropped bzip2 ... zeromq zlib
+ boost_cropped bzip2 ... zeromq zlib
checking for the package system in use... debian
configure: hint: installing the following system packages is recommended and
may avoid building some of the above SPKGs from source:
- configure: $ sudo apt-get install libflint-arb-dev ... libzmq3-dev libz-dev
+ configure: $ sudo apt-get install ... libzmq3-dev libz-dev
configure: After installation, re-run configure using:
configure: $ make reconfigure
This information comes from Sage's database of equivalent system
packages. For example::
- root@39d693b2a75d:/sage# ls build/pkgs/arb/distros/
- arch.txt conda.txt debian.txt gentoo.txt
- root@39d693b2a75d:/sage# cat build/pkgs/arb/distros/debian.txt
- libflint-arb-dev
+ $ ls build/pkgs/flint/distros/
+ alpine.txt cygwin.txt fedora.txt gentoo.txt macports.txt opensuse.txt void.txt
+ conda.txt debian.txt freebsd.txt homebrew.txt nix.txt repology.txt
+ $ cat build/pkgs/flint/distros/debian.txt
+ libflint-dev
Note that these package equivalencies are based on a current stable or
testing version of the distribution; the packages are not guaranteed
@@ -885,7 +886,7 @@ an isolated copy of Homebrew with all prerequisites for bootstrapping::
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
...
- configure: notice: the following SPKGs did not find equivalent system packages: arb cbc cliquer ... tachyon xz zeromq
+ configure: notice: the following SPKGs did not find equivalent system packages: cbc cliquer ... tachyon xz zeromq
checking for the package system in use... homebrew
configure: hint: installing the following system packages is recommended and may avoid building some of the above SPKGs from source:
configure: $ brew install cmake gcc gsl mpfi ninja openblas gpatch r readline xz zeromq
@@ -1085,7 +1086,7 @@ Scrolling down in the right pane shows "Annotations":
docker (fedora-31, standard)
artifacts/logs-commit-8ca1c2df8f1fb4c6d54b44b34b4d8320ebecb164-tox-docker-fedora-31-standard/config.log#L1
- configure: notice: the following SPKGs did not find equivalent system packages: arb cbc cddlib cmake eclib ecm fflas_ffpack flint fplll givaro gp
+ configure: notice: the following SPKGs did not find equivalent system packages: cbc cddlib cmake eclib ecm fflas_ffpack flint fplll givaro gp
Clicking on the annotations does not take you to a very useful
place. To view details, click on one of the items in the pane. This
diff --git a/src/doc/en/reference/rings_numerical/index.rst b/src/doc/en/reference/rings_numerical/index.rst
index a20066c2ec7..c728ac57d88 100644
--- a/src/doc/en/reference/rings_numerical/index.rst
+++ b/src/doc/en/reference/rings_numerical/index.rst
@@ -28,7 +28,7 @@ Interval Arithmetic
-------------------
Sage implements real and complex interval arithmetic using MPFI
-(RealIntervalField, ComplexIntervalField) and arb (RealBallField,
+(RealIntervalField, ComplexIntervalField) and FLINT (RealBallField,
ComplexBallField).
.. toctree::
diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py
index 8fd46542236..b0c7610ed1e 100644
--- a/src/sage/arith/misc.py
+++ b/src/sage/arith/misc.py
@@ -291,8 +291,10 @@ def bernoulli(n, algorithm='default', num_threads=1):
- ``'default'`` -- use 'flint' for n <= 20000, then 'arb' for n <= 300000
and 'bernmm' for larger values (this is just a heuristic, and not guaranteed
to be optimal on all hardware)
- - ``'arb'`` -- use the arb library
- - ``'flint'`` -- use the FLINT library
+ - ``'arb'`` -- use the ``bernoulli_fmpq_ui`` function (formerly part of
+ Arb) of the FLINT library
+ - ``'flint'`` -- use the ``arith_bernoulli_number`` function of the FLINT
+ library
- ``'pari'`` -- use the PARI C library
- ``'gap'`` -- use GAP
- ``'gp'`` -- use PARI/GP interpreter
diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py
index d78c95e38b2..75a925895c5 100644
--- a/src/sage/features/sagemath.py
+++ b/src/sage/features/sagemath.py
@@ -360,7 +360,7 @@ def __init__(self):
class sage__libs__flint(JoinFeature):
r"""
A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.flint`
- and other modules depending on FLINT and arb.
+ and other modules depending on FLINT.
In addition to the modularization purposes that this tag serves, it also provides attribution
to the upstream project.
diff --git a/src/sage/matrix/matrix_complex_ball_dense.pxd b/src/sage/matrix/matrix_complex_ball_dense.pxd
index 9a17089a1c7..6b34affb338 100644
--- a/src/sage/matrix/matrix_complex_ball_dense.pxd
+++ b/src/sage/matrix/matrix_complex_ball_dense.pxd
@@ -1,4 +1,4 @@
-from sage.libs.arb.types cimport acb_mat_t
+from sage.libs.flint.types cimport acb_mat_t
from sage.matrix.matrix_dense cimport Matrix_dense
from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense
from sage.structure.parent cimport Parent
diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx
index 2f0131f4064..4d4c3b2d6bd 100644
--- a/src/sage/matrix/matrix_complex_ball_dense.pyx
+++ b/src/sage/matrix/matrix_complex_ball_dense.pyx
@@ -1,14 +1,14 @@
# distutils: libraries = flint
r"""
-Arbitrary precision complex ball matrices using Arb
+Arbitrary precision complex ball matrices
AUTHORS:
- Clemens Heuberger (2014-10-25): Initial version.
-This is a rudimentary binding to the `Arb library
-`_; it may be useful to refer to its
-documentation for more details.
+This is an incomplete interface to the `acb_mat module
+`_ of FLINT; it may be useful to refer
+to its documentation for more details.
TESTS::
@@ -36,8 +36,8 @@ from cpython.object cimport Py_EQ, Py_NE
from cysignals.signals cimport sig_on, sig_str, sig_off
from sage.arith.power cimport generic_power_pos
-from sage.libs.arb.acb cimport *
-from sage.libs.arb.acb_mat cimport *
+from sage.libs.flint.acb cimport *
+from sage.libs.flint.acb_mat cimport *
from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_get_ui
from sage.matrix.constructor import matrix
from sage.matrix.args cimport SparseEntry, MatrixArgs_init
@@ -120,7 +120,7 @@ cdef inline long prec(Matrix_complex_ball_dense mat) noexcept:
cdef class Matrix_complex_ball_dense(Matrix_dense):
"""
Matrix over a complex ball field. Implemented using the
- ``acb_mat`` type of the Arb library.
+ ``acb_mat`` type of the FLINT library.
EXAMPLES::
@@ -143,7 +143,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
sage: type(a)
"""
- sig_str("Arb exception")
+ sig_str("FLINT exception")
acb_mat_init(self.value, self._nrows, self._ncols)
sig_off()
@@ -695,7 +695,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
There is currently no guarantee that the algorithm converges as the
working precision is increased.
- See the `Arb documentation `__
+ See the `FLINT documentation `__
for more information.
EXAMPLES::
@@ -764,7 +764,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
No guarantees are made about the accuracy of the output.
- See the `Arb documentation `__
+ See the `FLINT documentation `__
for more information.
EXAMPLES::
@@ -822,7 +822,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
Additionally, there is currently no guarantee that the algorithm
converges as the working precision is increased.
- See the `Arb documentation `__
+ See the `FLINT documentation `__
for more information.
EXAMPLES::
@@ -882,7 +882,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
No guarantees are made about the accuracy of the output.
- See the `Arb documentation `__
+ See the `FLINT documentation `__
for more information.
EXAMPLES::
@@ -921,7 +921,7 @@ cdef class Matrix_complex_ball_dense(Matrix_dense):
Additionally, there is currently no guarantee that the algorithm
converges as the working precision is increased.
- See the `Arb documentation `__
+ See the `FLINT documentation `__
for more information.
EXAMPLES::
diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py
index d64c53ac3e8..0cc5344bced 100644
--- a/src/sage/misc/package.py
+++ b/src/sage/misc/package.py
@@ -24,7 +24,6 @@
sage: sorted(pkgs.keys()) # optional - sage_spkg, random
['4ti2',
'alabaster',
- 'arb',
...
'zlib']
@@ -299,7 +298,6 @@ def list_packages(*pkg_types: str, pkg_sources: List[str] = ['normal', 'pip', 's
sage: L = list_packages('standard')
sage: sorted(L.keys()) # random
['alabaster',
- 'arb',
'babel',
...
'zlib']
diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx
index 81bb5f36af5..fceda721c53 100644
--- a/src/sage/numerical/gauss_legendre.pyx
+++ b/src/sage/numerical/gauss_legendre.pyx
@@ -103,8 +103,8 @@ def nodes_uncached(degree, prec):
.. TODO::
- It may be worth testing if using the Arb algorithm for finding the
- nodes and weights in ``arb/acb_calc/integrate_gl_auto_deg.c`` has better
+ It may be worth testing if using the FLINT/Arb algorithm for finding the
+ nodes and weights in ``src/acb_calc/integrate_gl_auto_deg.c`` has better
performance.
"""
cdef long j,j1,n
diff --git a/src/sage/rings/complex_arb.pxd b/src/sage/rings/complex_arb.pxd
index d985745c1f5..a0836e8876c 100644
--- a/src/sage/rings/complex_arb.pxd
+++ b/src/sage/rings/complex_arb.pxd
@@ -1,4 +1,4 @@
-from sage.libs.arb.acb cimport acb_t
+from sage.libs.flint.acb cimport acb_t
from sage.rings.complex_interval cimport ComplexIntervalFieldElement
from sage.rings.real_arb cimport RealBall
from sage.structure.element cimport RingElement
diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx
index b7384f628dc..298db794f09 100644
--- a/src/sage/rings/complex_arb.pyx
+++ b/src/sage/rings/complex_arb.pyx
@@ -1,17 +1,17 @@
# -*- coding: utf-8
r"""
-Arbitrary precision complex balls using Arb
+Arbitrary precision complex balls
-This is a binding to the `Arb library `_; it
-may be useful to refer to its documentation for more details.
+This is an incomplete interface to the `acb module of FLINT `_;
+it may be useful to refer to its documentation for more details.
-Parts of the documentation for this module are copied or adapted from
-Arb's own documentation, licenced under the GNU General Public License
-version 2, or later.
+Parts of the documentation for this module are copied or adapted from Arb's
+(now FLINT's) own documentation, licenced at the time under the GNU General
+Public License version 2, or later.
.. SEEALSO::
- - :mod:`Real balls using Arb `
+ - :mod:`Real balls `
- :mod:`Complex interval field (using MPFI) `
- :mod:`Complex intervals (using MPFI) `
@@ -19,12 +19,12 @@ Data Structure
==============
A :class:`ComplexBall` represents a complex number with error bounds. It wraps
-an Arb object of type ``acb_t``, which consists of a pair of real number balls
+an object of type ``acb_t``, which consists of a pair of real number balls
representing the real and imaginary part with separate error bounds. (See the
documentation of :mod:`sage.rings.real_arb` for more information.)
A :class:`ComplexBall` thus represents a rectangle `[m_1-r_1, m_1+r_1] +
-[m_2-r_2, m_2+r_2] i` in the complex plane. This is used in Arb instead of a
+[m_2-r_2, m_2+r_2] i` in the complex plane. This is used instead of a
disk or square representation (consisting of a complex floating-point midpoint
with a single radius), since it allows implementing many operations more
conveniently by splitting into ball operations on the real and imaginary parts.
@@ -43,7 +43,7 @@ Comparison
.. WARNING::
- In accordance with the semantics of Arb, identical :class:`ComplexBall`
+ In accordance with the semantics of FLINT/Arb, identical :class:`ComplexBall`
objects are understood to give permission for algebraic simplification.
This assumption is made to improve performance. For example, setting ``z =
x*x`` sets `z` to a ball enclosing the set `\{t^2 : t \in x\}` and not the
@@ -161,16 +161,16 @@ from cpython.complex cimport PyComplex_FromDoubles
from sage.ext.stdsage cimport PY_NEW
from sage.libs.mpfr cimport MPFR_RNDU, MPFR_RNDD, MPFR_PREC_MIN, mpfr_get_d_2exp
-from sage.libs.arb.types cimport ARF_RND_NEAR, arf_t, mag_t
-from sage.libs.arb.arb cimport *
-from sage.libs.arb.acb cimport *
-from sage.libs.arb.acb_calc cimport *
-from sage.libs.arb.acb_hypgeom cimport *
-from sage.libs.arb.acb_elliptic cimport *
-from sage.libs.arb.acb_modular cimport *
-from sage.libs.arb.acb_poly cimport *
-from sage.libs.arb.arf cimport arf_init, arf_get_d, arf_get_mpfr, arf_clear, arf_set, arf_is_nan
-from sage.libs.arb.mag cimport (mag_init, mag_clear, mag_set_d,
+from sage.libs.flint.types cimport ARF_RND_NEAR, arf_t, mag_t
+from sage.libs.flint.arb cimport *
+from sage.libs.flint.acb cimport *
+from sage.libs.flint.acb_calc cimport *
+from sage.libs.flint.acb_hypgeom cimport *
+from sage.libs.flint.acb_elliptic cimport *
+from sage.libs.flint.acb_modular cimport *
+from sage.libs.flint.acb_poly cimport *
+from sage.libs.flint.arf cimport arf_init, arf_get_d, arf_get_mpfr, arf_clear, arf_set, arf_is_nan
+from sage.libs.flint.mag cimport (mag_init, mag_clear, mag_set_d,
MAG_BITS, mag_zero, mag_set_ui_2exp_si,
mag_mul_2exp_si)
from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear
@@ -371,7 +371,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField):
sage: CBF.base_ring()
Real ball field with 53 bits of precision
- There are direct coercions from ZZ and QQ (for which arb provides
+ There are direct coercions from ZZ and QQ (for which FLINT provides
construction functions)::
sage: CBF.coerce_map_from(ZZ)
@@ -1029,7 +1029,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField):
the ball field) -- absolute accuracy goal
Additionally, the following optional parameters can be used to control
- the integration algorithm. See the `Arb documentation `_
+ the integration algorithm. See the `FLINT documentation `_
for more information.
- ``deg_limit`` -- maximum quadrature degree for each
@@ -1150,8 +1150,8 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField):
ALGORITHM:
- Uses the `acb_calc `_ module of the Arb
- library.
+ Uses the `acb_calc `_ module of
+ the FLINT library.
TESTS::
@@ -1256,7 +1256,7 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField):
cdef inline bint _do_sig(long prec) noexcept:
"""
- Whether signal handlers should be installed for calls to arb.
+ Whether signal handlers should be installed for calls to FLINT.
"""
return (prec > 1000)
@@ -1298,8 +1298,7 @@ cdef inline real_ball_field(ComplexBall ball) noexcept:
cdef class ComplexBall(RingElement):
"""
- Hold one ``acb_t`` of the `Arb library
- `_
+ Hold one ``acb_t`` of the `FLINT library `_
EXAMPLES::
diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pxd b/src/sage/rings/number_field/number_field_element_quadratic.pxd
index b9fbb294a54..cf679098a2f 100644
--- a/src/sage/rings/number_field/number_field_element_quadratic.pxd
+++ b/src/sage/rings/number_field/number_field_element_quadratic.pxd
@@ -1,5 +1,5 @@
from sage.libs.gmp.types cimport mpz_t
-from sage.libs.arb.types cimport arb_t
+from sage.libs.flint.types cimport arb_t
from sage.rings.integer cimport Integer
from sage.rings.rational cimport Rational
from sage.rings.number_field.number_field_element cimport NumberFieldElement, NumberFieldElement_absolute
diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx
index 0212d85b6b5..ad6a31d5e87 100644
--- a/src/sage/rings/number_field/number_field_element_quadratic.pyx
+++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx
@@ -43,8 +43,8 @@ from cysignals.signals cimport sig_on, sig_off
from sage.libs.gmp.mpz cimport *
from sage.libs.gmp.mpq cimport *
from sage.libs.flint.fmpz cimport *
-from sage.libs.arb.arb cimport *
-from sage.libs.arb.acb cimport *
+from sage.libs.flint.arb cimport *
+from sage.libs.flint.acb cimport *
from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ
from sage.libs.ntl.ntl_ZZX cimport ntl_ZZX
from sage.libs.mpfi cimport *
diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pxd b/src/sage/rings/polynomial/polynomial_complex_arb.pxd
index 29e3cc4fd61..cffe352e2cf 100644
--- a/src/sage/rings/polynomial/polynomial_complex_arb.pxd
+++ b/src/sage/rings/polynomial/polynomial_complex_arb.pxd
@@ -1,4 +1,4 @@
-from sage.libs.arb.acb_poly cimport *
+from sage.libs.flint.acb_poly cimport *
from sage.rings.polynomial.polynomial_element cimport Polynomial
cdef class Polynomial_complex_arb(Polynomial):
diff --git a/src/sage/rings/polynomial/polynomial_complex_arb.pyx b/src/sage/rings/polynomial/polynomial_complex_arb.pyx
index 81e799b7a8d..ed52bb01b56 100644
--- a/src/sage/rings/polynomial/polynomial_complex_arb.pyx
+++ b/src/sage/rings/polynomial/polynomial_complex_arb.pyx
@@ -1,13 +1,13 @@
# -*- coding: utf-8
r"""
-Univariate polynomials over `\CC` with interval coefficients using Arb.
+Univariate polynomials over `\CC` with Arb ball coefficients.
-This is a binding to the `Arb library `_; it
-may be useful to refer to its documentation for more details.
+This is a binding to the `acb_poly module of FLINT `_;
+it may be useful to refer to its documentation for more details.
-Parts of the documentation for this module are copied or adapted from
-Arb's own documentation, licenced under the GNU General Public License
-version 2, or later.
+Parts of the documentation for this module are copied or adapted from Arb's
+(now FLINT's) own documentation, licenced (at the time) under the GNU General
+Public License version 2, or later.
.. SEEALSO::
@@ -25,7 +25,7 @@ TESTS:
from cysignals.signals cimport sig_on, sig_off
-from sage.libs.arb.acb cimport *
+from sage.libs.flint.acb cimport *
from sage.libs.flint.fmpz cimport *
from sage.rings.integer cimport Integer, smallInteger
from sage.rings.complex_arb cimport ComplexBall
@@ -39,7 +39,7 @@ cdef inline long prec(Polynomial_complex_arb pol) noexcept:
cdef class Polynomial_complex_arb(Polynomial):
r"""
- Wrapper for `Arb `_ polynomials of type
+ Wrapper for `FLINT `_ polynomials of type
``acb_poly_t``
EXAMPLES::
@@ -806,7 +806,7 @@ cdef class Polynomial_complex_arb(Polynomial):
For ``a = 1``, this computes the usual Riemann zeta function.
- If ``deflate`` is True, evaluate ζ(s,a) + 1/(1-s), see the Arb
+ If ``deflate`` is True, evaluate ζ(s,a) + 1/(1-s), see the FLINT
documentation for details.
EXAMPLES::
diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx
index 8498f3dd760..5db98ce257c 100644
--- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx
+++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx
@@ -61,7 +61,7 @@ from sage.structure.factorization import Factorization
from sage.rings.fraction_field_element import FractionFieldElement
from sage.arith.functions import lcm
-from sage.libs.arb.arb_fmpz_poly cimport arb_fmpz_poly_evaluate_arb, arb_fmpz_poly_evaluate_acb
+from sage.libs.flint.arb_fmpz_poly cimport arb_fmpz_poly_evaluate_arb, arb_fmpz_poly_evaluate_acb
from sage.libs.flint.fmpz cimport *
from sage.libs.flint.fmpz_poly cimport *
from sage.libs.flint.fmpz_poly_sage cimport *
diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx
index 1ef3cbaf681..8c779b7786c 100644
--- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx
+++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx
@@ -28,16 +28,16 @@ from cysignals.signals cimport sig_on, sig_str, sig_off
from cpython.long cimport PyLong_AsLong
from sage.arith.long cimport pyobject_to_long
-from sage.libs.arb.acb cimport acb_div_fmpz
-from sage.libs.arb.arb cimport arb_div_fmpz
-from sage.libs.arb.arb_fmpz_poly cimport _arb_fmpz_poly_evaluate_arb, _arb_fmpz_poly_evaluate_acb
-from sage.libs.gmp.mpz cimport *
-from sage.libs.gmp.mpq cimport *
+from sage.libs.flint.acb cimport acb_div_fmpz
+from sage.libs.flint.arb cimport arb_div_fmpz
+from sage.libs.flint.arb_fmpz_poly cimport _arb_fmpz_poly_evaluate_arb, _arb_fmpz_poly_evaluate_acb
from sage.libs.flint.fmpz cimport *
from sage.libs.flint.fmpq cimport *
from sage.libs.flint.fmpz_poly cimport *
from sage.libs.flint.fmpq_poly cimport *
from sage.libs.flint.fmpq_poly_sage cimport *
+from sage.libs.gmp.mpz cimport *
+from sage.libs.gmp.mpq cimport *
from sage.interfaces.singular import singular as singular_default
diff --git a/src/sage/rings/real_arb.pxd b/src/sage/rings/real_arb.pxd
index 0a3a68d1057..263a411ee10 100644
--- a/src/sage/rings/real_arb.pxd
+++ b/src/sage/rings/real_arb.pxd
@@ -1,4 +1,4 @@
-from sage.libs.arb.arb cimport arb_t
+from sage.libs.flint.arb cimport arb_t
from sage.libs.mpfi.types cimport mpfi_t
from sage.rings.real_mpfi cimport RealIntervalField_class, RealIntervalFieldElement
from sage.structure.parent cimport Parent
diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx
index 8509b19cc66..46fafb0a552 100644
--- a/src/sage/rings/real_arb.pyx
+++ b/src/sage/rings/real_arb.pyx
@@ -1,17 +1,17 @@
# -*- coding: utf-8
r"""
-Arbitrary precision real balls using Arb
+Arbitrary precision real balls
-This is a binding to the `Arb library `_ for ball
-arithmetic. It may be useful to refer to its documentation for more details.
+This is a binding to the `arb module of FLINT `_.
+It may be useful to refer to its documentation for more details.
-Parts of the documentation for this module are copied or adapted from
-Arb's own documentation, licenced under the GNU General Public License
-version 2, or later.
+Parts of the documentation for this module are copied or adapted from Arb's
+(now FLINT's) own documentation, licenced (at the time) under the GNU General
+Public License version 2, or later.
.. SEEALSO::
- - :mod:`Complex balls using Arb `
+ - :mod:`Complex balls `
- :mod:`Real intervals using MPFI `
Data Structure
@@ -24,7 +24,7 @@ the overhead of traditional (inf-sup) interval arithmetic at high precision,
and eliminating much of the need for time-consuming and bug-prone manual error
analysis associated with standard floating-point arithmetic.
-Sage :class:`RealBall` objects wrap Arb objects of type ``arb_t``. A real
+Sage :class:`RealBall` objects wrap FLINT objects of type ``arb_t``. A real
ball represents a ball over the real numbers, that is, an interval `[m-r,m+r]`
where the midpoint `m` and the radius `r` are (extended) real numbers::
@@ -62,7 +62,7 @@ Comparison
.. WARNING::
- In accordance with the semantics of Arb, identical :class:`RealBall`
+ In accordance with the semantics of FLINT/Arb, identical :class:`RealBall`
objects are understood to give permission for algebraic simplification.
This assumption is made to improve performance. For example, setting ``z =
x*x`` may set `z` to a ball enclosing the set `\{t^2 : t \in x\}` and not
@@ -204,11 +204,11 @@ from cpython.long cimport PyLong_AsLong
from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE
from libc.stdlib cimport abort
-from sage.libs.arb.arb cimport *
-from sage.libs.arb.arb_hypgeom cimport *
-from sage.libs.arb.arf cimport *
-from sage.libs.arb.arf cimport *
-from sage.libs.arb.mag cimport *
+from sage.libs.flint.arb cimport *
+from sage.libs.flint.arb_hypgeom cimport *
+from sage.libs.flint.arf cimport *
+from sage.libs.flint.arf cimport *
+from sage.libs.flint.mag cimport *
from sage.libs.flint.flint cimport flint_free
from sage.libs.flint.fmpz cimport *
from sage.libs.flint.fmpq cimport *
@@ -1147,7 +1147,7 @@ class RealBallField(UniqueRepresentation, sage.rings.abc.RealBallField):
cdef inline bint _do_sig(long prec) noexcept:
"""
- Whether signal handlers should be installed for calls to arb.
+ Whether signal handlers should be installed for calls to FLINT.
TESTS::
@@ -1185,8 +1185,7 @@ def create_RealBall(parent, serialized):
cdef class RealBall(RingElement):
"""
- Hold one ``arb_t`` of the `Arb library
- `_
+ Hold one ``arb_t``
EXAMPLES::
diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py
index 1f0fd8bd34e..10ca75edb41 100644
--- a/src/sage/schemes/elliptic_curves/cm.py
+++ b/src/sage/schemes/elliptic_curves/cm.py
@@ -65,7 +65,8 @@ def hilbert_class_polynomial(D, algorithm=None):
ALGORITHM:
- - If ``algorithm`` = "arb" (default): Use Arb's implementation which uses complex interval arithmetic.
+ - If ``algorithm`` = "arb" (default): Use FLINT's implementation inherited
+ from Arb which uses complex interval arithmetic.
- If ``algorithm`` = "sage": Use complex approximations to the roots.
From af2e89265f1c77b1c29b0514d9cdcff1d760a7d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Fri, 23 Feb 2024 20:03:26 +0100
Subject: [PATCH 049/518] fix suggested details and some more
---
src/sage/monoids/free_monoid_element.py | 15 ++++------
src/sage/monoids/string_ops.py | 38 ++++++++++++++-----------
src/sage/monoids/trace_monoid.py | 8 +++---
3 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py
index d2cf92c4ba8..9b22ca68cca 100644
--- a/src/sage/monoids/free_monoid_element.py
+++ b/src/sage/monoids/free_monoid_element.py
@@ -128,7 +128,7 @@ def _repr_(self):
s = "1"
return s
- def _latex_(self):
+ def _latex_(self) -> str:
r"""
Return latex representation of self.
@@ -278,7 +278,7 @@ def __invert__(self):
"""
raise NotImplementedError
- def __len__(self):
+ def __len__(self) -> int:
"""
Return the degree of the monoid element ``self``, where each
generator of the free monoid is given degree `1`.
@@ -296,12 +296,9 @@ def __len__(self):
sage: len(a[0]**2 * a[1])
3
"""
- s = 0
- for x in self._element_list:
- s += x[1]
- return s
+ return sum(x[1] for x in self._element_list)
- def _richcmp_(self, other, op):
+ def _richcmp_(self, other, op) -> bool:
"""
Compare two free monoid elements with the same parents.
@@ -374,9 +371,9 @@ def to_word(self, alph=None):
alph = gens
alph = [str(c) for c in alph]
W = Words(alph, infinite=False)
- return W(sum([[alph[gens.index(i[0])]] * i[1] for i in iter(self)], []))
+ return W(sum([[alph[gens.index(i[0])]] * i[1] for i in self], []))
- def to_list(self, indices=False):
+ def to_list(self, indices=False) -> list:
r"""
Return ``self`` as a list of generators.
diff --git a/src/sage/monoids/string_ops.py b/src/sage/monoids/string_ops.py
index bdfabe79cdd..e34164712fb 100644
--- a/src/sage/monoids/string_ops.py
+++ b/src/sage/monoids/string_ops.py
@@ -12,24 +12,27 @@
from .string_monoid_element import StringMonoidElement
-def strip_encoding(S):
+def strip_encoding(S) -> str:
"""
- The upper case string of S stripped of all non-alphabetic characters.
+ Return the upper case string of S stripped of all non-alphabetic characters.
EXAMPLES::
sage: S = "The cat in the hat."
sage: strip_encoding(S)
'THECATINTHEHAT'
+
+ TESTS::
+
+ sage: S = "The cat in the hat."
+ sage: strip_encoding(44)
+ Traceback (most recent call last):
+ ...
+ TypeError: argument S (= 44) must be a string
"""
if not isinstance(S, str):
- raise TypeError("Argument S (= %s) must be a string.")
- X = ''
- for i in range(len(S)):
- C = S[i]
- if C.isalpha():
- X += S[i].upper()
- return X
+ raise TypeError(f"argument S (= {S}) must be a string")
+ return ''.join(letter.upper() for letter in S if letter.isalpha())
def frequency_distribution(S, n=1, field=None):
@@ -59,7 +62,7 @@ def frequency_distribution(S, n=1, field=None):
def coincidence_index(S, n=1):
"""
- The coincidence index of the string S.
+ Return the coincidence index of the string ``S``.
EXAMPLES::
@@ -87,9 +90,14 @@ def coincidence_index(S, n=1):
def coincidence_discriminant(S, n=2):
"""
- Input: A tuple of strings, e.g. produced as decimation of transposition
+ INPUT:
+
+ A tuple of strings, e.g. produced as decimation of transposition
ciphertext, or a sample plaintext.
- Output: A measure of the difference of probability of association of
+
+ OUTPUT:
+
+ A measure of the difference of probability of association of
character pairs, relative to their independent one-character probabilities.
EXAMPLES::
@@ -102,11 +110,9 @@ def coincidence_discriminant(S, n=2):
raise TypeError("Argument S (= %s) must be a list or tuple" % S)
if n != 2:
raise ValueError("Argument n (= %s) is only implemented for n = 2" % n)
- truth = all(isinstance(c, (str, StringMonoidElement)) for c in S)
- if not truth:
+ if not all(isinstance(c, (str, StringMonoidElement)) for c in S):
raise TypeError("Argument S (= %s) must be a list of strings.")
- truth = all(len(c) == n for c in S)
- if not truth:
+ if not all(len(c) == n for c in S):
raise ValueError("Argument S (= %s) must be a list of strings of length 2" % S)
X1 = [frequency_distribution([s[i] for s in S]) for i in range(2)]
XX = frequency_distribution(S)
diff --git a/src/sage/monoids/trace_monoid.py b/src/sage/monoids/trace_monoid.py
index d13b405421b..bc7edc7ab08 100644
--- a/src/sage/monoids/trace_monoid.py
+++ b/src/sage/monoids/trace_monoid.py
@@ -88,7 +88,7 @@ class TraceMonoidElement(ElementWrapper, MonoidElement):
sage: x.foata_normal_form()
(b, a*d, a, b*c)
"""
- def _repr_(self):
+ def _repr_(self) -> str:
"""
Textual representation of ``self``.
@@ -819,7 +819,7 @@ def dependence_graph(self):
True
"""
return Graph({frozenset((e1, e2)) if e1 != e2 else (e1, e2)
- for e1, e2 in self.dependence()}, loops=True,
+ for e1, e2 in self.dependence()}, loops=True,
format="list_of_edges",
immutable=True)
@@ -963,9 +963,9 @@ def _sorted_independence(self):
[[a, c]]
"""
return sorted(sorted(x_y)
- for x_y in sorted(self.independence()))
+ for x_y in self.independence())
- def _repr_(self):
+ def _repr_(self) -> str:
r"""
Textual representation of trace monoids.
From 7c033b600241fd6dd1781c358a8bda7550ff998d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sat, 24 Feb 2024 13:16:53 +0100
Subject: [PATCH 050/518] formal deprecation of old group class
---
.../additive_abelian_group.py | 12 +++---
src/sage/groups/group.pyx | 5 +--
src/sage/groups/old.pyx | 40 ++++---------------
.../modular/arithgroup/arithgroup_generic.py | 8 ++--
4 files changed, 21 insertions(+), 44 deletions(-)
diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py
index c0a90f0c110..f913115f576 100644
--- a/src/sage/groups/additive_abelian/additive_abelian_group.py
+++ b/src/sage/groups/additive_abelian/additive_abelian_group.py
@@ -6,10 +6,11 @@
major differences are in the way elements are printed.
"""
-from sage.groups.old import AbelianGroup
+from sage.structure.parent import Parent
from sage.modules.fg_pid.fgp_module import FGP_Module_class
from sage.modules.fg_pid.fgp_element import FGP_Element
from sage.rings.integer_ring import ZZ
+from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups
def AdditiveAbelianGroup(invs, remember_generators=True):
@@ -173,12 +174,12 @@ def _hermite_lift(self):
for i in range(H.nrows()):
if i in pivot_rows:
j = pivots[i]
- N = H[i,j]
+ N = H[i, j]
a = (y[j] - (y[j] % N)) // N
- y = y - a*H.row(i)
+ y = y - a * H.row(i)
return y
- def _repr_(self):
+ def _repr_(self) -> str:
r"""
String representation. This uses a canonical lifting of elements of
this group (represented as a quotient `G/H` of free abelian groups) to
@@ -201,7 +202,7 @@ def _repr_(self):
# since we want to inherit things like __hash__ from there rather than the
# hyper-generic implementation for abstract abelian groups.
-class AdditiveAbelianGroup_class(FGP_Module_class, AbelianGroup):
+class AdditiveAbelianGroup_class(FGP_Module_class, Parent):
r"""
An additive abelian group, implemented using the `\ZZ`-module machinery.
@@ -225,6 +226,7 @@ def __init__(self, cover, relations):
True
"""
FGP_Module_class.__init__(self, cover, relations)
+ Parent.__init__(self, category=CommutativeAdditiveGroups())
def _repr_(self):
r"""
diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx
index ef639a0c367..fdfe79bd9eb 100644
--- a/src/sage/groups/group.pyx
+++ b/src/sage/groups/group.pyx
@@ -21,7 +21,7 @@ from sage.structure.parent cimport Parent
from sage.rings.infinity import infinity
-def is_Group(x):
+def is_Group(x) -> bool:
"""
Return whether ``x`` is a group object.
@@ -42,8 +42,7 @@ def is_Group(x):
sage: is_Group("a string")
False
"""
- from sage.groups.old import Group as OldGroup
- return isinstance(x, (Group, OldGroup))
+ return isinstance(x, Group)
cdef class Group(Parent):
diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx
index 761034cff65..c831d1273dd 100644
--- a/src/sage/groups/old.pyx
+++ b/src/sage/groups/old.pyx
@@ -1,5 +1,5 @@
"""
-Base class for groups
+Deprecated Base class for groups
"""
# ****************************************************************************
@@ -17,11 +17,11 @@ Base class for groups
# https://www.gnu.org/licenses/
# ****************************************************************************
-doc="""
-Base class for all groups
+doc = """
+Deprecated Base class for all groups
"""
-from sage.rings.infinity import infinity
import sage.rings.integer_ring
+from sage.misc.superseded import deprecation
cdef class Group(sage.structure.parent.Parent):
@@ -35,6 +35,9 @@ cdef class Group(sage.structure.parent.Parent):
sage: from sage.groups.old import Group
sage: G = Group()
+ doctest:warning...:
+ DeprecationWarning: do not use the old Group class
+ See https://github.com/sagemath/sage/issues/34567 for details.
sage: G.category()
Category of groups
sage: G = Group(category = Groups()) # todo: do the same test with some subcategory of Groups when there will exist one
@@ -54,6 +57,7 @@ cdef class Group(sage.structure.parent.Parent):
sage: h == hash(G)
True
"""
+ deprecation(34567, 'do not use the old Group class')
from sage.categories.basic import Groups
if category is None:
category = Groups()
@@ -63,12 +67,6 @@ cdef class Group(sage.structure.parent.Parent):
sage.structure.parent.Parent.__init__(self,
base=sage.rings.integer_ring.ZZ, category=category)
- #def __call__(self, x): # this gets in the way of the coercion mechanism
- # """
- # Coerce x into this group.
- # """
- # raise NotImplementedError
-
def __contains__(self, x):
r"""
True if coercion of `x` into self is defined.
@@ -88,13 +86,6 @@ cdef class Group(sage.structure.parent.Parent):
return False
return True
-# def category(self):
-# """
-# The category of all groups
-# """
-# import sage.categories.all
-# return sage.categories.all.Groups()
-
def is_abelian(self):
"""
Return True if this group is abelian.
@@ -142,21 +133,6 @@ cdef class Group(sage.structure.parent.Parent):
"""
raise NotImplementedError
- def is_finite(self):
- """
- Returns True if this group is finite.
-
- EXAMPLES::
-
- sage: from sage.groups.old import Group
- sage: G = Group()
- sage: G.is_finite()
- Traceback (most recent call last):
- ...
- NotImplementedError
- """
- return self.order() != infinity
-
def is_multiplicative(self):
r"""
Returns True if the group operation is given by \* (rather than
diff --git a/src/sage/modular/arithgroup/arithgroup_generic.py b/src/sage/modular/arithgroup/arithgroup_generic.py
index c647f8d0e50..08990c1b7d5 100644
--- a/src/sage/modular/arithgroup/arithgroup_generic.py
+++ b/src/sage/modular/arithgroup/arithgroup_generic.py
@@ -14,12 +14,12 @@
#
################################################################################
-from sage.groups.old import Group
+from sage.groups.group import Group
from sage.categories.groups import Groups
from sage.rings.integer_ring import ZZ
from sage.arith.functions import lcm
from sage.misc.cachefunc import cached_method
-from copy import copy # for making copies of lists of cusps
+from copy import copy # for making copies of lists of cusps
from sage.modular.modsym.p1list import lift_to_sl2z
from sage.modular.cusps import Cusp
@@ -30,7 +30,7 @@
from .arithgroup_element import ArithmeticSubgroupElement, M2Z as Mat2Z
-def is_ArithmeticSubgroup(x):
+def is_ArithmeticSubgroup(x) -> bool:
r"""
Return ``True`` if ``x`` is of type :class:`ArithmeticSubgroup`.
@@ -67,7 +67,7 @@ def __init__(self):
"""
Group.__init__(self, category=Groups().Infinite())
- def _repr_(self):
+ def _repr_(self) -> str:
r"""
Return the string representation of self.
From 39252f445745c6cf01a212c8d3cf31ba7325683c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sat, 24 Feb 2024 13:18:58 +0100
Subject: [PATCH 051/518] fix issue number
---
src/sage/groups/old.pyx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx
index c831d1273dd..43b63062c84 100644
--- a/src/sage/groups/old.pyx
+++ b/src/sage/groups/old.pyx
@@ -37,7 +37,7 @@ cdef class Group(sage.structure.parent.Parent):
sage: G = Group()
doctest:warning...:
DeprecationWarning: do not use the old Group class
- See https://github.com/sagemath/sage/issues/34567 for details.
+ See https://github.com/sagemath/sage/issues/37449 for details.
sage: G.category()
Category of groups
sage: G = Group(category = Groups()) # todo: do the same test with some subcategory of Groups when there will exist one
@@ -57,7 +57,7 @@ cdef class Group(sage.structure.parent.Parent):
sage: h == hash(G)
True
"""
- deprecation(34567, 'do not use the old Group class')
+ deprecation(37449, 'do not use the old Group class')
from sage.categories.basic import Groups
if category is None:
category = Groups()
From 4cff0fef2e859812dc3d375a531f6365a1a8d2cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sat, 24 Feb 2024 20:52:42 +0100
Subject: [PATCH 052/518] fix suggested details
---
src/sage/categories/action.pyx | 4 ++--
.../additive_abelian/additive_abelian_group.py | 12 +++++++-----
src/sage/groups/group.pyx | 9 ++++++---
src/sage/groups/old.pyx | 4 ++--
4 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/src/sage/categories/action.pyx b/src/sage/categories/action.pyx
index 0e54ceffa27..730cfff4786 100644
--- a/src/sage/categories/action.pyx
+++ b/src/sage/categories/action.pyx
@@ -381,10 +381,10 @@ cdef class InverseAction(Action):
def __init__(self, Action action):
G = action.G
try:
- from sage.groups.group import is_Group
+ from sage.groups.group import Group
# We must be in the case that parent(~a) == parent(a)
# so we can invert in _call_ code below.
- if (is_Group(G) and G.is_multiplicative()) or G.is_field():
+ if (isinstance(G, Group) and G.is_multiplicative()) or G.is_field():
Action.__init__(self, G, action.underlying_set(), action._is_left)
self._action = action
return
diff --git a/src/sage/groups/additive_abelian/additive_abelian_group.py b/src/sage/groups/additive_abelian/additive_abelian_group.py
index f913115f576..71af80acdf9 100644
--- a/src/sage/groups/additive_abelian/additive_abelian_group.py
+++ b/src/sage/groups/additive_abelian/additive_abelian_group.py
@@ -6,11 +6,10 @@
major differences are in the way elements are printed.
"""
-from sage.structure.parent import Parent
-from sage.modules.fg_pid.fgp_module import FGP_Module_class
+from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups
from sage.modules.fg_pid.fgp_element import FGP_Element
+from sage.modules.fg_pid.fgp_module import FGP_Module_class
from sage.rings.integer_ring import ZZ
-from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups
def AdditiveAbelianGroup(invs, remember_generators=True):
@@ -202,7 +201,7 @@ def _repr_(self) -> str:
# since we want to inherit things like __hash__ from there rather than the
# hyper-generic implementation for abstract abelian groups.
-class AdditiveAbelianGroup_class(FGP_Module_class, Parent):
+class AdditiveAbelianGroup_class(FGP_Module_class):
r"""
An additive abelian group, implemented using the `\ZZ`-module machinery.
@@ -224,9 +223,12 @@ def __init__(self, cover, relations):
Additive abelian group isomorphic to Z
sage: G == loads(dumps(G))
True
+ sage: G.category()
+ Category of modules over Integer Ring
+ sage: G in CommutativeAdditiveGroups()
+ True
"""
FGP_Module_class.__init__(self, cover, relations)
- Parent.__init__(self, category=CommutativeAdditiveGroups())
def _repr_(self):
r"""
diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx
index fdfe79bd9eb..8fa6d3dec3c 100644
--- a/src/sage/groups/group.pyx
+++ b/src/sage/groups/group.pyx
@@ -16,12 +16,12 @@ Base class for groups
#
# https://www.gnu.org/licenses/
# ****************************************************************************
-
-from sage.structure.parent cimport Parent
+from sage.misc.superseded import deprecation
from sage.rings.infinity import infinity
+from sage.structure.parent cimport Parent
-def is_Group(x) -> bool:
+def is_Group(x):
"""
Return whether ``x`` is a group object.
@@ -38,10 +38,13 @@ def is_Group(x) -> bool:
sage: F. = FreeGroup() # needs sage.groups
sage: from sage.groups.group import is_Group
sage: is_Group(F) # needs sage.groups
+ doctest:warning...DeprecationWarning: use instead G in Groups()
+ See https://github.com/sagemath/sage/issues/37449 for details.
True
sage: is_Group("a string")
False
"""
+ deprecation(37449, 'use instead G in Groups()')
return isinstance(x, Group)
diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx
index 43b63062c84..2d76c9bc66d 100644
--- a/src/sage/groups/old.pyx
+++ b/src/sage/groups/old.pyx
@@ -1,5 +1,5 @@
"""
-Deprecated Base class for groups
+Deprecated base class for groups
"""
# ****************************************************************************
@@ -18,7 +18,7 @@ Deprecated Base class for groups
# ****************************************************************************
doc = """
-Deprecated Base class for all groups
+Deprecated base class for all groups
"""
import sage.rings.integer_ring
from sage.misc.superseded import deprecation
From 1a147c1d61765f361507ff6178aa0d6d4b5de222 Mon Sep 17 00:00:00 2001
From: Kwankyu Lee
Date: Sun, 25 Feb 2024 19:16:16 +0900
Subject: [PATCH 053/518] Restore _NumberFields and remove unnecessary checks
---
src/sage/schemes/affine/affine_point.py | 6 +++---
src/sage/schemes/projective/projective_point.py | 11 ++++-------
2 files changed, 7 insertions(+), 10 deletions(-)
diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py
index 32bfb3a42cf..00779dbb2d6 100644
--- a/src/sage/schemes/affine/affine_point.py
+++ b/src/sage/schemes/affine/affine_point.py
@@ -26,6 +26,8 @@
from sage.schemes.generic.morphism import SchemeMorphism_point, SchemeMorphism, is_SchemeMorphism
from sage.structure.sequence import Sequence
+_NumberFields = NumberFields()
+
# --------------------------------------------------------------------
# Rational points on schemes, which we view as morphisms determined by
@@ -201,7 +203,7 @@ def global_height(self, prec=None):
R = RealField(prec)
H = max([self[i].abs() for i in range(self.codomain().ambient_space().dimension_relative())])
return R(max(H,1)).log()
- if self.domain().base_ring() in NumberFields() or isinstance(self.domain().base_ring(), sage.rings.abc.Order):
+ if self.domain().base_ring() in _NumberFields or isinstance(self.domain().base_ring(), sage.rings.abc.Order):
return max([self[i].global_height(prec) for i in range(self.codomain().ambient_space().dimension_relative())])
else:
raise NotImplementedError("must be over a number field or a number field Order")
@@ -421,8 +423,6 @@ def as_subscheme(self):
g = A.gens()
v = self._coords
n = len(v)
- if n != len(g):
- raise ValueError('not a point of the ambient space')
return A.subscheme([g[i] - v[i] for i in range(n)])
diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py
index d90b2a440fd..7208b70294b 100644
--- a/src/sage/schemes/projective/projective_point.py
+++ b/src/sage/schemes/projective/projective_point.py
@@ -28,6 +28,7 @@
from sage.categories.integral_domains import IntegralDomains
from sage.categories.number_fields import NumberFields
+_NumberFields = NumberFields()
from sage.rings.fraction_field import FractionField
from sage.rings.number_field.order import is_NumberFieldOrder, Order as NumberFieldOrder
from sage.rings.qqbar import number_field_elements_from_algebraics
@@ -747,7 +748,7 @@ def global_height(self, prec=None):
if prec is None:
prec = 53
K = self.codomain().base_ring()
- if K in NumberFields() or is_NumberFieldOrder(K):
+ if K in _NumberFields or is_NumberFieldOrder(K):
P = self
else:
try:
@@ -802,7 +803,7 @@ def local_height(self, v, prec=None):
0.693147180559945
"""
K = FractionField(self.domain().base_ring())
- if K not in NumberFields():
+ if K not in _NumberFields:
raise TypeError("must be over a number field or a number field order")
return max([K(c).local_height(v, prec=prec) for c in self])
@@ -837,7 +838,7 @@ def local_height_arch(self, i, prec=None):
3.401197381662155375413236691607
"""
K = FractionField(self.domain().base_ring())
- if K not in NumberFields():
+ if K not in _NumberFields:
raise TypeError("must be over a number field or a number field order")
if K == QQ:
return max(K(c).local_height_arch(prec=prec) for c in self)
@@ -1418,13 +1419,9 @@ def as_subscheme(self):
g = P.gens()
v = self._coords
n = len(v)
- if n != len(g):
- raise ValueError('not a point of the ambient space')
for i in range(n - 1, -1, -1):
if v[i]:
break
- else:
- raise ValueError('invalid homogeneous coordinates')
a = v[i]
x = g[i]
return P.subscheme([a*g[j] - v[j]*x for j in range(n) if j != i])
From fadbcae1ff1c9ef3f5810e3401aa1dd020cc9f97 Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Sun, 25 Feb 2024 14:43:03 +0200
Subject: [PATCH 054/518] Minor improvements in testing
---
src/sage/matroids/flats_matroid.pyx | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/sage/matroids/flats_matroid.pyx b/src/sage/matroids/flats_matroid.pyx
index 3c69920717b..14816ddd7b5 100644
--- a/src/sage/matroids/flats_matroid.pyx
+++ b/src/sage/matroids/flats_matroid.pyx
@@ -150,9 +150,9 @@ cdef class FlatsMatroid(Matroid):
EXAMPLES::
sage: from sage.matroids.flats_matroid import FlatsMatroid
- sage: M = FlatsMatroid(matroids.Theta(7))
+ sage: M = FlatsMatroid(matroids.Theta(6))
sage: M.full_rank()
- 7
+ 6
"""
return self._matroid_rank
@@ -410,6 +410,12 @@ cdef class FlatsMatroid(Matroid):
sage: M = FlatsMatroid(matroids.catalog.XY13())
sage: M.whitney_numbers2()
[1, 13, 78, 250, 394, 191, 1]
+
+ TESTS::
+
+ sage: from sage.matroids.flats_matroid import FlatsMatroid
+ sage: for M in matroids.AllMatroids(4): # optional - matroid_database
+ ....: assert M.whitney_numbers2() == FlatsMatroid(M).whitney_numbers2()
"""
cdef list W = []
cdef int i
From 057e73d1de82439b750ce1b180db1e4c8c3dbeb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 15:58:18 +0100
Subject: [PATCH 055/518] formal deprecation of old CommutativeAlgebra class
---
src/sage/rings/commutative_algebra.py | 11 ++---
src/sage/rings/ring.pyx | 67 +++++++--------------------
2 files changed, 21 insertions(+), 57 deletions(-)
diff --git a/src/sage/rings/commutative_algebra.py b/src/sage/rings/commutative_algebra.py
index 7f6b2ba5dfa..1e209e453c4 100644
--- a/src/sage/rings/commutative_algebra.py
+++ b/src/sage/rings/commutative_algebra.py
@@ -14,7 +14,7 @@
#
# The full text of the GPL is available at:
#
-# http://www.gnu.org/licenses/
+# https://www.gnu.org/licenses/
#*****************************************************************************
from sage.categories.commutative_algebras import CommutativeAlgebras
@@ -22,18 +22,17 @@
def is_CommutativeAlgebra(x):
"""
- Check to see if ``x`` is a :class:`CommutativeAlgebra`.
+ Check to see if ``x`` is in the category of ``CommutativeAlgebras``.
EXAMPLES::
sage: from sage.rings.commutative_algebra import is_CommutativeAlgebra
- sage: from sage.rings.ring import CommutativeAlgebra
- sage: is_CommutativeAlgebra(CommutativeAlgebra(ZZ))
+ sage: is_CommutativeAlgebra(QQ['x'])
doctest:warning...
DeprecationWarning: the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead
- See https://github.com/sagemath/sage/issues/35253 for details.
+ See https://github.com/sagemath/sage/issues/35999 for details.
True
"""
from sage.misc.superseded import deprecation
- deprecation(35253, "the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead")
+ deprecation(35999, "the function is_CommutativeAlgebra is deprecated; use '... in Algebras(base_ring).Commutative()' instead")
return x in CommutativeAlgebras(x.base_ring())
diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx
index 4585881eaad..9ee95ec8fd3 100644
--- a/src/sage/rings/ring.pyx
+++ b/src/sage/rings/ring.pyx
@@ -8,7 +8,7 @@ specific base classes.
.. WARNING::
Those classes, except maybe for the lowest ones like
- :class:`CommutativeRing` and :class:`CommutativeAlgebra`,
+ :class:`CommutativeRing` and :class:`Field`,
are being progressively deprecated in favor of the corresponding
categories. which are more flexible, in particular with respect to multiple
inheritance.
@@ -21,7 +21,7 @@ The class inheritance hierarchy is:
- :class:`CommutativeRing`
- :class:`NoetherianRing` (deprecated)
- - :class:`CommutativeAlgebra` (to be deprecated)
+ - :class:`CommutativeAlgebra` (deprecated and essentially removed)
- :class:`IntegralDomain` (deprecated)
- :class:`DedekindDomain` (deprecated and essentially removed)
@@ -58,7 +58,7 @@ TESTS:
This is to test a deprecation::
- sage: from sage.rings.ring import DedekindDomain
+ sage: from sage.rings.ring import DedekindDomain, CommutativeAlgebra
sage: class No(DedekindDomain):
....: pass
sage: F = No(QQ)
@@ -67,6 +67,15 @@ This is to test a deprecation::
See https://github.com/sagemath/sage/issues/37234 for details.
sage: F.category()
Category of Dedekind domains
+
+ sage: class Nein(CommutativeAlgebra):
+ ....: pass
+ sage: F = Nein(QQ, QQ)
+ ...:
+ DeprecationWarning: use the category CommutativeAlgebras
+ See https://github.com/sagemath/sage/issues/37999 for details.
+ sage: F.category()
+ Category of commutative rings
"""
# ****************************************************************************
@@ -2150,55 +2159,11 @@ cdef class Algebra(Ring):
cdef class CommutativeAlgebra(CommutativeRing):
- """
- Generic commutative algebra
- """
- def __init__(self, base_ring, names=None, normalize=True, category=None):
- r"""
- Standard init function. This just checks that the base is a commutative
- ring and then passes the buck.
-
- EXAMPLES::
-
- sage: sage.rings.ring.CommutativeAlgebra(QQ)
-
-
- sage: sage.rings.ring.CommutativeAlgebra(QuaternionAlgebra(QQ, -1, -1)) # needs sage.combinat sage.modules
- Traceback (most recent call last):
- ...
- TypeError: base ring must be a commutative ring
- """
- # TODO: use the idiom base_ring in CommutativeRings()
- try:
- if not base_ring.is_commutative():
- raise TypeError("base ring must be a commutative ring")
- except (AttributeError, NotImplementedError):
- raise TypeError("base ring must be a commutative ring")
- # This is a low-level class. For performance, we trust that
- # the category is fine, if it is provided. If it isn't, we use
- # the category of commutative algebras.
- if category is None:
- category = CommutativeAlgebras(base_ring)
- CommutativeRing.__init__(self, base_ring, names=names, normalize=normalize, category=category)
-
- def is_commutative(self):
- """
- Return ``True`` since this algebra is commutative.
-
- EXAMPLES:
-
- Any commutative ring is a commutative algebra over itself::
-
- sage: A = sage.rings.ring.CommutativeAlgebra
- sage: A(ZZ).is_commutative()
- True
- sage: A(QQ).is_commutative()
- True
+ __default_category = CommutativeRings()
- Trying to create a commutative algebra over a non-commutative ring
- will result in a ``TypeError``.
- """
- return True
+ def __init__(self, base_ring, *args, **kwds):
+ deprecation(37999, "use the category CommutativeAlgebras")
+ super().__init__(*args, **kwds)
def is_Ring(x):
From 26408a828d87f67881426ead9f5748da77c597dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 16:47:00 +0100
Subject: [PATCH 056/518] remove almost all calls to is_Ring
---
src/sage/matrix/matrix2.pyx | 12 ++--
src/sage/matrix/matrix_rational_dense.pyx | 4 +-
src/sage/matrix/matrix_sparse.pyx | 4 +-
src/sage/matrix/special.py | 56 +++++++++---------
src/sage/modular/abvar/abvar.py | 8 +--
src/sage/modular/dirichlet.py | 4 +-
src/sage/modules/free_module_element.pyx | 20 +++----
.../quadratic_forms/random_quadraticform.py | 4 +-
src/sage/quadratic_forms/ternary_qf.py | 58 +++++++++----------
src/sage/rings/fraction_field.py | 10 ++--
src/sage/rings/polynomial/polynomial_ring.py | 7 ++-
.../polynomial/polynomial_ring_constructor.py | 6 +-
.../schemes/elliptic_curves/constructor.py | 6 +-
13 files changed, 99 insertions(+), 100 deletions(-)
diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx
index 3f0ddb6d84a..db3197ae532 100644
--- a/src/sage/matrix/matrix2.pyx
+++ b/src/sage/matrix/matrix2.pyx
@@ -75,10 +75,15 @@ AUTHORS:
# the License, or (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************
+from copy import copy
from cpython cimport *
from cysignals.signals cimport sig_check
+from sage.categories.fields import Fields
+from sage.categories.integral_domains import IntegralDomains
+from sage.categories.principal_ideal_domains import PrincipalIdealDomains
+from sage.categories.rings import Rings
from sage.misc.lazy_string import lazy_string
from sage.misc.randstate cimport current_randstate
from sage.structure.coerce cimport py_scalar_parent
@@ -87,16 +92,11 @@ from sage.structure.coerce cimport coercion_model
from sage.structure.element import is_Vector
from sage.structure.element cimport have_same_parent
from sage.misc.verbose import verbose
-from sage.categories.fields import Fields
-from sage.categories.integral_domains import IntegralDomains
-from sage.categories.principal_ideal_domains import PrincipalIdealDomains
-from sage.rings.ring import is_Ring
from sage.rings.number_field.number_field_base import NumberField
from sage.rings.integer_ring import ZZ, is_IntegerRing
from sage.rings.rational_field import QQ, is_RationalField
import sage.rings.abc
from sage.arith.numerical_approx cimport digits_to_bits
-from copy import copy
import sage.modules.free_module
from sage.matrix import berlekamp_massey
@@ -2787,7 +2787,7 @@ cdef class Matrix(Matrix1):
if isinstance(R, type):
R = py_scalar_parent(R)
- if not is_Ring(R):
+ if R not in Rings():
raise TypeError("unable to find a common ring for all elements")
if sparse is None or sparse is self.is_sparse():
diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx
index 6a039b077e3..275dc3c5253 100644
--- a/src/sage/matrix/matrix_rational_dense.pyx
+++ b/src/sage/matrix/matrix_rational_dense.pyx
@@ -72,6 +72,7 @@ Test hashing::
from libc.string cimport strcpy, strlen
+from sage.categories.rings import Rings
from sage.cpython.string cimport char_to_str, str_to_bytes
from sage.modules.vector_rational_dense cimport Vector_rational_dense
@@ -109,7 +110,6 @@ from sage.matrix.args cimport SparseEntry, MatrixArgs_init
from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense, _lift_crt
from sage.structure.element cimport Element, Vector
from sage.rings.integer cimport Integer
-from sage.rings.ring import is_Ring
from sage.rings.integer_ring import ZZ, is_IntegerRing
import sage.rings.abc
from sage.rings.rational_field import QQ
@@ -1459,7 +1459,7 @@ cdef class Matrix_rational_dense(Matrix_dense):
[-+---]
[0|1 2]
"""
- if not is_Ring(R):
+ if R not in Rings():
raise TypeError("R must be a ring")
if R == self._base_ring:
if self._is_immutable:
diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx
index ef707ebe3f8..c537798ba92 100644
--- a/src/sage/matrix/matrix_sparse.pyx
+++ b/src/sage/matrix/matrix_sparse.pyx
@@ -15,9 +15,9 @@ from cysignals.signals cimport sig_check
cimport sage.matrix.matrix as matrix
cimport sage.matrix.matrix0 as matrix0
+from sage.categories.rings import Rings
from sage.structure.element cimport Element, Vector
from sage.structure.richcmp cimport richcmp_item, rich_to_bool
-from sage.rings.ring import is_Ring
from cpython cimport *
from cpython.object cimport Py_EQ, Py_NE
@@ -62,7 +62,7 @@ cdef class Matrix_sparse(matrix.Matrix):
[2.00000000000000*x -2.00000000000000]
[-------------------------------------]
"""
- if not is_Ring(ring):
+ if R not in Rings():
raise TypeError("input must be a ring")
if ring is self._base_ring:
if self._is_immutable:
diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py
index 7233892ce2f..205c5f11497 100644
--- a/src/sage/matrix/special.py
+++ b/src/sage/matrix/special.py
@@ -64,8 +64,8 @@
# ****************************************************************************
from copy import copy
-from sage.rings.ring import is_Ring
import sage.matrix.matrix_space as matrix_space
+from sage.categories.rings import Rings
from sage.modules.free_module_element import vector
from sage.structure.element import is_Matrix, parent
from sage.structure.sequence import Sequence
@@ -845,7 +845,7 @@ def diagonal_matrix(arg0=None, arg1=None, arg2=None, sparse=True):
# Leads with a ring?
# Formats 3, 4, else remains None
ring = None
- if is_Ring(arg0):
+ if arg0 in Rings():
ring = arg0
arg0 = arg1
arg1 = arg2
@@ -1414,10 +1414,10 @@ def elementary_matrix(arg0, arg1=None, **kwds):
"""
import sage.structure.element
# determine ring and matrix size
- if arg1 is not None and not is_Ring(arg0):
+ if arg1 is not None and arg0 not in Rings():
raise TypeError('optional first parameter must be a ring, not {0}'.format(arg0))
scale = kwds.pop('scale', None)
- if is_Ring(arg0):
+ if arg0 in Rings():
R = arg0
arg0 = arg1
elif scale is not None:
@@ -1602,7 +1602,7 @@ def _determine_block_matrix_grid(sub_matrices):
if is_Matrix(M):
sub_width = M.ncols()
sub_height = M.nrows()
- elif M: # non-zero scalar is interpreted as a square matrix
+ elif M: # non-zero scalar is interpreted as a square matrix
if row_heights[i] is None:
sub_width = col_widths[j]
else:
@@ -1939,7 +1939,7 @@ def block_matrix(*args, **kwds):
else:
return matrix_space.MatrixSpace(ZZ, 0, 0)([])
- if len(args) >= 1 and is_Ring(args[0]):
+ if len(args) >= 1 and args[0] in Rings():
# A ring is specified
if kwds.get('ring', args[0]) != args[0]:
raise ValueError("base ring specified twice and they are different")
@@ -2092,7 +2092,7 @@ def block_matrix(*args, **kwds):
M = matrix(ring, row_heights[i], zero_widths[i], 0, sparse=sparse)
zero_widths[i] = 0
else:
- continue # zero-width matrix
+ continue # zero-width matrix
else:
if zero_widths is not None:
M = matrix(ring, row_heights[i], row_heights[i], M, sparse=sparse)
@@ -2722,7 +2722,7 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100):
if ring is QQ or ring is ZZ:
# If upper_bound is not set, don't control entry size.
if upper_bound is None:
- # If size control is not desired, the routine will run slightly faster, particularly with large matrices.
+ # If size control is not desired, the routine will run slightly faster, particularly with large matrices.
for pivots in range(rank-1, -1, -1):
row_index = 0
while row_index < rows:
@@ -2741,30 +2741,30 @@ def random_echelonizable_matrix(parent, rank, upper_bound=None, max_tries=100):
while max(abs(c) for c in matrix.list()) >= upper_bound:
matrix = random_rref_matrix(parent, rank)
tries += 1
- if tries > max_tries: # to prevent endless attempts
+ if tries > max_tries: # to prevent endless attempts
raise ValueError("tried "+str(max_tries)+" times to get a rank 1 random matrix. Try bigger upper_bound?")
matrix_copy = matrix
for pivots in range(len(matrix.pivots()) - 1, -1, -1):
- # keep track of the pivot column positions from the pivot column with the largest index to
- # the one with the smallest.
+ # keep track of the pivot column positions from the pivot column with the largest index to
+ # the one with the smallest.
row_index = 0
tries = 0
while row_index < rows:
# To each row in a pivot column add a scalar multiple of the pivot row.
# for full rank, square matrices, using only this row operation preserves the determinant of 1.
if pivots != row_index:
- # To ensure a leading one is not removed by the addition of the pivot row by its
- # additive inverse.
+ # To ensure a leading one is not removed by the addition of the pivot row by its
+ # additive inverse.
matrix_copy = matrix.with_added_multiple_of_row(row_index,matrix.pivot_rows()[pivots],randint(-5,5))
tries += 1
# Range for scalar multiples determined experimentally.
- if max(map(abs,matrix_copy.list())) < upper_bound:
- # Continue if the largest entry after a row operation is within the bound.
+ if max(map(abs, matrix_copy.list())) < upper_bound:
+ # Continue if the largest entry after a row operation is within the bound.
matrix = matrix_copy
row_index += 1
tries = 0
- if tries > max_tries: # to prevent endless unsuccessful row adding
+ if tries > max_tries: # to prevent endless unsuccessful row adding
raise ValueError("tried "+str(max_tries)+" times to get row number "+str(row_index)+". Try bigger upper_bound?")
# The leading one in row one has not been altered, so add a scalar multiple of a random row
# to row one.
@@ -3192,19 +3192,19 @@ def random_diagonalizable_matrix(parent,eigenvalues=None,dimensions=None):
raise ValueError("the list of dimensions must have a list of corresponding eigenvalues.")
if eigenvalues is None and dimensions is None:
values = []
- #create a list with "size" number of entries
+ # create a list with "size" number of entries
for eigen_index in range(size):
eigenvalue = randint(-10, 10)
values.append(eigenvalue)
values.sort()
dimensions = []
eigenvalues = []
- #create a list with no duplicate values to be the eigenvalues
+ # create a list with no duplicate values to be the eigenvalues
for eigenvalue in range(size):
if values[eigenvalue] not in eigenvalues:
eigenvalues.append(values[eigenvalue])
for dimension in range(len(eigenvalues)):
- #dimension is equal to how many times an eigenvalue was generated in the 'values' list
+ # dimension is equal to how many times an eigenvalue was generated in the 'values' list
dimensions.append(values.count(eigenvalues[dimension]))
size_check = 0
for check in range(len(dimensions)):
@@ -3217,11 +3217,11 @@ def random_diagonalizable_matrix(parent,eigenvalues=None,dimensions=None):
raise ValueError("eigenspaces must have a dimension of at least 1.")
if len(eigenvalues) != len(dimensions):
raise ValueError("each eigenvalue must have a corresponding dimension and each dimension a corresponding eigenvalue.")
- #sort the dimensions in order of increasing size, and sort the eigenvalues list in an identical fashion, to maintain corresponding values.
+ # sort the dimensions in order of increasing size, and sort the eigenvalues list in an identical fashion, to maintain corresponding values.
dimensions_sort = sorted(zip(dimensions, eigenvalues))
dimensions = [x[0] for x in dimensions_sort]
eigenvalues = [x[1] for x in dimensions_sort]
- #Create the matrix of eigenvalues on the diagonal. Use a lower limit and upper limit determined by the eigenvalue dimensions.
+ # Create the matrix of eigenvalues on the diagonal. Use a lower limit and upper limit determined by the eigenvalue dimensions.
diagonal_matrix = matrix(QQ, size)
up_bound = 0
low_bound = 0
@@ -3235,33 +3235,33 @@ def random_diagonalizable_matrix(parent,eigenvalues=None,dimensions=None):
eigenvector_matrix = matrix(QQ, size, size, 1)
upper_limit = 0
lower_limit = 0
- #run the routine over the necessary number of columns corresponding eigenvalue dimension.
+ # run the routine over the necessary number of columns corresponding eigenvalue dimension.
for dimension_index in range(len(dimensions)-1):
upper_limit = upper_limit+dimensions[dimension_index]
lowest_index_row_with_one = size-dimensions[dimension_index]
- #assign a one to the row that is the eigenvalue dimension rows up from the bottom row then assign ones diagonally down to the right.
+ # assign a one to the row that is the eigenvalue dimension rows up from the bottom row then assign ones diagonally down to the right.
for eigen_ones in range(lower_limit,upper_limit):
eigenvector_matrix[lowest_index_row_with_one,eigen_ones] = 1
lowest_index_row_with_one += 1
lower_limit = lower_limit+dimensions[dimension_index]
- #Create a list to give the eigenvalue dimension corresponding to each column.
+ # Create a list to give the eigenvalue dimension corresponding to each column.
dimension_check = []
for i in range(len(dimensions)):
for k in range(dimensions[i]):
dimension_check.append(dimensions[i])
- #run routine over the rows that are in the range of the protected ones. Use addition of column multiples to fill entries.
+ # run routine over the rows that are in the range of the protected ones. Use addition of column multiples to fill entries.
for dimension_multiplicity in range(max(dimensions),min(dimensions),-1):
highest_one_row = size-dimension_multiplicity
highest_one_column = 0
- #find the column with the protected one in the lowest indexed row.
+ # find the column with the protected one in the lowest indexed row.
while eigenvector_matrix[highest_one_row,highest_one_column] == 0:
highest_one_column += 1
- #dimension_check determines if column has a low enough eigenvalue dimension to take a column multiple.
+ # dimension_check determines if column has a low enough eigenvalue dimension to take a column multiple.
for bottom_entry_filler in range(len(dimension_check)):
if dimension_check[bottom_entry_filler] < dimension_multiplicity and eigenvector_matrix[highest_one_row,bottom_entry_filler] == 0:
# randint range determined experimentally to keep entries manageable.
eigenvector_matrix.add_multiple_of_column(bottom_entry_filler,highest_one_column,randint(-4,4))
- #Fill remaining rows using scalar row addition.
+ # Fill remaining rows using scalar row addition.
for row in range(size-max(dimensions),size):
for upper_row in range(size-max(dimensions)):
# range of multiplier determined experimentally so that entries stay manageable for small matrices
diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py
index f47e1fc7448..d431c24267e 100644
--- a/src/sage/modular/abvar/abvar.py
+++ b/src/sage/modular/abvar/abvar.py
@@ -37,6 +37,7 @@
from sage.arith.functions import lcm as LCM
from sage.arith.misc import divisors, next_prime, is_prime
+from sage.categories.fields import Fields
from sage.categories.modular_abelian_varieties import ModularAbelianVarieties
from sage.matrix.constructor import matrix
from sage.matrix.special import block_diagonal_matrix, identity_matrix
@@ -58,7 +59,6 @@
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.rational_field import QQ
-from sage.rings.ring import is_Ring
from sage.schemes.elliptic_curves.constructor import EllipticCurve
from sage.sets.primes import Primes
from sage.structure.parent import Parent
@@ -172,7 +172,7 @@ def __init__(self, groups, base_field, is_simple=None, newform_level=None,
self.__degen_t = number
if isogeny_number is not None:
self.__isogeny_number = isogeny_number
- if check and not is_Ring(base_field) and base_field.is_field():
+ if check and base_field not in Fields():
raise TypeError("base_field must be a field")
Parent.__init__(self, base=base_field,
category=ModularAbelianVarieties(base_field))
@@ -2060,8 +2060,8 @@ def newform_level(self, none_if_not_known=False):
if none_if_not_known:
return None
level = LCM([f.level() for f in self.newform_decomposition('a')])
- groups = sorted({f.group() for f in
- self.newform_decomposition('a')})
+ groups = sorted({f.group()
+ for f in self.newform_decomposition('a')})
if len(groups) == 1:
groups = groups[0]
self.__newform_level = level, groups
diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py
index b8d988b3112..16e83811ffd 100644
--- a/src/sage/modular/dirichlet.py
+++ b/src/sage/modular/dirichlet.py
@@ -65,6 +65,7 @@
from sage.arith.misc import bernoulli, binomial, factorial, kronecker, factor, gcd, fundamental_discriminant, euler_phi, valuation
from sage.categories.map import Map
from sage.categories.objects import Objects
+from sage.categories.rings import Rings
from sage.misc.cachefunc import cached_method
from sage.misc.fast_methods import WithEqualityById
from sage.misc.functional import round
@@ -77,7 +78,6 @@
from sage.rings.integer_ring import ZZ
from sage.rings.power_series_ring import PowerSeriesRing
from sage.rings.rational_field import QQ, is_RationalField
-from sage.rings.ring import is_Ring
from sage.structure.element import MultiplicativeGroupElement
from sage.structure.factory import UniqueFactory
from sage.structure.gens_py import multiplicative_iterator
@@ -2481,7 +2481,7 @@ def create_key(self, N, base_ring=None, zeta=None, zeta_order=None,
if integral:
base_ring = base_ring.ring_of_integers()
- if not is_Ring(base_ring):
+ if base_ring not in Rings():
raise TypeError("base_ring (= %s) must be a ring" % base_ring)
# If either zeta or zeta_order is given, compute the other.
diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx
index 3492dbf2eb1..82d93179154 100644
--- a/src/sage/modules/free_module_element.pyx
+++ b/src/sage/modules/free_module_element.pyx
@@ -115,13 +115,13 @@ This is a test from :trac:`20211`::
cimport cython
from cpython.slice cimport PySlice_GetIndicesEx
+from sage.categories.rings import Rings
from sage.structure.sequence import Sequence
from sage.structure.element cimport Element, RingElement, Vector
from sage.structure.element import canonical_coercion
from sage.structure.richcmp cimport richcmp_not_equal, richcmp, rich_to_bool
import sage.rings.abc
-from sage.rings.ring import is_Ring
from sage.rings.infinity import Infinity, AnInfinity
from sage.rings.integer_ring import ZZ
from sage.rings.abc import RealDoubleField, ComplexDoubleField
@@ -483,9 +483,8 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False):
# over a ring. See trac 11657.
# !! PLEASE DO NOT MOVE THIS CODE LOWER IN THIS FUNCTION !!
arg1_integer = isinstance(arg1, (int, Integer))
- if arg2 is None and is_Ring(arg0) and arg1_integer:
- M = FreeModule(arg0, arg1, bool(sparse))
- v = M.zero_vector()
+ if arg2 is None and arg1_integer and arg0 in Rings():
+ v = FreeModule(arg0, arg1, bool(sparse)).zero_vector()
if immutable:
v.set_immutable()
return v
@@ -527,7 +526,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False):
# else we size-check arg2 and slide it into arg1
degree = arg1
if arg2 is None:
- if not is_Ring(arg0):
+ if arg0 not in Rings():
msg = "first argument must be base ring of zero vector, not {0}"
raise TypeError(msg.format(arg0))
else:
@@ -536,10 +535,10 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False):
arg1 = arg2
# Analyze arg0 and arg1 to create a ring (R) and entries (v)
- if is_Ring(arg0):
+ if arg0 in Rings():
R = arg0
v = arg1
- elif is_Ring(arg1):
+ elif arg1 in Rings():
R = arg1
v = arg0
else:
@@ -686,10 +685,11 @@ def prepare(v, R, degree=None):
pass
v = Sequence(v, universe=R, use_sage_types=True)
ring = v.universe()
- if not is_Ring(ring):
+ if ring not in Rings():
raise TypeError("unable to find a common ring for all elements")
return v, ring
+
def zero_vector(arg0, arg1=None):
r"""
Returns a vector or free module element with a specified number of zeros.
@@ -763,7 +763,7 @@ def zero_vector(arg0, arg1=None):
arg0 = ZZ(arg0)
# default to a zero vector over the integers (ZZ) if no ring given
return (ZZ**arg0).zero_vector()
- if is_Ring(arg0):
+ if arg0 in Rings():
return (arg0**arg1).zero_vector()
raise TypeError("first argument must be a ring")
@@ -922,7 +922,7 @@ def random_vector(ring, degree=None, *args, **kwds):
raise TypeError("degree of a random vector must be an integer, not %s" % degree)
if degree < 0:
raise ValueError("degree of a random vector must be non-negative, not %s" % degree)
- if not is_Ring(ring):
+ if ring not in Rings():
raise TypeError("elements of a vector, or module element, must come from a ring, not %s" % ring)
if not hasattr(ring, "random_element"):
raise AttributeError("cannot create a random vector since there is no random_element() method for %s" % ring )
diff --git a/src/sage/quadratic_forms/random_quadraticform.py b/src/sage/quadratic_forms/random_quadraticform.py
index aa6a069a8b5..e372c625f57 100644
--- a/src/sage/quadratic_forms/random_quadraticform.py
+++ b/src/sage/quadratic_forms/random_quadraticform.py
@@ -3,9 +3,9 @@
This file contains a set of routines to create a random quadratic form.
"""
+from sage.categories.rings import Rings
from sage.quadratic_forms.quadratic_form import QuadraticForm
from sage.quadratic_forms.ternary_qf import TernaryQF
-from sage.rings.ring import is_Ring
from sage.rings.integer_ring import ZZ
@@ -60,7 +60,7 @@ def random_quadraticform(R, n, rand_arg_list=[]):
if len(rand_arg_list) > 3:
raise TypeError("the list of randomness arguments can have "
"at most 3 elements")
- if not is_Ring(R):
+ if R not in Rings():
raise TypeError("the first argument must be a ring")
# Create a list of upper-triangular entries for the quadratic form
n2 = (n * (n + 1)) // 2
diff --git a/src/sage/quadratic_forms/ternary_qf.py b/src/sage/quadratic_forms/ternary_qf.py
index 2808c04548f..d5384632694 100644
--- a/src/sage/quadratic_forms/ternary_qf.py
+++ b/src/sage/quadratic_forms/ternary_qf.py
@@ -27,6 +27,7 @@
# ****************************************************************************
from sage.arith.misc import gcd, kronecker as kronecker_symbol
+from sage.categories.rings import Rings
from sage.matrix.constructor import matrix, identity_matrix
from sage.misc.prandom import randint
from sage.quadratic_forms.quadratic_form import QuadraticForm
@@ -42,7 +43,6 @@
from sage.rings.finite_rings.integer_mod import mod
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring import polygens
-from sage.rings.ring import is_Ring
from sage.structure.element import is_Vector, is_Matrix
from sage.structure.sage_object import SageObject
@@ -166,7 +166,7 @@ def polynomial(self, names='x,y,z'):
sage: p.parent()
Multivariate Polynomial Ring in x, y, z over Integer Ring
"""
- (x,y,z) = polygens(ZZ,names)
+ x, y, z = polygens(ZZ, names)
return self._a * x**2 + self._b * y**2 + self._c * z**2 + self._t * x*y + self._s * x*z + self._r * y*z
def _repr_(self):
@@ -233,12 +233,13 @@ def __call__(self, v):
# Check if v has 3 cols
if v.ncols() == 3:
M = v.transpose() * self.matrix() * v
- return TernaryQF([M[0,0]//2, M[1,1]//2, M[2,2]//2, M[1,2], M[0,2], M[0,1]])
+ return TernaryQF([M[0, 0]//2, M[1, 1]//2, M[2, 2]//2,
+ M[1, 2], M[0, 2], M[0, 1]])
else:
return QuadraticForm(ZZ, v.transpose() * self.matrix() * v)
elif (is_Vector(v) or isinstance(v, (list, tuple))):
# Check that v has length 3
- if not (len(v) == 3):
+ if len(v) != 3:
raise TypeError("your vector needs to have length 3")
v0, v1, v2 = v
a, b, c, r, s, t = self.coefficients()
@@ -521,19 +522,16 @@ def scale_by_factor(self, k):
[ * 2/3 0 ]
[ * * 4/3 ]
"""
- if k*self.content() in ZZ:
+ if k * self.content() in ZZ:
+ return TernaryQF([ZZ(k*self._a), ZZ(k*self._b), ZZ(k*self._c),
+ ZZ(k*self._r), ZZ(k*self._s), ZZ(k*self._t)])
- return TernaryQF([ZZ(k*self._a), ZZ(k*self._b), ZZ(k*self._c), ZZ(k*self._r), ZZ(k*self._s), ZZ(k*self._t)])
+ R = k.parent()
+ if R not in Rings():
+ raise TypeError(f"{k} does not belong to a ring")
- else:
- # arreglar con un try?
- R = k.parent()
- if is_Ring(R):
-
- return QuadraticForm(R, 3, [k*self._a, k*self._t, k*self._s, k*self._b, k*self._r, k*self._c])
-
- else:
- raise TypeError(f"{k} does not belong to a Ring")
+ return QuadraticForm(R, 3, [k * self._a, k * self._t, k * self._s,
+ k * self._b, k * self._r, k * self._c])
def reciprocal(self):
"""
@@ -735,7 +733,8 @@ def is_eisenstein_reduced(self) -> bool:
sage: Q.is_eisenstein_reduced()
False
"""
- [a,b,c,r,s,t] = [self._a,self._b,self._c,self._r,self._s,self._t]
+ a, b, c, r, s, t = [self._a, self._b, self._c,
+ self._r, self._s, self._t]
# cond 2
if not (r > 0 and t > 0 and s > 0):
@@ -809,11 +808,11 @@ def reduced_form_eisenstein(self, matrix=True):
[3 2 1]
"""
if matrix:
- [v,M] = _reduced_ternary_form_eisenstein_with_matrix(self._a,self._b,self._c,self._r,self._s,self._t)
+ v, M = _reduced_ternary_form_eisenstein_with_matrix(self._a, self._b, self._c, self._r, self._s, self._t)
return TernaryQF(v), M
- else:
- v = _reduced_ternary_form_eisenstein_without_matrix(self._a,self._b,self._c,self._r,self._s,self._t)
- return TernaryQF(v)
+
+ v = _reduced_ternary_form_eisenstein_without_matrix(self._a, self._b, self._c, self._r, self._s, self._t)
+ return TernaryQF(v)
def pseudorandom_primitive_zero_mod_p(self, p):
"""
@@ -835,7 +834,7 @@ def pseudorandom_primitive_zero_mod_p(self, p):
sage: v[2] # needs sage.libs.pari
1
"""
- [a,b,c,r,s,t] = self.coefficients()
+ a, b, c, r, s, t = self.coefficients()
while True:
r1 = randint(0,p-1)
@@ -848,7 +847,7 @@ def pseudorandom_primitive_zero_mod_p(self, p):
disc = beta**2-4*alpha*gamma
if mod(disc, p).is_square():
- z = (-beta+mod(disc,p).sqrt().lift())*(2*alpha).inverse_mod(p)
+ z = (-beta+mod(disc, p).sqrt().lift())*(2*alpha).inverse_mod(p)
# return vector((z,r1*z+r2,1))%p
return z % p, (r1*z+r2) % p, 1
@@ -871,16 +870,13 @@ def find_zeros_mod_p(self, p):
sage: [Q(v)%17 for v in zeros_17] # needs sage.libs.pari
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"""
-
if p == 2:
+ return _find_zeros_mod_p_2(self._a, self._b, self._c,
+ self._r, self._s, self._t)
- return _find_zeros_mod_p_2(self._a, self._b, self._c, self._r, self._s, self._t)
-
- else:
-
- v = self.pseudorandom_primitive_zero_mod_p(p)
- [a, b, c, r, s, t] = self.coefficients()
- return _find_zeros_mod_p_odd(a, b, c, r, s, t, p, v)
+ v = self.pseudorandom_primitive_zero_mod_p(p)
+ a, b, c, r, s, t = self.coefficients()
+ return _find_zeros_mod_p_odd(a, b, c, r, s, t, p, v)
def find_p_neighbor_from_vec(self, p, v, mat=False):
r"""
@@ -1775,7 +1771,7 @@ def automorphisms(self, slow=True):
auts = self._automorphisms_reduced_fast()
self._automorphisms = [matrix(ZZ, 3, A) for A in auts]
else:
- [Qr, M] = self.reduced_form_eisenstein()
+ Qr, M = self.reduced_form_eisenstein()
auts = Qr.automorphisms(slow)
M_inv = M.inverse()
self._automorphisms = [M*m*M_inv for m in auts]
diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py
index bfa690cec0e..38af4f084e8 100644
--- a/src/sage/rings/fraction_field.py
+++ b/src/sage/rings/fraction_field.py
@@ -80,7 +80,7 @@
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
-# http://www.gnu.org/licenses/
+# https://www.gnu.org/licenses/
# ****************************************************************************
import sage.misc.latex as latex
@@ -135,16 +135,16 @@ def FractionField(R, names=None):
sage: Frac(Integers(4))
Traceback (most recent call last):
...
- TypeError: R must be an integral domain.
+ TypeError: R must be an integral domain
"""
- if not ring.is_Ring(R):
+ if R not in Rings():
raise TypeError("R must be a ring")
if not R.is_integral_domain():
- raise TypeError("R must be an integral domain.")
+ raise TypeError("R must be an integral domain")
return R.fraction_field()
-def is_FractionField(x):
+def is_FractionField(x) -> bool:
"""
Test whether or not ``x`` inherits from :class:`FractionField_generic`.
diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py
index c38fd158f53..9649765449c 100644
--- a/src/sage/rings/polynomial/polynomial_ring.py
+++ b/src/sage/rings/polynomial/polynomial_ring.py
@@ -147,9 +147,9 @@
import sage.categories as categories
from sage.categories.morphism import IdentityMorphism
+from sage.categories.rings import Rings
-from sage.rings.ring import (Ring, IntegralDomain,
- PrincipalIdealDomain, is_Ring)
+from sage.rings.ring import (Ring, IntegralDomain, PrincipalIdealDomain)
from sage.structure.element import is_RingElement
import sage.rings.rational_field as rational_field
from sage.rings.rational_field import QQ
@@ -3596,7 +3596,7 @@ def polygen(ring_or_element, name="x"):
"""
if is_RingElement(ring_or_element):
base_ring = ring_or_element.parent()
- elif is_Ring(ring_or_element):
+ elif ring_or_element in Rings():
base_ring = ring_or_element
else:
raise TypeError("input must be a ring or ring element")
@@ -3607,6 +3607,7 @@ def polygen(ring_or_element, name="x"):
return t.gens()
return t.gen()
+
def polygens(base_ring, names="x", *args):
"""
Return indeterminates over the given base ring with the given
diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py
index 42391e37f61..38f1c5e8211 100644
--- a/src/sage/rings/polynomial/polynomial_ring_constructor.py
+++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py
@@ -20,8 +20,9 @@
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************
+
from sage.structure.category_object import normalize_names
-from sage.rings.ring import is_Ring, IntegralDomain
+from sage.rings.ring import IntegralDomain
try:
import sage.rings.padics.padic_base_leaves as padic_base_leaves
@@ -39,6 +40,7 @@ class padic_base_leaves:
from sage.misc.cachefunc import weak_cached_function
import sage.misc.weak_dict
+from sage.categories.rings import Rings
from sage.categories.fields import Fields
from sage.categories.commutative_rings import CommutativeRings
from sage.categories.domains import Domains
@@ -621,7 +623,7 @@ def PolynomialRing(base_ring, *args, **kwds):
sage: R. = PolynomialRing(RIF,2)
sage: TestSuite(R).run(skip=['_test_elements', '_test_elements_eq_transitive'])
"""
- if not is_Ring(base_ring):
+ if base_ring not in Rings():
raise TypeError("base_ring {!r} must be a ring".format(base_ring))
n = -1 # Unknown number of variables
diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py
index 396a2ab2797..0a9e9b3eea9 100644
--- a/src/sage/schemes/elliptic_curves/constructor.py
+++ b/src/sage/schemes/elliptic_curves/constructor.py
@@ -32,8 +32,8 @@
from sage.rings.number_field.number_field_base import NumberField
from sage.rings.finite_rings.finite_field_base import FiniteField
from sage.rings.polynomial.multi_polynomial import MPolynomial
-from sage.rings.ring import is_Ring
+from sage.categories.rings import Rings
from sage.categories.fields import Fields
_Fields = Fields()
@@ -405,8 +405,8 @@ def create_key_and_extra_args(self, x=None, y=None, j=None, minimal_twist=True,
True
"""
R = None
- if is_Ring(x):
- (R, x) = (x, y)
+ if x in Rings():
+ R, x = (x, y)
if j is not None:
if R is not None:
From 559174e8da4329690c9f00dcf488497350c66b4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 18:31:21 +0100
Subject: [PATCH 057/518] parent for pari
---
src/sage/rings/pari_ring.py | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py
index e11dcf4b123..f34a45829a0 100644
--- a/src/sage/rings/pari_ring.py
+++ b/src/sage/rings/pari_ring.py
@@ -14,10 +14,11 @@
#
# The full text of the GPL is available at:
#
-# http://www.gnu.org/licenses/
+# https://www.gnu.org/licenses/
# ****************************************************************************
import sage.libs.pari.all as pari
-from sage.rings.ring import Ring
+from sage.categories.rings import Rings
+from sage.structure.parent import Parent
from sage.structure.element import RingElement
from sage.structure.richcmp import richcmp
from sage.misc.fast_methods import Singleton
@@ -27,7 +28,7 @@ class Pari(RingElement):
"""
Element of Pari pseudo-ring.
"""
- def __init__(self, x, parent=None):
+ def __init__(self, x, parent=None) -> None:
"""
EXAMPLES::
@@ -45,7 +46,7 @@ def __init__(self, x, parent=None):
RingElement.__init__(self, parent)
self.__x = pari.pari(x)
- def __repr__(self):
+ def __repr__(self) -> str:
"""
EXAMPLES::
@@ -138,7 +139,7 @@ def __invert__(self):
"""
return self.__class__(~self.__x, parent=_inst)
- def _richcmp_(self, other, op):
+ def _richcmp_(self, other, op) -> bool:
"""
EXAMPLES::
@@ -154,11 +155,11 @@ def _richcmp_(self, other, op):
"""
return richcmp(self.__x, other.__x, op)
- def __int__(self):
+ def __int__(self) -> int:
return int(self.__x)
-class PariRing(Singleton, Ring):
+class PariRing(Singleton, Parent):
"""
EXAMPLES::
@@ -170,9 +171,9 @@ class PariRing(Singleton, Ring):
Element = Pari
def __init__(self):
- Ring.__init__(self, self)
+ Parent.__init__(self, self, category=Rings())
- def __repr__(self):
+ def __repr__(self) -> str:
return 'Pseudoring of all PARI objects.'
def _element_constructor_(self, x):
@@ -180,7 +181,7 @@ def _element_constructor_(self, x):
return x
return self.element_class(x, parent=self)
- def is_field(self, proof=True):
+ def is_field(self, proof=True) -> bool:
return False
def characteristic(self):
From 95b4ad360bf2bdbd6f3cb95f2595379d213ac6f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 20:31:55 +0100
Subject: [PATCH 058/518] some list comprehensions in rings (ruff PERF)
---
...ptotics_multivariate_generating_functions.py | 16 +++++-----------
src/sage/rings/derivation.py | 12 +++---------
src/sage/rings/fraction_field.py | 9 ++++-----
src/sage/rings/function_field/divisor.py | 7 ++++---
src/sage/rings/invariants/invariant_theory.py | 17 +++++++----------
src/sage/rings/number_field/S_unit_solver.py | 10 ++++------
src/sage/rings/number_field/bdd_height.py | 11 ++++-------
src/sage/rings/number_field/number_field.py | 11 ++++-------
src/sage/rings/number_field/totallyreal_rel.py | 8 +++-----
src/sage/rings/padics/generic_nodes.py | 7 +++----
src/sage/rings/qqbar.py | 7 +++----
11 files changed, 44 insertions(+), 71 deletions(-)
diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
index c91ea7a6e0a..039c0e8b805 100644
--- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
+++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
@@ -2647,10 +2647,8 @@ def is_convenient_multiple_point(self, p):
# Test 4: Is p convenient?
M = matrix(self.log_grads(p))
- convenient_coordinates = []
- for j in range(d):
- if 0 not in M.columns()[j]:
- convenient_coordinates.append(j)
+ convenient_coordinates = [j for j in range(d)
+ if 0 not in M.columns()[j]]
if not convenient_coordinates:
return (False, 'multiple point but not convenient')
@@ -2746,10 +2744,8 @@ def smooth_critical_ideal(self, alpha):
d = self.dimension()
# Expand K by the variables of alpha if there are any.
- indets = []
- for a in alpha:
- if a not in K and a in SR:
- indets.append(a)
+ indets = [a for a in alpha if a not in K and a in SR]
+
indets = sorted(set(indets), key=str) # Delete duplicates in indets.
if indets:
L = PolynomialRing(K, indets).fraction_field()
@@ -2845,9 +2841,7 @@ def maclaurin_coefficients(self, multi_indices, numerical=0):
return coeffs
# Create biggest multi-index needed.
- alpha = []
- for i in range(d):
- alpha.append(max(nu[i] for nu in multi_indices))
+ alpha = [max(nu[i] for nu in multi_indices) for i in range(d)]
# Compute Maclaurin expansion of self up to index alpha.
# Use iterated univariate expansions.
diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py
index d7d4fe6ebdf..2702f497d66 100644
--- a/src/sage/rings/derivation.py
+++ b/src/sage/rings/derivation.py
@@ -1133,9 +1133,7 @@ def _bracket_(self, other):
parent = self.parent()
if parent.domain() is not parent.codomain():
raise TypeError("the bracket is only defined for derivations with same domain and codomain")
- arg = [ ]
- for x in parent.dual_basis():
- arg.append(self(other(x)) - other(self(x)))
+ arg = [self(other(x)) - other(self(x)) for x in parent.dual_basis()]
return parent(arg)
def pth_power(self):
@@ -1268,9 +1266,7 @@ def precompose(self, morphism):
elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())):
raise TypeError("you must give a homomorphism of rings")
M = RingDerivationModule(morphism.domain(), parent.defining_morphism() * morphism)
- arg = [ ]
- for x in M.dual_basis():
- arg.append(self(morphism(x)))
+ arg = [self(morphism(x)) for x in M.dual_basis()]
return M(arg)
def postcompose(self, morphism):
@@ -1328,9 +1324,7 @@ def postcompose(self, morphism):
elif not (isinstance(morphism, Map) and morphism.category_for().is_subcategory(Rings())):
raise TypeError("you must give a homomorphism of rings")
M = RingDerivationModule(parent.domain(), morphism * parent.defining_morphism())
- arg = [ ]
- for x in M.dual_basis():
- arg.append(morphism(self(x)))
+ arg = [morphism(self(x)) for x in M.dual_basis()]
return M(arg)
def extend_to_fraction_field(self):
diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py
index bfa690cec0e..92d53b9048e 100644
--- a/src/sage/rings/fraction_field.py
+++ b/src/sage/rings/fraction_field.py
@@ -917,13 +917,12 @@ def some_elements(self):
(2*x^2 + 2)/x^3,
(2*x^2 + 2)/(x^2 - 1),
2]
-
"""
ret = [self.zero(), self.one()]
- for a in self._R.some_elements():
- for b in self._R.some_elements():
- if a != b and self(a) and self(b):
- ret.append(self(a) / self(b))
+ ret.extend(self(a) / self(b)
+ for a in self._R.some_elements()
+ for b in self._R.some_elements()
+ if a != b and self(a) and self(b))
return ret
def _gcd_univariate_polynomial(self, f, g):
diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py
index 84c837ffb09..79d952e2247 100644
--- a/src/sage/rings/function_field/divisor.py
+++ b/src/sage/rings/function_field/divisor.py
@@ -871,9 +871,10 @@ def _basis(self):
# invariants of M.
basis = []
for j in range(n):
- i,ideg = pivot_row[j][0]
- for k in range( den.degree() - ideg + 1 ):
- basis.append(one.shift(k) * gens[i])
+ i, ideg = pivot_row[j][0]
+ gi = gens[i]
+ basis.extend(one.shift(k) * gi
+ for k in range(den.degree() - ideg + 1))
# Done!
return basis
diff --git a/src/sage/rings/invariants/invariant_theory.py b/src/sage/rings/invariants/invariant_theory.py
index 6d5e8646186..1420192a67a 100644
--- a/src/sage/rings/invariants/invariant_theory.py
+++ b/src/sage/rings/invariants/invariant_theory.py
@@ -1038,18 +1038,15 @@ def monomials(self):
def prod(a, b):
if a is None and b is None:
return self._ring.one()
- elif a is None:
+ if a is None:
return b
- elif b is None:
+ if b is None:
return a
- else:
- return a * b
- squares = tuple( prod(x,x) for x in var )
- mixed = []
- for i in range(self._n):
- for j in range(i+1, self._n):
- mixed.append(prod(var[i], var[j]))
- mixed = tuple(mixed)
+ return a * b
+
+ squares = tuple(prod(x, x) for x in var)
+ mixed = tuple([prod(var[i], var[j]) for i in range(self._n)
+ for j in range(i + 1, self._n)])
return squares + mixed
@cached_method
diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py
index 4b6b201375f..1bc9e07d9c0 100644
--- a/src/sage/rings/number_field/S_unit_solver.py
+++ b/src/sage/rings/number_field/S_unit_solver.py
@@ -150,23 +150,21 @@ def c3_func(SUK, prec=106):
- [AKMRVW]_ :arxiv:`1903.00977`
"""
-
R = RealField(prec)
all_places = list(SUK.primes()) + SUK.number_field().places(prec)
Possible_U = Combinations(all_places, SUK.rank())
- c1 = R(1) # guarantees final c1 >= 1
+ c1 = R(1) # guarantees final c1 >= 1
for U in Possible_U:
# first, build the matrix C_{i,U}
- columns_of_C = []
- for unit in SUK.fundamental_units():
- columns_of_C.append(column_Log(SUK, unit, U, prec))
+ columns_of_C = [column_Log(SUK, unit, U, prec)
+ for unit in SUK.fundamental_units()]
C = matrix(SUK.rank(), SUK.rank(), columns_of_C)
# Is it invertible?
if abs(C.determinant()) > 10**(-10):
poss_c1 = C.inverse().apply_map(abs).norm(Infinity)
c1 = R(max(poss_c1, c1))
- return R(0.9999999) / (c1*SUK.rank())
+ return R(0.9999999) / (c1 * SUK.rank())
def c4_func(SUK, v, A, prec=106):
diff --git a/src/sage/rings/number_field/bdd_height.py b/src/sage/rings/number_field/bdd_height.py
index 2b3d18a6578..262f7f8a2ad 100644
--- a/src/sage/rings/number_field/bdd_height.py
+++ b/src/sage/rings/number_field/bdd_height.py
@@ -188,7 +188,7 @@ def bdd_height_iq(K, height_bound):
possible_norm_set = set()
for n in range(class_number):
for m in range(1, int(height_bound + 1)):
- possible_norm_set.add(m*class_group_rep_norms[n])
+ possible_norm_set.add(m * class_group_rep_norms[n])
bdd_ideals = bdd_norm_pr_gens_iq(K, possible_norm_set)
# Distribute the principal ideals
@@ -196,11 +196,8 @@ def bdd_height_iq(K, height_bound):
for n in range(class_number):
this_ideal = class_group_reps[n]
this_ideal_norm = class_group_rep_norms[n]
- gens = []
- for i in range(1, int(height_bound + 1)):
- for g in bdd_ideals[i*this_ideal_norm]:
- if g in this_ideal:
- gens.append(g)
+ gens = [g for i in range(1, int(height_bound + 1))
+ for g in bdd_ideals[i * this_ideal_norm] if g in this_ideal]
generator_lists.append(gens)
# Build all the output numbers
@@ -210,7 +207,7 @@ def bdd_height_iq(K, height_bound):
for i in range(s):
for j in range(i + 1, s):
if K.ideal(gens[i], gens[j]) == class_group_reps[n]:
- new_number = gens[i]/gens[j]
+ new_number = gens[i] / gens[j]
for zeta in roots_of_unity:
yield zeta * new_number
yield zeta / new_number
diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py
index 5fd638f9396..2ba3a012abe 100644
--- a/src/sage/rings/number_field/number_field.py
+++ b/src/sage/rings/number_field/number_field.py
@@ -10438,9 +10438,8 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True):
L.append(P)
# This adds some infinite places to L
- for sigma in self.real_places():
- if sigma(b) < 0 and sigma not in S:
- L.append(sigma)
+ L.extend(sigma for sigma in self.real_places()
+ if sigma(b) < 0 and sigma not in S)
Cl = self.class_group(proof=False)
U = self.unit_group(proof=False).gens()
SL = S + L
@@ -10462,10 +10461,8 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True):
# on the set of generators
def phi(x):
- v = []
- for p in SL:
- v.append((1-self.hilbert_symbol(x, b, p))//2)
- return V(v)
+ return V([(1 - self.hilbert_symbol(x, b, p)) // 2 for p in SL])
+
M = matrix([phi(g) for g in U])
# we have to work around the inconvenience that multiplicative
diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py
index 9b416d2ad02..baf5aca9f68 100644
--- a/src/sage/rings/number_field/totallyreal_rel.py
+++ b/src/sage/rings/number_field/totallyreal_rel.py
@@ -453,15 +453,13 @@ def incr(self, f_out, verbose=False, haltk=0):
# Enumerate all elements of Z_F with T_2 <= br
T2s = []
trace_elts_found = False
- for i in range(len(self.trace_elts)):
- tre = self.trace_elts[i]
+ for tre in self.trace_elts:
if tre[0] <= bl and tre[1] >= br:
trace_elts_found = True
if verbose >= 2:
print(" found copy!")
- for theta in tre[2]:
- if theta.trace() >= bl and theta.trace() <= br:
- T2s.append(theta)
+ T2s.extend(theta for theta in tre[2]
+ if bl <= theta.trace() <= br)
break
if not trace_elts_found:
T2s = self.F._positive_integral_elements_with_trace([bl,br])
diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py
index 3a135548a32..44cd8b7af4e 100644
--- a/src/sage/rings/padics/generic_nodes.py
+++ b/src/sage/rings/padics/generic_nodes.py
@@ -683,10 +683,8 @@ def convert_multiple(self, *elts):
raise NotImplementedError("multiple conversion of a set of variables for which the module precision is not a lattice is not implemented yet")
for j in range(len(L)):
x = L[j]
- dx = []
- for i in range(j):
- dx.append([L[i], lattice[i,j]])
- prec = lattice[j,j].valuation(p)
+ dx = [[L[i], lattice[i, j]] for i in range(j)]
+ prec = lattice[j, j].valuation(p)
y = self._element_class(self, x.value(), prec, dx=dx, dx_mode='values', check=False, reduce=False)
for i in indices[id(x)]:
ans[i] = y
@@ -700,6 +698,7 @@ def convert_multiple(self, *elts):
# We return the created elements
return ans
+
class pAdicRelaxedGeneric(pAdicGeneric):
r"""
Generic class for relaxed `p`-adics.
diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py
index 4d92d64d80f..2c0f5dbc6d8 100644
--- a/src/sage/rings/qqbar.py
+++ b/src/sage/rings/qqbar.py
@@ -4801,11 +4801,10 @@ def radical_expression(self):
roots = poly.roots(SR, multiplicities=False)
if len(roots) != poly.degree():
return self
+ itv = interval_field(self._value)
while True:
- candidates = []
- for root in roots:
- if interval_field(root).overlaps(interval_field(self._value)):
- candidates.append(root)
+ candidates = [root for root in roots
+ if interval_field(root).overlaps(itv)]
if len(candidates) == 1:
return candidates[0]
roots = candidates
From 42ded7cdf16c51052c896949ab1c19157a788e28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 21:02:39 +0100
Subject: [PATCH 059/518] various details in permutation.py (pep8 and ruff
PERF)
---
src/sage/combinat/permutation.py | 270 ++++++++++++++-----------------
1 file changed, 124 insertions(+), 146 deletions(-)
diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py
index 92e187287fd..def83c9750f 100644
--- a/src/sage/combinat/permutation.py
+++ b/src/sage/combinat/permutation.py
@@ -451,20 +451,17 @@ def __classcall_private__(cls, l, check=True):
return l
elif isinstance(l, PermutationGroupElement):
l = l.domain()
- #if l is a string, then assume it is in cycle notation
+ # if l is a string, then assume it is in cycle notation
elif isinstance(l, str):
if l == "()" or l == "":
return from_cycles(0, [])
cycles = l.split(")(")
cycles[0] = cycles[0][1:]
cycles[-1] = cycles[-1][:-1]
- cycle_list = []
- for c in cycles:
- cycle_list.append([int(_) for _ in c.split(",")])
-
+ cycle_list = [[int(k) for k in c.split(",")] for c in cycles]
return from_cycles(max(max(c) for c in cycle_list), cycle_list)
- #if l is a pair of standard tableaux or a pair of lists
+ # if l is a pair of standard tableaux or a pair of lists
elif isinstance(l, (tuple, list)) and len(l) == 2 and \
all(isinstance(x, Tableau) for x in l):
return RSK_inverse(*l, output='permutation')
@@ -692,7 +689,7 @@ def _latex_(self):
return repr(self._list)
if display == "cycle":
ret = self.cycle_string()
- else: # Must be cycles with singletons
+ else: # Must be cycles with singletons
ret = self.cycle_string(singletons=True)
return ret.replace(",", " \\; ")
@@ -772,26 +769,26 @@ def __next__(self):
n = len(self)
first = -1
- #Starting from the end, find the first o such that
- #p[o] < p[o+1]
+ # Starting from the end, find the first o such that
+ # p[o] < p[o+1]
for i in reversed(range(n - 1)):
if p[i] < p[i + 1]:
first = i
break
- #If first is still -1, then we are already at the last permutation
+ # if first is still -1, then we are already at the last permutation
if first == -1:
return False
- #Starting from the end, find the first j such that p[j] > p[first]
+ # Starting from the end, find the first j such that p[j] > p[first]
j = n - 1
while p[j] < p[first]:
j -= 1
- #Swap positions first and j
+ # Swap positions first and j
(p[j], p[first]) = (p[first], p[j])
- #Reverse the list between first and the end
+ # Reverse the list between first and the end
first_half = p[:first+1]
last_half = p[first+1:]
last_half.reverse()
@@ -832,15 +829,15 @@ def prev(self):
n = len(self)
first = -1
- #Starting from the end, find the first o such that
- #p[o] > p[o+1]
+ # Starting from the end, find the first o such that
+ # p[o] > p[o+1]
for i in reversed(range(n - 1)):
if p[i] > p[i + 1]:
first = i
break
- #If first is still -1, that is we didn't find any descents,
- #then we are already at the last permutation
+ # if first is still -1, that is we didn't find any descents,
+ # then we are already at the last permutation
if first == -1:
return False
@@ -1008,31 +1005,31 @@ def _to_cycles_orig(self, singletons=True):
l = [i + 1 for i in range(len(p))]
cycle = []
- #Go through until we've considered every number between
- #1 and len(p)
+ # Go through until we've considered every number between
+ # 1 and len(p)
while l:
- #If we are at the end of a cycle
- #then we want to add it to the cycles list
+ # if we are at the end of a cycle
+ # then we want to add it to the cycles list
if toConsider == -1:
- #Add the cycle to the list of cycles
+ # Add the cycle to the list of cycles
if singletons:
if cycle:
cycles.append(tuple(cycle))
else:
if len(cycle) > 1:
cycles.append(tuple(cycle))
- #Start with the first element in the list
+ # Start with the first element in the list
toConsider = l[0]
l.remove(toConsider)
cycle = [toConsider]
cycleFirst = toConsider
- #Figure out where the element under consideration
- #gets mapped to.
+ # Figure out where the element under consideration
+ # gets mapped to.
next = p[toConsider - 1]
- #If the next element is the first one in the list
- #then we've reached the end of the cycle
+ # if the next element is the first one in the list
+ # then we've reached the end of the cycle
if next == cycleFirst:
toConsider = -1
else:
@@ -1070,12 +1067,12 @@ def _to_cycles_set(self, singletons=True):
cycles = []
if not singletons:
- #remove the fixed points
- L = set(i+1 for i,pi in enumerate(p) if pi != i+1)
+ # remove the fixed points
+ L = {i+1 for i,pi in enumerate(p) if pi != i+1}
else:
L = set(range(1,len(p)+1))
- #Go through until we've considered every remaining number
+ # Go through until we've considered every remaining number
while L:
# take the first remaining element
cycleFirst = L.pop()
@@ -1111,14 +1108,14 @@ def _to_cycles_list(self, singletons=True):
cycles = []
if not singletons:
- #remove the fixed points
- L = [i+1 for i,pi in enumerate(p) if pi != i+1]
+ # remove the fixed points
+ L = [i + 1 for i, pi in enumerate(p) if pi != i + 1]
else:
L = list(range(1, len(p) + 1))
from bisect import bisect_left
- #Go through until we've considered every remaining number
+ # Go through until we've considered every remaining number
while L:
# take the first remaining element
cycleFirst = L.pop(0)
@@ -1169,7 +1166,7 @@ def signature(self) -> Integer:
"""
return (-1)**(len(self)-len(self.to_cycles()))
- #one can also use sign as an alias for signature
+ # one can also use sign as an alias for signature
sign = signature
def is_even(self) -> bool:
@@ -1228,7 +1225,7 @@ def to_matrix(self):
"""
# build the dictionary of entries since the matrix is
# extremely sparse
- entries = { (v-1, i): 1 for i, v in enumerate(self) }
+ entries = {(v - 1, i): 1 for i, v in enumerate(self)}
M = MatrixSpace(ZZ, len(self), sparse=True)
return M(entries)
@@ -1599,7 +1596,7 @@ def merge_and_countv(ivA_A, ivB_B):
def base_case(L):
s = sorted(L)
- d = dict((j, i) for i, j in enumerate(s))
+ d = {j: i for i, j in enumerate(s)}
iv = [0]*len(L)
checked = [1]*len(L)
for pi in reversed(L):
@@ -1631,7 +1628,7 @@ def inversions(self) -> list:
"""
p = self[:]
n = len(p)
- return [tuple([i+1,j+1]) for i in range(n-1) for j in range(i+1,n)
+ return [(i+1,j+1) for i in range(n-1) for j in range(i+1,n)
if p[i] > p[j]]
def stack_sort(self) -> Permutation:
@@ -1753,9 +1750,9 @@ def show(self, representation="cycles", orientation="landscape", **args):
from sage.plot.text import text
if orientation == "landscape":
- r = lambda x,y : (x,y)
+ r = lambda x, y: (x, y)
elif orientation == "portrait":
- r = lambda x,y : (-y,x)
+ r = lambda x, y: (-y, x)
else:
raise ValueError("The value of 'orientation' must be either " +
"'landscape' or 'portrait'.")
@@ -1828,7 +1825,8 @@ def noninversions(self, k) -> list:
"""
if k > len(self):
return []
- return [list(pos) for pos in itertools.combinations(self, k) if all( pos[i] < pos[i+1] for i in range(k-1) )]
+ return [list(pos) for pos in itertools.combinations(self, k)
+ if all(pos[i] < pos[i + 1] for i in range(k - 1))]
def number_of_noninversions(self, k) -> Integer:
r"""
@@ -2569,7 +2567,7 @@ def foata_bijection_inverse(self) -> Permutation:
[1]
"""
L = list(self)
- Mrev = [] # The resulting permutation, in reverse.
+ Mrev = [] # The resulting permutation, in reverse.
while L:
e = L.pop()
Mrev.append(e)
@@ -2721,7 +2719,7 @@ def fundamental_transformation_inverse(self):
record_value = 0
previous_record = None
previous_entry = None
- res = [0] * (n+1) # We'll use res[1], res[2], ..., res[n] only.
+ res = [0] * (n + 1) # We'll use res[1], res[2], ..., res[n] only.
for entry in self:
if entry > record_value:
record_value = entry
@@ -3193,13 +3191,8 @@ def recoils(self) -> list:
sage: Permutation([]).recoils()
[]
"""
- p = self
- recoils = []
- for i in range(len(p)):
- if p[i] != len(self) and self.index(p[i] + 1) < i:
- recoils.append(i)
-
- return recoils
+ return [i for i, pi in enumerate(self)
+ if pi != len(self) and self.index(pi + 1) < i]
def number_of_recoils(self) -> Integer:
r"""
@@ -3606,13 +3599,12 @@ def to_major_code(self, final_descent=False):
P = Permutations()
for i in range(n):
major_indices[i] = P(smaller).major_index(final_descent)
- #Create the permutation that "erases" all the numbers
- #smaller than i+1
+ # Create the permutation that "erases" all the numbers
+ # smaller than i+1
smaller.remove(1)
- smaller = [i-1 for i in smaller]
+ smaller = [i - 1 for i in smaller]
- major_code = [ major_indices[i] - major_indices[i+1] for i in range(n) ]
- return major_code
+ return [major_indices[i] - major_indices[i + 1] for i in range(n)]
#########
# Peaks #
@@ -3635,12 +3627,8 @@ def peaks(self) -> list:
[]
"""
p = self
- peaks = []
- for i in range(1,len(p)-1):
- if p[i-1] <= p[i] and p[i] > p[i+1]:
- peaks.append(i)
-
- return peaks
+ return [i for i in range(1, len(p) - 1)
+ if p[i - 1] <= p[i] and p[i] > p[i + 1]]
def number_of_peaks(self) -> Integer:
r"""
@@ -3790,11 +3778,7 @@ def weak_excedences(self) -> list:
sage: Permutation([1,4,3,2,5]).weak_excedences()
[1, 4, 3, 5]
"""
- res = []
- for i in range(len(self)):
- if self[i] >= i + 1:
- res.append(self[i])
- return res
+ return [pi for i, pi in enumerate(self) if pi >= i + 1]
def bruhat_inversions(self) -> list:
r"""
@@ -4621,7 +4605,7 @@ def simion_schmidt(self, avoid=[1,2,3]):
targetPermutation[i] = nonMinima.pop()
return Permutations()(targetPermutation)
- @combinatorial_map(order=2,name='reverse')
+ @combinatorial_map(order=2, name='reverse')
def reverse(self):
"""
Return the permutation obtained by reversing the list.
@@ -4633,9 +4617,9 @@ def reverse(self):
sage: Permutation([1,2,3,4,5]).reverse()
[5, 4, 3, 2, 1]
"""
- return self.__class__(self.parent(), [i for i in reversed(self)] )
+ return self.__class__(self.parent(), list(reversed(self)))
- @combinatorial_map(order=2,name='complement')
+ @combinatorial_map(order=2, name='complement')
def complement(self):
r"""
Return the complement of the permutation ``self``.
@@ -4651,7 +4635,7 @@ def complement(self):
[3, 1, 2]
"""
n = len(self)
- return self.__class__(self.parent(), [n - x + 1 for x in self] )
+ return self.__class__(self.parent(), [n - x + 1 for x in self])
@combinatorial_map(name='permutation poset')
def permutation_poset(self):
@@ -5074,8 +5058,8 @@ def remove_extra_fixed_points(self):
"""
if not self:
return Permutations()([1])
- #Strip off all extra fixed points at the end of
- #the permutation.
+ # Strip off all extra fixed points at the end of
+ # the permutation.
i = len(self)-1
while i >= 1:
if i != self[i] - 1:
@@ -5849,11 +5833,11 @@ def __classcall_private__(cls, n=None, k=None, **kwargs):
elif k is not None:
number_of_arguments += 1
- #Make sure that exactly one keyword was passed
+ # Make sure that exactly one keyword was passed
for key in kwargs:
if key not in valid_args:
raise ValueError("unknown keyword argument: %s" % key)
- if key not in [ 'avoiding' ]:
+ if key != 'avoiding':
number_of_arguments += 1
if number_of_arguments == 0:
@@ -5927,13 +5911,13 @@ def __classcall_private__(cls, n=None, k=None, **kwargs):
else:
return Permutations_msetk(n, k)
elif 'descents' in kwargs:
- #Descent positions specified
+ # Descent positions specified
if isinstance(kwargs['descents'], tuple):
- #Descent positions and size specified
+ # Descent positions and size specified
args = kwargs['descents']
return StandardPermutations_descents(tuple(args[0]), args[1])
else:
- #Size not specified
+ # Size not specified
return StandardPermutations_descents(kwargs['descents'])
elif 'bruhat_smaller' in kwargs:
return StandardPermutations_bruhat_smaller(Permutation(kwargs['bruhat_smaller']))
@@ -5992,37 +5976,37 @@ class options(GlobalOptions):
"""
NAME = 'Permutations'
module = 'sage.combinat.permutation'
- display = dict(default="list",
- description="Specifies how the permutations should be printed",
- values=dict(list="the permutations are displayed in list notation"
+ display = {'default': "list",
+ 'description': "Specifies how the permutations should be printed",
+ 'values': {'list': "the permutations are displayed in list notation"
" (aka 1-line notation)",
- cycle="the permutations are displayed in cycle notation"
+ 'cycle': "the permutations are displayed in cycle notation"
" (i. e., as products of disjoint cycles)",
- singleton="the permutations are displayed in cycle notation"
+ 'singleton': "the permutations are displayed in cycle notation"
" with singleton cycles shown as well",
- reduced_word="the permutations are displayed as reduced words"),
- alias=dict(word="reduced_word", reduced_expression="reduced_word"),
- case_sensitive=False)
- latex = dict(default="list",
- description="Specifies how the permutations should be latexed",
- values=dict(list="latex as a list in one-line notation",
- twoline="latex in two-line notation",
- cycle="latex in cycle notation",
- singleton="latex in cycle notation with singleton cycles shown as well",
- reduced_word="latex as reduced words"),
- alias=dict(word="reduced_word", reduced_expression="reduced_word", oneline="list"),
- case_sensitive=False)
- latex_empty_str = dict(default="1",
- description='The LaTeX representation of a reduced word when said word is empty',
- checker=lambda char: isinstance(char,str))
- generator_name = dict(default="s",
- description="the letter used in latexing the reduced word",
- checker=lambda char: isinstance(char,str))
- mult = dict(default="l2r",
- description="The multiplication of permutations",
- values=dict(l2r=r"left to right: `(p_1 \cdot p_2)(x) = p_2(p_1(x))`",
- r2l=r"right to left: `(p_1 \cdot p_2)(x) = p_1(p_2(x))`"),
- case_sensitive=False)
+ 'reduced_word': "the permutations are displayed as reduced words"},
+ 'alias': {'word': "reduced_word", 'reduced_expression': "reduced_word"},
+ 'case_sensitive': False}
+ latex = {'default': "list",
+ 'description': "Specifies how the permutations should be latexed",
+ 'values': {'list': "latex as a list in one-line notation",
+ 'twoline': "latex in two-line notation",
+ 'cycle': "latex in cycle notation",
+ 'singleton': "latex in cycle notation with singleton cycles shown as well",
+ 'reduced_word': "latex as reduced words"},
+ 'alias': {'word': "reduced_word", 'reduced_expression': "reduced_word", 'oneline': "list"},
+ 'case_sensitive': False}
+ latex_empty_str = {'default': "1",
+ 'description': 'The LaTeX representation of a reduced word when said word is empty',
+ 'checker': lambda char: isinstance(char,str)}
+ generator_name = {'default': "s",
+ 'description': "the letter used in latexing the reduced word",
+ 'checker': lambda char: isinstance(char,str)}
+ mult = {'default': "l2r",
+ 'description': "The multiplication of permutations",
+ 'values': {'l2r': r"left to right: `(p_1 \cdot p_2)(x) = p_2(p_1(x))`",
+ 'r2l': r"right to left: `(p_1 \cdot p_2)(x) = p_1(p_2(x))`"},
+ 'case_sensitive': False}
class Permutations_nk(Permutations):
@@ -6287,7 +6271,7 @@ def __iter__(self):
return
while next_perm(mset_list):
- #Yield the permutation
+ # Yield the permutation
yield self.element_class(self, map_to_list(mset_list, mset, n), check=False)
def cardinality(self):
@@ -6477,7 +6461,7 @@ def unrank(self, r):
# ``self.mset`` and always kept sorted. ``m`` represents ``mset`` in
# element→count form. One element is removed from both representations
# of the multiset in each loop iteration.
- mset = list(sorted(self.mset))
+ mset = sorted(self.mset)
m = {}
for x in mset:
m[x] = m.get(x, 0) + 1
@@ -7413,12 +7397,10 @@ def element_in_conjugacy_classes(self, nu):
l = []
i = 0
for nui in nu:
- for j in range(nui-1):
- l.append(i+j+2)
- l.append(i+1)
+ l.extend(i + j + 2 for j in range(nui - 1))
+ l.append(i + 1)
i += nui
- for i in range(nu.size(), self.n):
- l.append(i+1)
+ l.extend(i + 1 for i in range(nu.size(), self.n))
return self.element_class(self, l, check=False)
def conjugacy_classes_representatives(self):
@@ -7455,8 +7437,8 @@ def conjugacy_classes_representatives(self):
[[1]]
"""
from sage.combinat.partition import Partitions_n
- return [ self.element_in_conjugacy_classes(la)
- for la in reversed(Partitions_n(self.n)) ]
+ return [self.element_in_conjugacy_classes(la)
+ for la in reversed(Partitions_n(self.n))]
def conjugacy_classes_iterator(self):
"""
@@ -7834,7 +7816,7 @@ def from_permutation_group_element(pge, parent=None):
raise TypeError("pge (= %s) must be a PermutationGroupElement" % pge)
if parent is None:
- parent = Permutations( len(pge.domain()) )
+ parent = Permutations(len(pge.domain()))
return parent(pge.domain())
@@ -7868,7 +7850,7 @@ def from_rank(n, rank):
sage: permutation.from_rank(6,10)
[1, 2, 4, 6, 3, 5]
"""
- #Find the factoradic of rank
+ # Find the factoradic of rank
factoradic = [None] * n
for j in range(1,n+1):
factoradic[n-j] = Integer(rank % j)
@@ -8003,14 +7985,10 @@ def from_lehmer_code(lehmer, parent=None):
sage: permutation.from_lehmer_code(lc)
[2, 1, 5, 4, 3]
"""
- p = []
- open_spots = list(range(1,len(lehmer)+1))
- for ivi in lehmer:
- p.append(open_spots.pop(ivi))
-
+ open_spots = list(range(1, len(lehmer) + 1))
if parent is None:
parent = Permutations()
- return parent(p)
+ return parent([open_spots.pop(ivi) for ivi in lehmer])
def from_lehmer_cocode(lehmer, parent=Permutations()):
@@ -8459,7 +8437,7 @@ def descents_composition_first(dc):
except TypeError:
raise TypeError("The argument must be of type Composition")
- cpl = [x for x in reversed(dc.conjugate())]
+ cpl = list(reversed(dc.conjugate()))
res = []
s = 0
for cpli in cpl:
@@ -8488,7 +8466,7 @@ def descents_composition_last(dc):
s = 0
res = []
for i in reversed(range(len(dc))):
- res = [j for j in range(s+1,s+dc[i]+1)] + res
+ res = list(range(s+1,s+dc[i]+1)) + res
s += dc[i]
return Permutations()(res)
@@ -8546,11 +8524,11 @@ def __iter__(self):
recoils = self.recoils
dag = DiGraph()
- #Add the nodes
+ # Add the nodes
for i in range(1, sum(recoils)+1):
dag.add_vertex(i)
- #Add the edges to guarantee a finer recoil composition
+ # Add the edges to guarantee a finer recoil composition
pos = 1
for part in recoils:
for i in range(part-1):
@@ -8620,11 +8598,11 @@ def __iter__(self):
recoils = self.recoils
dag = DiGraph()
- #Add the nodes
+ # Add the nodes
for i in range(1, sum(recoils)+1):
dag.add_vertex(i)
- #Add the edges to guarantee a fatter recoil composition
+ # Add the edges to guarantee a fatter recoil composition
pos = 0
for i in range(len(recoils)-1):
pos += recoils[i]
@@ -8684,11 +8662,11 @@ def __iter__(self):
recoils = self.recoils
dag = DiGraph()
- #Add all the nodes
+ # Add all the nodes
for i in range(1, sum(recoils)+1):
dag.add_vertex(i)
- #Add the edges which guarantee a finer recoil comp.
+ # Add the edges which guarantee a finer recoil comp.
pos = 1
for part in recoils:
for i in range(part-1):
@@ -8696,7 +8674,7 @@ def __iter__(self):
pos += 1
pos += 1
- #Add the edges which guarantee a fatter recoil comp.
+ # Add the edges which guarantee a fatter recoil comp.
pos = 0
for i in range(len(recoils)-1):
pos += recoils[i]
@@ -8755,24 +8733,24 @@ def from_major_code(mc, final_descent=False):
if not mc:
w = []
else:
- #define w^(n) to be the one-letter word n
+ # define w^(n) to be the one-letter word n
w = [len(mc)]
- #for i=n-1,..,1 let w^i be the unique word obtained by inserting
- #the letter i into the word w^(i+1) in such a way that
- #maj(w^i)-maj(w^(i+1)) = mc[i]
+ # for i=n-1,..,1 let w^i be the unique word obtained by inserting
+ # the letter i into the word w^(i+1) in such a way that
+ # maj(w^i)-maj(w^(i+1)) = mc[i]
for i in reversed(range(1,len(mc))):
- #Lemma 2.2 in Skandera
+ # Lemma 2.2 in Skandera
- #Get the descents of w and place them in reverse order
+ # Get the descents of w and place them in reverse order
d = Permutation(w, check=False).descents(final_descent=final_descent)
d.reverse()
- #a is the list of all positions which are not descents
+ # a is the list of all positions which are not descents
a = [x for x in range(1, len(w) + 1) if x not in d]
- #d_k = -1 -- 0 in the lemma, but -1 due to 0-based indexing
+ # d_k = -1 -- 0 in the lemma, but -1 due to 0-based indexing
d.append(0)
l = mc[i-1]
indices = d + a
@@ -9308,7 +9286,7 @@ def list(self, distinct=False):
###############################################
-#Avoiding
+# Avoiding
class StandardPermutations_all_avoiding(StandardPermutations_all):
"""
@@ -9642,12 +9620,12 @@ def __iter__(self):
yield self.element_class(self, p, check=False)
return
- #Yield all the 132 avoiding permutations to the right.
+ # Yield all the 132 avoiding permutations to the right.
for right in StandardPermutations_avoiding_132(self.n - 1):
yield self.element_class(self, [self.n] + list(right),
check=False)
- #yi
+ # yi
for i in range(1, self.n-1):
for left in StandardPermutations_avoiding_132(i):
for right in StandardPermutations_avoiding_132(self.n-i-1):
@@ -9656,7 +9634,7 @@ def __iter__(self):
+ [self.n] + list(right),
check=False)
- #Yield all the 132 avoiding permutations to the left
+ # Yield all the 132 avoiding permutations to the left
for left in StandardPermutations_avoiding_132(self.n - 1):
yield self.element_class(self, list(left) + [self.n],
check=False)
@@ -9709,8 +9687,8 @@ def __iter__(self):
return
for p in StandardPermutations_avoiding_132(self.n):
- #Convert p to a 123 avoiding permutation by
- m = self.n+1
+ # Convert p to a 123 avoiding permutation by
+ m = self.n + 1
minima_pos = []
minima = []
for i in range(self.n):
@@ -9725,10 +9703,10 @@ def __iter__(self):
b = 0
for i in range(self.n):
if i in minima_pos:
- new_p.append( minima[a] )
+ new_p.append(minima[a])
a += 1
else:
- new_p.append( non_minima[b] )
+ new_p.append(non_minima[b])
b += 1
yield self.element_class(self, new_p, check=False)
From a77ed12c577b37b79886465e934a696284aabf97 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Sun, 25 Feb 2024 21:06:59 +0100
Subject: [PATCH 060/518] suggested detail
---
...symptotics_multivariate_generating_functions.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
index 039c0e8b805..814adfcfb47 100644
--- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
+++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
@@ -540,7 +540,7 @@ def quotient(self):
"""
return self.numerator() / self.denominator()
- def _repr_(self):
+ def _repr_(self) -> str:
r"""
Return a string representation of ``self``.
@@ -2647,8 +2647,8 @@ def is_convenient_multiple_point(self, p):
# Test 4: Is p convenient?
M = matrix(self.log_grads(p))
- convenient_coordinates = [j for j in range(d)
- if 0 not in M.columns()[j]]
+ cols = M.columns()
+ convenient_coordinates = [j for j in range(d) if 0 not in cols[j]]
if not convenient_coordinates:
return (False, 'multiple point but not convenient')
@@ -3079,7 +3079,7 @@ def __init__(self, denominator_ring, numerator_ring=None, category=None):
self._denominator_ring = denominator_ring
Parent.__init__(self, denominator_ring, category=category)
- def _repr_(self):
+ def _repr_(self) -> str:
r"""
Return a representation.
@@ -3333,7 +3333,7 @@ class FractionWithFactoredDenominatorSum(list):
- Daniel Krenn (2014-12-01)
"""
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
Return a string representation of ``self``.
@@ -3353,7 +3353,7 @@ def __repr__(self):
"""
return ' + '.join(repr(r) for r in self)
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
r"""
Return ``True`` if ``self`` is equal to ``other``.
@@ -3379,7 +3379,7 @@ def __eq__(self, other):
return (sorted(self, key=methodcaller('_total_order_key_')) ==
sorted(other, key=methodcaller('_total_order_key_')))
- def __ne__(self, other):
+ def __ne__(self, other) -> bool:
r"""
Return ``True`` if ``self`` is not equal to ``other``.
From c1051740747e47cc3cfd375fe4922527b3400425 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Mon, 26 Feb 2024 04:31:45 +0000
Subject: [PATCH 061/518] =?UTF-8?q?=F0=9F=8E=89=20fix=20failing=20doctests?=
=?UTF-8?q?=20=F0=9F=8E=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/sage/rings/polynomial/polynomial_template.pxi | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi
index 076a9753b29..f02ede09890 100644
--- a/src/sage/rings/polynomial/polynomial_template.pxi
+++ b/src/sage/rings/polynomial/polynomial_template.pxi
@@ -360,7 +360,9 @@ cdef class Polynomial_template(Polynomial):
sage: R. = Zmod(4)[]
sage: f = R(2 * x)
sage: f.gcd(f)
- 2*x
+ Traceback (most recent call last):
+ ...
+ ValueError: leading coefficient must be invertible
::
From 7243f216c33800464598e6eae0d0ecb9618de8a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Mon, 26 Feb 2024 08:33:34 +0100
Subject: [PATCH 062/518] fix mistake
---
src/sage/matrix/matrix_sparse.pyx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx
index c537798ba92..efe90dbb066 100644
--- a/src/sage/matrix/matrix_sparse.pyx
+++ b/src/sage/matrix/matrix_sparse.pyx
@@ -62,7 +62,7 @@ cdef class Matrix_sparse(matrix.Matrix):
[2.00000000000000*x -2.00000000000000]
[-------------------------------------]
"""
- if R not in Rings():
+ if ring not in Rings():
raise TypeError("input must be a ring")
if ring is self._base_ring:
if self._is_immutable:
From 8988fda0ed9753caeae2ff48e57c17e9e8c17f04 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?=
Date: Mon, 26 Feb 2024 09:04:12 +0100
Subject: [PATCH 063/518] really use suggestion
---
.../asymptotic/asymptotics_multivariate_generating_functions.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
index 814adfcfb47..d8c268e5ad7 100644
--- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
+++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py
@@ -2648,7 +2648,7 @@ def is_convenient_multiple_point(self, p):
# Test 4: Is p convenient?
M = matrix(self.log_grads(p))
cols = M.columns()
- convenient_coordinates = [j for j in range(d) if 0 not in cols[j]]
+ convenient_coordinates = [j for j, c in enumerate(cols) if 0 not in c]
if not convenient_coordinates:
return (False, 'multiple point but not convenient')
From d4a56b42096aa6ae89d4b065ab10bebba939fa65 Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Mon, 26 Feb 2024 20:32:47 +0200
Subject: [PATCH 064/518] Add `# long time` to `topology`
---
src/sage/topology/moment_angle_complex.py | 8 ++++----
src/sage/topology/simplicial_complex.py | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/sage/topology/moment_angle_complex.py b/src/sage/topology/moment_angle_complex.py
index 4d900e3ae15..15bf84f94db 100644
--- a/src/sage/topology/moment_angle_complex.py
+++ b/src/sage/topology/moment_angle_complex.py
@@ -410,7 +410,7 @@ def components(self):
sage: product_of_spheres = S3.product(S3)
sage: Z.cohomology()
{0: 0, 1: 0, 2: 0, 3: Z x Z, 4: 0, 5: 0, 6: Z}
- sage: Z.cohomology() == product_of_spheres.cohomology()
+ sage: Z.cohomology() == product_of_spheres.cohomology() # long time
True
"""
return self._components
@@ -592,7 +592,7 @@ def homology(self, dim=None, base_ring=ZZ, cohomology=False,
sage: Z = MomentAngleComplex([[0,1,2,3,4,5], [0,1,2,3,4,6],
....: [0,1,2,3,5,7], [0,1,2,3,6,8,9]])
- sage: Z.homology()
+ sage: Z.homology() # long time
{0: 0,
1: 0,
2: 0,
@@ -664,7 +664,7 @@ def cohomology(self, dim=None, base_ring=ZZ, algorithm='pari',
sage: product_of_spheres = S3.product(S3)
sage: Z.cohomology()
{0: 0, 1: 0, 2: 0, 3: Z x Z, 4: 0, 5: 0, 6: Z}
- sage: Z.cohomology() == product_of_spheres.cohomology()
+ sage: Z.cohomology() == product_of_spheres.cohomology() # long time
True
"""
return self.homology(dim=dim, cohomology=True, base_ring=base_ring,
@@ -718,7 +718,7 @@ def euler_characteristic(self):
sage: X = SimplicialComplex([[0,1,2,3,4,5], [0,1,2,3,4,6],
....: [0,1,2,3,5,7], [0,1,2,3,6,8,9]])
sage: M = MomentAngleComplex(X)
- sage: M.euler_characteristic()
+ sage: M.euler_characteristic() # long time
0
sage: Z = MomentAngleComplex([[0,1,2,3,4]])
sage: Z.euler_characteristic()
diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py
index 36128a5405f..fc21f1bcaf1 100644
--- a/src/sage/topology/simplicial_complex.py
+++ b/src/sage/topology/simplicial_complex.py
@@ -917,7 +917,7 @@ class SimplicialComplex(Parent, GenericCellComplex):
sage: l = designs.ProjectiveGeometryDesign(2, 1, GF(4,name='a')) # needs sage.rings.finite_rings
sage: f = lambda S: not any(len(set(S).intersection(x))>2 for x in l)
- sage: SimplicialComplex(from_characteristic_function=(f, l.ground_set())) # needs sage.rings.finite_rings
+ sage: SimplicialComplex(from_characteristic_function=(f, l.ground_set())) # needs sage.rings.finite_rings, long time
Simplicial complex with 21 vertices and 168 facets
TESTS:
@@ -4776,7 +4776,7 @@ def is_partitionable(self, certificate=False,
Shellable complexes are partitionable::
- sage: # needs sage.numerical.mip
+ sage: # needs sage.numerical.mip, long time
sage: X = SimplicialComplex([[1,3,5], [1,3,6], [1,4,5], [1,4,6],
....: [2,3,5], [2,3,6], [2,4,5]])
sage: X.is_partitionable()
From 3aedb1879d7c3f1947fd5fe552860b5060fcb679 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Mon, 26 Feb 2024 20:20:05 -0800
Subject: [PATCH 065/518] src/doc/en/developer/review.rst: Update section
'Release Process' to current practice
---
src/doc/en/developer/review.rst | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst
index b2bc38353cb..2dc134248ba 100644
--- a/src/doc/en/developer/review.rst
+++ b/src/doc/en/developer/review.rst
@@ -180,14 +180,15 @@ Sage Release Manager uses to make releases. Here it is as of 2023:
**Beta Release Stage**: For preparing a new beta release or the first release
candidate, all positively reviewed PRs with the forthcoming release
milestone are considered. PRs that have dependencies not merged yet are ignored.
-The Release Manager merges PRs in batches of 10 to 20 PRs, taking the
+The Release Manager merges PRs in batches of 10 to 20 PRs, without taking the
PR priority into account. If a merge conflict of a PR to the Release
Manager's branch occurs, the PR is set back to "needs work" status by the
-Release Manager, and the list of the PRs already merged to the Release
-Manager's branch is posted. The author of the PR needs to identify
-conflicting PRs in the list, make merge commits and declare them as
-dependencies, before setting back to "positive review" status. Each batch of
-merged PRs then undergoes integration testing. If problems are detected, a
+Release Manager. (The author of the PR can try to guess which other
+PRs may be causing the conflict, make merge commits and declare them as
+dependencies, before setting back to "positive review" status.
+Alternatively, the PR author can wait until the next beta release and
+resolve the conflict then.) Each batch of
+merged PRs undergoes integration testing. If problems are detected, a
PR will be set back to "needs work" status and unmerged. When a batch of
PRs is ready, the Release Manager closes these PRs and proceeds to the
next batch. After a few batches, a new beta release is tagged, pushed to the
@@ -206,7 +207,8 @@ release of high quality. Be aware that there is a risk/benefit trade-off in
merging a PR. The benefit of merging a PR is the improvement that the
PR brings, such as fixing a bug. However, any code change has a risk of
introducing unforeseen new problems and thus delaying the release: If a new
-issue triggers another release candidate, it delays the release by 1-2 weeks.
+issue triggers another release candidate, it can delay the release by up to
+2 weeks.
Hence developers should use "blocker" priority sparingly and should indicate
the rationale on the PR. Though there is no one fixed rule or authority
that determines what is appropriate for "blocker" status,
From 6280d4d561b22f7b41814d91adbfec942da0dbb3 Mon Sep 17 00:00:00 2001
From: Gareth Ma
Date: Tue, 27 Feb 2024 05:55:30 +0000
Subject: [PATCH 066/518] apply review changes
---
src/sage/rings/ring.pyx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx
index 6e48778625c..15a798ada01 100644
--- a/src/sage/rings/ring.pyx
+++ b/src/sage/rings/ring.pyx
@@ -441,8 +441,8 @@ cdef class Ring(ParentWithGens):
g = gens[0]
if len(gens) == 1:
try:
- # TODO: Rewrite this properly
- g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
+ # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc.
+ g = g.gcd(g)
except (AttributeError, NotImplementedError):
pass
else:
From ba37c38a6c1d1a2608052bfd35816f8c2b235906 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Mon, 26 Feb 2024 22:16:30 -0800
Subject: [PATCH 067/518] src/doc/en/developer/review.rst: Add link to
https://github.com/sagemath/sage-release-management
---
src/doc/en/developer/review.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst
index 2dc134248ba..3d9d7b7fef6 100644
--- a/src/doc/en/developer/review.rst
+++ b/src/doc/en/developer/review.rst
@@ -175,7 +175,7 @@ The release process
===================
It is good for developers and reviewers to be aware of the procedure that the
-Sage Release Manager uses to make releases. Here it is as of 2023:
+Sage Release Manager uses to make releases. Here it is as of 2024:
**Beta Release Stage**: For preparing a new beta release or the first release
candidate, all positively reviewed PRs with the forthcoming release
@@ -224,3 +224,5 @@ that determines what is appropriate for "blocker" status,
the Release Manager turns it to the final release. It is tagged with the
release milestone, and announced on ``sage-release``.
+Release management scripts are maintained in the repository
+`sagemath/sage-release-management `_.
From a34a3a4dcc11163949dfaa151f433b1c99c217c9 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 11:44:01 +0000
Subject: [PATCH 068/518] Start working on random element
---
.../polynomial/laurent_polynomial_ring_base.py | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index bbb6b9e1fc9..6a6ec585ce7 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -488,7 +488,7 @@ def krull_dimension(self):
"""
raise NotImplementedError
- def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=False,*args, **kwds):
+ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=False, *args, **kwds):
"""
EXAMPLES::
@@ -497,6 +497,20 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
...
NotImplementedError
"""
+ # Univariate case we sample a random polynomial of degree
+ # (high_degree + low_degree) in a polynomial ring over the
+ # base field, then we shift this polynomial by low_degree.
+ if self._n == 1:
+ abs_deg = (high_degree + abs(low_degree))
+ f_rand = self._R.random_element(degree=abs_deg, terms=terms)
+
+ # Coerce back to ``self`` and then shift down
+ f = self(f_rand)
+ x = self.gen()
+ f *= x**low_degree
+
+ return f
+
raise NotImplementedError
def is_exact(self):
From 54a1376ad4b5a3241eb2f41f80ba453b8146728b Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 11:55:05 +0000
Subject: [PATCH 069/518] Fix issue 37488
---
src/sage/algebras/quatalg/quaternion_algebra.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py
index e85cf86846f..fb88893ca07 100644
--- a/src/sage/algebras/quatalg/quaternion_algebra.py
+++ b/src/sage/algebras/quatalg/quaternion_algebra.py
@@ -2165,7 +2165,7 @@ def isomorphism_to(self, other, *, conjugator=False):
....: break
sage: O1 = (b * O0).left_order()
sage: iso = O0.isomorphism_to(O1); iso
- Ring morphism: ...
+ Ring ...
sage: iso.domain() == O0
True
sage: iso.codomain() == O1
From a0bd3e9fa261e3b24359ea1a68e60a23e72d96c4 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 12:48:27 +0000
Subject: [PATCH 070/518] implement random sampling for laurent polynomials
---
.../laurent_polynomial_ring_base.py | 104 +++++++++++++++---
1 file changed, 86 insertions(+), 18 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index 6a6ec585ce7..4be6a338bb8 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -28,6 +28,7 @@
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.ring import CommutativeRing
from sage.structure.parent import Parent
+from sage.misc.misc_c import prod
class LaurentPolynomialRing_generic(CommutativeRing, Parent):
@@ -490,28 +491,95 @@ def krull_dimension(self):
def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=False, *args, **kwds):
"""
+ Return a random polynomial of degree at most ``high_degree`` with
+ lowest degree at most ``low_degree``.
+
+ Internally uses the random sampling from
+ :meth:`sage.rings.polynomial.multi_polynomial_ring_base.MPolynomialRing_base.random_element`
+ then shifts this polynomial down to compute the correct degrees.
+
+ INPUT:
+
+ - ``low_degree`` -- non-positive integer (default: -2).
+ The smallest valuation of monomial in the polynomial
+
+ - ``high_degree`` -- non-negative integer (default: 2).
+ The maximal valuation of monomial in the polynomial
+
+ - ``terms`` -- number of terms requested (default: 5). If more
+ terms are requested than exist, then this parameter is
+ silently reduced to the maximum number of available terms.
+
+ - ``choose_degree`` -- choose degrees of monomials randomly first
+ rather than monomials uniformly random.
+
+ - ``**kwargs`` -- passed to the random element generator of the base
+ ring
+
EXAMPLES::
- sage: LaurentPolynomialRing(QQ, 2, 'x').random_element()
- Traceback (most recent call last):
- ...
- NotImplementedError
- """
- # Univariate case we sample a random polynomial of degree
- # (high_degree + low_degree) in a polynomial ring over the
- # base field, then we shift this polynomial by low_degree.
- if self._n == 1:
- abs_deg = (high_degree + abs(low_degree))
- f_rand = self._R.random_element(degree=abs_deg, terms=terms)
-
- # Coerce back to ``self`` and then shift down
- f = self(f_rand)
- x = self.gen()
- f *= x**low_degree
+ sage: R = LaurentPolynomialRing(QQ, 1, 'x')
+ sage: f = R.random_element()
+ sage: f.degree() <= 2
+ True
+ sage: f.monomials()[-1].degree() >= -2
+ True
- return f
+ ::
- raise NotImplementedError
+ sage: R = LaurentPolynomialRing(QQ, 1, 'x')
+ sage: f = R.random_element(-10, 20)
+ sage: f.degree() <= 20
+ True
+ sage: f.monomials()[-1].degree() >= -10
+ True
+
+ ::
+
+ sage: R = LaurentPolynomialRing(ZZ, 3, 'x')p
+ sage: x = R.gen()
+ sage: f = R.random_element(-5, 5)
+ sage: f = R.random_element(-5, 10)
+ sage: f.degree(x) <= 10
+ True
+ sage: f.degree(x) >= -5
+ True
+
+ TESTS::
+
+ sage: rings = [QQ, ZZ, GF(13), GF(7^3)]
+ sage: for ring in rings:
+ ....: R = LaurentPolynomialRing(ring, 3, 'x')
+ ....: for _ in range(100):
+ ....: f = R.random_element(-3, 7)
+ ....: for x in R.gens():
+ ....: assert f.degree(x) <= 7
+ ....: assert f.degree(x) >= -3
+ """
+ # Ensure the low_degree is non-positive
+ if low_degree > 0:
+ raise ValueError("`low_degree` is expected to be a non-positive integer")
+
+ # Ensure the high_degree is non-negative
+ if high_degree < 0:
+ raise ValueError("`low_degree` is expected to be a non-negative integer")
+
+ # First sample a polynomial from the associated polynomial
+ # ring of `self` of degree `(high_degree + abs(low_degree))`
+ abs_deg = (high_degree + abs(low_degree))
+ f_rand = self._R.random_element(degree=abs_deg, terms=terms, choose_degree=choose_degree, *args, **kwds)
+
+ # Coerce back to ``self``. We now have a polynomial of only
+ # positive valuation monomials
+ f = self(f_rand)
+
+ # Finally, shift the entire polynomial down by low_degree
+ # which will result in a polynomial with highest degree
+ # high_degree and lowest degree low_degree
+ monomial = prod(self.gens())
+ f *= monomial**low_degree
+
+ return f
def is_exact(self):
"""
From 0e88a871a71c0eb93083b73dd2b2d2de1a65a82f Mon Sep 17 00:00:00 2001
From: Giorgos Mousa
Date: Tue, 27 Feb 2024 15:31:13 +0200
Subject: [PATCH 071/518] Suggestions by tscrim
---
src/sage/matroids/basis_exchange_matroid.pyx | 9 +-
src/sage/matroids/circuits_matroid.pyx | 3 +-
src/sage/matroids/matroid.pxd | 2 +-
src/sage/matroids/matroid.pyx | 143 +++++++++----------
src/sage/matroids/utilities.py | 14 ++
5 files changed, 92 insertions(+), 79 deletions(-)
diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx
index f336b1d77b8..4f0a657cff1 100644
--- a/src/sage/matroids/basis_exchange_matroid.pyx
+++ b/src/sage/matroids/basis_exchange_matroid.pyx
@@ -1237,8 +1237,8 @@ cdef class BasisExchangeMatroid(Matroid):
Return the Whitney numbers of the second kind of the matroid.
The Whitney numbers of the second kind are here encoded as a vector
- `(W_0, ..., W_r)`, where `W_i` is the number of flats of rank `i`, and
- `r` is the rank of the matroid.
+ `(W_0, \ldots, W_r)`, where `W_i` is the number of flats of rank `i`,
+ and `r` is the rank of the matroid.
OUTPUT: a list of integers
@@ -1949,6 +1949,7 @@ cdef class BasisExchangeMatroid(Matroid):
sage: M._weak_invariant() == N._weak_invariant()
False
"""
+ from sage.matroids.utilities import cmp_elements_key
if self._weak_invariant_var is None:
if self.full_rank() == 0 or self.full_corank() == 0:
self._weak_invariant_var = 0
@@ -1956,8 +1957,8 @@ cdef class BasisExchangeMatroid(Matroid):
else:
k = min(self.full_rank() - 1, 2)
fie, f_vec = self._flat_element_inv(k)
- self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie, key=str)]), f_vec]))
- self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie, key=str)])
+ self._weak_invariant_var = hash(tuple([tuple([(f, len(fie[f])) for f in sorted(fie, key=cmp_elements_key)]), f_vec]))
+ self._weak_partition_var = SetSystem(self._E, [fie[f] for f in sorted(fie, key=cmp_elements_key)])
return self._weak_invariant_var
cpdef _weak_partition(self) noexcept:
diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx
index bbcf6ce8319..77744a2b888 100644
--- a/src/sage/matroids/circuits_matroid.pyx
+++ b/src/sage/matroids/circuits_matroid.pyx
@@ -612,8 +612,9 @@ cdef class CircuitsMatroid(Matroid):
sage: C1 == C2
True
"""
+ from sage.matroids.utilities import cmp_elements_key
if ordering is None:
- ordering = sorted(self.groundset(), key=str)
+ ordering = sorted(self.groundset(), key=cmp_elements_key)
else:
if frozenset(ordering) != self.groundset():
raise ValueError("not an ordering of the groundset")
diff --git a/src/sage/matroids/matroid.pxd b/src/sage/matroids/matroid.pxd
index 7ab91af018f..12d748a3cba 100644
--- a/src/sage/matroids/matroid.pxd
+++ b/src/sage/matroids/matroid.pxd
@@ -220,7 +220,7 @@ cdef class Matroid(SageObject):
cpdef _internal(self, B) noexcept
cpdef _external(self, B) noexcept
cpdef tutte_polynomial(self, x=*, y=*) noexcept
- cpdef characteristic_polynomial(self, l=*) noexcept
+ cpdef characteristic_polynomial(self, la=*) noexcept
cpdef flat_cover(self, solver=*, verbose=*, integrality_tolerance=*) noexcept
# misc
diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx
index cf6ea63943a..c5a2280b6f9 100644
--- a/src/sage/matroids/matroid.pyx
+++ b/src/sage/matroids/matroid.pyx
@@ -179,7 +179,7 @@ a rule leave the regular method alone.
These underscored methods are not documented in the reference manual. To see
them, within Sage you can create a matroid ``M`` and type ``M._`` followed by
-:kbd:`Tab`. Then ``M._rank?`` followed by :kbd:`Tab` will bring up the
+:kbd:`Tab`. Then ``M._rank?`` followed by :kbd:`Enter` will bring up the
documentation string of the ``_rank()`` method.
Creating new Matroid subclasses
@@ -355,7 +355,7 @@ MixedIntegerLinearProgram = LazyImport('sage.numerical.mip', 'MixedIntegerLinear
from sage.matroids.lean_matrix cimport BinaryMatrix, TernaryMatrix
from sage.matroids.set_system cimport SetSystem
-from sage.matroids.utilities import newlabel, sanitize_contractions_deletions, spanning_forest, spanning_stars
+from sage.matroids.utilities import newlabel, sanitize_contractions_deletions, spanning_forest, spanning_stars, cmp_elements_key
# On some systems, macros "minor()" and "major()" are defined in system header
@@ -1079,10 +1079,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``N`` -- An instance of a ``Matroid`` object,
- - ``certificate`` -- boolean (Default: ``False``) If ``True``, returns
- ``True, (X, Y, dic) where ``N`` is isomorphic to ``self.minor(X, Y)``,
- and ``dic`` is an isomorphism between ``N`` and ``self.minor(X, Y)``.
+ - ``N`` -- an instance of a :class:`Matroid` object
+ - ``certificate`` -- boolean (default: ``False``); if ``True``, returns
+ ``True, (X, Y, dic)`` where ``N`` is isomorphic to
+ ``self.minor(X, Y)``, and ``dic`` is an isomorphism between ``N`` and
+ ``self.minor(X, Y)``
OUTPUT: boolean or tuple
@@ -2304,7 +2305,7 @@ cdef class Matroid(SageObject):
.. SEEALSO::
- :meth:`M.circuit() `
+ :meth:`~sage.matroids.matroid.Matroid.circuit`
EXAMPLES::
@@ -2538,6 +2539,10 @@ cdef class Matroid(SageObject):
:meth:`M.basis() `
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``self.full_rank()``.
+
EXAMPLES::
sage: M = matroids.Uniform(2, 4)
@@ -2545,10 +2550,6 @@ cdef class Matroid(SageObject):
[]
sage: [sorted(X) for X in matroids.catalog.P6().nonbases_iterator()]
[['a', 'b', 'c']]
-
- ALGORITHM:
-
- Test all subsets of the groundset of cardinality ``self.full_rank()``
"""
for X in combinations(self.groundset(), self.full_rank()):
if self._rank(X) < len(X):
@@ -2560,7 +2561,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``r`` -- a nonnegative integer.
+ - ``r`` -- a nonnegative integer
EXAMPLES::
@@ -2589,7 +2590,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``r`` -- a nonnegative integer.
+ - ``r`` -- a nonnegative integer
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``r``.
EXAMPLES::
@@ -2600,10 +2605,6 @@ cdef class Matroid(SageObject):
....: matroids.catalog.Vamos().dependent_r_sets_iterator(4)])
[['a', 'b', 'c', 'd'], ['a', 'b', 'e', 'f'], ['a', 'b', 'g', 'h'],
['c', 'd', 'e', 'f'], ['e', 'f', 'g', 'h']]
-
- ALGORITHM:
-
- Test all subsets of the groundset of cardinality ``r``
"""
for X in combinations(self.groundset(), r):
X = frozenset(X)
@@ -2645,16 +2646,16 @@ cdef class Matroid(SageObject):
A *basis* is a maximal independent set.
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``self.full_rank()``.
+
EXAMPLES::
sage: M = matroids.Uniform(2, 4)
sage: sorted([sorted(X) for X in M.bases_iterator()])
[[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
- ALGORITHM:
-
- Test all subsets of the groundset of cardinality ``self.full_rank()``
-
.. SEEALSO::
:meth:`M.independent_r_sets() `
@@ -2746,7 +2747,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``r`` -- a nonnegative integer.
+ - ``r`` -- a nonnegative integer
+
+ ALGORITHM:
+
+ Test all subsets of the groundset of cardinality ``r``.
EXAMPLES::
@@ -2759,10 +2764,6 @@ cdef class Matroid(SageObject):
sage: frozenset({'a', 'c', 'e'}) in S
True
- ALGORITHM:
-
- Test all subsets of the groundset of cardinality ``r``
-
.. SEEALSO::
:meth:`M.independent_sets() `
@@ -2782,7 +2783,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``r`` -- a nonnegative integer.
+ - ``r`` -- a nonnegative integer
EXAMPLES::
@@ -2907,7 +2908,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``r`` -- a nonnegative integer.
+ - ``r`` -- a nonnegative integer
OUTPUT: a SetSystem
@@ -3072,7 +3073,7 @@ cdef class Matroid(SageObject):
[[1, 2], [1, 4], [2, 3, 4]]
"""
if ordering is None:
- ordering = sorted(self.groundset(), key=str)
+ ordering = sorted(self.groundset(), key=cmp_elements_key)
else:
orderset = frozenset(ordering)
if len(orderset) != len(self.groundset()) or orderset != self.groundset():
@@ -3131,12 +3132,12 @@ cdef class Matroid(SageObject):
minimal-removal convention, while the implementation is not
modified from the published algorithm.
"""
- if len(self.loops()) > 0:
+ if self.loops():
return []
cdef list rev_order
if ordering is None:
- rev_order = sorted(self.groundset(), key=str, reverse=True)
+ rev_order = sorted(self.groundset(), key=cmp_elements_key, reverse=True)
else:
if frozenset(ordering) != self.groundset():
raise ValueError("not an ordering of the groundset")
@@ -3202,10 +3203,10 @@ cdef class Matroid(SageObject):
Simplicial complex with vertex set (1, 2, 3, 4, 5)
and facets {(1, 3, 5), (2, 3, 5), (2, 4, 5), (3, 4, 5)}
"""
- if len(self.loops()) == 0:
+ if not self.loops():
if ordering is None:
- rev_order = sorted(self.groundset(), key=str, reverse=True)
+ rev_order = sorted(self.groundset(), key=cmp_elements_key, reverse=True)
else:
if frozenset(ordering) != self.groundset():
raise ValueError("not an ordering of the groundset")
@@ -3244,12 +3245,12 @@ cdef class Matroid(SageObject):
- ``R`` -- the base ring
- ``ordering`` -- (optional) an ordering of the groundset
- - ``invariant`` -- (optional, default: None) either a semigroup ``G``
- whose ``__call__`` acts on the groundset, or pair ``(G, action)``
- where ``G`` is a semigroup and ``action`` is a function
- ``action(g,e)`` which takes a pair of a group element and a grounset
- element and returns the groundset element which is the result of
- ``e`` acted upon by ``g``
+ - ``invariant`` -- (optional) either a semigroup ``G`` whose
+ ``__call__`` acts on the groundset, or pair ``(G, action)`` where
+ ``G`` is a semigroup and ``action`` is a function ``action(g,e)``
+ which takes a pair of a group element and a grounset element and
+ returns the groundset element which is the result of ``e`` acted upon
+ by ``g``
.. SEEALSO::
@@ -3339,10 +3340,10 @@ cdef class Matroid(SageObject):
convert = {ind: i for i, ind in enumerate(self.groundset())}
vertices = []
for B in self.bases_iterator():
- sum = 0
+ total = 0
for i in B:
- sum += vector_e[convert[i]]
- vertices += [sum]
+ total += vector_e[convert[i]]
+ vertices += [total]
return Polyhedron(vertices)
cpdef independence_matroid_polytope(self) noexcept:
@@ -3385,12 +3386,12 @@ cdef class Matroid(SageObject):
ambient = FreeModule(ZZ, n)
vector_e = ambient.basis()
convert = {ind: i for i, ind in enumerate(self.groundset())}
- vertices = []
+ cdef list lst, vertices = []
for IS in self.independent_sets_iterator():
lst = []
for i in IS:
- lst += [vector_e[convert[i]]]
- vertices += [ambient.sum(lst)]
+ lst.append(vector_e[convert[i]])
+ vertices.append(ambient.sum(lst))
return Polyhedron(vertices)
# isomorphism and equality
@@ -3405,7 +3406,7 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- a matroid,
+ - ``other`` -- a matroid
- ``certificate`` -- boolean (default: ``False``)
OUTPUT: boolean, and, if ``certificate = True``, a dictionary or
@@ -3445,8 +3446,8 @@ cdef class Matroid(SageObject):
INPUT:
- - ``other`` -- A matroid,
- - ``certificate`` -- boolean (default: ``False``).
+ - ``other`` -- a matroid
+ - ``certificate`` -- boolean (default: ``False``)
OUTPUT: boolean, and, if ``certificate=True``, a dictionary giving the
isomorphism or ``None``
@@ -4216,10 +4217,11 @@ cdef class Matroid(SageObject):
INPUT:
- - ``N`` -- An instance of a ``Matroid`` object,
- - ``certificate`` -- boolean (Default: ``False``) If ``True``, returns
- ``True, (X, Y, dic) where ``N`` is isomorphic to ``self.minor(X, Y)``,
- and ``dic`` is an isomorphism between ``N`` and ``self.minor(X, Y)``.
+ - ``N`` -- an instance of a :class:`Matroid` object
+ - ``certificate`` -- boolean (default: ``False``); if ``True``, returns
+ ``True, (X, Y, dic)`` where ``N`` is isomorphic to
+ ``self.minor(X, Y)``, and ``dic`` is an isomorphism between ``N`` and
+ ``self.minor(X, Y)``
OUTPUT: boolean or tuple
@@ -6313,9 +6315,9 @@ cdef class Matroid(SageObject):
if basis is None:
basis = self.basis()
- basis = sorted(basis, key=str)
+ basis = sorted(basis, key=cmp_elements_key)
bdx = {basis[i]: i for i in range(len(basis))}
- E = sorted(self.groundset(), key=str)
+ E = sorted(self.groundset(), key=cmp_elements_key)
idx = {Ei: i for i, Ei in enumerate(E)}
A = TernaryMatrix(len(basis), len(E))
for e in basis:
@@ -7399,8 +7401,7 @@ cdef class Matroid(SageObject):
INPUT:
- ``other`` -- a matroid with the same groundset as ``self``
- - ``Y`` -- an common independent set of ``self`` and ``other`` of size
- `k`
+ - ``Y`` -- an common independent set of ``self`` and ``other`` of size `k`
OUTPUT:
@@ -7626,7 +7627,7 @@ cdef class Matroid(SageObject):
N = self.groundset() - B
A = set()
for e in B:
- if min(self._cocircuit(N | set([e])), key=str) == e:
+ if min(self._cocircuit(N | set([e])), key=cmp_elements_key) == e:
A.add(e)
return A
@@ -7663,7 +7664,7 @@ cdef class Matroid(SageObject):
N = self.groundset() - B
A = set()
for e in N:
- if min(self._circuit(B | set([e])), key=str) == e:
+ if min(self._circuit(B | set([e])), key=cmp_elements_key) == e:
A.add(e)
return A
@@ -7728,7 +7729,7 @@ cdef class Matroid(SageObject):
T = T(a, b)
return T
- cpdef characteristic_polynomial(self, l=None) noexcept:
+ cpdef characteristic_polynomial(self, la=None) noexcept:
r"""
Return the characteristic polynomial of the matroid.
@@ -7745,10 +7746,10 @@ cdef class Matroid(SageObject):
INPUT:
- - ``l`` -- a variable or numerical argument (optional)
+ - ``la`` -- a variable or numerical argument (optional)
OUTPUT: the characteristic polynomial, `\chi_M(\lambda)`, where
- `\lambda` is substituted with any value provided as input.
+ `\lambda` is substituted with any value provided as input
EXAMPLES::
@@ -7762,18 +7763,14 @@ cdef class Matroid(SageObject):
.. SEEALSO::
- :meth:`whitney_numbers() `
+ :meth:`~sage.matroids.matroid.Matroid.whitney_numbers`
"""
- val = l
R = ZZ['l']
- l = R._first_ngens(1)[0]
- chi = R(0)
- cdef int r = self.rank()
cdef list w = self.whitney_numbers()
- for i in range(len(w)):
- chi += w[i] * l**(r-i)
- if val is not None:
- return chi(val)
+ w.reverse()
+ chi = R(w)
+ if la is not None:
+ return chi(la)
return chi
cpdef flat_cover(self, solver=None, verbose=0, integrality_tolerance=1e-3) noexcept:
@@ -7919,7 +7916,7 @@ cdef class Matroid(SageObject):
# Create the ambient polynomial ring
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
try:
- names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=str))) for F in flats]
+ names = ['A{}'.format(''.join(str(x) for x in sorted(F, key=cmp_elements_key))) for F in flats]
P = PolynomialRing(R, names)
except ValueError: # variables are not proper names
P = PolynomialRing(R, 'A', len(flats))
@@ -8119,7 +8116,7 @@ cdef class Matroid(SageObject):
cdef list facets = []
for S in self.no_broken_circuits_sets_iterator(ordering):
if len(S) == r:
- facets += [S]
+ facets.append(S)
return SimplicialComplex(facets, maximality_check=False)
cpdef automorphism_group(self) noexcept:
@@ -8273,7 +8270,7 @@ cdef class Matroid(SageObject):
# add the facet
DM.add_face([f'L{i}' for i in I] +
- [f'R{sorted(F, key=str)}' for F in c])
+ [f'R{sorted(F)}' for F in c])
return DM
def union(self, matroids):
diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py
index b42011533ca..a0cf14f5276 100644
--- a/src/sage/matroids/utilities.py
+++ b/src/sage/matroids/utilities.py
@@ -800,3 +800,17 @@ def split_vertex(G, u, v=None, edges=None):
# This modifies the graph without needing to return anything
return
+
+
+def cmp_elements_key(x):
+ """
+ A helper function to compare elements which may be integers or strings.
+
+ EXAMPLES::
+
+ sage: from sage.matroids.utilities import cmp_elements_key
+ sage: l = ['a', 'b', 1, 3, 2, 10, 111, 100, 'c', 'aa']
+ sage: sorted(l, key=cmp_elements_key)
+ [1, 2, 3, 10, 100, 111, 'a', 'aa', 'b', 'c']
+ """
+ return (isinstance(x, str), x)
From df4980f15369a298efde1799e3709c1771260dcd Mon Sep 17 00:00:00 2001
From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com>
Date: Tue, 27 Feb 2024 15:05:08 +0100
Subject: [PATCH 072/518] Fix stupid typo
---
src/sage/rings/polynomial/laurent_polynomial_ring_base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index 4be6a338bb8..b8c7ba49298 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -536,7 +536,7 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
::
- sage: R = LaurentPolynomialRing(ZZ, 3, 'x')p
+ sage: R = LaurentPolynomialRing(ZZ, 3, 'x')
sage: x = R.gen()
sage: f = R.random_element(-5, 5)
sage: f = R.random_element(-5, 10)
From 8cda862d750e4fd0bdbe8c49fff251ccd7b38d60 Mon Sep 17 00:00:00 2001
From: Matthias Koeppe
Date: Tue, 27 Feb 2024 07:46:01 -0800
Subject: [PATCH 073/518] src/doc/en/developer/review.rst: Remove mention of
dependencies (not handled in current scripts), remove mention of priorities
in beta stage
---
src/doc/en/developer/review.rst | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/doc/en/developer/review.rst b/src/doc/en/developer/review.rst
index 3d9d7b7fef6..0a55393955b 100644
--- a/src/doc/en/developer/review.rst
+++ b/src/doc/en/developer/review.rst
@@ -179,10 +179,9 @@ Sage Release Manager uses to make releases. Here it is as of 2024:
**Beta Release Stage**: For preparing a new beta release or the first release
candidate, all positively reviewed PRs with the forthcoming release
-milestone are considered. PRs that have dependencies not merged yet are ignored.
-The Release Manager merges PRs in batches of 10 to 20 PRs, without taking the
-PR priority into account. If a merge conflict of a PR to the Release
-Manager's branch occurs, the PR is set back to "needs work" status by the
+milestone are considered. The Release Manager merges PRs in batches of
+10 to 20 PRs. If a merge conflict of a PR to the Release Manager's
+branch occurs, the PR is set back to "needs work" status by the
Release Manager. (The author of the PR can try to guess which other
PRs may be causing the conflict, make merge commits and declare them as
dependencies, before setting back to "positive review" status.
From 70453764c6b551b8460a954f5ba5361ae9d0b602 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 16:51:22 +0000
Subject: [PATCH 074/518] More randomness in random testing
---
.../rings/polynomial/laurent_polynomial_ring_base.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index b8c7ba49298..c9bc9d72001 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -549,12 +549,14 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
sage: rings = [QQ, ZZ, GF(13), GF(7^3)]
sage: for ring in rings:
- ....: R = LaurentPolynomialRing(ring, 3, 'x')
+ ....: d = randint(1, 5)
+ ....: R = LaurentPolynomialRing(ring, d, 'x')
....: for _ in range(100):
- ....: f = R.random_element(-3, 7)
+ ....: n, m = randint(0, 10), randint(0, 10)
+ ....: f = R.random_element(-n, m)
....: for x in R.gens():
- ....: assert f.degree(x) <= 7
- ....: assert f.degree(x) >= -3
+ ....: assert f.degree(x) <= m
+ ....: assert f.degree(x) >= -n
"""
# Ensure the low_degree is non-positive
if low_degree > 0:
From fe312a1d7d1990907b4e3375913bc943054d5913 Mon Sep 17 00:00:00 2001
From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com>
Date: Tue, 27 Feb 2024 17:53:47 +0000
Subject: [PATCH 075/518] Apply suggestions from code review
Co-authored-by: grhkm21 <83517584+grhkm21@users.noreply.github.com>
---
.../polynomial/laurent_polynomial_ring_base.py | 15 ++++-----------
1 file changed, 4 insertions(+), 11 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index c9bc9d72001..e1621e35cf2 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -491,8 +491,8 @@ def krull_dimension(self):
def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=False, *args, **kwds):
"""
- Return a random polynomial of degree at most ``high_degree`` with
- lowest degree at most ``low_degree``.
+ Return a random polynomial of degree at most ``high_degree`` and
+ lowest degree at least ``low_degree``.
Internally uses the random sampling from
:meth:`sage.rings.polynomial.multi_polynomial_ring_base.MPolynomialRing_base.random_element`
@@ -506,14 +506,7 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
- ``high_degree`` -- non-negative integer (default: 2).
The maximal valuation of monomial in the polynomial
- - ``terms`` -- number of terms requested (default: 5). If more
- terms are requested than exist, then this parameter is
- silently reduced to the maximum number of available terms.
-
- - ``choose_degree`` -- choose degrees of monomials randomly first
- rather than monomials uniformly random.
-
- - ``**kwargs`` -- passed to the random element generator of the base
+ - ``*args, **kwds`` -- passed to the random element generator of the base
ring
EXAMPLES::
@@ -568,7 +561,7 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
# First sample a polynomial from the associated polynomial
# ring of `self` of degree `(high_degree + abs(low_degree))`
- abs_deg = (high_degree + abs(low_degree))
+ abs_deg = (high_degree - low_degree)
f_rand = self._R.random_element(degree=abs_deg, terms=terms, choose_degree=choose_degree, *args, **kwds)
# Coerce back to ``self``. We now have a polynomial of only
From 7eaddc4bb38b69ca90f70e7eab5475fb7b1bdcb2 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 18:38:03 +0000
Subject: [PATCH 076/518] Review suggestions and more doctests
---
.../laurent_polynomial_ring_base.py | 61 ++++++++++++-------
1 file changed, 39 insertions(+), 22 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index e1621e35cf2..db110eefa08 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -500,14 +500,22 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
INPUT:
- - ``low_degree`` -- non-positive integer (default: -2).
+ - ``low_degree`` -- integer (default: ``-2``).
The smallest valuation of monomial in the polynomial
- - ``high_degree`` -- non-negative integer (default: 2).
+ - ``high_degree`` -- integer (default: ``2``).
The maximal valuation of monomial in the polynomial
- - ``*args, **kwds`` -- passed to the random element generator of the base
- ring
+ - ``terms`` -- number of terms requested (default: ``5``).
+ If more terms are requested than exist, then this parameter is
+ silently reduced to the maximum number of available terms.
+
+ - ``choose_degree`` -- bool(default: ``False``)
+ choose degrees of monomials randomly first rather than monomials
+ uniformly random.
+
+ - ``*args, **kwds`` -- passed to the random element generator
+ of the underlying polynomial ring and respective base ring
EXAMPLES::
@@ -515,27 +523,37 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
sage: f = R.random_element()
sage: f.degree() <= 2
True
- sage: f.monomials()[-1].degree() >= -2
+ sage: f.degree() >= -2
+ True
+ sage: f.parent() is R
True
::
- sage: R = LaurentPolynomialRing(QQ, 1, 'x')
+ sage: R = LaurentPolynomialRing(ZZ, 2, 'x')
sage: f = R.random_element(-10, 20)
sage: f.degree() <= 20
True
- sage: f.monomials()[-1].degree() >= -10
- True
+ sage: tuple(f.degree(x) >= -10 for x in R.gens())
+ (True, True)
::
- sage: R = LaurentPolynomialRing(ZZ, 3, 'x')
- sage: x = R.gen()
+ sage: R = LaurentPolynomialRing(GF(13), 3, 'x')
sage: f = R.random_element(-5, 5)
sage: f = R.random_element(-5, 10)
- sage: f.degree(x) <= 10
+ sage: tuple(f.degree(x) <= 10 for x in R.gens())
+ (True, True, True)
+ sage: tuple(f.degree(x) >= -5 for x in R.gens())
+ (True, True, True)
+
+ ::
+
+ sage: R = LaurentPolynomialRing(QQ, 5, 'x')
+ sage: f = R.random_element(-3, 5, terms=6, choose_degree=True)
+ sage: f.parent() is R
True
- sage: f.degree(x) >= -5
+ sage: len(list(f)) <= 6
True
TESTS::
@@ -545,19 +563,18 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
....: d = randint(1, 5)
....: R = LaurentPolynomialRing(ring, d, 'x')
....: for _ in range(100):
- ....: n, m = randint(0, 10), randint(0, 10)
- ....: f = R.random_element(-n, m)
+ ....: n, m = randint(-10, -1), randint(1, 10)
+ ....: if m < n:
+ ....: m, n = n, m
+ ....: f = R.random_element(n, m)
+ ....: if f.is_zero(): continue # the zero polynomial is defined to have degree -1
....: for x in R.gens():
....: assert f.degree(x) <= m
- ....: assert f.degree(x) >= -n
+ ....: assert f.degree(x) >= n
"""
- # Ensure the low_degree is non-positive
- if low_degree > 0:
- raise ValueError("`low_degree` is expected to be a non-positive integer")
-
- # Ensure the high_degree is non-negative
- if high_degree < 0:
- raise ValueError("`low_degree` is expected to be a non-negative integer")
+ # Ensure the degree parameters are sensible
+ if high_degree < low_degree:
+ raise ValueError("`high_degree` must be greater than or equal to `low_degree`")
# First sample a polynomial from the associated polynomial
# ring of `self` of degree `(high_degree + abs(low_degree))`
From 3c911d21c0769265ea67557e3c459787c3d14fa3 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 18:40:19 +0000
Subject: [PATCH 077/518] space
---
src/sage/rings/polynomial/laurent_polynomial_ring_base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index db110eefa08..8c7b8b05aff 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -510,7 +510,7 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
If more terms are requested than exist, then this parameter is
silently reduced to the maximum number of available terms.
- - ``choose_degree`` -- bool(default: ``False``)
+ - ``choose_degree`` -- bool (default: ``False``)
choose degrees of monomials randomly first rather than monomials
uniformly random.
From 4ed19cd6d8348a9ba93dbc46e5d45843f0cd1585 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Tue, 27 Feb 2024 18:56:45 +0000
Subject: [PATCH 078/518] more randomness in testing
---
src/sage/rings/polynomial/laurent_polynomial_ring_base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index 8c7b8b05aff..77619ffa99e 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -563,7 +563,7 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
....: d = randint(1, 5)
....: R = LaurentPolynomialRing(ring, d, 'x')
....: for _ in range(100):
- ....: n, m = randint(-10, -1), randint(1, 10)
+ ....: n, m = randint(-10, 10), randint(1, 10)
....: if m < n:
....: m, n = n, m
....: f = R.random_element(n, m)
From 0495d4efc58528a9fd84035559b8966cff285448 Mon Sep 17 00:00:00 2001
From: Giacomo Pope <44242839+GiacomoPope@users.noreply.github.com>
Date: Wed, 28 Feb 2024 10:42:14 +0000
Subject: [PATCH 079/518] Apply suggestions from code review
Co-authored-by: Travis Scrimshaw
---
.../laurent_polynomial_ring_base.py | 27 ++++++++++---------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index 77619ffa99e..d9a5657841d 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -494,25 +494,26 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
Return a random polynomial of degree at most ``high_degree`` and
lowest degree at least ``low_degree``.
- Internally uses the random sampling from
- :meth:`sage.rings.polynomial.multi_polynomial_ring_base.MPolynomialRing_base.random_element`
- then shifts this polynomial down to compute the correct degrees.
+ Internally uses the random sampling from the corresponding
+ polynomial ring then shifts this polynomial down to compute
+ the correct degrees.
INPUT:
- - ``low_degree`` -- integer (default: ``-2``).
- The smallest valuation of monomial in the polynomial
+ - ``min_valuation`` -- integer (default: ``-2``); the
+ minimal allowed valuation of the polynomial
- - ``high_degree`` -- integer (default: ``2``).
- The maximal valuation of monomial in the polynomial
+ - ``max_degree`` -- integer (default: ``2``); the
+ maximal allowed degree of the polynomial
- - ``terms`` -- number of terms requested (default: ``5``).
- If more terms are requested than exist, then this parameter is
- silently reduced to the maximum number of available terms.
+ - ``terms`` -- (default: ``5``) number of terms requested;
+ if more terms are requested than exist (from specifying
+ the valuation and degree), then this parameter is silently
+ reduced to the maximum number of available terms
- - ``choose_degree`` -- bool (default: ``False``)
- choose degrees of monomials randomly first rather than monomials
- uniformly random.
+ - ``choose_degree`` -- bool (default: ``False``) choose
+ degrees of monomials randomly first rather than monomials
+ uniformly random
- ``*args, **kwds`` -- passed to the random element generator
of the underlying polynomial ring and respective base ring
From 7b2165a97cd112c09f4ab3b7c7a8ba247ad01762 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Wed, 28 Feb 2024 10:57:33 +0000
Subject: [PATCH 080/518] Add more doctests
---
.../laurent_polynomial_ring_base.py | 84 ++++++++++---------
1 file changed, 43 insertions(+), 41 deletions(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index d9a5657841d..19eafdb9b90 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -489,10 +489,10 @@ def krull_dimension(self):
"""
raise NotImplementedError
- def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=False, *args, **kwds):
+ def random_element(self, min_valuation=-2, high_degree=2, *args, **kwds):
"""
Return a random polynomial of degree at most ``high_degree`` and
- lowest degree at least ``low_degree``.
+ lowest valuation at least ``min_valuation``.
Internally uses the random sampling from the corresponding
polynomial ring then shifts this polynomial down to compute
@@ -506,91 +506,93 @@ def random_element(self, low_degree=-2, high_degree=2, terms=5, choose_degree=Fa
- ``max_degree`` -- integer (default: ``2``); the
maximal allowed degree of the polynomial
- - ``terms`` -- (default: ``5``) number of terms requested;
- if more terms are requested than exist (from specifying
- the valuation and degree), then this parameter is silently
- reduced to the maximum number of available terms
-
- - ``choose_degree`` -- bool (default: ``False``) choose
- degrees of monomials randomly first rather than monomials
- uniformly random
-
- ``*args, **kwds`` -- passed to the random element generator
of the underlying polynomial ring and respective base ring
EXAMPLES::
- sage: R = LaurentPolynomialRing(QQ, 1, 'x')
- sage: f = R.random_element()
+ sage: L. = LaurentPolynomialRing(QQ)
+ sage: f = L.random_element()
sage: f.degree() <= 2
True
sage: f.degree() >= -2
True
- sage: f.parent() is R
+ sage: f.parent() is L
True
::
- sage: R = LaurentPolynomialRing(ZZ, 2, 'x')
- sage: f = R.random_element(-10, 20)
+ sage: L = LaurentPolynomialRing(ZZ, 2, 'x')
+ sage: f = L.random_element(-10, 20)
sage: f.degree() <= 20
True
- sage: tuple(f.degree(x) >= -10 for x in R.gens())
+ sage: tuple(f.degree(x) >= -10 for x in L.gens())
(True, True)
::
- sage: R = LaurentPolynomialRing(GF(13), 3, 'x')
- sage: f = R.random_element(-5, 5)
- sage: f = R.random_element(-5, 10)
- sage: tuple(f.degree(x) <= 10 for x in R.gens())
+ sage: L = LaurentPolynomialRing(GF(13), 3, 'x')
+ sage: f = L.random_element(-5, 5)
+ sage: f = L.random_element(-5, 10)
+ sage: tuple(f.degree(x) <= 10 for x in L.gens())
(True, True, True)
- sage: tuple(f.degree(x) >= -5 for x in R.gens())
+ sage: tuple(f.degree(x) >= -5 for x in L.gens())
(True, True, True)
::
- sage: R = LaurentPolynomialRing(QQ, 5, 'x')
- sage: f = R.random_element(-3, 5, terms=6, choose_degree=True)
- sage: f.parent() is R
- True
- sage: len(list(f)) <= 6
- True
+ sage: L = LaurentPolynomialRing(RR, 2, 'x')
+ sage: f = L.random_element()
+ sage: f = L.random_element()
+ sage: tuple(f.degree(x) <= 2 for x in L.gens())
+ (True, True)
+ sage: tuple(f.degree(x) >= -2 for x in L.gens())
+ (True, True)
+
+ ::
+
+ sage: L = LaurentPolynomialRing(RR, 5, 'x')
+ sage: f = L.random_element(-1, 1)
+ sage: f = L.random_element(-1, 1)
+ sage: tuple(f.degree(x) <= 1 for x in L.gens())
+ (True, True, True, True, True)
+ sage: tuple(f.degree(x) >= -1 for x in L.gens())
+ (True, True, True, True, True)
TESTS::
- sage: rings = [QQ, ZZ, GF(13), GF(7^3)]
+ sage: rings = [RR, QQ, ZZ, GF(13), GF(7^3)]
sage: for ring in rings:
....: d = randint(1, 5)
- ....: R = LaurentPolynomialRing(ring, d, 'x')
+ ....: L = LaurentPolynomialRing(ring, d, 'x')
....: for _ in range(100):
....: n, m = randint(-10, 10), randint(1, 10)
....: if m < n:
....: m, n = n, m
- ....: f = R.random_element(n, m)
+ ....: f = L.random_element(n, m)
....: if f.is_zero(): continue # the zero polynomial is defined to have degree -1
- ....: for x in R.gens():
+ ....: for x in L.gens():
....: assert f.degree(x) <= m
....: assert f.degree(x) >= n
"""
# Ensure the degree parameters are sensible
- if high_degree < low_degree:
- raise ValueError("`high_degree` must be greater than or equal to `low_degree`")
+ if high_degree < min_valuation:
+ raise ValueError("`high_degree` must be greater than or equal to `min_valuation`")
# First sample a polynomial from the associated polynomial
- # ring of `self` of degree `(high_degree + abs(low_degree))`
- abs_deg = (high_degree - low_degree)
- f_rand = self._R.random_element(degree=abs_deg, terms=terms, choose_degree=choose_degree, *args, **kwds)
+ # ring of `self` of degree `(high_degree - min_valuation)`
+ abs_deg = (high_degree - min_valuation)
+ f_rand = self._R.random_element(degree=abs_deg, *args, **kwds)
- # Coerce back to ``self``. We now have a polynomial of only
+ # Coerce back to `self`. We now have a polynomial of only
# positive valuation monomials
f = self(f_rand)
- # Finally, shift the entire polynomial down by low_degree
+ # Finally, shift the entire polynomial down by min_valuation
# which will result in a polynomial with highest degree
- # high_degree and lowest degree low_degree
+ # high_degree and lowest valuation min_valuation
monomial = prod(self.gens())
- f *= monomial**low_degree
+ f *= monomial**min_valuation
return f
From 5cad57b6c1d387b045a3aabbbaf5596c1e35df95 Mon Sep 17 00:00:00 2001
From: Giacomo Pope
Date: Wed, 28 Feb 2024 11:01:27 +0000
Subject: [PATCH 081/518] QQbar instead of RR
---
src/sage/rings/polynomial/laurent_polynomial_ring_base.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
index 19eafdb9b90..311042380af 100644
--- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
+++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py
@@ -551,7 +551,7 @@ def random_element(self, min_valuation=-2, high_degree=2, *args, **kwds):
::
- sage: L = LaurentPolynomialRing(RR, 5, 'x')
+ sage: L = LaurentPolynomialRing(QQbar, 5, 'x')
sage: f = L.random_element(-1, 1)
sage: f = L.random_element(-1, 1)
sage: tuple(f.degree(x) <= 1 for x in L.gens())
From ba91c00d9daf7240faa331b6950a7d0ca300a04b Mon Sep 17 00:00:00 2001
From: Martin Rubey
Date: Wed, 28 Feb 2024 15:01:57 +0100
Subject: [PATCH 082/518] remove zombie code
---
src/sage/matrix/matrix_modn_sparse.pyx | 299 -------------------------
1 file changed, 299 deletions(-)
diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx
index f6c505b5965..841f870c2bd 100644
--- a/src/sage/matrix/matrix_modn_sparse.pyx
+++ b/src/sage/matrix/matrix_modn_sparse.pyx
@@ -870,302 +870,3 @@ cdef class Matrix_modn_sparse(Matrix_sparse):
return d
else:
raise ValueError("no algorithm '%s'"%algorithm)
-
- def _solve_right_nonsingular_square(self, B, algorithm=None, check_rank=False):
- r"""
- If self is a matrix `A`, then this function returns a
- vector or matrix `X` such that `A X = B`. If
- `B` is a vector then `X` is a vector and if
- `B` is a matrix, then `X` is a matrix.
-
- .. NOTE::
-
- DEPRECATED. In Sage one can also write ``A \ B`` for
- ``A.solve_right(B)``, i.e., Sage implements the "the
- MATLAB/Octave backslash operator".
-
- INPUT:
-
-
- - ``B`` - a matrix or vector
-
- - ``algorithm`` - one of the following:
-
- - ``'linbox'`` or ``'linbox_default'`` - (default) use LinBox
- and let it chooses the appropriate algorithm
-
- - ``linbox_dense_elimination'`` - use LinBox dense elimination
-
- - ``'linbox_sparse_elimination'`` - use LinBox sparse elimination
-
- - ``'linbox_ blackbox'`` - LinBox via a Blackbox algorithm
-
- - ``'linbox_wiedemann'`` - use LinBox implementation of
- Wiedemann's algorithm
-
- - ``'generic'`` - use the Sage generic implementation
- (via inversion)
-
- - ``check_rank`` - whether to check that the rank is maximal
-
- OUTPUT: a matrix or vector
-
- EXAMPLES::
-
- sage: A = matrix(ZZ, 3, [1,2,3,-1,2,5,2,3,1], sparse=True)
- sage: b = vector(ZZ, [1,2,3])
- sage: x = A.solve_right(b)
- sage: x
- (-13/12, 23/12, -7/12)
- sage: A * x
- (1, 2, 3)
-
- sage: u = matrix(ZZ, 3, 2, [0,1,1,1,0,2])
- sage: x = A.solve_right(u)
- sage: x
- [-7/12 -1/6]
- [ 5/12 5/6]
- [-1/12 -1/6]
- sage: A * x
- [0 1]
- [1 1]
- [0 2]
- """
- if check_rank and self.rank() < self.nrows():
- from sage.matrix.matrix2 import NotFullRankError
- raise NotFullRankError
-
- if self.base_ring() != B.base_ring():
- B = B.change_ring(self.base_ring())
- if self.nrows() != B.nrows():
- raise ValueError("input matrices must have the same number of rows.")
-
- if algorithm == "generic":
- return Matrix_sparse.solve_right(self, B)
- else:
- if isinstance(B, Matrix):
- from sage.matrix.special import diagonal_matrix
- m, d = self._solve_matrix_linbox(B, algorithm)
- return m * diagonal_matrix([QQ((1,x)) for x in d])
- else:
- v, d = self._solve_vector_linbox(B, algorithm)
- return v / d
-
- def _solve_vector_linbox(self, v, algorithm=None):
- r"""
- Return a pair ``(a, d)`` so that ``d * b = m * a``
-
- If there is no solution a ``ValueError`` is raised.
-
- INPUT:
-
- - ``b`` -- a dense integer vector
-
- - ``algorithm`` -- (optional) either ``None``, ``'dense_elimination'``,
- ``'sparse_elimination'``, ``'wiedemann'`` or ``'blackbox'``.
-
- OUTPUT: a pair ``(a, d)`` consisting of
-
- - ``a`` -- a dense integer vector
-
- - ``d`` -- an integer
-
- EXAMPLES::
-
- sage: m = matrix(ZZ, 4, sparse=True)
- sage: m[0,0] = m[1,2] = m[2,0] = m[3,3] = 2
- sage: m[0,2] = m[1,1] = -1
- sage: m[2,3] = m[3,0] = -3
-
- sage: b0 = vector((1,1,1,1))
- sage: m._solve_vector_linbox(b0)
- ((-1, -7, -3, -1), 1)
- sage: m._solve_vector_linbox(b0, 'dense_elimination')
- ((-1, -7, -3, -1), 1)
- sage: m._solve_vector_linbox(b0, 'sparse_elimination')
- ((-1, -7, -3, -1), 1)
- sage: m._solve_vector_linbox(b0, 'wiedemann')
- ((-1, -7, -3, -1), 1)
- sage: m._solve_vector_linbox(b0, 'blackbox')
- ((-1, -7, -3, -1), 1)
-
- sage: b1 = vector((1,1,-1,1))
- sage: a1, d1 = m._solve_vector_linbox(b1)
- sage: d1 * b1 == m * a1
- True
-
- TESTS::
-
- sage: algos = ["default", "dense_elimination", "sparse_elimination",
- ....: "blackbox", "wiedemann"]
- sage: for i in range(20):
- ....: dim = randint(1, 30)
- ....: M = MatrixSpace(ZZ, dim, sparse=True)
- ....: density = min(1, 4/dim)
- ....: m = M.random_element(density=density)
- ....: while m.rank() != dim:
- ....: m = M.random_element(density=density)
- ....: U = m.column_space().dense_module()
- ....: for algo in algos:
- ....: u, d = m._solve_vector_linbox(U.zero(), algorithm=algo)
- ....: assert u.is_zero()
- ....: b = U.random_element()
- ....: x, d = m._solve_vector_linbox(b, algorithm=algo)
- ....: assert m * x == d * b
- """
- Vin = self.column_ambient_module(base_ring=None, sparse=False)
- v = Vin(v)
-
- if self._nrows == 0 or self._ncols == 0:
- raise ValueError("not implemented for nrows=0 or ncols=0")
-
- # LinBox "solve" is mostly broken for nonsquare or singular matrices.
- # The conditions below could be removed once all LinBox issues has
- # been solved.
- if self._nrows != self._ncols or self.rank() != self._nrows:
- raise ValueError("only available for full rank square matrices")
-
- cdef givaro.ZRing givZZ
- cdef linbox.SparseMatrix_integer * A = new_linbox_matrix_integer_sparse(givZZ, self)
- cdef linbox.DenseVector_integer * b = new_linbox_vector_integer_dense(givZZ, v)
- cdef linbox.DenseVector_integer * res = new linbox.DenseVector_integer(givZZ, self._ncols)
- cdef givaro.Integer D
-
- method = get_method(algorithm)
-
- if method == METHOD_DEFAULT:
- linbox.solve(res[0], D, A[0], b[0])
- elif method == METHOD_WIEDEMANN:
- linbox.solve(res[0], D, A[0], b[0], linbox.Method.Wiedemann())
- elif method == METHOD_DENSE_ELIMINATION:
- linbox.solve(res[0], D, A[0], b[0], linbox.Method.DenseElimination())
- elif method == METHOD_SPARSE_ELIMINATION:
- linbox.solve(res[0], D, A[0], b[0], linbox.Method.SparseElimination())
- elif method == METHOD_BLACKBOX:
- linbox.solve(res[0], D, A[0], b[0], linbox.Method.Blackbox())
-
- Vout = self.row_ambient_module(base_ring=None, sparse=False)
- res_sage = new_sage_vector_integer_dense(Vout, res[0])
- cdef Integer d = PY_NEW(Integer)
- mpz_set(d.value, D.get_mpz_const())
-
- del A
- del b
- del res
-
- return (res_sage, d)
-
- def _solve_matrix_linbox(self, mat, algorithm=None):
- r"""
- Solve the equation ``A x = mat`` where ``A`` is this matrix.
-
- EXAMPLES::
-
- sage: m = matrix(ZZ, [[1,2],[1,0]], sparse=True)
- sage: b = matrix(ZZ, 2, 4, [1,0,2,0,1,1,2,0], sparse=False)
- sage: u, d = m._solve_matrix_linbox(b)
- sage: u
- [ 1 2 2 0]
- [ 0 -1 0 0]
- sage: m * u == b * diagonal_matrix(d)
- True
-
- sage: u, d = m._solve_matrix_linbox([[1,3,4],[0,1,0]])
- sage: u
- [0 1 0]
- [1 1 2]
- sage: d
- (2, 1, 1)
-
- Test input::
-
- sage: m = matrix(ZZ, [[1,2],[1,0]], sparse=True)
- sage: b = matrix(ZZ, 3, 3, range(9))
- sage: m._solve_matrix_linbox(b)
- Traceback (most recent call last):
- ...
- ValueError: wrong matrix dimension
-
- sage: m._solve_matrix_linbox([[1,1],[2,3]], algorithm='hop')
- Traceback (most recent call last):
- ...
- ValueError: unknown algorithm
-
- TESTS::
-
- sage: algos = ["default", "dense_elimination", "sparse_elimination",
- ....: "blackbox", "wiedemann"]
-
- sage: for _ in range(10):
- ....: dim = randint(2, 10)
- ....: M = MatrixSpace(ZZ, dim, sparse=True)
- ....: m = M.random_element(density=min(1,10/dim))
- ....: while m.rank() != dim:
- ....: m = M.random_element(density=min(1,10/dim))
- ....: b = random_matrix(ZZ, dim, 7)
- ....: Mb = b.parent()
- ....: for algo in algos:
- ....: u, d = m._solve_matrix_linbox(b, algo)
- ....: assert m * u == b * diagonal_matrix(d)
- """
- if self._nrows == 0 or self._ncols == 0:
- raise ValueError("not implemented for nrows=0 or ncols=0")
-
- from sage.matrix.constructor import matrix
- from sage.modules.free_module_element import vector
-
- cdef Matrix_integer_dense B
- if not isinstance(mat, Matrix2):
- B = matrix(ZZ, mat, sparse=False)
- else:
- B =