From bd626404ce53f6ed056145bc89287648be52f863 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 5 Jan 2025 01:26:00 +0800 Subject: [PATCH 1/4] Various small improvements to imports Mostly to try to reduce a few circular imports that happen when you don't import `sage.all`. A few of these changes are purely cosmetic. --- src/sage/categories/category_singleton.pyx | 5 ++- src/sage/categories/morphism.pyx | 4 +- .../characteristic_cohomology_class.py | 2 +- src/sage/misc/lazy_attribute.pyi | 3 ++ src/sage/modules/free_module.py | 23 +++++----- src/sage/rings/integer_ring.pxd | 1 - src/sage/rings/integer_ring.pyx | 42 +++++++++---------- src/sage/rings/rational.pyx | 11 +++-- src/sage/structure/sequence.py | 5 +-- 9 files changed, 52 insertions(+), 44 deletions(-) diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index 0423ab9d83a..83f61842034 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -10,7 +10,6 @@ Singleton categories # https://www.gnu.org/licenses/ # ***************************************************************************** -from sage.misc.constant_function import ConstantFunction from sage.misc.lazy_attribute import lazy_class_attribute from sage.categories.category import Category from sage.structure.category_object cimport CategoryObject @@ -318,10 +317,12 @@ class Category_singleton(Category): ... AssertionError: is not a direct subclass of """ + from sage.misc.constant_function import ConstantFunction + from sage.categories.category_with_axiom import CategoryWithAxiom_singleton + if isinstance(cls, DynamicMetaclass): # cls is something like Rings_with_category cls = cls.__base__ # TODO: find a better way to check that cls is an abstract class - from sage.categories.category_with_axiom import CategoryWithAxiom_singleton assert (cls.__mro__[1] is Category_singleton or cls.__mro__[1] is CategoryWithAxiom_singleton), \ "{} is not a direct subclass of {}".format(cls, Category_singleton) obj = super().__classcall__(cls, *args) diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index 28e89a36299..19b705b1721 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -39,8 +39,6 @@ AUTHORS: from cpython.object cimport * -from sage.misc.constant_function import ConstantFunction - from sage.structure.element cimport Element, ModuleElement from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.structure.parent cimport Parent @@ -92,6 +90,8 @@ cdef class Morphism(Map): sage: phi Defunct morphism """ + from sage.misc.constant_function import ConstantFunction + D = self.domain() if D is None: return "Defunct morphism" diff --git a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py index 63d0384e149..fd10ab41491 100644 --- a/src/sage/manifolds/differentiable/characteristic_cohomology_class.py +++ b/src/sage/manifolds/differentiable/characteristic_cohomology_class.py @@ -281,13 +281,13 @@ # ***************************************************************************** from sage.algebras.finite_gca import FiniteGCAlgebra -from sage.combinat.free_module import IndexedFreeModuleElement from sage.manifolds.differentiable.affine_connection import AffineConnection from sage.manifolds.differentiable.bundle_connection import BundleConnection from sage.manifolds.differentiable.levi_civita_connection import LeviCivitaConnection from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.misc.fast_methods import Singleton +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.sage_object import SageObject diff --git a/src/sage/misc/lazy_attribute.pyi b/src/sage/misc/lazy_attribute.pyi index 6a174a37aa2..b857216eed1 100644 --- a/src/sage/misc/lazy_attribute.pyi +++ b/src/sage/misc/lazy_attribute.pyi @@ -1,5 +1,8 @@ # This type-stub file helps pyright understand the decorator @lazy_attribute. +from collections.abc import Callable +from typing import Any + # Adapted from https://github.com/python/typeshed/blob/b9640005eb586afdbe0a57bac2b88a7a12465069/stdlib/builtins.pyi#L1237-L1254 class lazy_attribute: def __init__( diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 471db14112b..686ca88e0f4 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -194,7 +194,11 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import LazyImport from sage.misc.randstate import current_randstate -from sage.modules import free_module_element +from sage.modules.free_module_element import ( + FreeModuleElement, + FreeModuleElement_generic_dense, + FreeModuleElement_generic_sparse, +) from sage.modules.module import Module from sage.rings.finite_rings.finite_field_base import FiniteField from sage.structure.factory import UniqueFactory @@ -213,7 +217,6 @@ ) from sage.structure.sequence import Sequence - ############################################################################### # # Constructor functions @@ -776,7 +779,7 @@ def span(gens, base_ring=None, check=True, already_echelonized=False): return FreeModule(R, 0) else: x = gens[0] - if isinstance(x, free_module_element.FreeModuleElement): + if isinstance(x, FreeModuleElement): M = x.parent() else: try: @@ -950,7 +953,7 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): """ if isinstance(x, (int, sage.rings.integer.Integer)) and x == 0: return self.zero_vector() - elif isinstance(x, free_module_element.FreeModuleElement): + elif isinstance(x, FreeModuleElement): if x.parent() is self: if copy: return x.__copy__() @@ -2205,7 +2208,7 @@ def _element_constructor_(self, x, coerce=True, copy=True, check=True): """ if (isinstance(x, (int, sage.rings.integer.Integer)) and x == 0): return self.zero_vector() - if isinstance(x, free_module_element.FreeModuleElement): + if isinstance(x, FreeModuleElement): if x.parent() is self: if copy: return x.__copy__() @@ -7022,7 +7025,7 @@ def echelon_coordinates(self, v, check=True): sage: W.echelon_coordinates([0,0,2,0,-1/2]) [0, 2] """ - if not isinstance(v, free_module_element.FreeModuleElement): + if not isinstance(v, FreeModuleElement): v = self.ambient_vector_space()(v) elif v.degree() != self.degree(): raise ArithmeticError("vector is not in free module") @@ -8051,7 +8054,7 @@ def echelon_coordinates(self, v, check=True): sage: vector(QQ, W.echelon_coordinates(v)) * W.basis_matrix() (1, 5, 9) """ - if not isinstance(v, free_module_element.FreeModuleElement): + if not isinstance(v, FreeModuleElement): v = self.ambient_vector_space()(v) if v.degree() != self.degree(): raise ArithmeticError("v (=%s) is not in self" % v) @@ -8195,7 +8198,7 @@ def element_class(R, is_sparse): else: if R.order() < MAX_MODULUS: return Vector_modn_dense - return free_module_element.FreeModuleElement_generic_dense + return FreeModuleElement_generic_dense elif isinstance(R, sage.rings.abc.RealDoubleField) and not is_sparse: try: from sage.modules.vector_real_double_dense import Vector_real_double_dense @@ -8224,9 +8227,9 @@ def element_class(R, is_sparse): return sage.modules.vector_symbolic_sparse.Vector_symbolic_sparse if is_sparse: - return free_module_element.FreeModuleElement_generic_sparse + return FreeModuleElement_generic_sparse else: - return free_module_element.FreeModuleElement_generic_dense + return FreeModuleElement_generic_dense @richcmp_method diff --git a/src/sage/rings/integer_ring.pxd b/src/sage/rings/integer_ring.pxd index 204ccbe141c..fb7e0d97b5b 100644 --- a/src/sage/rings/integer_ring.pxd +++ b/src/sage/rings/integer_ring.pxd @@ -1,5 +1,4 @@ from sage.rings.ring cimport CommutativeRing -from sage.rings.integer cimport Integer from sage.libs.gmp.types cimport mpz_t cdef class IntegerRing_class(CommutativeRing): diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index d6555ac0eab..9d8bc4f09bd 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -66,9 +66,7 @@ from sage.structure.richcmp cimport rich_to_bool from sage.misc.misc_c import prod from sage.misc.randstate cimport randstate, current_randstate, SAGE_RAND_MAX -cimport sage.rings.integer as integer - -from sage.rings import ring +from sage.rings.integer cimport Integer arith = None cdef void late_import() noexcept: @@ -329,7 +327,7 @@ cdef class IntegerRing_class(CommutativeRing): self._populate_coercion_lists_(init_no_parent=True, convert_method_name='_integer_') - _element_constructor_ = integer.Integer + _element_constructor_ = Integer def __reduce__(self): """ @@ -479,20 +477,20 @@ cdef class IntegerRing_class(CommutativeRing): if step is None: step = 1 if type(step) is not int: - if not isinstance(step, integer.Integer): - step = integer.Integer(step) + if not isinstance(step, Integer): + step = Integer(step) if mpz_fits_slong_p((step).value): step = int(step) - if not isinstance(start, integer.Integer): - start = integer.Integer(start) - if not isinstance(end, integer.Integer): - end = integer.Integer(end) - cdef integer.Integer a = start - cdef integer.Integer b = end + if not isinstance(start, Integer): + start = Integer(start) + if not isinstance(end, Integer): + end = Integer(end) + cdef Integer a = start + cdef Integer b = end cdef int step_sign cdef long istep - cdef integer.Integer zstep, last + cdef Integer zstep, last L = [] if type(step) is int: @@ -797,8 +795,8 @@ cdef class IntegerRing_class(CommutativeRing): sage: ZZ.random_element() # indirect doctest # random 6 """ - cdef integer.Integer r - cdef integer.Integer n_max, n_min, n_width + cdef Integer r + cdef Integer n_max, n_min, n_width cdef randstate rstate = current_randstate() cdef int den = rstate.c_random()-SAGE_RAND_MAX/2 if den == 0: den = 1 @@ -809,11 +807,11 @@ cdef class IntegerRing_class(CommutativeRing): if x is None: mpz_set_si(value, rstate.c_random()%5 - 2) else: - n_max = x if isinstance(x, integer.Integer) else self(x) + n_max = x if isinstance(x, Integer) else self(x) mpz_urandomm(value, rstate.gmp_state, n_max.value) else: - n_min = x if isinstance(x, integer.Integer) else self(x) - n_max = y if isinstance(y, integer.Integer) else self(y) + n_min = x if isinstance(x, Integer) else self(x) + n_max = y if isinstance(y, Integer) else self(y) n_width = n_max - n_min if mpz_sgn(n_width.value) <= 0: n_min = self(-2) @@ -954,7 +952,7 @@ cdef class IntegerRing_class(CommutativeRing): ... TypeError: I must be an ideal of ZZ """ - if isinstance(I, sage.rings.integer.Integer): + if isinstance(I, Integer): n = I elif isinstance(I, sage.rings.ideal.Ideal_generic): if not (I.ring() is self): @@ -1023,7 +1021,7 @@ cdef class IntegerRing_class(CommutativeRing): ... TypeError: 96 is not prime """ - if isinstance(prime, sage.rings.integer.Integer): + if isinstance(prime, Integer): p = self.ideal(prime) elif isinstance(prime, sage.rings.ideal.Ideal_generic): if not (prime.ring() is self): @@ -1211,9 +1209,9 @@ cdef class IntegerRing_class(CommutativeRing): ValueError: n must be positive in zeta() """ if n == 1: - return sage.rings.integer.Integer(1) + return Integer(1) elif n == 2: - return sage.rings.integer.Integer(-1) + return Integer(-1) elif n < 1: raise ValueError("n must be positive in zeta()") else: diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 68ee004a251..5fd63a21d3b 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -65,7 +65,7 @@ from cysignals.signals cimport sig_on, sig_off import operator import fractions -import sage.rings.rational_field +from sage.rings.rational_field import Q from sage.arith.long cimport integer_check_long_py from sage.categories.morphism cimport Morphism @@ -73,7 +73,6 @@ from sage.categories.map cimport Map from sage.cpython.string cimport char_to_str, str_to_bytes from sage.libs.gmp.pylong cimport mpz_set_pylong from sage.rings.integer cimport Integer, smallInteger -from sage.rings.integer_ring import ZZ from sage.structure.coerce cimport coercion_model, is_numpy_type from sage.structure.element cimport Element from sage.structure.parent cimport Parent @@ -182,7 +181,7 @@ cdef Rational_sub_(Rational self, Rational other): return x -cdef Parent the_rational_ring = sage.rings.rational_field.Q +cdef Parent the_rational_ring = Q # make sure zero/one elements are set cdef set_zero_one_elements(): @@ -2105,10 +2104,14 @@ cdef class Rational(sage.structure.element.FieldElement): num, exact = self.numerator().nth_root(n, 1) if not exact: + from sage.rings.integer_ring import ZZ + raise ValueError("not a perfect %s power" % ZZ(n).ordinal_str()) den, exact = self.denominator().nth_root(n, 1) if not exact: + from sage.rings.integer_ring import ZZ + raise ValueError("not a perfect %s power" % ZZ(n).ordinal_str()) if negative: @@ -3195,6 +3198,8 @@ cdef class Rational(sage.structure.element.FieldElement): sage: (-1/2).log(3) # needs sage.symbolic (I*pi + log(1/2))/log(3) """ + from sage.rings.integer_ring import ZZ + cdef int self_sgn if self.denom().is_one(): return ZZ(self.numer()).log(m, prec) diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index 614ad8c0fe4..4ba6fea120f 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -71,8 +71,7 @@ # **************************************************************************** from sage.misc.persist import register_unpickle_override -import sage.structure.sage_object -import sage.structure.coerce +from sage.structure.sage_object import SageObject def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=None, use_sage_types=False): @@ -306,7 +305,7 @@ def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=Non return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types) -class Sequence_generic(sage.structure.sage_object.SageObject, list): +class Sequence_generic(SageObject, list): """ A mutable list of elements with a common guaranteed universe, which can be set immutable. From 3050d63b33fc3dd7efaccc8956bc30176228bf50 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 5 Jan 2025 02:45:10 +0800 Subject: [PATCH 2/4] Fix linter --- src/sage/modules/free_module.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 686ca88e0f4..5ba526b7fa5 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -223,6 +223,7 @@ # ############################################################################### + class FreeModuleFactory(UniqueFactory): r""" Factory class for the finite-dimensional free modules with standard basis From 270c4a1b9dbb245039fd393725f9a3692509d5d1 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Thu, 6 Feb 2025 00:01:12 +0100 Subject: [PATCH 3/4] Move initialization of zero and one element to RationalField --- src/sage/rings/rational.pyx | 21 +++++---------------- src/sage/rings/rational_field.py | 3 +++ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 5fd63a21d3b..c46491493f7 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -65,7 +65,7 @@ from cysignals.signals cimport sig_on, sig_off import operator import fractions -from sage.rings.rational_field import Q +from sage.rings.rational_field import QQ from sage.arith.long cimport integer_check_long_py from sage.categories.morphism cimport Morphism @@ -181,16 +181,6 @@ cdef Rational_sub_(Rational self, Rational other): return x -cdef Parent the_rational_ring = Q - -# make sure zero/one elements are set -cdef set_zero_one_elements(): - global the_rational_ring - the_rational_ring._zero_element = Rational(0) - the_rational_ring._one_element = Rational(1) - -set_zero_one_elements() - cpdef Integer integer_rational_power(Integer a, Rational b): """ Compute `a^b` as an integer, if it is integral, or return ``None``. @@ -506,9 +496,8 @@ cdef class Rational(sage.structure.element.FieldElement): sage: p.parent() Rational Field """ - global the_rational_ring mpq_init(self.value) - self._parent = the_rational_ring + self._parent = QQ def __init__(self, x=None, unsigned int base=0): """ @@ -4124,10 +4113,10 @@ cdef class Z_to_Q(Morphism): From: Integer Ring To: Rational Field """ - from sage.rings import integer_ring - from sage.rings import rational_field + from sage.rings.integer_ring import ZZ + import sage.categories.homset - Morphism.__init__(self, sage.categories.homset.Hom(integer_ring.ZZ, rational_field.QQ)) + Morphism.__init__(self, sage.categories.homset.Hom(ZZ, QQ)) cpdef Element _call_(self, x): """ diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 7975a5053a9..7a9fad58b1e 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -133,6 +133,9 @@ class RationalField(Singleton, number_field_base.NumberField): sage: QQ(RealField(45)(t)) 1/5 """ + _zero_element = Rational(0) + _one_element = Rational(1) + def __new__(cls): """ This method actually is not needed for using :class:`RationalField`. From b6f6411cfae508f9704c6c0aa5bd706c51874c69 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Wed, 12 Feb 2025 14:03:38 +0100 Subject: [PATCH 4/4] Partially revert changes to fix tests --- src/sage/rings/rational.pyx | 13 ++++++++++++- src/sage/rings/rational_field.py | 3 --- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index c46491493f7..0ee311f273b 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -181,6 +181,16 @@ cdef Rational_sub_(Rational self, Rational other): return x +cdef Parent the_rational_ring = QQ + +# make sure zero/one elements are set +cdef set_zero_one_elements(): + global the_rational_ring + the_rational_ring._zero_element = Rational(0) + the_rational_ring._one_element = Rational(1) + +set_zero_one_elements() + cpdef Integer integer_rational_power(Integer a, Rational b): """ Compute `a^b` as an integer, if it is integral, or return ``None``. @@ -496,8 +506,9 @@ cdef class Rational(sage.structure.element.FieldElement): sage: p.parent() Rational Field """ + global the_rational_ring mpq_init(self.value) - self._parent = QQ + self._parent = the_rational_ring def __init__(self, x=None, unsigned int base=0): """ diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index ea2f069b43a..a4ca268a0c6 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -133,9 +133,6 @@ class RationalField(Singleton, number_field_base.NumberField): sage: QQ(RealField(45)(t)) 1/5 """ - _zero_element = Rational(0) - _one_element = Rational(1) - def __new__(cls): """ This method actually is not needed for using :class:`RationalField`.