Skip to content

Commit 04ecb27

Browse files
author
Release Manager
committed
gh-37512: FiniteRankFreeModule.isomorphism_with_fixed_basis: Make it an actual isomorphism <!-- ^ Please provide a concise and informative title. --> <!-- ^ Don't put issue numbers in the title, do this in the PR description below. --> <!-- ^ For example, instead of "Fixes #12345" use "Introduce new method to calculate 1 + 2". --> <!-- v Describe your changes below in detail. --> <!-- v Why is this change required? What problem does it solve? --> <!-- v If this PR resolves an open issue, please link to it here. For example, "Fixes #12345". --> For this we define a new class `SetIsomorphism`, which knows its inverse. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [ ] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #37512 Reported by: Matthias Köppe Reviewer(s): Eric Gourgoulhon, Travis Scrimshaw
2 parents ca59b3e + 6903923 commit 04ecb27

File tree

4 files changed

+243
-30
lines changed

4 files changed

+243
-30
lines changed

src/sage/categories/map.pyx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,7 @@ cdef class Map(Element):
12721272

12731273
def section(self):
12741274
"""
1275-
Return a section of self.
1275+
Return a section of ``self``.
12761276
12771277
.. NOTE::
12781278
@@ -1439,6 +1439,7 @@ cdef class Section(Map):
14391439
"""
14401440
return self._inverse
14411441

1442+
14421443
cdef class FormalCompositeMap(Map):
14431444
"""
14441445
Formal composite maps.

src/sage/categories/morphism.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ cdef class Morphism(Map):
88
cdef class SetMorphism(Morphism):
99
cdef object _function
1010
cpdef bint _eq_c_impl(left, Element right) noexcept
11+
12+
cdef class SetIsomorphism(SetMorphism):
13+
cdef object _inverse

src/sage/categories/morphism.pyx

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ cdef class SetMorphism(Morphism):
570570
571571
- ``parent`` -- a Homset
572572
- ``function`` -- a Python function that takes elements
573-
of the domain as input and returns elements of the domain.
573+
of the domain as input and returns elements of the codomain.
574574
575575
EXAMPLES::
576576
@@ -736,3 +736,159 @@ cdef class SetMorphism(Morphism):
736736
return not (isinstance(right, Element) and self._eq_c_impl(right))
737737
else:
738738
return False
739+
740+
741+
cdef class SetIsomorphism(SetMorphism):
742+
r"""
743+
An isomorphism of sets.
744+
745+
INPUT:
746+
747+
- ``parent`` -- a Homset
748+
- ``function`` -- a Python function that takes elements
749+
of the domain as input and returns elements of the codomain.
750+
751+
EXAMPLES::
752+
753+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
754+
....: operator.__neg__); f
755+
Generic endomorphism of Integer Ring
756+
sage: f._set_inverse(f)
757+
sage: ~f is f
758+
True
759+
"""
760+
def _set_inverse(self, inverse):
761+
r"""
762+
Set the inverse morphism of ``self`` to be ``inverse``.
763+
764+
INPUT:
765+
766+
- ``inverse`` -- a :class:`SetIsomorphism`
767+
768+
EXAMPLES::
769+
770+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
771+
....: operator.__neg__)
772+
sage: f._set_inverse(f)
773+
sage: ~f is f
774+
True
775+
"""
776+
self._inverse = inverse
777+
778+
def __invert__(self):
779+
r"""
780+
Return the inverse morphism of ``self``.
781+
782+
If :meth:`_set_inverse` has not been called yet, an error is raised.
783+
784+
EXAMPLES::
785+
786+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
787+
....: operator.__neg__)
788+
sage: ~f
789+
Traceback (most recent call last):
790+
...
791+
RuntimeError: inverse morphism has not been set
792+
sage: f._set_inverse(f)
793+
sage: ~f
794+
Generic endomorphism of Integer Ring
795+
"""
796+
if not self._inverse:
797+
raise RuntimeError('inverse morphism has not been set')
798+
return self._inverse
799+
800+
cdef dict _extra_slots(self) noexcept:
801+
"""
802+
Extend the dictionary with extra slots for this class.
803+
804+
INPUT:
805+
806+
- ``_slots`` -- a dictionary
807+
808+
EXAMPLES::
809+
810+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
811+
....: operator.__neg__)
812+
sage: f._set_inverse(f)
813+
sage: f._extra_slots_test()
814+
{'_codomain': Integer Ring,
815+
'_domain': Integer Ring,
816+
'_function': <built-in function neg>,
817+
'_inverse': Generic endomorphism of Integer Ring,
818+
'_is_coercion': False,
819+
'_repr_type_str': None}
820+
"""
821+
slots = SetMorphism._extra_slots(self)
822+
slots['_inverse'] = self._inverse
823+
return slots
824+
825+
cdef _update_slots(self, dict _slots) noexcept:
826+
"""
827+
Update the slots of ``self`` from the data in the dictionary.
828+
829+
INPUT:
830+
831+
- ``_slots`` -- a dictionary
832+
833+
EXAMPLES::
834+
835+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
836+
....: operator.__neg__)
837+
sage: f._update_slots_test({'_function': operator.__neg__,
838+
....: '_inverse': f,
839+
....: '_domain': QQ,
840+
....: '_codomain': QQ,
841+
....: '_repr_type_str': 'bla'})
842+
sage: f(3)
843+
-3
844+
sage: f._repr_type()
845+
'bla'
846+
sage: f.domain()
847+
Rational Field
848+
sage: f.codomain()
849+
Rational Field
850+
sage: f.inverse() == f
851+
True
852+
"""
853+
self._inverse = _slots['_inverse']
854+
SetMorphism._update_slots(self, _slots)
855+
856+
def section(self):
857+
"""
858+
Return a section of this morphism.
859+
860+
EXAMPLES::
861+
862+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
863+
....: operator.__neg__)
864+
sage: f._set_inverse(f)
865+
sage: f.section() is f
866+
True
867+
"""
868+
return self.__invert__()
869+
870+
def is_surjective(self):
871+
r"""
872+
Return whether this morphism is surjective.
873+
874+
EXAMPLES::
875+
876+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
877+
....: operator.__neg__)
878+
sage: f.is_surjective()
879+
True
880+
"""
881+
return True
882+
883+
def is_injective(self):
884+
r"""
885+
Return whether this morphism is injective.
886+
887+
EXAMPLES::
888+
889+
sage: f = sage.categories.morphism.SetIsomorphism(Hom(ZZ, ZZ, Sets()),
890+
....: operator.__neg__)
891+
sage: f.is_injective()
892+
True
893+
"""
894+
return True

0 commit comments

Comments
 (0)