homomorphisms.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. """
  2. Computations with homomorphisms of modules and rings.
  3. This module implements classes for representing homomorphisms of rings and
  4. their modules. Instead of instantiating the classes directly, you should use
  5. the function ``homomorphism(from, to, matrix)`` to create homomorphism objects.
  6. """
  7. from sympy.polys.agca.modules import (Module, FreeModule, QuotientModule,
  8. SubModule, SubQuotientModule)
  9. from sympy.polys.polyerrors import CoercionFailed
  10. # The main computational task for module homomorphisms is kernels.
  11. # For this reason, the concrete classes are organised by domain module type.
  12. class ModuleHomomorphism:
  13. """
  14. Abstract base class for module homomoprhisms. Do not instantiate.
  15. Instead, use the ``homomorphism`` function:
  16. >>> from sympy import QQ
  17. >>> from sympy.abc import x
  18. >>> from sympy.polys.agca import homomorphism
  19. >>> F = QQ.old_poly_ring(x).free_module(2)
  20. >>> homomorphism(F, F, [[1, 0], [0, 1]])
  21. Matrix([
  22. [1, 0], : QQ[x]**2 -> QQ[x]**2
  23. [0, 1]])
  24. Attributes:
  25. - ring - the ring over which we are considering modules
  26. - domain - the domain module
  27. - codomain - the codomain module
  28. - _ker - cached kernel
  29. - _img - cached image
  30. Non-implemented methods:
  31. - _kernel
  32. - _image
  33. - _restrict_domain
  34. - _restrict_codomain
  35. - _quotient_domain
  36. - _quotient_codomain
  37. - _apply
  38. - _mul_scalar
  39. - _compose
  40. - _add
  41. """
  42. def __init__(self, domain, codomain):
  43. if not isinstance(domain, Module):
  44. raise TypeError('Source must be a module, got %s' % domain)
  45. if not isinstance(codomain, Module):
  46. raise TypeError('Target must be a module, got %s' % codomain)
  47. if domain.ring != codomain.ring:
  48. raise ValueError('Source and codomain must be over same ring, '
  49. 'got %s != %s' % (domain, codomain))
  50. self.domain = domain
  51. self.codomain = codomain
  52. self.ring = domain.ring
  53. self._ker = None
  54. self._img = None
  55. def kernel(self):
  56. r"""
  57. Compute the kernel of ``self``.
  58. That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute
  59. `ker(\phi) = \{x \in M | \phi(x) = 0\}`. This is a submodule of `M`.
  60. Examples
  61. ========
  62. >>> from sympy import QQ
  63. >>> from sympy.abc import x
  64. >>> from sympy.polys.agca import homomorphism
  65. >>> F = QQ.old_poly_ring(x).free_module(2)
  66. >>> homomorphism(F, F, [[1, 0], [x, 0]]).kernel()
  67. <[x, -1]>
  68. """
  69. if self._ker is None:
  70. self._ker = self._kernel()
  71. return self._ker
  72. def image(self):
  73. r"""
  74. Compute the image of ``self``.
  75. That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute
  76. `im(\phi) = \{\phi(x) | x \in M \}`. This is a submodule of `N`.
  77. Examples
  78. ========
  79. >>> from sympy import QQ
  80. >>> from sympy.abc import x
  81. >>> from sympy.polys.agca import homomorphism
  82. >>> F = QQ.old_poly_ring(x).free_module(2)
  83. >>> homomorphism(F, F, [[1, 0], [x, 0]]).image() == F.submodule([1, 0])
  84. True
  85. """
  86. if self._img is None:
  87. self._img = self._image()
  88. return self._img
  89. def _kernel(self):
  90. """Compute the kernel of ``self``."""
  91. raise NotImplementedError
  92. def _image(self):
  93. """Compute the image of ``self``."""
  94. raise NotImplementedError
  95. def _restrict_domain(self, sm):
  96. """Implementation of domain restriction."""
  97. raise NotImplementedError
  98. def _restrict_codomain(self, sm):
  99. """Implementation of codomain restriction."""
  100. raise NotImplementedError
  101. def _quotient_domain(self, sm):
  102. """Implementation of domain quotient."""
  103. raise NotImplementedError
  104. def _quotient_codomain(self, sm):
  105. """Implementation of codomain quotient."""
  106. raise NotImplementedError
  107. def restrict_domain(self, sm):
  108. """
  109. Return ``self``, with the domain restricted to ``sm``.
  110. Here ``sm`` has to be a submodule of ``self.domain``.
  111. Examples
  112. ========
  113. >>> from sympy import QQ
  114. >>> from sympy.abc import x
  115. >>> from sympy.polys.agca import homomorphism
  116. >>> F = QQ.old_poly_ring(x).free_module(2)
  117. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  118. >>> h
  119. Matrix([
  120. [1, x], : QQ[x]**2 -> QQ[x]**2
  121. [0, 0]])
  122. >>> h.restrict_domain(F.submodule([1, 0]))
  123. Matrix([
  124. [1, x], : <[1, 0]> -> QQ[x]**2
  125. [0, 0]])
  126. This is the same as just composing on the right with the submodule
  127. inclusion:
  128. >>> h * F.submodule([1, 0]).inclusion_hom()
  129. Matrix([
  130. [1, x], : <[1, 0]> -> QQ[x]**2
  131. [0, 0]])
  132. """
  133. if not self.domain.is_submodule(sm):
  134. raise ValueError('sm must be a submodule of %s, got %s'
  135. % (self.domain, sm))
  136. if sm == self.domain:
  137. return self
  138. return self._restrict_domain(sm)
  139. def restrict_codomain(self, sm):
  140. """
  141. Return ``self``, with codomain restricted to to ``sm``.
  142. Here ``sm`` has to be a submodule of ``self.codomain`` containing the
  143. image.
  144. Examples
  145. ========
  146. >>> from sympy import QQ
  147. >>> from sympy.abc import x
  148. >>> from sympy.polys.agca import homomorphism
  149. >>> F = QQ.old_poly_ring(x).free_module(2)
  150. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  151. >>> h
  152. Matrix([
  153. [1, x], : QQ[x]**2 -> QQ[x]**2
  154. [0, 0]])
  155. >>> h.restrict_codomain(F.submodule([1, 0]))
  156. Matrix([
  157. [1, x], : QQ[x]**2 -> <[1, 0]>
  158. [0, 0]])
  159. """
  160. if not sm.is_submodule(self.image()):
  161. raise ValueError('the image %s must contain sm, got %s'
  162. % (self.image(), sm))
  163. if sm == self.codomain:
  164. return self
  165. return self._restrict_codomain(sm)
  166. def quotient_domain(self, sm):
  167. """
  168. Return ``self`` with domain replaced by ``domain/sm``.
  169. Here ``sm`` must be a submodule of ``self.kernel()``.
  170. Examples
  171. ========
  172. >>> from sympy import QQ
  173. >>> from sympy.abc import x
  174. >>> from sympy.polys.agca import homomorphism
  175. >>> F = QQ.old_poly_ring(x).free_module(2)
  176. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  177. >>> h
  178. Matrix([
  179. [1, x], : QQ[x]**2 -> QQ[x]**2
  180. [0, 0]])
  181. >>> h.quotient_domain(F.submodule([-x, 1]))
  182. Matrix([
  183. [1, x], : QQ[x]**2/<[-x, 1]> -> QQ[x]**2
  184. [0, 0]])
  185. """
  186. if not self.kernel().is_submodule(sm):
  187. raise ValueError('kernel %s must contain sm, got %s' %
  188. (self.kernel(), sm))
  189. if sm.is_zero():
  190. return self
  191. return self._quotient_domain(sm)
  192. def quotient_codomain(self, sm):
  193. """
  194. Return ``self`` with codomain replaced by ``codomain/sm``.
  195. Here ``sm`` must be a submodule of ``self.codomain``.
  196. Examples
  197. ========
  198. >>> from sympy import QQ
  199. >>> from sympy.abc import x
  200. >>> from sympy.polys.agca import homomorphism
  201. >>> F = QQ.old_poly_ring(x).free_module(2)
  202. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  203. >>> h
  204. Matrix([
  205. [1, x], : QQ[x]**2 -> QQ[x]**2
  206. [0, 0]])
  207. >>> h.quotient_codomain(F.submodule([1, 1]))
  208. Matrix([
  209. [1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]>
  210. [0, 0]])
  211. This is the same as composing with the quotient map on the left:
  212. >>> (F/[(1, 1)]).quotient_hom() * h
  213. Matrix([
  214. [1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]>
  215. [0, 0]])
  216. """
  217. if not self.codomain.is_submodule(sm):
  218. raise ValueError('sm must be a submodule of codomain %s, got %s'
  219. % (self.codomain, sm))
  220. if sm.is_zero():
  221. return self
  222. return self._quotient_codomain(sm)
  223. def _apply(self, elem):
  224. """Apply ``self`` to ``elem``."""
  225. raise NotImplementedError
  226. def __call__(self, elem):
  227. return self.codomain.convert(self._apply(self.domain.convert(elem)))
  228. def _compose(self, oth):
  229. """
  230. Compose ``self`` with ``oth``, that is, return the homomorphism
  231. obtained by first applying then ``self``, then ``oth``.
  232. (This method is private since in this syntax, it is non-obvious which
  233. homomorphism is executed first.)
  234. """
  235. raise NotImplementedError
  236. def _mul_scalar(self, c):
  237. """Scalar multiplication. ``c`` is guaranteed in self.ring."""
  238. raise NotImplementedError
  239. def _add(self, oth):
  240. """
  241. Homomorphism addition.
  242. ``oth`` is guaranteed to be a homomorphism with same domain/codomain.
  243. """
  244. raise NotImplementedError
  245. def _check_hom(self, oth):
  246. """Helper to check that oth is a homomorphism with same domain/codomain."""
  247. if not isinstance(oth, ModuleHomomorphism):
  248. return False
  249. return oth.domain == self.domain and oth.codomain == self.codomain
  250. def __mul__(self, oth):
  251. if isinstance(oth, ModuleHomomorphism) and self.domain == oth.codomain:
  252. return oth._compose(self)
  253. try:
  254. return self._mul_scalar(self.ring.convert(oth))
  255. except CoercionFailed:
  256. return NotImplemented
  257. # NOTE: _compose will never be called from rmul
  258. __rmul__ = __mul__
  259. def __truediv__(self, oth):
  260. try:
  261. return self._mul_scalar(1/self.ring.convert(oth))
  262. except CoercionFailed:
  263. return NotImplemented
  264. def __add__(self, oth):
  265. if self._check_hom(oth):
  266. return self._add(oth)
  267. return NotImplemented
  268. def __sub__(self, oth):
  269. if self._check_hom(oth):
  270. return self._add(oth._mul_scalar(self.ring.convert(-1)))
  271. return NotImplemented
  272. def is_injective(self):
  273. """
  274. Return True if ``self`` is injective.
  275. That is, check if the elements of the domain are mapped to the same
  276. codomain element.
  277. Examples
  278. ========
  279. >>> from sympy import QQ
  280. >>> from sympy.abc import x
  281. >>> from sympy.polys.agca import homomorphism
  282. >>> F = QQ.old_poly_ring(x).free_module(2)
  283. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  284. >>> h.is_injective()
  285. False
  286. >>> h.quotient_domain(h.kernel()).is_injective()
  287. True
  288. """
  289. return self.kernel().is_zero()
  290. def is_surjective(self):
  291. """
  292. Return True if ``self`` is surjective.
  293. That is, check if every element of the codomain has at least one
  294. preimage.
  295. Examples
  296. ========
  297. >>> from sympy import QQ
  298. >>> from sympy.abc import x
  299. >>> from sympy.polys.agca import homomorphism
  300. >>> F = QQ.old_poly_ring(x).free_module(2)
  301. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  302. >>> h.is_surjective()
  303. False
  304. >>> h.restrict_codomain(h.image()).is_surjective()
  305. True
  306. """
  307. return self.image() == self.codomain
  308. def is_isomorphism(self):
  309. """
  310. Return True if ``self`` is an isomorphism.
  311. That is, check if every element of the codomain has precisely one
  312. preimage. Equivalently, ``self`` is both injective and surjective.
  313. Examples
  314. ========
  315. >>> from sympy import QQ
  316. >>> from sympy.abc import x
  317. >>> from sympy.polys.agca import homomorphism
  318. >>> F = QQ.old_poly_ring(x).free_module(2)
  319. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  320. >>> h = h.restrict_codomain(h.image())
  321. >>> h.is_isomorphism()
  322. False
  323. >>> h.quotient_domain(h.kernel()).is_isomorphism()
  324. True
  325. """
  326. return self.is_injective() and self.is_surjective()
  327. def is_zero(self):
  328. """
  329. Return True if ``self`` is a zero morphism.
  330. That is, check if every element of the domain is mapped to zero
  331. under self.
  332. Examples
  333. ========
  334. >>> from sympy import QQ
  335. >>> from sympy.abc import x
  336. >>> from sympy.polys.agca import homomorphism
  337. >>> F = QQ.old_poly_ring(x).free_module(2)
  338. >>> h = homomorphism(F, F, [[1, 0], [x, 0]])
  339. >>> h.is_zero()
  340. False
  341. >>> h.restrict_domain(F.submodule()).is_zero()
  342. True
  343. >>> h.quotient_codomain(h.image()).is_zero()
  344. True
  345. """
  346. return self.image().is_zero()
  347. def __eq__(self, oth):
  348. try:
  349. return (self - oth).is_zero()
  350. except TypeError:
  351. return False
  352. def __ne__(self, oth):
  353. return not (self == oth)
  354. class MatrixHomomorphism(ModuleHomomorphism):
  355. r"""
  356. Helper class for all homomoprhisms which are expressed via a matrix.
  357. That is, for such homomorphisms ``domain`` is contained in a module
  358. generated by finitely many elements `e_1, \ldots, e_n`, so that the
  359. homomorphism is determined uniquely by its action on the `e_i`. It
  360. can thus be represented as a vector of elements of the codomain module,
  361. or potentially a supermodule of the codomain module
  362. (and hence conventionally as a matrix, if there is a similar interpretation
  363. for elements of the codomain module).
  364. Note that this class does *not* assume that the `e_i` freely generate a
  365. submodule, nor that ``domain`` is even all of this submodule. It exists
  366. only to unify the interface.
  367. Do not instantiate.
  368. Attributes:
  369. - matrix - the list of images determining the homomorphism.
  370. NOTE: the elements of matrix belong to either self.codomain or
  371. self.codomain.container
  372. Still non-implemented methods:
  373. - kernel
  374. - _apply
  375. """
  376. def __init__(self, domain, codomain, matrix):
  377. ModuleHomomorphism.__init__(self, domain, codomain)
  378. if len(matrix) != domain.rank:
  379. raise ValueError('Need to provide %s elements, got %s'
  380. % (domain.rank, len(matrix)))
  381. converter = self.codomain.convert
  382. if isinstance(self.codomain, (SubModule, SubQuotientModule)):
  383. converter = self.codomain.container.convert
  384. self.matrix = tuple(converter(x) for x in matrix)
  385. def _sympy_matrix(self):
  386. """Helper function which returns a SymPy matrix ``self.matrix``."""
  387. from sympy.matrices import Matrix
  388. c = lambda x: x
  389. if isinstance(self.codomain, (QuotientModule, SubQuotientModule)):
  390. c = lambda x: x.data
  391. return Matrix([[self.ring.to_sympy(y) for y in c(x)] for x in self.matrix]).T
  392. def __repr__(self):
  393. lines = repr(self._sympy_matrix()).split('\n')
  394. t = " : %s -> %s" % (self.domain, self.codomain)
  395. s = ' '*len(t)
  396. n = len(lines)
  397. for i in range(n // 2):
  398. lines[i] += s
  399. lines[n // 2] += t
  400. for i in range(n//2 + 1, n):
  401. lines[i] += s
  402. return '\n'.join(lines)
  403. def _restrict_domain(self, sm):
  404. """Implementation of domain restriction."""
  405. return SubModuleHomomorphism(sm, self.codomain, self.matrix)
  406. def _restrict_codomain(self, sm):
  407. """Implementation of codomain restriction."""
  408. return self.__class__(self.domain, sm, self.matrix)
  409. def _quotient_domain(self, sm):
  410. """Implementation of domain quotient."""
  411. return self.__class__(self.domain/sm, self.codomain, self.matrix)
  412. def _quotient_codomain(self, sm):
  413. """Implementation of codomain quotient."""
  414. Q = self.codomain/sm
  415. converter = Q.convert
  416. if isinstance(self.codomain, SubModule):
  417. converter = Q.container.convert
  418. return self.__class__(self.domain, self.codomain/sm,
  419. [converter(x) for x in self.matrix])
  420. def _add(self, oth):
  421. return self.__class__(self.domain, self.codomain,
  422. [x + y for x, y in zip(self.matrix, oth.matrix)])
  423. def _mul_scalar(self, c):
  424. return self.__class__(self.domain, self.codomain, [c*x for x in self.matrix])
  425. def _compose(self, oth):
  426. return self.__class__(self.domain, oth.codomain, [oth(x) for x in self.matrix])
  427. class FreeModuleHomomorphism(MatrixHomomorphism):
  428. """
  429. Concrete class for homomorphisms with domain a free module or a quotient
  430. thereof.
  431. Do not instantiate; the constructor does not check that your data is well
  432. defined. Use the ``homomorphism`` function instead:
  433. >>> from sympy import QQ
  434. >>> from sympy.abc import x
  435. >>> from sympy.polys.agca import homomorphism
  436. >>> F = QQ.old_poly_ring(x).free_module(2)
  437. >>> homomorphism(F, F, [[1, 0], [0, 1]])
  438. Matrix([
  439. [1, 0], : QQ[x]**2 -> QQ[x]**2
  440. [0, 1]])
  441. """
  442. def _apply(self, elem):
  443. if isinstance(self.domain, QuotientModule):
  444. elem = elem.data
  445. return sum(x * e for x, e in zip(elem, self.matrix))
  446. def _image(self):
  447. return self.codomain.submodule(*self.matrix)
  448. def _kernel(self):
  449. # The domain is either a free module or a quotient thereof.
  450. # It does not matter if it is a quotient, because that won't increase
  451. # the kernel.
  452. # Our generators {e_i} are sent to the matrix entries {b_i}.
  453. # The kernel is essentially the syzygy module of these {b_i}.
  454. syz = self.image().syzygy_module()
  455. return self.domain.submodule(*syz.gens)
  456. class SubModuleHomomorphism(MatrixHomomorphism):
  457. """
  458. Concrete class for homomorphism with domain a submodule of a free module
  459. or a quotient thereof.
  460. Do not instantiate; the constructor does not check that your data is well
  461. defined. Use the ``homomorphism`` function instead:
  462. >>> from sympy import QQ
  463. >>> from sympy.abc import x
  464. >>> from sympy.polys.agca import homomorphism
  465. >>> M = QQ.old_poly_ring(x).free_module(2)*x
  466. >>> homomorphism(M, M, [[1, 0], [0, 1]])
  467. Matrix([
  468. [1, 0], : <[x, 0], [0, x]> -> <[x, 0], [0, x]>
  469. [0, 1]])
  470. """
  471. def _apply(self, elem):
  472. if isinstance(self.domain, SubQuotientModule):
  473. elem = elem.data
  474. return sum(x * e for x, e in zip(elem, self.matrix))
  475. def _image(self):
  476. return self.codomain.submodule(*[self(x) for x in self.domain.gens])
  477. def _kernel(self):
  478. syz = self.image().syzygy_module()
  479. return self.domain.submodule(
  480. *[sum(xi*gi for xi, gi in zip(s, self.domain.gens))
  481. for s in syz.gens])
  482. def homomorphism(domain, codomain, matrix):
  483. r"""
  484. Create a homomorphism object.
  485. This function tries to build a homomorphism from ``domain`` to ``codomain``
  486. via the matrix ``matrix``.
  487. Examples
  488. ========
  489. >>> from sympy import QQ
  490. >>> from sympy.abc import x
  491. >>> from sympy.polys.agca import homomorphism
  492. >>> R = QQ.old_poly_ring(x)
  493. >>> T = R.free_module(2)
  494. If ``domain`` is a free module generated by `e_1, \ldots, e_n`, then
  495. ``matrix`` should be an n-element iterable `(b_1, \ldots, b_n)` where
  496. the `b_i` are elements of ``codomain``. The constructed homomorphism is the
  497. unique homomorphism sending `e_i` to `b_i`.
  498. >>> F = R.free_module(2)
  499. >>> h = homomorphism(F, T, [[1, x], [x**2, 0]])
  500. >>> h
  501. Matrix([
  502. [1, x**2], : QQ[x]**2 -> QQ[x]**2
  503. [x, 0]])
  504. >>> h([1, 0])
  505. [1, x]
  506. >>> h([0, 1])
  507. [x**2, 0]
  508. >>> h([1, 1])
  509. [x**2 + 1, x]
  510. If ``domain`` is a submodule of a free module, them ``matrix`` determines
  511. a homomoprhism from the containing free module to ``codomain``, and the
  512. homomorphism returned is obtained by restriction to ``domain``.
  513. >>> S = F.submodule([1, 0], [0, x])
  514. >>> homomorphism(S, T, [[1, x], [x**2, 0]])
  515. Matrix([
  516. [1, x**2], : <[1, 0], [0, x]> -> QQ[x]**2
  517. [x, 0]])
  518. If ``domain`` is a (sub)quotient `N/K`, then ``matrix`` determines a
  519. homomorphism from `N` to ``codomain``. If the kernel contains `K`, this
  520. homomorphism descends to ``domain`` and is returned; otherwise an exception
  521. is raised.
  522. >>> homomorphism(S/[(1, 0)], T, [0, [x**2, 0]])
  523. Matrix([
  524. [0, x**2], : <[1, 0] + <[1, 0]>, [0, x] + <[1, 0]>, [1, 0] + <[1, 0]>> -> QQ[x]**2
  525. [0, 0]])
  526. >>> homomorphism(S/[(0, x)], T, [0, [x**2, 0]])
  527. Traceback (most recent call last):
  528. ...
  529. ValueError: kernel <[1, 0], [0, 0]> must contain sm, got <[0,x]>
  530. """
  531. def freepres(module):
  532. """
  533. Return a tuple ``(F, S, Q, c)`` where ``F`` is a free module, ``S`` is a
  534. submodule of ``F``, and ``Q`` a submodule of ``S``, such that
  535. ``module = S/Q``, and ``c`` is a conversion function.
  536. """
  537. if isinstance(module, FreeModule):
  538. return module, module, module.submodule(), lambda x: module.convert(x)
  539. if isinstance(module, QuotientModule):
  540. return (module.base, module.base, module.killed_module,
  541. lambda x: module.convert(x).data)
  542. if isinstance(module, SubQuotientModule):
  543. return (module.base.container, module.base, module.killed_module,
  544. lambda x: module.container.convert(x).data)
  545. # an ordinary submodule
  546. return (module.container, module, module.submodule(),
  547. lambda x: module.container.convert(x))
  548. SF, SS, SQ, _ = freepres(domain)
  549. TF, TS, TQ, c = freepres(codomain)
  550. # NOTE this is probably a bit inefficient (redundant checks)
  551. return FreeModuleHomomorphism(SF, TF, [c(x) for x in matrix]
  552. ).restrict_domain(SS).restrict_codomain(TS
  553. ).quotient_codomain(TQ).quotient_domain(SQ)