algebraicfield.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. """Implementation of :class:`AlgebraicField` class. """
  2. from sympy.core.add import Add
  3. from sympy.core.mul import Mul
  4. from sympy.core.singleton import S
  5. from sympy.polys.domains.characteristiczero import CharacteristicZero
  6. from sympy.polys.domains.field import Field
  7. from sympy.polys.domains.simpledomain import SimpleDomain
  8. from sympy.polys.polyclasses import ANP
  9. from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
  10. from sympy.utilities import public
  11. @public
  12. class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
  13. r"""Algebraic number field :ref:`QQ(a)`
  14. A :ref:`QQ(a)` domain represents an `algebraic number field`_
  15. `\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
  16. :ref:`polys-domainsintro`).
  17. A :py:class:`~.Poly` created from an expression involving `algebraic
  18. numbers`_ will treat the algebraic numbers as generators if the generators
  19. argument is not specified.
  20. >>> from sympy import Poly, Symbol, sqrt
  21. >>> x = Symbol('x')
  22. >>> Poly(x**2 + sqrt(2))
  23. Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
  24. That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
  25. generators (variables). If the generators are explicitly specified then
  26. ``sqrt(2)`` will be considered to be a coefficient but by default the
  27. :ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
  28. domain the argument ``extension=True`` can be given.
  29. >>> Poly(x**2 + sqrt(2), x)
  30. Poly(x**2 + sqrt(2), x, domain='EX')
  31. >>> Poly(x**2 + sqrt(2), x, extension=True)
  32. Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
  33. A generator of the algebraic field extension can also be specified
  34. explicitly which is particularly useful if the coefficients are all
  35. rational but an extension field is needed (e.g. to factor the
  36. polynomial).
  37. >>> Poly(x**2 + 1)
  38. Poly(x**2 + 1, x, domain='ZZ')
  39. >>> Poly(x**2 + 1, extension=sqrt(2))
  40. Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
  41. It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
  42. the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
  43. explicitly.
  44. >>> from sympy import factor, QQ
  45. >>> factor(x**2 - 2)
  46. x**2 - 2
  47. >>> factor(x**2 - 2, extension=sqrt(2))
  48. (x - sqrt(2))*(x + sqrt(2))
  49. >>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
  50. (x - sqrt(2))*(x + sqrt(2))
  51. >>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
  52. (x - sqrt(2))*(x + sqrt(2))
  53. The ``extension=True`` argument can be used but will only create an
  54. extension that contains the coefficients which is usually not enough to
  55. factorise the polynomial.
  56. >>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
  57. >>> factor(p) # treats sqrt(2) as a symbol
  58. (x + sqrt(2))*(x**2 - 2)
  59. >>> factor(p, extension=True)
  60. (x - sqrt(2))*(x + sqrt(2))**2
  61. >>> factor(x**2 - 2, extension=True) # all rational coefficients
  62. x**2 - 2
  63. It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
  64. and :py:func:`~.gcd` functions.
  65. >>> from sympy import cancel, gcd
  66. >>> cancel((x**2 - 2)/(x - sqrt(2)))
  67. (x**2 - 2)/(x - sqrt(2))
  68. >>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
  69. x + sqrt(2)
  70. >>> gcd(x**2 - 2, x - sqrt(2))
  71. 1
  72. >>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
  73. x - sqrt(2)
  74. When using the domain directly :ref:`QQ(a)` can be used as a constructor
  75. to create instances which then support the operations ``+,-,*,**,/``. The
  76. :py:meth:`~.Domain.algebraic_field` method is used to construct a
  77. particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
  78. can be used to create domain elements from normal SymPy expressions.
  79. >>> K = QQ.algebraic_field(sqrt(2))
  80. >>> K
  81. QQ<sqrt(2)>
  82. >>> xk = K.from_sympy(3 + 4*sqrt(2))
  83. >>> xk # doctest: +SKIP
  84. ANP([4, 3], [1, 0, -2], QQ)
  85. Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
  86. limited printing support. The raw display shows the internal
  87. representation of the element as the list ``[4, 3]`` representing the
  88. coefficients of ``1`` and ``sqrt(2)`` for this element in the form
  89. ``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
  90. The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
  91. the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
  92. :py:meth:`~.Domain.to_sympy` to get a better printed form for the
  93. elements and to see the results of operations.
  94. >>> xk = K.from_sympy(3 + 4*sqrt(2))
  95. >>> yk = K.from_sympy(2 + 3*sqrt(2))
  96. >>> xk * yk # doctest: +SKIP
  97. ANP([17, 30], [1, 0, -2], QQ)
  98. >>> K.to_sympy(xk * yk)
  99. 17*sqrt(2) + 30
  100. >>> K.to_sympy(xk + yk)
  101. 5 + 7*sqrt(2)
  102. >>> K.to_sympy(xk ** 2)
  103. 24*sqrt(2) + 41
  104. >>> K.to_sympy(xk / yk)
  105. sqrt(2)/14 + 9/7
  106. Any expression representing an algebraic number can be used to generate
  107. a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
  108. The function :py:func:`~.minpoly` function is used for this.
  109. >>> from sympy import exp, I, pi, minpoly
  110. >>> g = exp(2*I*pi/3)
  111. >>> g
  112. exp(2*I*pi/3)
  113. >>> g.is_algebraic
  114. True
  115. >>> minpoly(g, x)
  116. x**2 + x + 1
  117. >>> factor(x**3 - 1, extension=g)
  118. (x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
  119. It is also possible to make an algebraic field from multiple extension
  120. elements.
  121. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  122. >>> K
  123. QQ<sqrt(2) + sqrt(3)>
  124. >>> p = x**4 - 5*x**2 + 6
  125. >>> factor(p)
  126. (x**2 - 3)*(x**2 - 2)
  127. >>> factor(p, domain=K)
  128. (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
  129. >>> factor(p, extension=[sqrt(2), sqrt(3)])
  130. (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
  131. Multiple extension elements are always combined together to make a single
  132. `primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
  133. element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
  134. as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
  135. element is computed using the :py:func:`~.primitive_element` function.
  136. >>> from sympy import primitive_element
  137. >>> primitive_element([sqrt(2), sqrt(3)], x)
  138. (x**4 - 10*x**2 + 1, [1, 1])
  139. >>> minpoly(sqrt(2) + sqrt(3), x)
  140. x**4 - 10*x**2 + 1
  141. The extension elements that generate the domain can be accessed from the
  142. domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
  143. instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
  144. the primitive element as a :py:class:`~.DMP` instance is available as
  145. :py:attr:`~.mod`.
  146. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  147. >>> K
  148. QQ<sqrt(2) + sqrt(3)>
  149. >>> K.ext
  150. sqrt(2) + sqrt(3)
  151. >>> K.orig_ext
  152. (sqrt(2), sqrt(3))
  153. >>> K.mod
  154. DMP([1, 0, -10, 0, 1], QQ, None)
  155. The `discriminant`_ of the field can be obtained from the
  156. :py:meth:`~.discriminant` method, and an `integral basis`_ from the
  157. :py:meth:`~.integral_basis` method. The latter returns a list of
  158. :py:class:`~.ANP` instances by default, but can be made to return instances
  159. of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
  160. argument. The maximal order, or ring of integers, of the field can also be
  161. obtained from the :py:meth:`~.maximal_order` method, as a
  162. :py:class:`~sympy.polys.numberfields.modules.Submodule`.
  163. >>> zeta5 = exp(2*I*pi/5)
  164. >>> K = QQ.algebraic_field(zeta5)
  165. >>> K
  166. QQ<exp(2*I*pi/5)>
  167. >>> K.discriminant()
  168. 125
  169. >>> K = QQ.algebraic_field(sqrt(5))
  170. >>> K
  171. QQ<sqrt(5)>
  172. >>> K.integral_basis(fmt='sympy')
  173. [1, 1/2 + sqrt(5)/2]
  174. >>> K.maximal_order()
  175. Submodule[[2, 0], [1, 1]]/2
  176. The factorization of a rational prime into prime ideals of the field is
  177. computed by the :py:meth:`~.primes_above` method, which returns a list
  178. of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
  179. >>> zeta7 = exp(2*I*pi/7)
  180. >>> K = QQ.algebraic_field(zeta7)
  181. >>> K
  182. QQ<exp(2*I*pi/7)>
  183. >>> K.primes_above(11)
  184. [[ (11, _x**3 + 5*_x**2 + 4*_x - 1) e=1, f=3 ],
  185. [ (11, _x**3 - 4*_x**2 - 5*_x - 1) e=1, f=3 ]]
  186. Notes
  187. =====
  188. It is not currently possible to generate an algebraic extension over any
  189. domain other than :ref:`QQ`. Ideally it would be possible to generate
  190. extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
  191. quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
  192. implementations of this kind of quotient ring/extension in the
  193. :py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
  194. classes. Each of those implementations needs some work to make them fully
  195. usable though.
  196. .. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
  197. .. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
  198. .. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
  199. .. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
  200. .. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
  201. .. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
  202. """
  203. dtype = ANP
  204. is_AlgebraicField = is_Algebraic = True
  205. is_Numerical = True
  206. has_assoc_Ring = False
  207. has_assoc_Field = True
  208. def __init__(self, dom, *ext):
  209. if not dom.is_QQ:
  210. raise DomainError("ground domain must be a rational field")
  211. from sympy.polys.numberfields import to_number_field
  212. if len(ext) == 1 and isinstance(ext[0], tuple):
  213. orig_ext = ext[0][1:]
  214. else:
  215. orig_ext = ext
  216. self.orig_ext = orig_ext
  217. """
  218. Original elements given to generate the extension.
  219. >>> from sympy import QQ, sqrt
  220. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  221. >>> K.orig_ext
  222. (sqrt(2), sqrt(3))
  223. """
  224. self.ext = to_number_field(ext)
  225. """
  226. Primitive element used for the extension.
  227. >>> from sympy import QQ, sqrt
  228. >>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
  229. >>> K.ext
  230. sqrt(2) + sqrt(3)
  231. """
  232. self.mod = self.ext.minpoly.rep
  233. """
  234. Minimal polynomial for the primitive element of the extension.
  235. >>> from sympy import QQ, sqrt
  236. >>> K = QQ.algebraic_field(sqrt(2))
  237. >>> K.mod
  238. DMP([1, 0, -2], QQ, None)
  239. """
  240. self.domain = self.dom = dom
  241. self.ngens = 1
  242. self.symbols = self.gens = (self.ext,)
  243. self.unit = self([dom(1), dom(0)])
  244. self.zero = self.dtype.zero(self.mod.rep, dom)
  245. self.one = self.dtype.one(self.mod.rep, dom)
  246. self._maximal_order = None
  247. self._discriminant = None
  248. self._nilradicals_mod_p = {}
  249. def new(self, element):
  250. return self.dtype(element, self.mod.rep, self.dom)
  251. def __str__(self):
  252. return str(self.dom) + '<' + str(self.ext) + '>'
  253. def __hash__(self):
  254. return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
  255. def __eq__(self, other):
  256. """Returns ``True`` if two domains are equivalent. """
  257. return isinstance(other, AlgebraicField) and \
  258. self.dtype == other.dtype and self.ext == other.ext
  259. def algebraic_field(self, *extension):
  260. r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
  261. return AlgebraicField(self.dom, *((self.ext,) + extension))
  262. def to_alg_num(self, a):
  263. """Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
  264. theta = self.ext
  265. # `self.ext.root` may be an `AlgebraicNumber`, in which case we should
  266. # use it instead of `self.ext`, in case it has an `alias`.
  267. if hasattr(theta.root, "field_element"):
  268. theta = theta.root
  269. return theta.field_element(a)
  270. def to_sympy(self, a):
  271. """Convert ``a`` of ``dtype`` to a SymPy object. """
  272. # Precompute a converter to be reused:
  273. if not hasattr(self, '_converter'):
  274. self._converter = _make_converter(self)
  275. return self._converter(a)
  276. def from_sympy(self, a):
  277. """Convert SymPy's expression to ``dtype``. """
  278. try:
  279. return self([self.dom.from_sympy(a)])
  280. except CoercionFailed:
  281. pass
  282. from sympy.polys.numberfields import to_number_field
  283. try:
  284. return self(to_number_field(a, self.ext).native_coeffs())
  285. except (NotAlgebraic, IsomorphismFailed):
  286. raise CoercionFailed(
  287. "%s is not a valid algebraic number in %s" % (a, self))
  288. def from_ZZ(K1, a, K0):
  289. """Convert a Python ``int`` object to ``dtype``. """
  290. return K1(K1.dom.convert(a, K0))
  291. def from_ZZ_python(K1, a, K0):
  292. """Convert a Python ``int`` object to ``dtype``. """
  293. return K1(K1.dom.convert(a, K0))
  294. def from_QQ(K1, a, K0):
  295. """Convert a Python ``Fraction`` object to ``dtype``. """
  296. return K1(K1.dom.convert(a, K0))
  297. def from_QQ_python(K1, a, K0):
  298. """Convert a Python ``Fraction`` object to ``dtype``. """
  299. return K1(K1.dom.convert(a, K0))
  300. def from_ZZ_gmpy(K1, a, K0):
  301. """Convert a GMPY ``mpz`` object to ``dtype``. """
  302. return K1(K1.dom.convert(a, K0))
  303. def from_QQ_gmpy(K1, a, K0):
  304. """Convert a GMPY ``mpq`` object to ``dtype``. """
  305. return K1(K1.dom.convert(a, K0))
  306. def from_RealField(K1, a, K0):
  307. """Convert a mpmath ``mpf`` object to ``dtype``. """
  308. return K1(K1.dom.convert(a, K0))
  309. def get_ring(self):
  310. """Returns a ring associated with ``self``. """
  311. raise DomainError('there is no ring associated with %s' % self)
  312. def is_positive(self, a):
  313. """Returns True if ``a`` is positive. """
  314. return self.dom.is_positive(a.LC())
  315. def is_negative(self, a):
  316. """Returns True if ``a`` is negative. """
  317. return self.dom.is_negative(a.LC())
  318. def is_nonpositive(self, a):
  319. """Returns True if ``a`` is non-positive. """
  320. return self.dom.is_nonpositive(a.LC())
  321. def is_nonnegative(self, a):
  322. """Returns True if ``a`` is non-negative. """
  323. return self.dom.is_nonnegative(a.LC())
  324. def numer(self, a):
  325. """Returns numerator of ``a``. """
  326. return a
  327. def denom(self, a):
  328. """Returns denominator of ``a``. """
  329. return self.one
  330. def from_AlgebraicField(K1, a, K0):
  331. """Convert AlgebraicField element 'a' to another AlgebraicField """
  332. return K1.from_sympy(K0.to_sympy(a))
  333. def from_GaussianIntegerRing(K1, a, K0):
  334. """Convert a GaussianInteger element 'a' to ``dtype``. """
  335. return K1.from_sympy(K0.to_sympy(a))
  336. def from_GaussianRationalField(K1, a, K0):
  337. """Convert a GaussianRational element 'a' to ``dtype``. """
  338. return K1.from_sympy(K0.to_sympy(a))
  339. def _do_round_two(self):
  340. from sympy.polys.numberfields.basis import round_two
  341. ZK, dK = round_two(self.ext.minpoly, radicals=self._nilradicals_mod_p)
  342. self._maximal_order = ZK
  343. self._discriminant = dK
  344. def maximal_order(self):
  345. """
  346. Compute the maximal order, or ring of integers, of the field.
  347. Returns
  348. =======
  349. :py:class:`~sympy.polys.numberfields.modules.Submodule`.
  350. See Also
  351. ========
  352. integral_basis()
  353. """
  354. if self._maximal_order is None:
  355. self._do_round_two()
  356. return self._maximal_order
  357. def integral_basis(self, fmt=None):
  358. r"""
  359. Get an integral basis for the field.
  360. Parameters
  361. ==========
  362. fmt : str, None, optional (default=None)
  363. If ``None``, return a list of :py:class:`~.ANP` instances.
  364. If ``"sympy"``, convert each element of the list to an
  365. :py:class:`~.Expr`, using ``self.to_sympy()``.
  366. If ``"alg"``, convert each element of the list to an
  367. :py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
  368. Examples
  369. ========
  370. >>> from sympy import QQ, AlgebraicNumber, sqrt
  371. >>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
  372. >>> k = QQ.algebraic_field(alpha)
  373. >>> B0 = k.integral_basis()
  374. >>> B1 = k.integral_basis(fmt='sympy')
  375. >>> B2 = k.integral_basis(fmt='alg')
  376. >>> print(B0[1]) # doctest: +SKIP
  377. ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
  378. >>> print(B1[1])
  379. 1/2 + alpha/2
  380. >>> print(B2[1])
  381. alpha/2 + 1/2
  382. In the last two cases we get legible expressions, which print somewhat
  383. differently because of the different types involved:
  384. >>> print(type(B1[1]))
  385. <class 'sympy.core.add.Add'>
  386. >>> print(type(B2[1]))
  387. <class 'sympy.core.numbers.AlgebraicNumber'>
  388. See Also
  389. ========
  390. to_sympy()
  391. to_alg_num()
  392. maximal_order()
  393. """
  394. ZK = self.maximal_order()
  395. M = ZK.QQ_matrix
  396. n = M.shape[1]
  397. B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
  398. if fmt == 'sympy':
  399. return [self.to_sympy(b) for b in B]
  400. elif fmt == 'alg':
  401. return [self.to_alg_num(b) for b in B]
  402. return B
  403. def discriminant(self):
  404. """Get the discriminant of the field."""
  405. if self._discriminant is None:
  406. self._do_round_two()
  407. return self._discriminant
  408. def primes_above(self, p):
  409. """Compute the prime ideals lying above a given rational prime *p*."""
  410. from sympy.polys.numberfields.primes import prime_decomp
  411. ZK = self.maximal_order()
  412. dK = self.discriminant()
  413. rad = self._nilradicals_mod_p.get(p)
  414. return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
  415. def _make_converter(K):
  416. """Construct the converter to convert back to Expr"""
  417. # Precompute the effect of converting to SymPy and expanding expressions
  418. # like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
  419. # conversion from K to Expr is slow. Here we compute the expansions for
  420. # each power of the generator and collect together the resulting algebraic
  421. # terms and the rational coefficients into a matrix.
  422. gen = K.ext.as_expr()
  423. todom = K.dom.from_sympy
  424. # We'll let Expr compute the expansions. We won't make any presumptions
  425. # about what this results in except that it is QQ-linear in some terms
  426. # that we will call algebraics. The final result will be expressed in
  427. # terms of those.
  428. powers = [S.One, gen]
  429. for n in range(2, K.mod.degree()):
  430. powers.append((gen * powers[-1]).expand())
  431. # Collect the rational coefficients and algebraic Expr that can
  432. # map the ANP coefficients into an expanded SymPy expression
  433. terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
  434. algebraics = set().union(*terms)
  435. matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
  436. # Create a function to do the conversion efficiently:
  437. def converter(a):
  438. """Convert a to Expr using converter"""
  439. ai = a.rep[::-1]
  440. tosympy = K.dom.to_sympy
  441. coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
  442. coeffs_sympy = [tosympy(c) for c in coeffs_dom]
  443. res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
  444. return res
  445. return converter