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

Commit 280e8a7

Browse files
committed
Adding support for quotients of quotients.
1 parent 568097b commit 280e8a7

File tree

3 files changed

+150
-60
lines changed

3 files changed

+150
-60
lines changed

src/sage/modules/free_module.py

+47-53
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,41 @@ def submodule(self, gens, check=True):
15701570
"a submodule of self" % gens)
15711571
return V
15721572

1573+
def quotient_module(self, sub, check=True):
1574+
r"""
1575+
Return the quotient of ``self`` by the given subspace ``sub``.
1576+
1577+
INPUT:
1578+
1579+
- ``sub`` -- a submodule of ``self`` or something that can
1580+
be turned into one via ``self.submodule(sub)``
1581+
- ``check`` -- (default: ``True``) whether or not to check that
1582+
``sub`` is a submodule
1583+
1584+
EXAMPLES::
1585+
1586+
sage: S.<x,y,z> = PolynomialRing(QQ)
1587+
sage: M = S**2
1588+
sage: N = M.submodule([vector([x - y, z]), vector([y * z, x * z])])
1589+
sage: M.quotient(N)
1590+
Quotient module by Submodule of Ambient free module of rank 2 over
1591+
the integral domain Multivariate Polynomial Ring in x, y, z over Rational Field
1592+
Generated by the rows of the matrix:
1593+
[x - y z]
1594+
[ y*z x*z]
1595+
"""
1596+
if isinstance(sub, Module_free_ambient) and self.base_ring() != sub.base_ring():
1597+
raise ValueError("base rings must be the same")
1598+
if check and (not isinstance(sub, Module_free_ambient) or not sub.is_submodule(self)):
1599+
try:
1600+
sub = self.submodule(sub)
1601+
except (TypeError, ArithmeticError):
1602+
raise ArithmeticError("sub must be a subspace of self")
1603+
from .quotient_module import QuotientModule_free_ambient
1604+
return QuotientModule_free_ambient(self, sub)
1605+
1606+
quotient = quotient_module
1607+
15731608
def __truediv__(self, sub):
15741609
"""
15751610
Return the quotient of ``self`` by the given submodule sub.
@@ -4120,15 +4155,12 @@ def quotient(self, sub, check=True, **kwds):
41204155
41214156
INPUT:
41224157
4123-
- ``sub`` - a submodule of self, or something that can
4124-
be turned into one via self.submodule(sub).
4125-
4126-
- ``check`` - (default: True) whether or not to check
4127-
that sub is a submodule.
4128-
4129-
- further named arguments, that are passed to the constructor
4130-
of the quotient space.
4131-
4158+
- ``sub`` -- a submodule of ``self``, or something that can
4159+
be turned into one via ``self.submodule(sub)``
4160+
- ``check`` -- (default: ``True``) whether or not to check
4161+
that ``sub`` is a submodule
4162+
- further named arguments, that are passed to the constructor
4163+
of the quotient space
41324164
41334165
EXAMPLES::
41344166
@@ -4146,8 +4178,8 @@ def quotient(self, sub, check=True, **kwds):
41464178
if self.base_ring() == sage.rings.integer_ring.ZZ:
41474179
from .fg_pid.fgp_module import FGP_Module
41484180
return FGP_Module(self, sub, check=False, **kwds)
4149-
else:
4150-
raise NotImplementedError("quotients of modules over rings other than fields or ZZ is not fully implemented")
4181+
4182+
raise NotImplementedError("quotients of modules over rings other than fields or ZZ is not fully implemented")
41514183

41524184

41534185
class FreeModule_generic_field(FreeModule_generic_pid):
@@ -5001,13 +5033,10 @@ def quotient(self, sub, check=True):
50015033
50025034
INPUT:
50035035
5004-
5005-
- ``sub`` - a submodule of self, or something that can
5006-
be turned into one via self.submodule(sub).
5007-
5008-
- ``check`` - (default: True) whether or not to check
5009-
that sub is a submodule.
5010-
5036+
- ``sub`` -- a submodule of ``self``, or something that can
5037+
be turned into one via ``self.submodule(sub)``
5038+
- ``check`` -- (default: ``True``) whether or not to check
5039+
that ``sub`` is a submodule
50115040
50125041
EXAMPLES::
50135042
@@ -6073,41 +6102,6 @@ def vector_space(self, base_field=None):
60736102
else:
60746103
return self.change_ring(base_field)
60756104

6076-
def quotient_module(self, sub, check=True):
6077-
"""
6078-
Return the quotient of this free module by the given subspace ``sub``.
6079-
6080-
INPUT:
6081-
6082-
- ``sub`` -- a submodule of this free module, or something that can
6083-
be turned into one via ``self.submodule(sub)``.
6084-
6085-
- ``check`` -- (default: True) whether or not to check that ``sub`` is
6086-
a submodule
6087-
6088-
EXAMPLES::
6089-
6090-
sage: S.<x,y,z> = PolynomialRing(QQ)
6091-
sage: M = S**2
6092-
sage: N = M.submodule([vector([x - y, z]), vector([y * z, x * z])])
6093-
sage: M.quotient(N)
6094-
Quotient module by Submodule of Ambient free module of rank 2 over the integral domain Multivariate Polynomial Ring in x, y, z over Rational Field
6095-
Generated by the rows of the matrix:
6096-
[x - y z]
6097-
[ y*z x*z]
6098-
"""
6099-
if isinstance(sub, Module_free_ambient) and self.base_ring() != sub.base_ring():
6100-
raise ValueError("base rings must be the same")
6101-
if check and (not isinstance(sub, Module_free_ambient) or not sub.is_submodule(self)):
6102-
try:
6103-
sub = self.submodule(sub)
6104-
except (TypeError, ArithmeticError):
6105-
raise ArithmeticError("sub must be a subspace of self")
6106-
from .quotient_module import QuotientModule_free_ambient
6107-
return QuotientModule_free_ambient(self, sub)
6108-
6109-
quotient = quotient_module
6110-
61116105

61126106
###############################################################################
61136107
#

src/sage/modules/quotient_module.py

+87-6
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ class QuotientModule_free_ambient(Module_free_ambient):
3737
3838
INPUT:
3939
40-
- ``module`` -- an ambient free module
41-
42-
- ``sub`` -- a submodule of the ambient free module
40+
- ``domain`` -- an ambient free module
41+
- ``sub`` -- a submodule of ``domain``
4342
4443
EXAMPLES::
4544
@@ -68,9 +67,22 @@ def __init__(self, domain, sub):
6867
base_ring = domain.base_ring()
6968
degree = domain.degree()
7069
sparse = domain.is_sparse()
71-
self._sub = sub
70+
# We store these in order to retain the information of how this was constructed
7271
self._domain = domain
73-
self.__hash = hash((domain, sub))
72+
self._sub = sub
73+
self.__hash = hash((self._domain, self._sub))
74+
75+
# We then convert the data into maps from free modules
76+
if isinstance(domain, QuotientModule_free_ambient):
77+
self._free_cover = domain.cover()
78+
C = self._free_cover.element_class
79+
v = [C(self._free_cover, x.list(), coerce=False, copy=False) for x in sub.gens()]
80+
w = [C(self._free_cover, x.list(), coerce=False, copy=False) for x in domain.free_relations().gens()]
81+
self._relations = self._free_cover.submodule(v + w, check=False)
82+
else: # Otherwise domain should be a free module
83+
self._free_cover = domain
84+
self._relations = sub
85+
7486
Module_free_ambient.__init__(self, base_ring, degree=degree, sparse=sparse)
7587

7688
def _repr_(self):
@@ -161,6 +173,15 @@ def _coerce_map_from_(self, M):
161173
if isinstance(M, FreeModule_ambient):
162174
return (self.base_ring().has_coerce_map_from(M.base_ring()) and
163175
self.degree() == M.degree())
176+
from sage.modules.submodule import Subquotient_free_ambient
177+
if isinstance(M, Subquotient_free_ambient):
178+
return self._domain.has_coerce_map_from(self.ambient_module())
179+
if (isinstance(M, QuotientModule_free_ambient)
180+
and M.free_cover() == self.free_cover()):
181+
try:
182+
return M.free_relations().is_submodule(self.free_relations())
183+
except NotImplementedError:
184+
pass
164185

165186
def ambient_module(self):
166187
"""
@@ -196,7 +217,7 @@ def cover(self):
196217

197218
def relations(self):
198219
r"""
199-
Given this quotient space `Q = V/W`, return `W`.
220+
Given this quotient space `Q = V/W`, return `W`
200221
201222
EXAMPLES::
202223
@@ -211,6 +232,66 @@ def relations(self):
211232

212233
W = relations
213234

235+
def free_cover(self):
236+
r"""
237+
Given this quotient space `Q = V/W`, return the free module
238+
that covers `V`.
239+
240+
EXAMPLES::
241+
242+
sage: S.<x,y,z> = PolynomialRing(QQ)
243+
sage: M = S**2
244+
sage: N = M.submodule([vector([x - y, z]), vector([y*z, x*z])])
245+
sage: Q = M / N
246+
sage: NQ = Q.submodule([Q([1,x])])
247+
sage: QNQ = Q / NQ
248+
sage: QNQ.free_cover() is Q.free_cover() is M
249+
True
250+
251+
Note that this is different than the immediate cover::
252+
253+
sage: QNQ.cover() is Q
254+
True
255+
sage: QNQ.cover() is QNQ.free_cover()
256+
False
257+
"""
258+
return self._free_cover
259+
260+
V = cover
261+
262+
def free_relations(self):
263+
r"""
264+
Given this quotient space `Q = V/W`, return the submodule
265+
that generates all relations of `Q`.
266+
267+
When `V` is a free module, then this returns `W`. Otherwise this
268+
returns the union of `W` lifted to the cover of `V` and the relations
269+
of `V` (repeated until `W` is a submodule of a free module).
270+
271+
EXAMPLES::
272+
273+
sage: S.<x,y,z> = PolynomialRing(QQ)
274+
sage: M = S**2
275+
sage: N = M.submodule([vector([x - y, z]), vector([y*z, x*z])])
276+
sage: Q = M / N
277+
sage: NQ = Q.submodule([Q([1,x])])
278+
sage: QNQ = Q / NQ
279+
sage: QNQ.free_relations()
280+
Submodule of Ambient free module of rank 2 over the integral domain Multivariate Polynomial Ring in x, y, z over Rational Field
281+
Generated by the rows of the matrix:
282+
[ 1 x]
283+
[x - y z]
284+
[ y*z x*z]
285+
286+
Note that this is different than the defining relations::
287+
288+
sage: QNQ.relations() is NQ
289+
True
290+
sage: QNQ.relations() == QNQ.free_relations()
291+
False
292+
"""
293+
return self._relations
294+
214295

215296
###############################################################################
216297
#

src/sage/modules/submodule.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,28 @@ def _repr_(self):
114114
115115
sage: S.<x,y,z> = PolynomialRing(QQ)
116116
sage: M = S**2
117-
sage: M.submodule([vector([x - y, z]), vector([y*z, x*z])])
117+
sage: N = M.submodule([vector([x - y, z]), vector([y*z, x*z])])
118+
sage: N
118119
Submodule of Ambient free module of rank 2 over the integral domain
119120
Multivariate Polynomial Ring in x, y, z over Rational Field
120121
Generated by the rows of the matrix:
121122
[x - y z]
122123
[ y*z x*z]
124+
125+
sage: Q = M.quotient_module(N)
126+
sage: NQ = Q.submodule([Q([1,x])])
127+
sage: NQ
128+
Subquotient of Ambient free module of rank 2 over the integral domain Multivariate Polynomial Ring in x, y, z over Rational Field
129+
Generated by the rows of the matrix:
130+
[1 x]
131+
With relations matrix:
132+
[x - y z]
133+
[ y*z x*z]
123134
"""
135+
if isinstance(self._ambient, QuotientModule_free_ambient):
136+
return ("Subquotient of %s\n" % self.ambient_module().free_cover() +
137+
"Generated by the rows of the matrix:\n%s" % self.matrix() +
138+
"\nWith relations matrix:\n%s" % self._ambient.free_relations().matrix())
124139
return ("Submodule of %s\n" % self.ambient_module() +
125140
"Generated by the rows of the matrix:\n%s" % self.matrix())
126141

0 commit comments

Comments
 (0)