modules.py 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111
  1. r"""Modules in number fields.
  2. The classes defined here allow us to work with finitely generated, free
  3. modules, whose generators are algebraic numbers.
  4. There is an abstract base class called :py:class:`~.Module`, which has two
  5. concrete subclasses, :py:class:`~.PowerBasis` and :py:class:`~.Submodule`.
  6. Every module is defined by its basis, or set of generators:
  7. * For a :py:class:`~.PowerBasis`, the generators are the first $n$ powers
  8. (starting with the zeroth) of an algebraic integer $\theta$ of degree $n$.
  9. The :py:class:`~.PowerBasis` is constructed by passing either the minimal
  10. polynomial of $\theta$, or an :py:class:`~.AlgebraicField` having $\theta$
  11. as its primitive element.
  12. * For a :py:class:`~.Submodule`, the generators are a set of
  13. $\mathbb{Q}$-linear combinations of the generators of another module. That
  14. other module is then the "parent" of the :py:class:`~.Submodule`. The
  15. coefficients of the $\mathbb{Q}$-linear combinations may be given by an
  16. integer matrix, and a positive integer denominator. Each column of the matrix
  17. defines a generator.
  18. >>> from sympy.polys import Poly, cyclotomic_poly, ZZ
  19. >>> from sympy.abc import x
  20. >>> from sympy.polys.matrices import DomainMatrix, DM
  21. >>> from sympy.polys.numberfields.modules import PowerBasis
  22. >>> T = Poly(cyclotomic_poly(5, x))
  23. >>> A = PowerBasis(T)
  24. >>> print(A)
  25. PowerBasis(x**4 + x**3 + x**2 + x + 1)
  26. >>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ), denom=3)
  27. >>> print(B)
  28. Submodule[[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 2, 0], [0, 0, 0, 2]]/3
  29. >>> print(B.parent)
  30. PowerBasis(x**4 + x**3 + x**2 + x + 1)
  31. Thus, every module is either a :py:class:`~.PowerBasis`,
  32. or a :py:class:`~.Submodule`, some ancestor of which is a
  33. :py:class:`~.PowerBasis`. (If ``S`` is a :py:class:`~.Submodule`, then its
  34. ancestors are ``S.parent``, ``S.parent.parent``, and so on).
  35. The :py:class:`~.ModuleElement` class represents a linear combination of the
  36. generators of any module. Critically, the coefficients of this linear
  37. combination are not restricted to be integers, but may be any rational
  38. numbers. This is necessary so that any and all algebraic integers be
  39. representable, starting from the power basis in a primitive element $\theta$
  40. for the number field in question. For example, in a quadratic field
  41. $\mathbb{Q}(\sqrt{d})$ where $d \equiv 1 \mod{4}$, a denominator of $2$ is
  42. needed.
  43. A :py:class:`~.ModuleElement` can be constructed from an integer column vector
  44. and a denominator:
  45. >>> U = Poly(x**2 - 5)
  46. >>> M = PowerBasis(U)
  47. >>> e = M(DM([[1], [1]], ZZ), denom=2)
  48. >>> print(e)
  49. [1, 1]/2
  50. >>> print(e.module)
  51. PowerBasis(x**2 - 5)
  52. The :py:class:`~.PowerBasisElement` class is a subclass of
  53. :py:class:`~.ModuleElement` that represents elements of a
  54. :py:class:`~.PowerBasis`, and adds functionality pertinent to elements
  55. represented directly over powers of the primitive element $\theta$.
  56. Arithmetic with module elements
  57. ===============================
  58. While a :py:class:`~.ModuleElement` represents a linear combination over the
  59. generators of a particular module, recall that every module is either a
  60. :py:class:`~.PowerBasis` or a descendant (along a chain of
  61. :py:class:`~.Submodule` objects) thereof, so that in fact every
  62. :py:class:`~.ModuleElement` represents an algebraic number in some field
  63. $\mathbb{Q}(\theta)$, where $\theta$ is the defining element of some
  64. :py:class:`~.PowerBasis`. It thus makes sense to talk about the number field
  65. to which a given :py:class:`~.ModuleElement` belongs.
  66. This means that any two :py:class:`~.ModuleElement` instances can be added,
  67. subtracted, multiplied, or divided, provided they belong to the same number
  68. field. Similarly, since $\mathbb{Q}$ is a subfield of every number field,
  69. any :py:class:`~.ModuleElement` may be added, multiplied, etc. by any
  70. rational number.
  71. >>> from sympy import QQ
  72. >>> from sympy.polys.numberfields.modules import to_col
  73. >>> T = Poly(cyclotomic_poly(5))
  74. >>> A = PowerBasis(T)
  75. >>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))
  76. >>> e = A(to_col([0, 2, 0, 0]), denom=3)
  77. >>> f = A(to_col([0, 0, 0, 7]), denom=5)
  78. >>> g = C(to_col([1, 1, 1, 1]))
  79. >>> e + f
  80. [0, 10, 0, 21]/15
  81. >>> e - f
  82. [0, 10, 0, -21]/15
  83. >>> e - g
  84. [-9, -7, -9, -9]/3
  85. >>> e + QQ(7, 10)
  86. [21, 20, 0, 0]/30
  87. >>> e * f
  88. [-14, -14, -14, -14]/15
  89. >>> e ** 2
  90. [0, 0, 4, 0]/9
  91. >>> f // g
  92. [7, 7, 7, 7]/15
  93. >>> f * QQ(2, 3)
  94. [0, 0, 0, 14]/15
  95. However, care must be taken with arithmetic operations on
  96. :py:class:`~.ModuleElement`, because the module $C$ to which the result will
  97. belong will be the nearest common ancestor (NCA) of the modules $A$, $B$ to
  98. which the two operands belong, and $C$ may be different from either or both
  99. of $A$ and $B$.
  100. >>> A = PowerBasis(T)
  101. >>> B = A.submodule_from_matrix(2 * DomainMatrix.eye(4, ZZ))
  102. >>> C = A.submodule_from_matrix(3 * DomainMatrix.eye(4, ZZ))
  103. >>> print((B(0) * C(0)).module == A)
  104. True
  105. Before the arithmetic operation is performed, copies of the two operands are
  106. automatically converted into elements of the NCA (the operands themselves are
  107. not modified). This upward conversion along an ancestor chain is easy: it just
  108. requires the successive multiplication by the defining matrix of each
  109. :py:class:`~.Submodule`.
  110. Conversely, downward conversion, i.e. representing a given
  111. :py:class:`~.ModuleElement` in a submodule, is also supported -- namely by
  112. the :py:meth:`~sympy.polys.numberfields.modules.Submodule.represent` method
  113. -- but is not guaranteed to succeed in general, since the given element may
  114. not belong to the submodule. The main circumstance in which this issue tends
  115. to arise is with multiplication, since modules, while closed under addition,
  116. need not be closed under multiplication.
  117. Multiplication
  118. --------------
  119. Generally speaking, a module need not be closed under multiplication, i.e. need
  120. not form a ring. However, many of the modules we work with in the context of
  121. number fields are in fact rings, and our classes do support multiplication.
  122. Specifically, any :py:class:`~.Module` can attempt to compute its own
  123. multiplication table, but this does not happen unless an attempt is made to
  124. multiply two :py:class:`~.ModuleElement` instances belonging to it.
  125. >>> A = PowerBasis(T)
  126. >>> print(A._mult_tab is None)
  127. True
  128. >>> a = A(0)*A(1)
  129. >>> print(A._mult_tab is None)
  130. False
  131. Every :py:class:`~.PowerBasis` is, by its nature, closed under multiplication,
  132. so instances of :py:class:`~.PowerBasis` can always successfully compute their
  133. multiplication table.
  134. When a :py:class:`~.Submodule` attempts to compute its multiplication table,
  135. it converts each of its own generators into elements of its parent module,
  136. multiplies them there, in every possible pairing, and then tries to
  137. represent the results in itself, i.e. as $\mathbb{Z}$-linear combinations
  138. over its own generators. This will succeed if and only if the submodule is
  139. in fact closed under multiplication.
  140. Module Homomorphisms
  141. ====================
  142. Many important number theoretic algorithms require the calculation of the
  143. kernel of one or more module homomorphisms. Accordingly we have several
  144. lightweight classes, :py:class:`~.ModuleHomomorphism`,
  145. :py:class:`~.ModuleEndomorphism`, :py:class:`~.InnerEndomorphism`, and
  146. :py:class:`~.EndomorphismRing`, which provide the minimal necessary machinery
  147. to support this.
  148. """
  149. from sympy.core.numbers import igcd, ilcm
  150. from sympy.core.symbol import Dummy
  151. from sympy.polys.polyclasses import ANP
  152. from sympy.polys.polytools import Poly
  153. from sympy.polys.densetools import dup_clear_denoms
  154. from sympy.polys.domains.algebraicfield import AlgebraicField
  155. from sympy.polys.domains.finitefield import FF
  156. from sympy.polys.domains.rationalfield import QQ
  157. from sympy.polys.domains.integerring import ZZ
  158. from sympy.polys.matrices.domainmatrix import DomainMatrix
  159. from sympy.polys.matrices.exceptions import DMBadInputError
  160. from sympy.polys.matrices.normalforms import hermite_normal_form
  161. from sympy.polys.polyerrors import CoercionFailed, UnificationFailed
  162. from sympy.polys.polyutils import IntegerPowerable
  163. from .exceptions import ClosureFailure, MissingUnityError, StructureError
  164. from .utilities import AlgIntPowers, is_rat, get_num_denom
  165. def to_col(coeffs):
  166. r"""Transform a list of integer coefficients into a column vector."""
  167. return DomainMatrix([[ZZ(c) for c in coeffs]], (1, len(coeffs)), ZZ).transpose()
  168. class Module:
  169. """
  170. Generic finitely-generated module.
  171. This is an abstract base class, and should not be instantiated directly.
  172. The two concrete subclasses are :py:class:`~.PowerBasis` and
  173. :py:class:`~.Submodule`.
  174. Every :py:class:`~.Submodule` is derived from another module, referenced
  175. by its ``parent`` attribute. If ``S`` is a submodule, then we refer to
  176. ``S.parent``, ``S.parent.parent``, and so on, as the "ancestors" of
  177. ``S``. Thus, every :py:class:`~.Module` is either a
  178. :py:class:`~.PowerBasis` or a :py:class:`~.Submodule`, some ancestor of
  179. which is a :py:class:`~.PowerBasis`.
  180. """
  181. @property
  182. def n(self):
  183. """The number of generators of this module."""
  184. raise NotImplementedError
  185. def mult_tab(self):
  186. """
  187. Get the multiplication table for this module (if closed under mult).
  188. Explanation
  189. ===========
  190. Computes a dictionary ``M`` of dictionaries of lists, representing the
  191. upper triangular half of the multiplication table.
  192. In other words, if ``0 <= i <= j < self.n``, then ``M[i][j]`` is the
  193. list ``c`` of coefficients such that
  194. ``g[i] * g[j] == sum(c[k]*g[k], k in range(self.n))``,
  195. where ``g`` is the list of generators of this module.
  196. If ``j < i`` then ``M[i][j]`` is undefined.
  197. Examples
  198. ========
  199. >>> from sympy.polys import Poly, cyclotomic_poly
  200. >>> from sympy.polys.numberfields.modules import PowerBasis
  201. >>> T = Poly(cyclotomic_poly(5))
  202. >>> A = PowerBasis(T)
  203. >>> print(A.mult_tab()) # doctest: +SKIP
  204. {0: {0: [1, 0, 0, 0], 1: [0, 1, 0, 0], 2: [0, 0, 1, 0], 3: [0, 0, 0, 1]},
  205. 1: {1: [0, 0, 1, 0], 2: [0, 0, 0, 1], 3: [-1, -1, -1, -1]},
  206. 2: {2: [-1, -1, -1, -1], 3: [1, 0, 0, 0]},
  207. 3: {3: [0, 1, 0, 0]}}
  208. Returns
  209. =======
  210. dict of dict of lists
  211. Raises
  212. ======
  213. ClosureFailure
  214. If the module is not closed under multiplication.
  215. """
  216. raise NotImplementedError
  217. @property
  218. def parent(self):
  219. """
  220. The parent module, if any, for this module.
  221. Explanation
  222. ===========
  223. For a :py:class:`~.Submodule` this is its ``parent`` attribute; for a
  224. :py:class:`~.PowerBasis` this is ``None``.
  225. Returns
  226. =======
  227. :py:class:`~.Module`, ``None``
  228. See Also
  229. ========
  230. Module
  231. """
  232. return None
  233. def represent(self, elt):
  234. r"""
  235. Represent a module element as an integer-linear combination over the
  236. generators of this module.
  237. Explanation
  238. ===========
  239. In our system, to "represent" always means to write a
  240. :py:class:`~.ModuleElement` as a :ref:`ZZ`-linear combination over the
  241. generators of the present :py:class:`~.Module`. Furthermore, the
  242. incoming :py:class:`~.ModuleElement` must belong to an ancestor of
  243. the present :py:class:`~.Module` (or to the present
  244. :py:class:`~.Module` itself).
  245. The most common application is to represent a
  246. :py:class:`~.ModuleElement` in a :py:class:`~.Submodule`. For example,
  247. this is involved in computing multiplication tables.
  248. On the other hand, representing in a :py:class:`~.PowerBasis` is an
  249. odd case, and one which tends not to arise in practice, except for
  250. example when using a :py:class:`~.ModuleEndomorphism` on a
  251. :py:class:`~.PowerBasis`.
  252. In such a case, (1) the incoming :py:class:`~.ModuleElement` must
  253. belong to the :py:class:`~.PowerBasis` itself (since the latter has no
  254. proper ancestors) and (2) it is "representable" iff it belongs to
  255. $\mathbb{Z}[\theta]$ (although generally a
  256. :py:class:`~.PowerBasisElement` may represent any element of
  257. $\mathbb{Q}(\theta)$, i.e. any algebraic number).
  258. Examples
  259. ========
  260. >>> from sympy import Poly, cyclotomic_poly
  261. >>> from sympy.polys.numberfields.modules import PowerBasis, to_col
  262. >>> from sympy.abc import zeta
  263. >>> T = Poly(cyclotomic_poly(5))
  264. >>> A = PowerBasis(T)
  265. >>> a = A(to_col([2, 4, 6, 8]))
  266. The :py:class:`~.ModuleElement` ``a`` has all even coefficients.
  267. If we represent ``a`` in the submodule ``B = 2*A``, the coefficients in
  268. the column vector will be halved:
  269. >>> B = A.submodule_from_gens([2*A(i) for i in range(4)])
  270. >>> b = B.represent(a)
  271. >>> print(b.transpose()) # doctest: +SKIP
  272. DomainMatrix([[1, 2, 3, 4]], (1, 4), ZZ)
  273. However, the element of ``B`` so defined still represents the same
  274. algebraic number:
  275. >>> print(a.poly(zeta).as_expr())
  276. 8*zeta**3 + 6*zeta**2 + 4*zeta + 2
  277. >>> print(B(b).over_power_basis().poly(zeta).as_expr())
  278. 8*zeta**3 + 6*zeta**2 + 4*zeta + 2
  279. Parameters
  280. ==========
  281. elt : :py:class:`~.ModuleElement`
  282. The module element to be represented. Must belong to some ancestor
  283. module of this module (including this module itself).
  284. Returns
  285. =======
  286. :py:class:`~.DomainMatrix` over :ref:`ZZ`
  287. This will be a column vector, representing the coefficients of a
  288. linear combination of this module's generators, which equals the
  289. given element.
  290. Raises
  291. ======
  292. ClosureFailure
  293. If the given element cannot be represented as a :ref:`ZZ`-linear
  294. combination over this module.
  295. See Also
  296. ========
  297. .Submodule.represent
  298. .PowerBasis.represent
  299. """
  300. raise NotImplementedError
  301. def ancestors(self, include_self=False):
  302. """
  303. Return the list of ancestor modules of this module, from the
  304. foundational :py:class:`~.PowerBasis` downward, optionally including
  305. ``self``.
  306. See Also
  307. ========
  308. Module
  309. """
  310. c = self.parent
  311. a = [] if c is None else c.ancestors(include_self=True)
  312. if include_self:
  313. a.append(self)
  314. return a
  315. def power_basis_ancestor(self):
  316. """
  317. Return the :py:class:`~.PowerBasis` that is an ancestor of this module.
  318. See Also
  319. ========
  320. Module
  321. """
  322. if isinstance(self, PowerBasis):
  323. return self
  324. c = self.parent
  325. if c is not None:
  326. return c.power_basis_ancestor()
  327. return None
  328. def nearest_common_ancestor(self, other):
  329. """
  330. Locate the nearest common ancestor of this module and another.
  331. Returns
  332. =======
  333. :py:class:`~.Module`, ``None``
  334. See Also
  335. ========
  336. Module
  337. """
  338. sA = self.ancestors(include_self=True)
  339. oA = other.ancestors(include_self=True)
  340. nca = None
  341. for sa, oa in zip(sA, oA):
  342. if sa == oa:
  343. nca = sa
  344. else:
  345. break
  346. return nca
  347. @property
  348. def number_field(self):
  349. r"""
  350. Return the associated :py:class:`~.AlgebraicField`, if any.
  351. Explanation
  352. ===========
  353. A :py:class:`~.PowerBasis` can be constructed on a :py:class:`~.Poly`
  354. $f$ or on an :py:class:`~.AlgebraicField` $K$. In the latter case, the
  355. :py:class:`~.PowerBasis` and all its descendant modules will return $K$
  356. as their ``.number_field`` property, while in the former case they will
  357. all return ``None``.
  358. Returns
  359. =======
  360. :py:class:`~.AlgebraicField`, ``None``
  361. """
  362. return self.power_basis_ancestor().number_field
  363. def is_compat_col(self, col):
  364. """Say whether *col* is a suitable column vector for this module."""
  365. return isinstance(col, DomainMatrix) and col.shape == (self.n, 1) and col.domain.is_ZZ
  366. def __call__(self, spec, denom=1):
  367. r"""
  368. Generate a :py:class:`~.ModuleElement` belonging to this module.
  369. Examples
  370. ========
  371. >>> from sympy.polys import Poly, cyclotomic_poly
  372. >>> from sympy.polys.numberfields.modules import PowerBasis, to_col
  373. >>> T = Poly(cyclotomic_poly(5))
  374. >>> A = PowerBasis(T)
  375. >>> e = A(to_col([1, 2, 3, 4]), denom=3)
  376. >>> print(e) # doctest: +SKIP
  377. [1, 2, 3, 4]/3
  378. >>> f = A(2)
  379. >>> print(f) # doctest: +SKIP
  380. [0, 0, 1, 0]
  381. Parameters
  382. ==========
  383. spec : :py:class:`~.DomainMatrix`, int
  384. Specifies the numerators of the coefficients of the
  385. :py:class:`~.ModuleElement`. Can be either a column vector over
  386. :ref:`ZZ`, whose length must equal the number $n$ of generators of
  387. this module, or else an integer ``j``, $0 \leq j < n$, which is a
  388. shorthand for column $j$ of $I_n$, the $n \times n$ identity
  389. matrix.
  390. denom : int, optional (default=1)
  391. Denominator for the coefficients of the
  392. :py:class:`~.ModuleElement`.
  393. Returns
  394. =======
  395. :py:class:`~.ModuleElement`
  396. The coefficients are the entries of the *spec* vector, divided by
  397. *denom*.
  398. """
  399. if isinstance(spec, int) and 0 <= spec < self.n:
  400. spec = DomainMatrix.eye(self.n, ZZ)[:, spec].to_dense()
  401. if not self.is_compat_col(spec):
  402. raise ValueError('Compatible column vector required.')
  403. return make_mod_elt(self, spec, denom=denom)
  404. def starts_with_unity(self):
  405. """Say whether the module's first generator equals unity."""
  406. raise NotImplementedError
  407. def basis_elements(self):
  408. """
  409. Get list of :py:class:`~.ModuleElement` being the generators of this
  410. module.
  411. """
  412. return [self(j) for j in range(self.n)]
  413. def zero(self):
  414. """Return a :py:class:`~.ModuleElement` representing zero."""
  415. return self(0) * 0
  416. def one(self):
  417. """
  418. Return a :py:class:`~.ModuleElement` representing unity,
  419. and belonging to the first ancestor of this module (including
  420. itself) that starts with unity.
  421. """
  422. return self.element_from_rational(1)
  423. def element_from_rational(self, a):
  424. """
  425. Return a :py:class:`~.ModuleElement` representing a rational number.
  426. Explanation
  427. ===========
  428. The returned :py:class:`~.ModuleElement` will belong to the first
  429. module on this module's ancestor chain (including this module
  430. itself) that starts with unity.
  431. Examples
  432. ========
  433. >>> from sympy.polys import Poly, cyclotomic_poly, QQ
  434. >>> from sympy.polys.numberfields.modules import PowerBasis
  435. >>> T = Poly(cyclotomic_poly(5))
  436. >>> A = PowerBasis(T)
  437. >>> a = A.element_from_rational(QQ(2, 3))
  438. >>> print(a) # doctest: +SKIP
  439. [2, 0, 0, 0]/3
  440. Parameters
  441. ==========
  442. a : int, :ref:`ZZ`, :ref:`QQ`
  443. Returns
  444. =======
  445. :py:class:`~.ModuleElement`
  446. """
  447. raise NotImplementedError
  448. def submodule_from_gens(self, gens, hnf=True, hnf_modulus=None):
  449. """
  450. Form the submodule generated by a list of :py:class:`~.ModuleElement`
  451. belonging to this module.
  452. Examples
  453. ========
  454. >>> from sympy.polys import Poly, cyclotomic_poly
  455. >>> from sympy.polys.numberfields.modules import PowerBasis
  456. >>> T = Poly(cyclotomic_poly(5))
  457. >>> A = PowerBasis(T)
  458. >>> gens = [A(0), 2*A(1), 3*A(2), 4*A(3)//5]
  459. >>> B = A.submodule_from_gens(gens)
  460. >>> print(B) # doctest: +SKIP
  461. Submodule[[5, 0, 0, 0], [0, 10, 0, 0], [0, 0, 15, 0], [0, 0, 0, 4]]/5
  462. Parameters
  463. ==========
  464. gens : list of :py:class:`~.ModuleElement` belonging to this module.
  465. hnf : boolean, optional (default=True)
  466. If True, we will reduce the matrix into Hermite Normal Form before
  467. forming the :py:class:`~.Submodule`.
  468. hnf_modulus : int, None, optional (default=None)
  469. Modulus for use in the HNF reduction algorithm. See
  470. :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.
  471. Returns
  472. =======
  473. :py:class:`~.Submodule`
  474. See Also
  475. ========
  476. submodule_from_matrix
  477. """
  478. if not all(g.module == self for g in gens):
  479. raise ValueError('Generators must belong to this module.')
  480. n = len(gens)
  481. if n == 0:
  482. raise ValueError('Need at least one generator.')
  483. m = gens[0].n
  484. d = gens[0].denom if n == 1 else ilcm(*[g.denom for g in gens])
  485. B = DomainMatrix.zeros((m, 0), ZZ).hstack(*[(d // g.denom) * g.col for g in gens])
  486. if hnf:
  487. B = hermite_normal_form(B, D=hnf_modulus)
  488. return self.submodule_from_matrix(B, denom=d)
  489. def submodule_from_matrix(self, B, denom=1):
  490. """
  491. Form the submodule generated by the elements of this module indicated
  492. by the columns of a matrix, with an optional denominator.
  493. Examples
  494. ========
  495. >>> from sympy.polys import Poly, cyclotomic_poly, ZZ
  496. >>> from sympy.polys.matrices import DM
  497. >>> from sympy.polys.numberfields.modules import PowerBasis
  498. >>> T = Poly(cyclotomic_poly(5))
  499. >>> A = PowerBasis(T)
  500. >>> B = A.submodule_from_matrix(DM([
  501. ... [0, 10, 0, 0],
  502. ... [0, 0, 7, 0],
  503. ... ], ZZ).transpose(), denom=15)
  504. >>> print(B) # doctest: +SKIP
  505. Submodule[[0, 10, 0, 0], [0, 0, 7, 0]]/15
  506. Parameters
  507. ==========
  508. B : :py:class:`~.DomainMatrix` over :ref:`ZZ`
  509. Each column gives the numerators of the coefficients of one
  510. generator of the submodule. Thus, the number of rows of *B* must
  511. equal the number of generators of the present module.
  512. denom : int, optional (default=1)
  513. Common denominator for all generators of the submodule.
  514. Returns
  515. =======
  516. :py:class:`~.Submodule`
  517. Raises
  518. ======
  519. ValueError
  520. If the given matrix *B* is not over :ref:`ZZ` or its number of rows
  521. does not equal the number of generators of the present module.
  522. See Also
  523. ========
  524. submodule_from_gens
  525. """
  526. m, n = B.shape
  527. if not B.domain.is_ZZ:
  528. raise ValueError('Matrix must be over ZZ.')
  529. if not m == self.n:
  530. raise ValueError('Matrix row count must match base module.')
  531. return Submodule(self, B, denom=denom)
  532. def whole_submodule(self):
  533. """
  534. Return a submodule equal to this entire module.
  535. Explanation
  536. ===========
  537. This is useful when you have a :py:class:`~.PowerBasis` and want to
  538. turn it into a :py:class:`~.Submodule` (in order to use methods
  539. belonging to the latter).
  540. """
  541. B = DomainMatrix.eye(self.n, ZZ)
  542. return self.submodule_from_matrix(B)
  543. def endomorphism_ring(self):
  544. """Form the :py:class:`~.EndomorphismRing` for this module."""
  545. return EndomorphismRing(self)
  546. class PowerBasis(Module):
  547. """The module generated by the powers of an algebraic integer."""
  548. def __init__(self, T):
  549. """
  550. Parameters
  551. ==========
  552. T : :py:class:`~.Poly`, :py:class:`~.AlgebraicField`
  553. Either (1) the monic, irreducible, univariate polynomial over
  554. :ref:`ZZ`, a root of which is the generator of the power basis,
  555. or (2) an :py:class:`~.AlgebraicField` whose primitive element
  556. is the generator of the power basis.
  557. """
  558. K = None
  559. if isinstance(T, AlgebraicField):
  560. K, T = T, T.ext.minpoly_of_element()
  561. # Sometimes incoming Polys are formally over QQ, although all their
  562. # coeffs are integral. We want them to be formally over ZZ.
  563. T = T.set_domain(ZZ)
  564. self.K = K
  565. self.T = T
  566. self._n = T.degree()
  567. self._mult_tab = None
  568. @property
  569. def number_field(self):
  570. return self.K
  571. def __repr__(self):
  572. return f'PowerBasis({self.T.as_expr()})'
  573. def __eq__(self, other):
  574. if isinstance(other, PowerBasis):
  575. return self.T == other.T
  576. return NotImplemented
  577. @property
  578. def n(self):
  579. return self._n
  580. def mult_tab(self):
  581. if self._mult_tab is None:
  582. self.compute_mult_tab()
  583. return self._mult_tab
  584. def compute_mult_tab(self):
  585. theta_pow = AlgIntPowers(self.T)
  586. M = {}
  587. n = self.n
  588. for u in range(n):
  589. M[u] = {}
  590. for v in range(u, n):
  591. M[u][v] = theta_pow[u + v]
  592. self._mult_tab = M
  593. def represent(self, elt):
  594. r"""
  595. Represent a module element as an integer-linear combination over the
  596. generators of this module.
  597. See Also
  598. ========
  599. .Module.represent
  600. .Submodule.represent
  601. """
  602. if elt.module == self and elt.denom == 1:
  603. return elt.column()
  604. else:
  605. raise ClosureFailure('Element not representable in ZZ[theta].')
  606. def starts_with_unity(self):
  607. return True
  608. def element_from_rational(self, a):
  609. return self(0) * a
  610. def element_from_poly(self, f):
  611. """
  612. Produce an element of this module, representing *f* after reduction mod
  613. our defining minimal polynomial.
  614. Parameters
  615. ==========
  616. f : :py:class:`~.Poly` over :ref:`ZZ` in same var as our defining poly.
  617. Returns
  618. =======
  619. :py:class:`~.PowerBasisElement`
  620. """
  621. n, k = self.n, f.degree()
  622. if k >= n:
  623. f = f % self.T
  624. if f == 0:
  625. return self.zero()
  626. d, c = dup_clear_denoms(f.rep.rep, QQ, convert=True)
  627. c = list(reversed(c))
  628. ell = len(c)
  629. z = [ZZ(0)] * (n - ell)
  630. col = to_col(c + z)
  631. return self(col, denom=d)
  632. def _element_from_rep_and_mod(self, rep, mod):
  633. """
  634. Produce a PowerBasisElement representing a given algebraic number.
  635. Parameters
  636. ==========
  637. rep : list of coeffs
  638. Represents the number as polynomial in the primitive element of the
  639. field.
  640. mod : list of coeffs
  641. Represents the minimal polynomial of the primitive element of the
  642. field.
  643. Returns
  644. =======
  645. :py:class:`~.PowerBasisElement`
  646. """
  647. if mod != self.T.rep.rep:
  648. raise UnificationFailed('Element does not appear to be in the same field.')
  649. return self.element_from_poly(Poly(rep, self.T.gen))
  650. def element_from_ANP(self, a):
  651. """Convert an ANP into a PowerBasisElement. """
  652. return self._element_from_rep_and_mod(a.rep, a.mod)
  653. def element_from_alg_num(self, a):
  654. """Convert an AlgebraicNumber into a PowerBasisElement. """
  655. return self._element_from_rep_and_mod(a.rep.rep, a.minpoly.rep.rep)
  656. class Submodule(Module, IntegerPowerable):
  657. """A submodule of another module."""
  658. def __init__(self, parent, matrix, denom=1, mult_tab=None):
  659. """
  660. Parameters
  661. ==========
  662. parent : :py:class:`~.Module`
  663. The module from which this one is derived.
  664. matrix : :py:class:`~.DomainMatrix` over :ref:`ZZ`
  665. The matrix whose columns define this submodule's generators as
  666. linear combinations over the parent's generators.
  667. denom : int, optional (default=1)
  668. Denominator for the coefficients given by the matrix.
  669. mult_tab : dict, ``None``, optional
  670. If already known, the multiplication table for this module may be
  671. supplied.
  672. """
  673. self._parent = parent
  674. self._matrix = matrix
  675. self._denom = denom
  676. self._mult_tab = mult_tab
  677. self._n = matrix.shape[1]
  678. self._QQ_matrix = None
  679. self._starts_with_unity = None
  680. self._is_sq_maxrank_HNF = None
  681. def __repr__(self):
  682. r = 'Submodule' + repr(self.matrix.transpose().to_Matrix().tolist())
  683. if self.denom > 1:
  684. r += f'/{self.denom}'
  685. return r
  686. def reduced(self):
  687. """
  688. Produce a reduced version of this submodule.
  689. Explanation
  690. ===========
  691. In the reduced version, it is guaranteed that 1 is the only positive
  692. integer dividing both the submodule's denominator, and every entry in
  693. the submodule's matrix.
  694. Returns
  695. =======
  696. :py:class:`~.Submodule`
  697. """
  698. if self.denom == 1:
  699. return self
  700. g = igcd(self.denom, *self.coeffs)
  701. if g == 1:
  702. return self
  703. return type(self)(self.parent, (self.matrix / g).convert_to(ZZ), denom=self.denom // g, mult_tab=self._mult_tab)
  704. def discard_before(self, r):
  705. """
  706. Produce a new module by discarding all generators before a given
  707. index *r*.
  708. """
  709. W = self.matrix[:, r:]
  710. s = self.n - r
  711. M = None
  712. mt = self._mult_tab
  713. if mt is not None:
  714. M = {}
  715. for u in range(s):
  716. M[u] = {}
  717. for v in range(u, s):
  718. M[u][v] = mt[r + u][r + v][r:]
  719. return Submodule(self.parent, W, denom=self.denom, mult_tab=M)
  720. @property
  721. def n(self):
  722. return self._n
  723. def mult_tab(self):
  724. if self._mult_tab is None:
  725. self.compute_mult_tab()
  726. return self._mult_tab
  727. def compute_mult_tab(self):
  728. gens = self.basis_element_pullbacks()
  729. M = {}
  730. n = self.n
  731. for u in range(n):
  732. M[u] = {}
  733. for v in range(u, n):
  734. M[u][v] = self.represent(gens[u] * gens[v]).flat()
  735. self._mult_tab = M
  736. @property
  737. def parent(self):
  738. return self._parent
  739. @property
  740. def matrix(self):
  741. return self._matrix
  742. @property
  743. def coeffs(self):
  744. return self.matrix.flat()
  745. @property
  746. def denom(self):
  747. return self._denom
  748. @property
  749. def QQ_matrix(self):
  750. """
  751. :py:class:`~.DomainMatrix` over :ref:`QQ`, equal to
  752. ``self.matrix / self.denom``, and guaranteed to be dense.
  753. Explanation
  754. ===========
  755. Depending on how it is formed, a :py:class:`~.DomainMatrix` may have
  756. an internal representation that is sparse or dense. We guarantee a
  757. dense representation here, so that tests for equivalence of submodules
  758. always come out as expected.
  759. Examples
  760. ========
  761. >>> from sympy.polys import Poly, cyclotomic_poly, ZZ
  762. >>> from sympy.abc import x
  763. >>> from sympy.polys.matrices import DomainMatrix
  764. >>> from sympy.polys.numberfields.modules import PowerBasis
  765. >>> T = Poly(cyclotomic_poly(5, x))
  766. >>> A = PowerBasis(T)
  767. >>> B = A.submodule_from_matrix(3*DomainMatrix.eye(4, ZZ), denom=6)
  768. >>> C = A.submodule_from_matrix(DomainMatrix.eye(4, ZZ), denom=2)
  769. >>> print(B.QQ_matrix == C.QQ_matrix)
  770. True
  771. Returns
  772. =======
  773. :py:class:`~.DomainMatrix` over :ref:`QQ`
  774. """
  775. if self._QQ_matrix is None:
  776. self._QQ_matrix = (self.matrix / self.denom).to_dense()
  777. return self._QQ_matrix
  778. def starts_with_unity(self):
  779. if self._starts_with_unity is None:
  780. self._starts_with_unity = self(0).equiv(1)
  781. return self._starts_with_unity
  782. def is_sq_maxrank_HNF(self):
  783. if self._is_sq_maxrank_HNF is None:
  784. self._is_sq_maxrank_HNF = is_sq_maxrank_HNF(self._matrix)
  785. return self._is_sq_maxrank_HNF
  786. def is_power_basis_submodule(self):
  787. return isinstance(self.parent, PowerBasis)
  788. def element_from_rational(self, a):
  789. if self.starts_with_unity():
  790. return self(0) * a
  791. else:
  792. return self.parent.element_from_rational(a)
  793. def basis_element_pullbacks(self):
  794. """
  795. Return list of this submodule's basis elements as elements of the
  796. submodule's parent module.
  797. """
  798. return [e.to_parent() for e in self.basis_elements()]
  799. def represent(self, elt):
  800. """
  801. Represent a module element as an integer-linear combination over the
  802. generators of this module.
  803. See Also
  804. ========
  805. .Module.represent
  806. .PowerBasis.represent
  807. """
  808. if elt.module == self:
  809. return elt.column()
  810. elif elt.module == self.parent:
  811. try:
  812. # The given element should be a ZZ-linear combination over our
  813. # basis vectors; however, due to the presence of denominators,
  814. # we need to solve over QQ.
  815. A = self.QQ_matrix
  816. b = elt.QQ_col
  817. x = A._solve(b)[0].transpose()
  818. x = x.convert_to(ZZ)
  819. except DMBadInputError:
  820. raise ClosureFailure('Element outside QQ-span of this basis.')
  821. except CoercionFailed:
  822. raise ClosureFailure('Element in QQ-span but not ZZ-span of this basis.')
  823. return x
  824. elif isinstance(self.parent, Submodule):
  825. coeffs_in_parent = self.parent.represent(elt)
  826. parent_element = self.parent(coeffs_in_parent)
  827. return self.represent(parent_element)
  828. else:
  829. raise ClosureFailure('Element outside ancestor chain of this module.')
  830. def is_compat_submodule(self, other):
  831. return isinstance(other, Submodule) and other.parent == self.parent
  832. def __eq__(self, other):
  833. if self.is_compat_submodule(other):
  834. return other.QQ_matrix == self.QQ_matrix
  835. return NotImplemented
  836. def add(self, other, hnf=True, hnf_modulus=None):
  837. """
  838. Add this :py:class:`~.Submodule` to another.
  839. Explanation
  840. ===========
  841. This represents the module generated by the union of the two modules'
  842. sets of generators.
  843. Parameters
  844. ==========
  845. other : :py:class:`~.Submodule`
  846. hnf : boolean, optional (default=True)
  847. If ``True``, reduce the matrix of the combined module to its
  848. Hermite Normal Form.
  849. hnf_modulus : :ref:`ZZ`, None, optional
  850. If a positive integer is provided, use this as modulus in the
  851. HNF reduction. See
  852. :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.
  853. Returns
  854. =======
  855. :py:class:`~.Submodule`
  856. """
  857. d, e = self.denom, other.denom
  858. m = ilcm(d, e)
  859. a, b = m // d, m // e
  860. B = (a * self.matrix).hstack(b * other.matrix)
  861. if hnf:
  862. B = hermite_normal_form(B, D=hnf_modulus)
  863. return self.parent.submodule_from_matrix(B, denom=m)
  864. def __add__(self, other):
  865. if self.is_compat_submodule(other):
  866. return self.add(other)
  867. return NotImplemented
  868. __radd__ = __add__
  869. def mul(self, other, hnf=True, hnf_modulus=None):
  870. """
  871. Multiply this :py:class:`~.Submodule` by a rational number, a
  872. :py:class:`~.ModuleElement`, or another :py:class:`~.Submodule`.
  873. Explanation
  874. ===========
  875. To multiply by a rational number or :py:class:`~.ModuleElement` means
  876. to form the submodule whose generators are the products of this
  877. quantity with all the generators of the present submodule.
  878. To multiply by another :py:class:`~.Submodule` means to form the
  879. submodule whose generators are all the products of one generator from
  880. the one submodule, and one generator from the other.
  881. Parameters
  882. ==========
  883. other : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.ModuleElement`, :py:class:`~.Submodule`
  884. hnf : boolean, optional (default=True)
  885. If ``True``, reduce the matrix of the product module to its
  886. Hermite Normal Form.
  887. hnf_modulus : :ref:`ZZ`, None, optional
  888. If a positive integer is provided, use this as modulus in the
  889. HNF reduction. See
  890. :py:func:`~sympy.polys.matrices.normalforms.hermite_normal_form`.
  891. Returns
  892. =======
  893. :py:class:`~.Submodule`
  894. """
  895. if is_rat(other):
  896. a, b = get_num_denom(other)
  897. if a == b == 1:
  898. return self
  899. else:
  900. return Submodule(self.parent,
  901. self.matrix * a, denom=self.denom * b,
  902. mult_tab=None).reduced()
  903. elif isinstance(other, ModuleElement) and other.module == self.parent:
  904. # The submodule is multiplied by an element of the parent module.
  905. # We presume this means we want a new submodule of the parent module.
  906. gens = [other * e for e in self.basis_element_pullbacks()]
  907. return self.parent.submodule_from_gens(gens, hnf=hnf, hnf_modulus=hnf_modulus)
  908. elif self.is_compat_submodule(other):
  909. # This case usually means you're multiplying ideals, and want another
  910. # ideal, i.e. another submodule of the same parent module.
  911. alphas, betas = self.basis_element_pullbacks(), other.basis_element_pullbacks()
  912. gens = [a * b for a in alphas for b in betas]
  913. return self.parent.submodule_from_gens(gens, hnf=hnf, hnf_modulus=hnf_modulus)
  914. return NotImplemented
  915. def __mul__(self, other):
  916. return self.mul(other)
  917. __rmul__ = __mul__
  918. def _first_power(self):
  919. return self
  920. def reduce_element(self, elt):
  921. r"""
  922. If this submodule $B$ has defining matrix $W$ in square, maximal-rank
  923. Hermite normal form, then, given an element $x$ of the parent module
  924. $A$, we produce an element $y \in A$ such that $x - y \in B$, and the
  925. $i$th coordinate of $y$ satisfies $0 \leq y_i < w_{i,i}$. This
  926. representative $y$ is unique, in the sense that every element of
  927. the coset $x + B$ reduces to it under this procedure.
  928. Explanation
  929. ===========
  930. In the special case where $A$ is a power basis for a number field $K$,
  931. and $B$ is a submodule representing an ideal $I$, this operation
  932. represents one of a few important ways of reducing an element of $K$
  933. modulo $I$ to obtain a "small" representative. See [Cohen00]_ Section
  934. 1.4.3.
  935. Examples
  936. ========
  937. >>> from sympy import QQ, Poly, symbols
  938. >>> t = symbols('t')
  939. >>> k = QQ.alg_field_from_poly(Poly(t**3 + t**2 - 2*t + 8))
  940. >>> Zk = k.maximal_order()
  941. >>> A = Zk.parent
  942. >>> B = (A(2) - 3*A(0))*Zk
  943. >>> B.reduce_element(A(2))
  944. [3, 0, 0]
  945. Parameters
  946. ==========
  947. elt : :py:class:`~.ModuleElement`
  948. An element of this submodule's parent module.
  949. Returns
  950. =======
  951. elt : :py:class:`~.ModuleElement`
  952. An element of this submodule's parent module.
  953. Raises
  954. ======
  955. NotImplementedError
  956. If the given :py:class:`~.ModuleElement` does not belong to this
  957. submodule's parent module.
  958. StructureError
  959. If this submodule's defining matrix is not in square, maximal-rank
  960. Hermite normal form.
  961. References
  962. ==========
  963. .. [Cohen00] Cohen, H. *Advanced Topics in Computational Number
  964. Theory.*
  965. """
  966. if not elt.module == self.parent:
  967. raise NotImplementedError
  968. if not self.is_sq_maxrank_HNF():
  969. msg = "Reduction not implemented unless matrix square max-rank HNF"
  970. raise StructureError(msg)
  971. B = self.basis_element_pullbacks()
  972. a = elt
  973. for i in range(self.n - 1, -1, -1):
  974. b = B[i]
  975. q = a.coeffs[i]*b.denom // (b.coeffs[i]*a.denom)
  976. a -= q*b
  977. return a
  978. def is_sq_maxrank_HNF(dm):
  979. r"""
  980. Say whether a :py:class:`~.DomainMatrix` is in that special case of Hermite
  981. Normal Form, in which the matrix is also square and of maximal rank.
  982. Explanation
  983. ===========
  984. We commonly work with :py:class:`~.Submodule` instances whose matrix is in
  985. this form, and it can be useful to be able to check that this condition is
  986. satisfied.
  987. For example this is the case with the :py:class:`~.Submodule` ``ZK``
  988. returned by :py:func:`~sympy.polys.numberfields.basis.round_two`, which
  989. represents the maximal order in a number field, and with ideals formed
  990. therefrom, such as ``2 * ZK``.
  991. """
  992. if dm.domain.is_ZZ and dm.is_square and dm.is_upper:
  993. n = dm.shape[0]
  994. for i in range(n):
  995. d = dm[i, i].element
  996. if d <= 0:
  997. return False
  998. for j in range(i + 1, n):
  999. if not (0 <= dm[i, j].element < d):
  1000. return False
  1001. return True
  1002. return False
  1003. def make_mod_elt(module, col, denom=1):
  1004. r"""
  1005. Factory function which builds a :py:class:`~.ModuleElement`, but ensures
  1006. that it is a :py:class:`~.PowerBasisElement` if the module is a
  1007. :py:class:`~.PowerBasis`.
  1008. """
  1009. if isinstance(module, PowerBasis):
  1010. return PowerBasisElement(module, col, denom=denom)
  1011. else:
  1012. return ModuleElement(module, col, denom=denom)
  1013. class ModuleElement(IntegerPowerable):
  1014. r"""
  1015. Represents an element of a :py:class:`~.Module`.
  1016. NOTE: Should not be constructed directly. Use the
  1017. :py:meth:`~.Module.__call__` method or the :py:func:`make_mod_elt()`
  1018. factory function instead.
  1019. """
  1020. def __init__(self, module, col, denom=1):
  1021. """
  1022. Parameters
  1023. ==========
  1024. module : :py:class:`~.Module`
  1025. The module to which this element belongs.
  1026. col : :py:class:`~.DomainMatrix` over :ref:`ZZ`
  1027. Column vector giving the numerators of the coefficients of this
  1028. element.
  1029. denom : int, optional (default=1)
  1030. Denominator for the coefficients of this element.
  1031. """
  1032. self.module = module
  1033. self.col = col
  1034. self.denom = denom
  1035. self._QQ_col = None
  1036. def __repr__(self):
  1037. r = str([int(c) for c in self.col.flat()])
  1038. if self.denom > 1:
  1039. r += f'/{self.denom}'
  1040. return r
  1041. def reduced(self):
  1042. """
  1043. Produce a reduced version of this ModuleElement, i.e. one in which the
  1044. gcd of the denominator together with all numerator coefficients is 1.
  1045. """
  1046. if self.denom == 1:
  1047. return self
  1048. g = igcd(self.denom, *self.coeffs)
  1049. if g == 1:
  1050. return self
  1051. return type(self)(self.module,
  1052. (self.col / g).convert_to(ZZ),
  1053. denom=self.denom // g)
  1054. def reduced_mod_p(self, p):
  1055. """
  1056. Produce a version of this :py:class:`~.ModuleElement` in which all
  1057. numerator coefficients have been reduced mod *p*.
  1058. """
  1059. return make_mod_elt(self.module,
  1060. self.col.convert_to(FF(p)).convert_to(ZZ),
  1061. denom=self.denom)
  1062. @classmethod
  1063. def from_int_list(cls, module, coeffs, denom=1):
  1064. """
  1065. Make a :py:class:`~.ModuleElement` from a list of ints (instead of a
  1066. column vector).
  1067. """
  1068. col = to_col(coeffs)
  1069. return cls(module, col, denom=denom)
  1070. @property
  1071. def n(self):
  1072. """The length of this element's column."""
  1073. return self.module.n
  1074. def __len__(self):
  1075. return self.n
  1076. def column(self, domain=None):
  1077. """
  1078. Get a copy of this element's column, optionally converting to a domain.
  1079. """
  1080. return self.col.convert_to(domain)
  1081. @property
  1082. def coeffs(self):
  1083. return self.col.flat()
  1084. @property
  1085. def QQ_col(self):
  1086. """
  1087. :py:class:`~.DomainMatrix` over :ref:`QQ`, equal to
  1088. ``self.col / self.denom``, and guaranteed to be dense.
  1089. See Also
  1090. ========
  1091. .Submodule.QQ_matrix
  1092. """
  1093. if self._QQ_col is None:
  1094. self._QQ_col = (self.col / self.denom).to_dense()
  1095. return self._QQ_col
  1096. def to_parent(self):
  1097. """
  1098. Transform into a :py:class:`~.ModuleElement` belonging to the parent of
  1099. this element's module.
  1100. """
  1101. if not isinstance(self.module, Submodule):
  1102. raise ValueError('Not an element of a Submodule.')
  1103. return make_mod_elt(
  1104. self.module.parent, self.module.matrix * self.col,
  1105. denom=self.module.denom * self.denom)
  1106. def to_ancestor(self, anc):
  1107. """
  1108. Transform into a :py:class:`~.ModuleElement` belonging to a given
  1109. ancestor of this element's module.
  1110. Parameters
  1111. ==========
  1112. anc : :py:class:`~.Module`
  1113. """
  1114. if anc == self.module:
  1115. return self
  1116. else:
  1117. return self.to_parent().to_ancestor(anc)
  1118. def over_power_basis(self):
  1119. """
  1120. Transform into a :py:class:`~.PowerBasisElement` over our
  1121. :py:class:`~.PowerBasis` ancestor.
  1122. """
  1123. e = self
  1124. while not isinstance(e.module, PowerBasis):
  1125. e = e.to_parent()
  1126. return e
  1127. def is_compat(self, other):
  1128. """
  1129. Test whether other is another :py:class:`~.ModuleElement` with same
  1130. module.
  1131. """
  1132. return isinstance(other, ModuleElement) and other.module == self.module
  1133. def unify(self, other):
  1134. """
  1135. Try to make a compatible pair of :py:class:`~.ModuleElement`, one
  1136. equivalent to this one, and one equivalent to the other.
  1137. Explanation
  1138. ===========
  1139. We search for the nearest common ancestor module for the pair of
  1140. elements, and represent each one there.
  1141. Returns
  1142. =======
  1143. Pair ``(e1, e2)``
  1144. Each ``ei`` is a :py:class:`~.ModuleElement`, they belong to the
  1145. same :py:class:`~.Module`, ``e1`` is equivalent to ``self``, and
  1146. ``e2`` is equivalent to ``other``.
  1147. Raises
  1148. ======
  1149. UnificationFailed
  1150. If ``self`` and ``other`` have no common ancestor module.
  1151. """
  1152. if self.module == other.module:
  1153. return self, other
  1154. nca = self.module.nearest_common_ancestor(other.module)
  1155. if nca is not None:
  1156. return self.to_ancestor(nca), other.to_ancestor(nca)
  1157. raise UnificationFailed(f"Cannot unify {self} with {other}")
  1158. def __eq__(self, other):
  1159. if self.is_compat(other):
  1160. return self.QQ_col == other.QQ_col
  1161. return NotImplemented
  1162. def equiv(self, other):
  1163. """
  1164. A :py:class:`~.ModuleElement` may test as equivalent to a rational
  1165. number or another :py:class:`~.ModuleElement`, if they represent the
  1166. same algebraic number.
  1167. Explanation
  1168. ===========
  1169. This method is intended to check equivalence only in those cases in
  1170. which it is easy to test; namely, when *other* is either a
  1171. :py:class:`~.ModuleElement` that can be unified with this one (i.e. one
  1172. which shares a common :py:class:`~.PowerBasis` ancestor), or else a
  1173. rational number (which is easy because every :py:class:`~.PowerBasis`
  1174. represents every rational number).
  1175. Parameters
  1176. ==========
  1177. other : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.ModuleElement`
  1178. Returns
  1179. =======
  1180. bool
  1181. Raises
  1182. ======
  1183. UnificationFailed
  1184. If ``self`` and ``other`` do not share a common
  1185. :py:class:`~.PowerBasis` ancestor.
  1186. """
  1187. if self == other:
  1188. return True
  1189. elif isinstance(other, ModuleElement):
  1190. a, b = self.unify(other)
  1191. return a == b
  1192. elif is_rat(other):
  1193. if isinstance(self, PowerBasisElement):
  1194. return self == self.module(0) * other
  1195. else:
  1196. return self.over_power_basis().equiv(other)
  1197. return False
  1198. def __add__(self, other):
  1199. """
  1200. A :py:class:`~.ModuleElement` can be added to a rational number, or to
  1201. another :py:class:`~.ModuleElement`.
  1202. Explanation
  1203. ===========
  1204. When the other summand is a rational number, it will be converted into
  1205. a :py:class:`~.ModuleElement` (belonging to the first ancestor of this
  1206. module that starts with unity).
  1207. In all cases, the sum belongs to the nearest common ancestor (NCA) of
  1208. the modules of the two summands. If the NCA does not exist, we return
  1209. ``NotImplemented``.
  1210. """
  1211. if self.is_compat(other):
  1212. d, e = self.denom, other.denom
  1213. m = ilcm(d, e)
  1214. u, v = m // d, m // e
  1215. col = to_col([u * a + v * b for a, b in zip(self.coeffs, other.coeffs)])
  1216. return type(self)(self.module, col, denom=m).reduced()
  1217. elif isinstance(other, ModuleElement):
  1218. try:
  1219. a, b = self.unify(other)
  1220. except UnificationFailed:
  1221. return NotImplemented
  1222. return a + b
  1223. elif is_rat(other):
  1224. return self + self.module.element_from_rational(other)
  1225. return NotImplemented
  1226. __radd__ = __add__
  1227. def __neg__(self):
  1228. return self * -1
  1229. def __sub__(self, other):
  1230. return self + (-other)
  1231. def __rsub__(self, other):
  1232. return -self + other
  1233. def __mul__(self, other):
  1234. """
  1235. A :py:class:`~.ModuleElement` can be multiplied by a rational number,
  1236. or by another :py:class:`~.ModuleElement`.
  1237. Explanation
  1238. ===========
  1239. When the multiplier is a rational number, the product is computed by
  1240. operating directly on the coefficients of this
  1241. :py:class:`~.ModuleElement`.
  1242. When the multiplier is another :py:class:`~.ModuleElement`, the product
  1243. will belong to the nearest common ancestor (NCA) of the modules of the
  1244. two operands, and that NCA must have a multiplication table. If the NCA
  1245. does not exist, we return ``NotImplemented``. If the NCA does not have
  1246. a mult. table, ``ClosureFailure`` will be raised.
  1247. """
  1248. if self.is_compat(other):
  1249. M = self.module.mult_tab()
  1250. A, B = self.col.flat(), other.col.flat()
  1251. n = self.n
  1252. C = [0] * n
  1253. for u in range(n):
  1254. for v in range(u, n):
  1255. c = A[u] * B[v]
  1256. if v > u:
  1257. c += A[v] * B[u]
  1258. if c != 0:
  1259. R = M[u][v]
  1260. for k in range(n):
  1261. C[k] += c * R[k]
  1262. d = self.denom * other.denom
  1263. return self.from_int_list(self.module, C, denom=d)
  1264. elif isinstance(other, ModuleElement):
  1265. try:
  1266. a, b = self.unify(other)
  1267. except UnificationFailed:
  1268. return NotImplemented
  1269. return a * b
  1270. elif is_rat(other):
  1271. a, b = get_num_denom(other)
  1272. if a == b == 1:
  1273. return self
  1274. else:
  1275. return make_mod_elt(self.module,
  1276. self.col * a, denom=self.denom * b).reduced()
  1277. return NotImplemented
  1278. __rmul__ = __mul__
  1279. def _zeroth_power(self):
  1280. return self.module.one()
  1281. def _first_power(self):
  1282. return self
  1283. def __floordiv__(self, a):
  1284. if is_rat(a):
  1285. a = QQ(a)
  1286. return self * (1/a)
  1287. elif isinstance(a, ModuleElement):
  1288. return self * (1//a)
  1289. return NotImplemented
  1290. def __rfloordiv__(self, a):
  1291. return a // self.over_power_basis()
  1292. def __mod__(self, m):
  1293. r"""
  1294. Reduce this :py:class:`~.ModuleElement` mod a :py:class:`~.Submodule`.
  1295. Parameters
  1296. ==========
  1297. m : int, :ref:`ZZ`, :ref:`QQ`, :py:class:`~.Submodule`
  1298. If a :py:class:`~.Submodule`, reduce ``self`` relative to this.
  1299. If an integer or rational, reduce relative to the
  1300. :py:class:`~.Submodule` that is our own module times this constant.
  1301. See Also
  1302. ========
  1303. .Submodule.reduce_element
  1304. """
  1305. if is_rat(m):
  1306. m = m * self.module.whole_submodule()
  1307. if isinstance(m, Submodule) and m.parent == self.module:
  1308. return m.reduce_element(self)
  1309. return NotImplemented
  1310. class PowerBasisElement(ModuleElement):
  1311. r"""
  1312. Subclass for :py:class:`~.ModuleElement` instances whose module is a
  1313. :py:class:`~.PowerBasis`.
  1314. """
  1315. @property
  1316. def T(self):
  1317. """Access the defining polynomial of the :py:class:`~.PowerBasis`."""
  1318. return self.module.T
  1319. def numerator(self, x=None):
  1320. """Obtain the numerator as a polynomial over :ref:`ZZ`."""
  1321. x = x or self.T.gen
  1322. return Poly(reversed(self.coeffs), x, domain=ZZ)
  1323. def poly(self, x=None):
  1324. """Obtain the number as a polynomial over :ref:`QQ`."""
  1325. return self.numerator(x=x) // self.denom
  1326. @property
  1327. def is_rational(self):
  1328. """Say whether this element represents a rational number."""
  1329. return self.col[1:, :].is_zero_matrix
  1330. @property
  1331. def generator(self):
  1332. """
  1333. Return a :py:class:`~.Symbol` to be used when expressing this element
  1334. as a polynomial.
  1335. If we have an associated :py:class:`~.AlgebraicField` whose primitive
  1336. element has an alias symbol, we use that. Otherwise we use the variable
  1337. of the minimal polynomial defining the power basis to which we belong.
  1338. """
  1339. K = self.module.number_field
  1340. return K.ext.alias if K and K.ext.is_aliased else self.T.gen
  1341. def as_expr(self, x=None):
  1342. """Create a Basic expression from ``self``. """
  1343. return self.poly(x or self.generator).as_expr()
  1344. def norm(self, T=None):
  1345. """Compute the norm of this number."""
  1346. T = T or self.T
  1347. x = T.gen
  1348. A = self.numerator(x=x)
  1349. return T.resultant(A) // self.denom ** self.n
  1350. def inverse(self):
  1351. f = self.poly()
  1352. f_inv = f.invert(self.T)
  1353. return self.module.element_from_poly(f_inv)
  1354. def __rfloordiv__(self, a):
  1355. return self.inverse() * a
  1356. def _negative_power(self, e, modulo=None):
  1357. return self.inverse() ** abs(e)
  1358. def to_ANP(self):
  1359. """Convert to an equivalent :py:class:`~.ANP`. """
  1360. return ANP(list(reversed(self.QQ_col.flat())), QQ.map(self.T.rep.rep), QQ)
  1361. def to_alg_num(self):
  1362. """
  1363. Try to convert to an equivalent :py:class:`~.AlgebraicNumber`.
  1364. Explanation
  1365. ===========
  1366. In general, the conversion from an :py:class:`~.AlgebraicNumber` to a
  1367. :py:class:`~.PowerBasisElement` throws away information, because an
  1368. :py:class:`~.AlgebraicNumber` specifies a complex embedding, while a
  1369. :py:class:`~.PowerBasisElement` does not. However, in some cases it is
  1370. possible to convert a :py:class:`~.PowerBasisElement` back into an
  1371. :py:class:`~.AlgebraicNumber`, namely when the associated
  1372. :py:class:`~.PowerBasis` has a reference to an
  1373. :py:class:`~.AlgebraicField`.
  1374. Returns
  1375. =======
  1376. :py:class:`~.AlgebraicNumber`
  1377. Raises
  1378. ======
  1379. StructureError
  1380. If the :py:class:`~.PowerBasis` to which this element belongs does
  1381. not have an associated :py:class:`~.AlgebraicField`.
  1382. """
  1383. K = self.module.number_field
  1384. if K:
  1385. return K.to_alg_num(self.to_ANP())
  1386. raise StructureError("No associated AlgebraicField")
  1387. class ModuleHomomorphism:
  1388. r"""A homomorphism from one module to another."""
  1389. def __init__(self, domain, codomain, mapping):
  1390. r"""
  1391. Parameters
  1392. ==========
  1393. domain : :py:class:`~.Module`
  1394. The domain of the mapping.
  1395. codomain : :py:class:`~.Module`
  1396. The codomain of the mapping.
  1397. mapping : callable
  1398. An arbitrary callable is accepted, but should be chosen so as
  1399. to represent an actual module homomorphism. In particular, should
  1400. accept elements of *domain* and return elements of *codomain*.
  1401. Examples
  1402. ========
  1403. >>> from sympy import Poly, cyclotomic_poly
  1404. >>> from sympy.polys.numberfields.modules import PowerBasis, ModuleHomomorphism
  1405. >>> T = Poly(cyclotomic_poly(5))
  1406. >>> A = PowerBasis(T)
  1407. >>> B = A.submodule_from_gens([2*A(j) for j in range(4)])
  1408. >>> phi = ModuleHomomorphism(A, B, lambda x: 6*x)
  1409. >>> print(phi.matrix()) # doctest: +SKIP
  1410. DomainMatrix([[3, 0, 0, 0], [0, 3, 0, 0], [0, 0, 3, 0], [0, 0, 0, 3]], (4, 4), ZZ)
  1411. """
  1412. self.domain = domain
  1413. self.codomain = codomain
  1414. self.mapping = mapping
  1415. def matrix(self, modulus=None):
  1416. r"""
  1417. Compute the matrix of this homomorphism.
  1418. Parameters
  1419. ==========
  1420. modulus : int, optional
  1421. A positive prime number $p$ if the matrix should be reduced mod
  1422. $p$.
  1423. Returns
  1424. =======
  1425. :py:class:`~.DomainMatrix`
  1426. The matrix is over :ref:`ZZ`, or else over :ref:`GF(p)` if a
  1427. modulus was given.
  1428. """
  1429. basis = self.domain.basis_elements()
  1430. cols = [self.codomain.represent(self.mapping(elt)) for elt in basis]
  1431. if not cols:
  1432. return DomainMatrix.zeros((self.codomain.n, 0), ZZ).to_dense()
  1433. M = cols[0].hstack(*cols[1:])
  1434. if modulus:
  1435. M = M.convert_to(FF(modulus))
  1436. return M
  1437. def kernel(self, modulus=None):
  1438. r"""
  1439. Compute a Submodule representing the kernel of this homomorphism.
  1440. Parameters
  1441. ==========
  1442. modulus : int, optional
  1443. A positive prime number $p$ if the kernel should be computed mod
  1444. $p$.
  1445. Returns
  1446. =======
  1447. :py:class:`~.Submodule`
  1448. This submodule's generators span the kernel of this
  1449. homomorphism over :ref:`ZZ`, or else over :ref:`GF(p)` if a
  1450. modulus was given.
  1451. """
  1452. M = self.matrix(modulus=modulus)
  1453. if modulus is None:
  1454. M = M.convert_to(QQ)
  1455. # Note: Even when working over a finite field, what we want here is
  1456. # the pullback into the integers, so in this case the conversion to ZZ
  1457. # below is appropriate. When working over ZZ, the kernel should be a
  1458. # ZZ-submodule, so, while the conversion to QQ above was required in
  1459. # order for the nullspace calculation to work, conversion back to ZZ
  1460. # afterward should always work.
  1461. # TODO:
  1462. # Watch <https://github.com/sympy/sympy/issues/21834>, which calls
  1463. # for fraction-free algorithms. If this is implemented, we can skip
  1464. # the conversion to `QQ` above.
  1465. K = M.nullspace().convert_to(ZZ).transpose()
  1466. return self.domain.submodule_from_matrix(K)
  1467. class ModuleEndomorphism(ModuleHomomorphism):
  1468. r"""A homomorphism from one module to itself."""
  1469. def __init__(self, domain, mapping):
  1470. r"""
  1471. Parameters
  1472. ==========
  1473. domain : :py:class:`~.Module`
  1474. The common domain and codomain of the mapping.
  1475. mapping : callable
  1476. An arbitrary callable is accepted, but should be chosen so as
  1477. to represent an actual module endomorphism. In particular, should
  1478. accept and return elements of *domain*.
  1479. """
  1480. super().__init__(domain, domain, mapping)
  1481. class InnerEndomorphism(ModuleEndomorphism):
  1482. r"""
  1483. An inner endomorphism on a module, i.e. the endomorphism corresponding to
  1484. multiplication by a fixed element.
  1485. """
  1486. def __init__(self, domain, multiplier):
  1487. r"""
  1488. Parameters
  1489. ==========
  1490. domain : :py:class:`~.Module`
  1491. The domain and codomain of the endomorphism.
  1492. multiplier : :py:class:`~.ModuleElement`
  1493. The element $a$ defining the mapping as $x \mapsto a x$.
  1494. """
  1495. super().__init__(domain, lambda x: multiplier * x)
  1496. self.multiplier = multiplier
  1497. class EndomorphismRing:
  1498. r"""The ring of endomorphisms on a module."""
  1499. def __init__(self, domain):
  1500. """
  1501. Parameters
  1502. ==========
  1503. domain : :py:class:`~.Module`
  1504. The domain and codomain of the endomorphisms.
  1505. """
  1506. self.domain = domain
  1507. def inner_endomorphism(self, multiplier):
  1508. r"""
  1509. Form an inner endomorphism belonging to this endomorphism ring.
  1510. Parameters
  1511. ==========
  1512. multiplier : :py:class:`~.ModuleElement`
  1513. Element $a$ defining the inner endomorphism $x \mapsto a x$.
  1514. Returns
  1515. =======
  1516. :py:class:`~.InnerEndomorphism`
  1517. """
  1518. return InnerEndomorphism(self.domain, multiplier)
  1519. def represent(self, element):
  1520. r"""
  1521. Represent an element of this endomorphism ring, as a single column
  1522. vector.
  1523. Explanation
  1524. ===========
  1525. Let $M$ be a module, and $E$ its ring of endomorphisms. Let $N$ be
  1526. another module, and consider a homomorphism $\varphi: N \rightarrow E$.
  1527. In the event that $\varphi$ is to be represented by a matrix $A$, each
  1528. column of $A$ must represent an element of $E$. This is possible when
  1529. the elements of $E$ are themselves representable as matrices, by
  1530. stacking the columns of such a matrix into a single column.
  1531. This method supports calculating such matrices $A$, by representing
  1532. an element of this endomorphism ring first as a matrix, and then
  1533. stacking that matrix's columns into a single column.
  1534. Examples
  1535. ========
  1536. Note that in these examples we print matrix transposes, to make their
  1537. columns easier to inspect.
  1538. >>> from sympy import Poly, cyclotomic_poly
  1539. >>> from sympy.polys.numberfields.modules import PowerBasis
  1540. >>> from sympy.polys.numberfields.modules import ModuleHomomorphism
  1541. >>> T = Poly(cyclotomic_poly(5))
  1542. >>> M = PowerBasis(T)
  1543. >>> E = M.endomorphism_ring()
  1544. Let $\zeta$ be a primitive 5th root of unity, a generator of our field,
  1545. and consider the inner endomorphism $\tau$ on the ring of integers,
  1546. induced by $\zeta$:
  1547. >>> zeta = M(1)
  1548. >>> tau = E.inner_endomorphism(zeta)
  1549. >>> tau.matrix().transpose() # doctest: +SKIP
  1550. DomainMatrix(
  1551. [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [-1, -1, -1, -1]],
  1552. (4, 4), ZZ)
  1553. The matrix representation of $\tau$ is as expected. The first column
  1554. shows that multiplying by $\zeta$ carries $1$ to $\zeta$, the second
  1555. column that it carries $\zeta$ to $\zeta^2$, and so forth.
  1556. The ``represent`` method of the endomorphism ring ``E`` stacks these
  1557. into a single column:
  1558. >>> E.represent(tau).transpose() # doctest: +SKIP
  1559. DomainMatrix(
  1560. [[0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1]],
  1561. (1, 16), ZZ)
  1562. This is useful when we want to consider a homomorphism $\varphi$ having
  1563. ``E`` as codomain:
  1564. >>> phi = ModuleHomomorphism(M, E, lambda x: E.inner_endomorphism(x))
  1565. and we want to compute the matrix of such a homomorphism:
  1566. >>> phi.matrix().transpose() # doctest: +SKIP
  1567. DomainMatrix(
  1568. [[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
  1569. [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1],
  1570. [0, 0, 1, 0, 0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0],
  1571. [0, 0, 0, 1, -1, -1, -1, -1, 1, 0, 0, 0, 0, 1, 0, 0]],
  1572. (4, 16), ZZ)
  1573. Note that the stacked matrix of $\tau$ occurs as the second column in
  1574. this example. This is because $\zeta$ is the second basis element of
  1575. ``M``, and $\varphi(\zeta) = \tau$.
  1576. Parameters
  1577. ==========
  1578. element : :py:class:`~.ModuleEndomorphism` belonging to this ring.
  1579. Returns
  1580. =======
  1581. :py:class:`~.DomainMatrix`
  1582. Column vector equalling the vertical stacking of all the columns
  1583. of the matrix that represents the given *element* as a mapping.
  1584. """
  1585. if isinstance(element, ModuleEndomorphism) and element.domain == self.domain:
  1586. M = element.matrix()
  1587. # Transform the matrix into a single column, which should reproduce
  1588. # the original columns, one after another.
  1589. m, n = M.shape
  1590. if n == 0:
  1591. return M
  1592. return M[:, 0].vstack(*[M[:, j] for j in range(1, n)])
  1593. raise NotImplementedError
  1594. def find_min_poly(alpha, domain, x=None, powers=None):
  1595. r"""
  1596. Find a polynomial of least degree (not necessarily irreducible) satisfied
  1597. by an element of a finitely-generated ring with unity.
  1598. Examples
  1599. ========
  1600. For the $n$th cyclotomic field, $n$ an odd prime, consider the quadratic
  1601. equation whose roots are the two periods of length $(n-1)/2$. Article 356
  1602. of Gauss tells us that we should get $x^2 + x - (n-1)/4$ or
  1603. $x^2 + x + (n+1)/4$ according to whether $n$ is 1 or 3 mod 4, respectively.
  1604. >>> from sympy import Poly, cyclotomic_poly, primitive_root, QQ
  1605. >>> from sympy.abc import x
  1606. >>> from sympy.polys.numberfields.modules import PowerBasis, find_min_poly
  1607. >>> n = 13
  1608. >>> g = primitive_root(n)
  1609. >>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))
  1610. >>> ee = [g**(2*k+1) % n for k in range((n-1)//2)]
  1611. >>> eta = sum(C(e) for e in ee)
  1612. >>> print(find_min_poly(eta, QQ, x=x).as_expr())
  1613. x**2 + x - 3
  1614. >>> n = 19
  1615. >>> g = primitive_root(n)
  1616. >>> C = PowerBasis(Poly(cyclotomic_poly(n, x)))
  1617. >>> ee = [g**(2*k+2) % n for k in range((n-1)//2)]
  1618. >>> eta = sum(C(e) for e in ee)
  1619. >>> print(find_min_poly(eta, QQ, x=x).as_expr())
  1620. x**2 + x + 5
  1621. Parameters
  1622. ==========
  1623. alpha : :py:class:`~.ModuleElement`
  1624. The element whose min poly is to be found, and whose module has
  1625. multiplication and starts with unity.
  1626. domain : :py:class:`~.Domain`
  1627. The desired domain of the polynomial.
  1628. x : :py:class:`~.Symbol`, optional
  1629. The desired variable for the polynomial.
  1630. powers : list, optional
  1631. If desired, pass an empty list. The powers of *alpha* (as
  1632. :py:class:`~.ModuleElement` instances) from the zeroth up to the degree
  1633. of the min poly will be recorded here, as we compute them.
  1634. Returns
  1635. =======
  1636. :py:class:`~.Poly`, ``None``
  1637. The minimal polynomial for alpha, or ``None`` if no polynomial could be
  1638. found over the desired domain.
  1639. Raises
  1640. ======
  1641. MissingUnityError
  1642. If the module to which alpha belongs does not start with unity.
  1643. ClosureFailure
  1644. If the module to which alpha belongs is not closed under
  1645. multiplication.
  1646. """
  1647. R = alpha.module
  1648. if not R.starts_with_unity():
  1649. raise MissingUnityError("alpha must belong to finitely generated ring with unity.")
  1650. if powers is None:
  1651. powers = []
  1652. one = R(0)
  1653. powers.append(one)
  1654. powers_matrix = one.column(domain=domain)
  1655. ak = alpha
  1656. m = None
  1657. for k in range(1, R.n + 1):
  1658. powers.append(ak)
  1659. ak_col = ak.column(domain=domain)
  1660. try:
  1661. X = powers_matrix._solve(ak_col)[0]
  1662. except DMBadInputError:
  1663. # This means alpha^k still isn't in the domain-span of the lower powers.
  1664. powers_matrix = powers_matrix.hstack(ak_col)
  1665. ak *= alpha
  1666. else:
  1667. # alpha^k is in the domain-span of the lower powers, so we have found a
  1668. # minimal-degree poly for alpha.
  1669. coeffs = [1] + [-c for c in reversed(X.to_list_flat())]
  1670. x = x or Dummy('x')
  1671. if domain.is_FF:
  1672. m = Poly(coeffs, x, modulus=domain.mod)
  1673. else:
  1674. m = Poly(coeffs, x, domain=domain)
  1675. break
  1676. return m