Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 242fecc

Browse files
committed
Add free_module method to Rings()
1 parent b4147b0 commit 242fecc

File tree

6 files changed

+264
-11
lines changed

6 files changed

+264
-11
lines changed

src/doc/en/thematic_tutorials/coercion_and_categories.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ And indeed, ``MS2`` has *more* methods than ``MS1``::
452452
sage: len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))])
453453
81
454454
sage: len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))])
455-
119
455+
120
456456

457457
This is because the class of ``MS2`` also inherits from the parent
458458
class for algebras::

src/sage/categories/rings.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,73 @@ def normalize_arg(arg):
10861086
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
10871087
return PolynomialRing(self, elts)
10881088

1089+
def free_module(self, base=None, basis=None, map=True):
1090+
"""
1091+
Return a free module `V` over the specified subring together with maps to and from `V`.
1092+
1093+
The default implementation only supports the case that the base ring is the ring itself.
1094+
1095+
INPUT:
1096+
1097+
- ``base`` -- a subring `R` so that this ring is isomorphic
1098+
to a finite-rank free `R`-module `V`
1099+
1100+
- ``basis`` -- (optional) a basis for this ring over the base
1101+
1102+
- ``map`` -- boolean (default ``True``), whether to return
1103+
`R`-linear maps to and from `V`
1104+
1105+
OUTPUT:
1106+
1107+
- A finite-rank free `R`-module `V`
1108+
1109+
- An `R`-module isomorphism from `V` to this ring
1110+
(only included if ``map`` is ``True``)
1111+
1112+
- An `R`-module isomorphism from this ring to `V`
1113+
(only included if ``map`` is ``True``)
1114+
1115+
EXAMPLES::
1116+
1117+
sage: R.<x> = QQ[[]]
1118+
sage: V, from_V, to_V = R.free_module(R)
1119+
sage: v = to_V(1+x); v
1120+
(1 + x)
1121+
sage: from_V(v)
1122+
1 + x
1123+
sage: W, from_W, to_W = R.free_module(R, basis=(1-x))
1124+
sage: W is V
1125+
True
1126+
sage: w = to_W(1+x); w
1127+
(1 - x^2)
1128+
sage: from_W(w)
1129+
1 + x + O(x^20)
1130+
"""
1131+
if base is None:
1132+
base = self.base_ring()
1133+
if base is self:
1134+
V = self**1
1135+
if not map:
1136+
return V
1137+
if basis is not None:
1138+
if isinstance(basis, (list, tuple)):
1139+
if len(basis) != 1:
1140+
raise ValueError("Basis must have length 1")
1141+
basis = basis[0]
1142+
basis = self(basis)
1143+
if not basis.is_unit():
1144+
raise ValueError("Basis element must be a unit")
1145+
from sage.modules.free_module_morphism import BaseIsomorphism1D_from_FM, BaseIsomorphism1D_to_FM
1146+
Hfrom = V.Hom(self)
1147+
Hto = self.Hom(V)
1148+
from_V = Hfrom.__make_element_class__(BaseIsomorphism1D_from_FM)(Hfrom, basis=basis)
1149+
to_V = Hto.__make_element_class__(BaseIsomorphism1D_to_FM)(Hto, basis=basis)
1150+
return V, from_V, to_V
1151+
else:
1152+
if not self.has_coerce_map_from(base):
1153+
raise ValueError("base must be a subring of this ring")
1154+
raise NotImplementedError
1155+
10891156
class ElementMethods:
10901157
def is_unit(self):
10911158
r"""

src/sage/modules/free_module_morphism.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343

4444
import sage.modules.free_module as free_module
4545
from . import matrix_morphism
46+
from sage.categories.morphism import Morphism
4647
from sage.structure.sequence import Sequence
48+
from sage.structure.richcmp import richcmp, rich_to_bool
4749

4850
from . import free_module_homspace
4951

@@ -612,3 +614,178 @@ def minimal_polynomial(self,var='x'):
612614
raise TypeError("not an endomorphism")
613615

614616
minpoly = minimal_polynomial
617+
618+
class BaseIsomorphism1D(Morphism):
619+
"""
620+
An isomorphism between a ring and a free rank-1 module over the ring.
621+
622+
EXAMPLES::
623+
624+
sage: R.<x,y> = QQ[]
625+
sage: V, from_V, to_V = R.free_module(R)
626+
sage: from_V
627+
Isomorphism morphism:
628+
From: Ambient free module of rank 1 over the integral domain Multivariate Polynomial Ring in x, y over Rational Field
629+
To: Multivariate Polynomial Ring in x, y over Rational Field
630+
"""
631+
def _repr_type(self):
632+
r"""
633+
EXAMPLES::
634+
635+
sage: R.<x,y> = QQ[]
636+
sage: V, from_V, to_V = R.free_module(R)
637+
sage: from_V._repr_type()
638+
'Isomorphism'
639+
"""
640+
return "Isomorphism"
641+
642+
def is_injective(self):
643+
r"""
644+
EXAMPLES::
645+
646+
sage: R.<x,y> = QQ[]
647+
sage: V, from_V, to_V = R.free_module(R)
648+
sage: from_V.is_injective()
649+
True
650+
"""
651+
return True
652+
653+
def is_surjective(self):
654+
r"""
655+
EXAMPLES::
656+
657+
sage: R.<x,y> = QQ[]
658+
sage: V, from_V, to_V = R.free_module(R)
659+
sage: from_V.is_surjective()
660+
True
661+
"""
662+
return True
663+
664+
def _richcmp_(self, other, op):
665+
r"""
666+
EXAMPLES::
667+
668+
sage: R.<x,y> = QQ[]
669+
sage: V, fr, to = R.free_module(R)
670+
sage: fr == loads(dumps(fr))
671+
True
672+
"""
673+
if isinstance(other, BaseIsomorphism1D):
674+
return richcmp(self._basis, other._basis, op)
675+
else:
676+
return rich_to_bool(op, 1)
677+
678+
class BaseIsomorphism1D_to_FM(BaseIsomorphism1D):
679+
"""
680+
An isomorphism from a ring to its 1-dimensional free module
681+
682+
INPUT:
683+
684+
- ``parent`` -- the homset
685+
- ``basis`` -- (default 1) an invertible element of the ring
686+
687+
EXAMPLES::
688+
689+
sage: R = Zmod(8)
690+
sage: V, from_V, to_V = R.free_module(R)
691+
sage: v = to_V(2); v
692+
(2)
693+
sage: from_V(v)
694+
2
695+
sage: W, from_W, to_W = R.free_module(R, basis=3)
696+
sage: W is V
697+
True
698+
sage: w = to_W(2); w
699+
(6)
700+
sage: from_W(w)
701+
2
702+
703+
The basis vector has to be a unit so that the map is an isomorphism::
704+
705+
sage: W, from_W, to_W = R.free_module(R, basis=4)
706+
Traceback (most recent call last):
707+
...
708+
ValueError: Basis element must be a unit
709+
"""
710+
def __init__(self, parent, basis=None):
711+
"""
712+
TESTS::
713+
714+
sage: R = Zmod(8)
715+
sage: W, from_W, to_W = R.free_module(R, basis=3)
716+
sage: TestSuite(to_W).run()
717+
"""
718+
Morphism.__init__(self, parent)
719+
self._basis = basis
720+
721+
def _call_(self, x):
722+
"""
723+
TESTS::
724+
725+
sage: R = Zmod(8)
726+
sage: W, from_W, to_W = R.free_module(R, basis=3)
727+
sage: to_W(6) # indirect doctest
728+
(2)
729+
"""
730+
if self._basis is not None:
731+
x *= self._basis
732+
return self.codomain()([x])
733+
734+
class BaseIsomorphism1D_from_FM(BaseIsomorphism1D):
735+
"""
736+
An isomorphism to a ring from its 1-dimensional free module
737+
738+
INPUT:
739+
740+
- ``parent`` -- the homset
741+
- ``basis`` -- (default 1) an invertible element of the ring
742+
743+
EXAMPLES::
744+
745+
sage: R.<x> = QQ[[]]
746+
sage: V, from_V, to_V = R.free_module(R)
747+
sage: v = to_V(1+x); v
748+
(1 + x)
749+
sage: from_V(v)
750+
1 + x
751+
sage: W, from_W, to_W = R.free_module(R, basis=(1-x))
752+
sage: W is V
753+
True
754+
sage: w = to_W(1+x); w
755+
(1 - x^2)
756+
sage: from_W(w)
757+
1 + x + O(x^20)
758+
759+
The basis vector has to be a unit so that the map is an isomorphism::
760+
761+
sage: W, from_W, to_W = R.free_module(R, basis=x)
762+
Traceback (most recent call last):
763+
...
764+
ValueError: Basis element must be a unit
765+
"""
766+
def __init__(self, parent, basis=None):
767+
"""
768+
TESTS::
769+
770+
sage: R.<x> = QQ[[]]
771+
sage: W, from_W, to_W = R.free_module(R, basis=(1-x))
772+
sage: TestSuite(from_W).run(skip='_test_nonzero_equal')
773+
"""
774+
Morphism.__init__(self, parent)
775+
self._basis = basis
776+
777+
def _call_(self, x):
778+
"""
779+
TESTS::
780+
781+
sage: R.<x> = QQ[[]]
782+
sage: W, from_W, to_W = R.free_module(R, basis=(1-x))
783+
sage: w = to_W(1+x); w
784+
(1 - x^2)
785+
sage: from_W(w)
786+
1 + x + O(x^20)
787+
"""
788+
if self._basis is None:
789+
return x[0]
790+
else:
791+
return self.codomain()(x[0] / self._basis)

src/sage/rings/number_field/number_field.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8170,7 +8170,11 @@ def free_module(self, base=None, basis=None, map=True):
81708170
sage: to_V(from_V(V([0,-1/7,0])))
81718171
(0, -1/7, 0)
81728172
"""
8173-
if basis is not None or base is not None:
8173+
if base is None:
8174+
base = QQ
8175+
elif base is self:
8176+
return super(NumberField_absolute, self).free_module(base=base, basis=basis, map=map)
8177+
if basis is not None or base is not QQ:
81748178
raise NotImplementedError
81758179
V = QQ**self.degree()
81768180
if not map:

src/sage/rings/padics/padic_extension_generic.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def random_element(self):
566566
@cached_method(key=(lambda self, base, basis, map: (base or self.base_ring(), map)))
567567
def free_module(self, base=None, basis=None, map=True):
568568
"""
569-
Return a free module V over a specified base ring together with maps to and from V.
569+
Return a free module `V` over a specified base ring together with maps to and from `V`.
570570
571571
INPUT:
572572
@@ -575,18 +575,18 @@ def free_module(self, base=None, basis=None, map=True):
575575
576576
- ``basis`` -- a basis for this ring/field over the base
577577
578-
- ``maps`` -- boolean (default ``True``), whether to return
578+
- ``map`` -- boolean (default ``True``), whether to return
579579
`R`-linear maps to and from `V`
580580
581581
OUTPUT:
582582
583583
- A finite-rank free `R`-module `V`
584584
585-
- An `R`-module isomorphism ``from_V`` from `V` to this ring/field
586-
(only included if ``maps`` is ``True``)
585+
- An `R`-module isomorphism from `V` to this ring/field
586+
(only included if ``map`` is ``True``)
587587
588-
- An `R`-module isomorphism ``to_V`` from this ring/field to `V`
589-
(only included if ``maps`` is ``True``)
588+
- An `R`-module isomorphism from this ring/field to `V`
589+
(only included if ``map`` is ``True``)
590590
591591
EXAMPLES::
592592
@@ -606,6 +606,10 @@ def free_module(self, base=None, basis=None, map=True):
606606
(O(5^21), 1 + O(5^20))
607607
sage: to_W0(pi + O(pi^11))
608608
(O(5^6), O(5^6), O(5^6), 1 + O(5^5), O(5^5), O(5^5))
609+
610+
sage: X, from_X, to_X = K.free_module(K)
611+
sage: to_X(a)
612+
(a + O(5^20))
609613
"""
610614
if basis is not None:
611615
raise NotImplementedError
@@ -624,6 +628,8 @@ def free_module(self, base=None, basis=None, map=True):
624628
V = A**d
625629
from_V = MapFreeModuleToTwoStep
626630
to_V = MapTwoStepToFreeModule
631+
elif base is self:
632+
return super(pAdicExtensionGeneric, self).free_module(base=base, basis=basis, map=map)
627633
else:
628634
raise NotImplementedError
629635
FromV = Hom(V, self)

src/sage/rings/polynomial/polynomial_element.pyx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3244,9 +3244,8 @@ cdef class Polynomial(CommutativeAlgebraElement):
32443244
True
32453245
"""
32463246
if isinstance(R, Map):
3247-
# we're given a hom of the base ring extend to a poly hom
3248-
if R.domain() == self.base_ring():
3249-
R = self._parent.hom(R, self._parent.change_ring(R.codomain()))
3247+
# extend to a hom of the base ring of the polynomial
3248+
R = self._parent.hom(R, self._parent.change_ring(R.codomain()))
32503249
return R(self)
32513250
else:
32523251
return self._parent.change_ring(R)(self.list(copy=False))

0 commit comments

Comments
 (0)