Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions src/sage/categories/map.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from __future__ import annotations

from typing import Any, Callable, Generic, Iterator, Mapping, Self, Sequence, TypeVar

from sage.categories.category import Category
from sage.structure.element import Element
from sage.structure.parent import Parent

DomainElementT_contra = TypeVar("DomainElementT_contra", contravariant=True)
CodomainElementT_co = TypeVar("CodomainElementT_co", covariant=True)
Comment on lines +9 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen contravariant and covariant in the Python docs before but haven't really seen them used. For the sake of helping me review this, could you briefly explain what these are doing?

SectionDomainT = TypeVar("SectionDomainT")
SectionCodomainT = TypeVar("SectionCodomainT")
CompositeDomainT = TypeVar("CompositeDomainT")
CompositeCodomainT = TypeVar("CompositeCodomainT")

class Map(Element, Generic[DomainElementT_contra, CodomainElementT_co]):
_coerce_cost: int
_is_coercion: bool
_repr_type_str: str | None
_category_for: Category | None
domain: Callable[[], Parent[Any] | None]
codomain: Callable[[], Parent[Any]]
_codomain: Parent[Any]

def __init__(self, parent: Any, codomain: Parent[Any] | None = ...) -> None: ...
def __copy__(self) -> Self: ...
def parent(self) -> Any: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this annotated to return Parent?

def _make_weak_references(self) -> None: ...
def _make_strong_references(self) -> None: ...
def _update_slots(self, slots: Mapping[str, Any]) -> None: ...
def _update_slots_test(self, _slots: Mapping[str, Any]) -> None: ...
def _extra_slots(self) -> dict[str, Any]: ...
def _extra_slots_test(self) -> dict[str, Any]: ...
def __reduce__(self) -> tuple[Any, tuple[Any, ...]]: ...
def _repr_type(self) -> str: ...
def _repr_defn(self) -> str: ...
def _repr_(self) -> str: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the type annotation for _repr_ (and other methods like __hash__) be inherited from SageObject? I though type checkers enforced that through the Liskov substitution principle (at least some do), which would mean that this annotation is redundant.

If I'm correct about the behaviour of type annotations with inheritance, that would mean that we would get the most value for the amount of work by annotating classes higher up in the inheritance hierarchy (like you've done in this PR).

def _default_repr_(self) -> str: ...
def domains(self) -> Iterator[Parent[Any] | None]: ...
def category_for(self) -> Category: ...
def __call__(
self, x: DomainElementT_contra, *args: Any, **kwds: Any
) -> CodomainElementT_co: ...
def _call_(self, x: DomainElementT_contra) -> CodomainElementT_co: ...
def _call_with_args(
self,
x: DomainElementT_contra,
args: Sequence[Any] = ...,
kwds: Mapping[str, Any] | None = ...,
) -> CodomainElementT_co: ...
def __mul__(self, right: Map[Any, Any]) -> Map[Any, Any]: ...
def _composition(self, right: Map[Any, Any]) -> Map[Any, Any]: ...
def _composition_(self, right: Map[Any, Any], homset: Any) -> Map[Any, Any]: ...
def pre_compose(self, right: Map[Any, Any]) -> Map[Any, Any]: ...
def post_compose(self, left: Map[Any, Any]) -> Map[Any, Any]: ...
def extend_domain(self, new_domain: Any) -> Map[Any, Any]: ...
def extend_codomain(self, new_codomain: Any) -> Map[Any, Any]: ...
def is_surjective(self) -> bool: ...
def _pow_int(self, n: int) -> Map[Any, Any]: ...
def section(self) -> Map[Any, Any] | None: ...
def __hash__(self) -> int: ...

class Section(
Map[SectionCodomainT, SectionDomainT], Generic[SectionDomainT, SectionCodomainT]
):
_inverse: Map[SectionDomainT, SectionCodomainT]

def __init__(self, map: Map[SectionDomainT, SectionCodomainT]) -> None: ...
def _extra_slots(self) -> dict[str, Any]: ...
def _update_slots(self, slots: Mapping[str, Any]) -> None: ...
def _repr_type(self) -> str: ...
def inverse(self) -> Map[SectionDomainT, SectionCodomainT]: ...

class FormalCompositeMap(
Map[CompositeDomainT, CompositeCodomainT],
Generic[CompositeDomainT, CompositeCodomainT],
):
__list: Sequence[Map[Any, Any]]

def __init__(
self,
parent: Any,
first: Map[Any, Any] | Sequence[Map[Any, Any]],
second: Map[Any, Any] | None = ...,
) -> None: ...
def __copy__(self) -> Self: ...
def _update_slots(self, slots: Mapping[str, Any]) -> None: ...
def _extra_slots(self) -> dict[str, Any]: ...
def __richcmp__(self, other: Any, op: int) -> bool: ...
def __hash__(self) -> int: ...
def __getitem__(self, i: int) -> Map[Any, Any]: ...
def _call_(self, x: CompositeDomainT) -> CompositeCodomainT: ...
def _call_with_args(
self,
x: CompositeDomainT,
args: Sequence[Any] = ...,
kwds: Mapping[str, Any] | None = ...,
) -> CompositeCodomainT: ...
def _repr_type(self) -> str: ...
def _repr_defn(self) -> str: ...
def first(self) -> Map[Any, Any]: ...
def then(self) -> Map[Any, Any]: ...
def is_injective(self) -> bool: ...
def is_surjective(self) -> bool: ...
def domains(self) -> Iterator[Parent[Any] | None]: ...
def section(self) -> Map[Any, Any] | None: ...

def unpickle_map(
_class: type[Map[Any, Any]],
parent: Any,
_dict: Mapping[str, Any],
_slots: Mapping[str, Any],
) -> Map[Any, Any]: ...
def is_Map(x: Any) -> bool: ...
10 changes: 4 additions & 6 deletions src/sage/rings/complex_conversion.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from typing import Any

from sage.structure.element import Element
from sage.categories.map import Map
from sage.rings.complex_double import ComplexDoubleElement
from sage.rings.complex_mpfr import ComplexNumber

class CCtoCDF(Map):
def _call_(self, x: Element) -> Element:
...
class CCtoCDF(Map[ComplexNumber, ComplexDoubleElement]):
def _call_(self, x: ComplexNumber) -> ComplexDoubleElement: ...
51 changes: 28 additions & 23 deletions src/sage/rings/complex_interval_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

from __future__ import annotations

import weakref
from typing import Literal

import sage.rings.abc
from sage.misc.cachefunc import cached_method
Expand All @@ -48,10 +49,10 @@
from sage.rings.ring import Field
from sage.structure.parent import Parent

cache = {}
cache: dict[int, weakref.ReferenceType[ComplexIntervalField_class]] = {}


def ComplexIntervalField(prec=53, names=None):
def ComplexIntervalField(prec=53, names=None) -> ComplexIntervalField_class:
"""
Return the complex interval field with real and imaginary parts having
``prec`` *bits* of precision.
Expand Down Expand Up @@ -180,9 +181,10 @@ class ComplexIntervalField_class(sage.rings.abc.ComplexIntervalField):
- :class:`sage.rings.complex_arb.ComplexBallField` (alternative
implementation of complex intervals, with more features)
"""

Element = complex_interval.ComplexIntervalFieldElement

def __init__(self, prec=53):
def __init__(self, prec=53) -> None:
"""
Initialize ``self``.

Expand All @@ -195,9 +197,11 @@ def __init__(self, prec=53):
"""
self._prec = int(prec)
from sage.categories.fields import Fields
Field.__init__(self, self.real_field(), ("I",), False,
category=Fields().Infinite())
self._populate_coercion_lists_(convert_method_name='_complex_mpfi_')

Field.__init__(
self, self.real_field(), ("I",), False, category=Fields().Infinite()
)
self._populate_coercion_lists_(convert_method_name="_complex_mpfi_")

def __reduce__(self):
"""
Expand All @@ -208,7 +212,7 @@ def __reduce__(self):
sage: loads(dumps(CIF)) == CIF
True
"""
return ComplexIntervalField, (self._prec, )
return ComplexIntervalField, (self._prec,)

def construction(self):
"""
Expand Down Expand Up @@ -241,9 +245,10 @@ def construction(self):
Univariate Polynomial Ring in x over Complex Interval Field with 53 bits of precision
"""
from sage.categories.pushout import AlgebraicClosureFunctor

return (AlgebraicClosureFunctor(), self.real_field())

def is_exact(self):
def is_exact(self) -> Literal[False]:
"""
The complex interval field is not exact.

Expand All @@ -254,7 +259,7 @@ def is_exact(self):
"""
return False

def prec(self):
def prec(self) -> int:
"""
Return the precision of ``self`` (in bits).

Expand All @@ -267,7 +272,7 @@ def prec(self):
"""
return self._prec

def to_prec(self, prec):
def to_prec(self, prec) -> ComplexIntervalField_class:
"""
Return a complex interval field with the given precision.

Expand All @@ -282,7 +287,7 @@ def to_prec(self, prec):
"""
return ComplexIntervalField(prec)

def _magma_init_(self, magma):
def _magma_init_(self, magma) -> str:
r"""
Return a string representation of ``self`` in the Magma language.

Expand Down Expand Up @@ -321,10 +326,10 @@ def _sage_input_(self, sib, coerce):
{call: {atomic:ComplexIntervalField}({atomic:2})}
"""
if self.prec() == 53:
return sib.name('CIF')
return sib.name("CIF")

v = sib.name('ComplexIntervalField')(sib.int(self.prec()))
name = 'CIF%d' % self.prec()
v = sib.name("ComplexIntervalField")(sib.int(self.prec()))
name = "CIF%d" % self.prec()
sib.cache(self, v, name)
return v

Expand Down Expand Up @@ -386,7 +391,7 @@ def __eq__(self, other):
return False
return self._prec == other._prec

def __hash__(self):
def __hash__(self) -> int:
"""
Return the hash.

Expand All @@ -400,7 +405,7 @@ def __hash__(self):
"""
return hash((self.__class__, self._prec))

def __ne__(self, other):
def __ne__(self, other) -> bool:
"""
Test whether ``self`` is not equal to ``other``.

Expand Down Expand Up @@ -521,8 +526,7 @@ def _coerce_map_from_(self, S):
# Direct and efficient conversions
if S is ZZ or S is QQ or S is int:
return True
if isinstance(S, (ComplexIntervalField_class,
RealIntervalField_class)):
if isinstance(S, (ComplexIntervalField_class, RealIntervalField_class)):
return S.precision() >= self._prec

# If coercion to CC is possible and there is a _complex_mpfi_
Expand All @@ -532,7 +536,7 @@ def _coerce_map_from_(self, S):
if f is not None:
return f

return self._coerce_map_via( (self.real_field(),), S)
return self._coerce_map_via((self.real_field(),), S)

def _repr_(self):
"""
Expand Down Expand Up @@ -650,7 +654,7 @@ def ngens(self):
"""
return 1

def zeta(self, n=2):
def zeta(self, n: int = 2):
r"""
Return a primitive `n`-th root of unity.

Expand All @@ -673,6 +677,7 @@ def zeta(self, n=2):
0.309016994374948? + 0.9510565162951536?*I
"""
from .integer import Integer

n = Integer(n)
if n == 1:
x = self.element_class(self, 1)
Expand All @@ -683,10 +688,10 @@ def zeta(self, n=2):
# e^(2*pi*i/n) = cos(2pi/n) + i *sin(2pi/n)
RIF = self.real_field()
pi = RIF.pi()
z = 2*pi/n
z = 2 * pi / n
x = self.element_class(self, z.cos(), z.sin())
# Uncomment after implemented
#x._set_multiplicative_order( n )
# x._set_multiplicative_order( n )
return x

def scientific_notation(self, status=None):
Expand Down
11 changes: 6 additions & 5 deletions src/sage/rings/morphism.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from sage.rings.integer import Integer
from sage.structure.element import Element
from sage.structure.parent import Parent
from typing import Any

from sage.categories.map import Map
from sage.categories.morphism import Morphism
from sage.rings.integer import Integer
from sage.structure.parent import Parent

class RingMap(Morphism):
pass

class RingMap_lift(RingMap):
S: Parent
to_S: Map
S: Parent[Any]
to_S: Map[Any, Any]

class RingHomomorphism(RingMap):
_lift: Morphism
Expand Down
Loading
Loading