domainmatrix.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791
  1. """
  2. Module for the DomainMatrix class.
  3. A DomainMatrix represents a matrix with elements that are in a particular
  4. Domain. Each DomainMatrix internally wraps a DDM which is used for the
  5. lower-level operations. The idea is that the DomainMatrix class provides the
  6. convenience routines for converting between Expr and the poly domains as well
  7. as unifying matrices with different domains.
  8. """
  9. from functools import reduce
  10. from typing import Union as tUnion, Tuple as tTuple
  11. from sympy.core.sympify import _sympify
  12. from ..domains import Domain
  13. from ..constructor import construct_domain
  14. from .exceptions import (DMNonSquareMatrixError, DMShapeError,
  15. DMDomainError, DMFormatError, DMBadInputError,
  16. DMNotAField)
  17. from .ddm import DDM
  18. from .sdm import SDM
  19. from .domainscalar import DomainScalar
  20. from sympy.polys.domains import ZZ, EXRAW, QQ
  21. def DM(rows, domain):
  22. """Convenient alias for DomainMatrix.from_list
  23. Examples
  24. =======
  25. >>> from sympy import ZZ
  26. >>> from sympy.polys.matrices import DM
  27. >>> DM([[1, 2], [3, 4]], ZZ)
  28. DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
  29. See also
  30. =======
  31. DomainMatrix.from_list
  32. """
  33. return DomainMatrix.from_list(rows, domain)
  34. class DomainMatrix:
  35. r"""
  36. Associate Matrix with :py:class:`~.Domain`
  37. Explanation
  38. ===========
  39. DomainMatrix uses :py:class:`~.Domain` for its internal representation
  40. which makes it faster than the SymPy Matrix class (currently) for many
  41. common operations, but this advantage makes it not entirely compatible
  42. with Matrix. DomainMatrix are analogous to numpy arrays with "dtype".
  43. In the DomainMatrix, each element has a domain such as :ref:`ZZ`
  44. or :ref:`QQ(a)`.
  45. Examples
  46. ========
  47. Creating a DomainMatrix from the existing Matrix class:
  48. >>> from sympy import Matrix
  49. >>> from sympy.polys.matrices import DomainMatrix
  50. >>> Matrix1 = Matrix([
  51. ... [1, 2],
  52. ... [3, 4]])
  53. >>> A = DomainMatrix.from_Matrix(Matrix1)
  54. >>> A
  55. DomainMatrix({0: {0: 1, 1: 2}, 1: {0: 3, 1: 4}}, (2, 2), ZZ)
  56. Directly forming a DomainMatrix:
  57. >>> from sympy import ZZ
  58. >>> from sympy.polys.matrices import DomainMatrix
  59. >>> A = DomainMatrix([
  60. ... [ZZ(1), ZZ(2)],
  61. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  62. >>> A
  63. DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
  64. See Also
  65. ========
  66. DDM
  67. SDM
  68. Domain
  69. Poly
  70. """
  71. rep: tUnion[SDM, DDM]
  72. shape: tTuple[int, int]
  73. domain: Domain
  74. def __new__(cls, rows, shape, domain, *, fmt=None):
  75. """
  76. Creates a :py:class:`~.DomainMatrix`.
  77. Parameters
  78. ==========
  79. rows : Represents elements of DomainMatrix as list of lists
  80. shape : Represents dimension of DomainMatrix
  81. domain : Represents :py:class:`~.Domain` of DomainMatrix
  82. Raises
  83. ======
  84. TypeError
  85. If any of rows, shape and domain are not provided
  86. """
  87. if isinstance(rows, (DDM, SDM)):
  88. raise TypeError("Use from_rep to initialise from SDM/DDM")
  89. elif isinstance(rows, list):
  90. rep = DDM(rows, shape, domain)
  91. elif isinstance(rows, dict):
  92. rep = SDM(rows, shape, domain)
  93. else:
  94. msg = "Input should be list-of-lists or dict-of-dicts"
  95. raise TypeError(msg)
  96. if fmt is not None:
  97. if fmt == 'sparse':
  98. rep = rep.to_sdm()
  99. elif fmt == 'dense':
  100. rep = rep.to_ddm()
  101. else:
  102. raise ValueError("fmt should be 'sparse' or 'dense'")
  103. return cls.from_rep(rep)
  104. def __getnewargs__(self):
  105. rep = self.rep
  106. if isinstance(rep, DDM):
  107. arg = list(rep)
  108. elif isinstance(rep, SDM):
  109. arg = dict(rep)
  110. else:
  111. raise RuntimeError # pragma: no cover
  112. return arg, self.shape, self.domain
  113. def __getitem__(self, key):
  114. i, j = key
  115. m, n = self.shape
  116. if not (isinstance(i, slice) or isinstance(j, slice)):
  117. return DomainScalar(self.rep.getitem(i, j), self.domain)
  118. if not isinstance(i, slice):
  119. if not -m <= i < m:
  120. raise IndexError("Row index out of range")
  121. i = i % m
  122. i = slice(i, i+1)
  123. if not isinstance(j, slice):
  124. if not -n <= j < n:
  125. raise IndexError("Column index out of range")
  126. j = j % n
  127. j = slice(j, j+1)
  128. return self.from_rep(self.rep.extract_slice(i, j))
  129. def getitem_sympy(self, i, j):
  130. return self.domain.to_sympy(self.rep.getitem(i, j))
  131. def extract(self, rowslist, colslist):
  132. return self.from_rep(self.rep.extract(rowslist, colslist))
  133. def __setitem__(self, key, value):
  134. i, j = key
  135. if not self.domain.of_type(value):
  136. raise TypeError
  137. if isinstance(i, int) and isinstance(j, int):
  138. self.rep.setitem(i, j, value)
  139. else:
  140. raise NotImplementedError
  141. @classmethod
  142. def from_rep(cls, rep):
  143. """Create a new DomainMatrix efficiently from DDM/SDM.
  144. Examples
  145. ========
  146. Create a :py:class:`~.DomainMatrix` with an dense internal
  147. representation as :py:class:`~.DDM`:
  148. >>> from sympy.polys.domains import ZZ
  149. >>> from sympy.polys.matrices import DomainMatrix
  150. >>> from sympy.polys.matrices.ddm import DDM
  151. >>> drep = DDM([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  152. >>> dM = DomainMatrix.from_rep(drep)
  153. >>> dM
  154. DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ)
  155. Create a :py:class:`~.DomainMatrix` with a sparse internal
  156. representation as :py:class:`~.SDM`:
  157. >>> from sympy.polys.matrices import DomainMatrix
  158. >>> from sympy.polys.matrices.sdm import SDM
  159. >>> from sympy import ZZ
  160. >>> drep = SDM({0:{1:ZZ(1)},1:{0:ZZ(2)}}, (2, 2), ZZ)
  161. >>> dM = DomainMatrix.from_rep(drep)
  162. >>> dM
  163. DomainMatrix({0: {1: 1}, 1: {0: 2}}, (2, 2), ZZ)
  164. Parameters
  165. ==========
  166. rep: SDM or DDM
  167. The internal sparse or dense representation of the matrix.
  168. Returns
  169. =======
  170. DomainMatrix
  171. A :py:class:`~.DomainMatrix` wrapping *rep*.
  172. Notes
  173. =====
  174. This takes ownership of rep as its internal representation. If rep is
  175. being mutated elsewhere then a copy should be provided to
  176. ``from_rep``. Only minimal verification or checking is done on *rep*
  177. as this is supposed to be an efficient internal routine.
  178. """
  179. if not isinstance(rep, (DDM, SDM)):
  180. raise TypeError("rep should be of type DDM or SDM")
  181. self = super().__new__(cls)
  182. self.rep = rep
  183. self.shape = rep.shape
  184. self.domain = rep.domain
  185. return self
  186. @classmethod
  187. def from_list(cls, rows, domain):
  188. r"""
  189. Convert a list of lists into a DomainMatrix
  190. Parameters
  191. ==========
  192. rows: list of lists
  193. Each element of the inner lists should be either the single arg,
  194. or tuple of args, that would be passed to the domain constructor
  195. in order to form an element of the domain. See examples.
  196. Returns
  197. =======
  198. DomainMatrix containing elements defined in rows
  199. Examples
  200. ========
  201. >>> from sympy.polys.matrices import DomainMatrix
  202. >>> from sympy import FF, QQ, ZZ
  203. >>> A = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], ZZ)
  204. >>> A
  205. DomainMatrix([[1, 0, 1], [0, 0, 1]], (2, 3), ZZ)
  206. >>> B = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], FF(7))
  207. >>> B
  208. DomainMatrix([[1 mod 7, 0 mod 7, 1 mod 7], [0 mod 7, 0 mod 7, 1 mod 7]], (2, 3), GF(7))
  209. >>> C = DomainMatrix.from_list([[(1, 2), (3, 1)], [(1, 4), (5, 1)]], QQ)
  210. >>> C
  211. DomainMatrix([[1/2, 3], [1/4, 5]], (2, 2), QQ)
  212. See Also
  213. ========
  214. from_list_sympy
  215. """
  216. nrows = len(rows)
  217. ncols = 0 if not nrows else len(rows[0])
  218. conv = lambda e: domain(*e) if isinstance(e, tuple) else domain(e)
  219. domain_rows = [[conv(e) for e in row] for row in rows]
  220. return DomainMatrix(domain_rows, (nrows, ncols), domain)
  221. @classmethod
  222. def from_list_sympy(cls, nrows, ncols, rows, **kwargs):
  223. r"""
  224. Convert a list of lists of Expr into a DomainMatrix using construct_domain
  225. Parameters
  226. ==========
  227. nrows: number of rows
  228. ncols: number of columns
  229. rows: list of lists
  230. Returns
  231. =======
  232. DomainMatrix containing elements of rows
  233. Examples
  234. ========
  235. >>> from sympy.polys.matrices import DomainMatrix
  236. >>> from sympy.abc import x, y, z
  237. >>> A = DomainMatrix.from_list_sympy(1, 3, [[x, y, z]])
  238. >>> A
  239. DomainMatrix([[x, y, z]], (1, 3), ZZ[x,y,z])
  240. See Also
  241. ========
  242. sympy.polys.constructor.construct_domain, from_dict_sympy
  243. """
  244. assert len(rows) == nrows
  245. assert all(len(row) == ncols for row in rows)
  246. items_sympy = [_sympify(item) for row in rows for item in row]
  247. domain, items_domain = cls.get_domain(items_sympy, **kwargs)
  248. domain_rows = [[items_domain[ncols*r + c] for c in range(ncols)] for r in range(nrows)]
  249. return DomainMatrix(domain_rows, (nrows, ncols), domain)
  250. @classmethod
  251. def from_dict_sympy(cls, nrows, ncols, elemsdict, **kwargs):
  252. """
  253. Parameters
  254. ==========
  255. nrows: number of rows
  256. ncols: number of cols
  257. elemsdict: dict of dicts containing non-zero elements of the DomainMatrix
  258. Returns
  259. =======
  260. DomainMatrix containing elements of elemsdict
  261. Examples
  262. ========
  263. >>> from sympy.polys.matrices import DomainMatrix
  264. >>> from sympy.abc import x,y,z
  265. >>> elemsdict = {0: {0:x}, 1:{1: y}, 2: {2: z}}
  266. >>> A = DomainMatrix.from_dict_sympy(3, 3, elemsdict)
  267. >>> A
  268. DomainMatrix({0: {0: x}, 1: {1: y}, 2: {2: z}}, (3, 3), ZZ[x,y,z])
  269. See Also
  270. ========
  271. from_list_sympy
  272. """
  273. if not all(0 <= r < nrows for r in elemsdict):
  274. raise DMBadInputError("Row out of range")
  275. if not all(0 <= c < ncols for row in elemsdict.values() for c in row):
  276. raise DMBadInputError("Column out of range")
  277. items_sympy = [_sympify(item) for row in elemsdict.values() for item in row.values()]
  278. domain, items_domain = cls.get_domain(items_sympy, **kwargs)
  279. idx = 0
  280. items_dict = {}
  281. for i, row in elemsdict.items():
  282. items_dict[i] = {}
  283. for j in row:
  284. items_dict[i][j] = items_domain[idx]
  285. idx += 1
  286. return DomainMatrix(items_dict, (nrows, ncols), domain)
  287. @classmethod
  288. def from_Matrix(cls, M, fmt='sparse',**kwargs):
  289. r"""
  290. Convert Matrix to DomainMatrix
  291. Parameters
  292. ==========
  293. M: Matrix
  294. Returns
  295. =======
  296. Returns DomainMatrix with identical elements as M
  297. Examples
  298. ========
  299. >>> from sympy import Matrix
  300. >>> from sympy.polys.matrices import DomainMatrix
  301. >>> M = Matrix([
  302. ... [1.0, 3.4],
  303. ... [2.4, 1]])
  304. >>> A = DomainMatrix.from_Matrix(M)
  305. >>> A
  306. DomainMatrix({0: {0: 1.0, 1: 3.4}, 1: {0: 2.4, 1: 1.0}}, (2, 2), RR)
  307. We can keep internal representation as ddm using fmt='dense'
  308. >>> from sympy import Matrix, QQ
  309. >>> from sympy.polys.matrices import DomainMatrix
  310. >>> A = DomainMatrix.from_Matrix(Matrix([[QQ(1, 2), QQ(3, 4)], [QQ(0, 1), QQ(0, 1)]]), fmt='dense')
  311. >>> A.rep
  312. [[1/2, 3/4], [0, 0]]
  313. See Also
  314. ========
  315. Matrix
  316. """
  317. if fmt == 'dense':
  318. return cls.from_list_sympy(*M.shape, M.tolist(), **kwargs)
  319. return cls.from_dict_sympy(*M.shape, M.todod(), **kwargs)
  320. @classmethod
  321. def get_domain(cls, items_sympy, **kwargs):
  322. K, items_K = construct_domain(items_sympy, **kwargs)
  323. return K, items_K
  324. def copy(self):
  325. return self.from_rep(self.rep.copy())
  326. def convert_to(self, K):
  327. r"""
  328. Change the domain of DomainMatrix to desired domain or field
  329. Parameters
  330. ==========
  331. K : Represents the desired domain or field.
  332. Alternatively, ``None`` may be passed, in which case this method
  333. just returns a copy of this DomainMatrix.
  334. Returns
  335. =======
  336. DomainMatrix
  337. DomainMatrix with the desired domain or field
  338. Examples
  339. ========
  340. >>> from sympy import ZZ, ZZ_I
  341. >>> from sympy.polys.matrices import DomainMatrix
  342. >>> A = DomainMatrix([
  343. ... [ZZ(1), ZZ(2)],
  344. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  345. >>> A.convert_to(ZZ_I)
  346. DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ_I)
  347. """
  348. if K is None:
  349. return self.copy()
  350. return self.from_rep(self.rep.convert_to(K))
  351. def to_sympy(self):
  352. return self.convert_to(EXRAW)
  353. def to_field(self):
  354. r"""
  355. Returns a DomainMatrix with the appropriate field
  356. Returns
  357. =======
  358. DomainMatrix
  359. DomainMatrix with the appropriate field
  360. Examples
  361. ========
  362. >>> from sympy import ZZ
  363. >>> from sympy.polys.matrices import DomainMatrix
  364. >>> A = DomainMatrix([
  365. ... [ZZ(1), ZZ(2)],
  366. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  367. >>> A.to_field()
  368. DomainMatrix([[1, 2], [3, 4]], (2, 2), QQ)
  369. """
  370. K = self.domain.get_field()
  371. return self.convert_to(K)
  372. def to_sparse(self):
  373. """
  374. Return a sparse DomainMatrix representation of *self*.
  375. Examples
  376. ========
  377. >>> from sympy.polys.matrices import DomainMatrix
  378. >>> from sympy import QQ
  379. >>> A = DomainMatrix([[1, 0],[0, 2]], (2, 2), QQ)
  380. >>> A.rep
  381. [[1, 0], [0, 2]]
  382. >>> B = A.to_sparse()
  383. >>> B.rep
  384. {0: {0: 1}, 1: {1: 2}}
  385. """
  386. if self.rep.fmt == 'sparse':
  387. return self
  388. return self.from_rep(SDM.from_ddm(self.rep))
  389. def to_dense(self):
  390. """
  391. Return a dense DomainMatrix representation of *self*.
  392. Examples
  393. ========
  394. >>> from sympy.polys.matrices import DomainMatrix
  395. >>> from sympy import QQ
  396. >>> A = DomainMatrix({0: {0: 1}, 1: {1: 2}}, (2, 2), QQ)
  397. >>> A.rep
  398. {0: {0: 1}, 1: {1: 2}}
  399. >>> B = A.to_dense()
  400. >>> B.rep
  401. [[1, 0], [0, 2]]
  402. """
  403. if self.rep.fmt == 'dense':
  404. return self
  405. return self.from_rep(SDM.to_ddm(self.rep))
  406. @classmethod
  407. def _unify_domain(cls, *matrices):
  408. """Convert matrices to a common domain"""
  409. domains = {matrix.domain for matrix in matrices}
  410. if len(domains) == 1:
  411. return matrices
  412. domain = reduce(lambda x, y: x.unify(y), domains)
  413. return tuple(matrix.convert_to(domain) for matrix in matrices)
  414. @classmethod
  415. def _unify_fmt(cls, *matrices, fmt=None):
  416. """Convert matrices to the same format.
  417. If all matrices have the same format, then return unmodified.
  418. Otherwise convert both to the preferred format given as *fmt* which
  419. should be 'dense' or 'sparse'.
  420. """
  421. formats = {matrix.rep.fmt for matrix in matrices}
  422. if len(formats) == 1:
  423. return matrices
  424. if fmt == 'sparse':
  425. return tuple(matrix.to_sparse() for matrix in matrices)
  426. elif fmt == 'dense':
  427. return tuple(matrix.to_dense() for matrix in matrices)
  428. else:
  429. raise ValueError("fmt should be 'sparse' or 'dense'")
  430. def unify(self, *others, fmt=None):
  431. """
  432. Unifies the domains and the format of self and other
  433. matrices.
  434. Parameters
  435. ==========
  436. others : DomainMatrix
  437. fmt: string 'dense', 'sparse' or `None` (default)
  438. The preferred format to convert to if self and other are not
  439. already in the same format. If `None` or not specified then no
  440. conversion if performed.
  441. Returns
  442. =======
  443. Tuple[DomainMatrix]
  444. Matrices with unified domain and format
  445. Examples
  446. ========
  447. Unify the domain of DomainMatrix that have different domains:
  448. >>> from sympy import ZZ, QQ
  449. >>> from sympy.polys.matrices import DomainMatrix
  450. >>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
  451. >>> B = DomainMatrix([[QQ(1, 2), QQ(2)]], (1, 2), QQ)
  452. >>> Aq, Bq = A.unify(B)
  453. >>> Aq
  454. DomainMatrix([[1, 2]], (1, 2), QQ)
  455. >>> Bq
  456. DomainMatrix([[1/2, 2]], (1, 2), QQ)
  457. Unify the format (dense or sparse):
  458. >>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ)
  459. >>> B = DomainMatrix({0:{0: ZZ(1)}}, (2, 2), ZZ)
  460. >>> B.rep
  461. {0: {0: 1}}
  462. >>> A2, B2 = A.unify(B, fmt='dense')
  463. >>> B2.rep
  464. [[1, 0], [0, 0]]
  465. See Also
  466. ========
  467. convert_to, to_dense, to_sparse
  468. """
  469. matrices = (self,) + others
  470. matrices = DomainMatrix._unify_domain(*matrices)
  471. if fmt is not None:
  472. matrices = DomainMatrix._unify_fmt(*matrices, fmt=fmt)
  473. return matrices
  474. def to_Matrix(self):
  475. r"""
  476. Convert DomainMatrix to Matrix
  477. Returns
  478. =======
  479. Matrix
  480. MutableDenseMatrix for the DomainMatrix
  481. Examples
  482. ========
  483. >>> from sympy import ZZ
  484. >>> from sympy.polys.matrices import DomainMatrix
  485. >>> A = DomainMatrix([
  486. ... [ZZ(1), ZZ(2)],
  487. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  488. >>> A.to_Matrix()
  489. Matrix([
  490. [1, 2],
  491. [3, 4]])
  492. See Also
  493. ========
  494. from_Matrix
  495. """
  496. from sympy.matrices.dense import MutableDenseMatrix
  497. elemlist = self.rep.to_list()
  498. elements_sympy = [self.domain.to_sympy(e) for row in elemlist for e in row]
  499. return MutableDenseMatrix(*self.shape, elements_sympy)
  500. def to_list(self):
  501. return self.rep.to_list()
  502. def to_list_flat(self):
  503. return self.rep.to_list_flat()
  504. def to_dok(self):
  505. return self.rep.to_dok()
  506. def __repr__(self):
  507. return 'DomainMatrix(%s, %r, %r)' % (str(self.rep), self.shape, self.domain)
  508. def transpose(self):
  509. """Matrix transpose of ``self``"""
  510. return self.from_rep(self.rep.transpose())
  511. def flat(self):
  512. rows, cols = self.shape
  513. return [self[i,j].element for i in range(rows) for j in range(cols)]
  514. @property
  515. def is_zero_matrix(self):
  516. return self.rep.is_zero_matrix()
  517. @property
  518. def is_upper(self):
  519. """
  520. Says whether this matrix is upper-triangular. True can be returned
  521. even if the matrix is not square.
  522. """
  523. return self.rep.is_upper()
  524. @property
  525. def is_lower(self):
  526. """
  527. Says whether this matrix is lower-triangular. True can be returned
  528. even if the matrix is not square.
  529. """
  530. return self.rep.is_lower()
  531. @property
  532. def is_square(self):
  533. return self.shape[0] == self.shape[1]
  534. def rank(self):
  535. rref, pivots = self.rref()
  536. return len(pivots)
  537. def hstack(A, *B):
  538. r"""Horizontally stack the given matrices.
  539. Parameters
  540. ==========
  541. B: DomainMatrix
  542. Matrices to stack horizontally.
  543. Returns
  544. =======
  545. DomainMatrix
  546. DomainMatrix by stacking horizontally.
  547. Examples
  548. ========
  549. >>> from sympy import ZZ
  550. >>> from sympy.polys.matrices import DomainMatrix
  551. >>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  552. >>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ)
  553. >>> A.hstack(B)
  554. DomainMatrix([[1, 2, 5, 6], [3, 4, 7, 8]], (2, 4), ZZ)
  555. >>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ)
  556. >>> A.hstack(B, C)
  557. DomainMatrix([[1, 2, 5, 6, 9, 10], [3, 4, 7, 8, 11, 12]], (2, 6), ZZ)
  558. See Also
  559. ========
  560. unify
  561. """
  562. A, *B = A.unify(*B, fmt='dense')
  563. return DomainMatrix.from_rep(A.rep.hstack(*(Bk.rep for Bk in B)))
  564. def vstack(A, *B):
  565. r"""Vertically stack the given matrices.
  566. Parameters
  567. ==========
  568. B: DomainMatrix
  569. Matrices to stack vertically.
  570. Returns
  571. =======
  572. DomainMatrix
  573. DomainMatrix by stacking vertically.
  574. Examples
  575. ========
  576. >>> from sympy import ZZ
  577. >>> from sympy.polys.matrices import DomainMatrix
  578. >>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  579. >>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ)
  580. >>> A.vstack(B)
  581. DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8]], (4, 2), ZZ)
  582. >>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ)
  583. >>> A.vstack(B, C)
  584. DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], (6, 2), ZZ)
  585. See Also
  586. ========
  587. unify
  588. """
  589. A, *B = A.unify(*B, fmt='dense')
  590. return DomainMatrix.from_rep(A.rep.vstack(*(Bk.rep for Bk in B)))
  591. def applyfunc(self, func, domain=None):
  592. if domain is None:
  593. domain = self.domain
  594. return self.from_rep(self.rep.applyfunc(func, domain))
  595. def __add__(A, B):
  596. if not isinstance(B, DomainMatrix):
  597. return NotImplemented
  598. A, B = A.unify(B, fmt='dense')
  599. return A.add(B)
  600. def __sub__(A, B):
  601. if not isinstance(B, DomainMatrix):
  602. return NotImplemented
  603. A, B = A.unify(B, fmt='dense')
  604. return A.sub(B)
  605. def __neg__(A):
  606. return A.neg()
  607. def __mul__(A, B):
  608. """A * B"""
  609. if isinstance(B, DomainMatrix):
  610. A, B = A.unify(B, fmt='dense')
  611. return A.matmul(B)
  612. elif B in A.domain:
  613. return A.scalarmul(B)
  614. elif isinstance(B, DomainScalar):
  615. A, B = A.unify(B)
  616. return A.scalarmul(B.element)
  617. else:
  618. return NotImplemented
  619. def __rmul__(A, B):
  620. if B in A.domain:
  621. return A.rscalarmul(B)
  622. elif isinstance(B, DomainScalar):
  623. A, B = A.unify(B)
  624. return A.rscalarmul(B.element)
  625. else:
  626. return NotImplemented
  627. def __pow__(A, n):
  628. """A ** n"""
  629. if not isinstance(n, int):
  630. return NotImplemented
  631. return A.pow(n)
  632. def _check(a, op, b, ashape, bshape):
  633. if a.domain != b.domain:
  634. msg = "Domain mismatch: %s %s %s" % (a.domain, op, b.domain)
  635. raise DMDomainError(msg)
  636. if ashape != bshape:
  637. msg = "Shape mismatch: %s %s %s" % (a.shape, op, b.shape)
  638. raise DMShapeError(msg)
  639. if a.rep.fmt != b.rep.fmt:
  640. msg = "Format mismatch: %s %s %s" % (a.rep.fmt, op, b.rep.fmt)
  641. raise DMFormatError(msg)
  642. def add(A, B):
  643. r"""
  644. Adds two DomainMatrix matrices of the same Domain
  645. Parameters
  646. ==========
  647. A, B: DomainMatrix
  648. matrices to add
  649. Returns
  650. =======
  651. DomainMatrix
  652. DomainMatrix after Addition
  653. Raises
  654. ======
  655. DMShapeError
  656. If the dimensions of the two DomainMatrix are not equal
  657. ValueError
  658. If the domain of the two DomainMatrix are not same
  659. Examples
  660. ========
  661. >>> from sympy import ZZ
  662. >>> from sympy.polys.matrices import DomainMatrix
  663. >>> A = DomainMatrix([
  664. ... [ZZ(1), ZZ(2)],
  665. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  666. >>> B = DomainMatrix([
  667. ... [ZZ(4), ZZ(3)],
  668. ... [ZZ(2), ZZ(1)]], (2, 2), ZZ)
  669. >>> A.add(B)
  670. DomainMatrix([[5, 5], [5, 5]], (2, 2), ZZ)
  671. See Also
  672. ========
  673. sub, matmul
  674. """
  675. A._check('+', B, A.shape, B.shape)
  676. return A.from_rep(A.rep.add(B.rep))
  677. def sub(A, B):
  678. r"""
  679. Subtracts two DomainMatrix matrices of the same Domain
  680. Parameters
  681. ==========
  682. A, B: DomainMatrix
  683. matrices to subtract
  684. Returns
  685. =======
  686. DomainMatrix
  687. DomainMatrix after Subtraction
  688. Raises
  689. ======
  690. DMShapeError
  691. If the dimensions of the two DomainMatrix are not equal
  692. ValueError
  693. If the domain of the two DomainMatrix are not same
  694. Examples
  695. ========
  696. >>> from sympy import ZZ
  697. >>> from sympy.polys.matrices import DomainMatrix
  698. >>> A = DomainMatrix([
  699. ... [ZZ(1), ZZ(2)],
  700. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  701. >>> B = DomainMatrix([
  702. ... [ZZ(4), ZZ(3)],
  703. ... [ZZ(2), ZZ(1)]], (2, 2), ZZ)
  704. >>> A.sub(B)
  705. DomainMatrix([[-3, -1], [1, 3]], (2, 2), ZZ)
  706. See Also
  707. ========
  708. add, matmul
  709. """
  710. A._check('-', B, A.shape, B.shape)
  711. return A.from_rep(A.rep.sub(B.rep))
  712. def neg(A):
  713. r"""
  714. Returns the negative of DomainMatrix
  715. Parameters
  716. ==========
  717. A : Represents a DomainMatrix
  718. Returns
  719. =======
  720. DomainMatrix
  721. DomainMatrix after Negation
  722. Examples
  723. ========
  724. >>> from sympy import ZZ
  725. >>> from sympy.polys.matrices import DomainMatrix
  726. >>> A = DomainMatrix([
  727. ... [ZZ(1), ZZ(2)],
  728. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  729. >>> A.neg()
  730. DomainMatrix([[-1, -2], [-3, -4]], (2, 2), ZZ)
  731. """
  732. return A.from_rep(A.rep.neg())
  733. def mul(A, b):
  734. r"""
  735. Performs term by term multiplication for the second DomainMatrix
  736. w.r.t first DomainMatrix. Returns a DomainMatrix whose rows are
  737. list of DomainMatrix matrices created after term by term multiplication.
  738. Parameters
  739. ==========
  740. A, B: DomainMatrix
  741. matrices to multiply term-wise
  742. Returns
  743. =======
  744. DomainMatrix
  745. DomainMatrix after term by term multiplication
  746. Examples
  747. ========
  748. >>> from sympy import ZZ
  749. >>> from sympy.polys.matrices import DomainMatrix
  750. >>> A = DomainMatrix([
  751. ... [ZZ(1), ZZ(2)],
  752. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  753. >>> B = DomainMatrix([
  754. ... [ZZ(1), ZZ(1)],
  755. ... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
  756. >>> A.mul(B)
  757. DomainMatrix([[DomainMatrix([[1, 1], [0, 1]], (2, 2), ZZ),
  758. DomainMatrix([[2, 2], [0, 2]], (2, 2), ZZ)],
  759. [DomainMatrix([[3, 3], [0, 3]], (2, 2), ZZ),
  760. DomainMatrix([[4, 4], [0, 4]], (2, 2), ZZ)]], (2, 2), ZZ)
  761. See Also
  762. ========
  763. matmul
  764. """
  765. return A.from_rep(A.rep.mul(b))
  766. def rmul(A, b):
  767. return A.from_rep(A.rep.rmul(b))
  768. def matmul(A, B):
  769. r"""
  770. Performs matrix multiplication of two DomainMatrix matrices
  771. Parameters
  772. ==========
  773. A, B: DomainMatrix
  774. to multiply
  775. Returns
  776. =======
  777. DomainMatrix
  778. DomainMatrix after multiplication
  779. Examples
  780. ========
  781. >>> from sympy import ZZ
  782. >>> from sympy.polys.matrices import DomainMatrix
  783. >>> A = DomainMatrix([
  784. ... [ZZ(1), ZZ(2)],
  785. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  786. >>> B = DomainMatrix([
  787. ... [ZZ(1), ZZ(1)],
  788. ... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
  789. >>> A.matmul(B)
  790. DomainMatrix([[1, 3], [3, 7]], (2, 2), ZZ)
  791. See Also
  792. ========
  793. mul, pow, add, sub
  794. """
  795. A._check('*', B, A.shape[1], B.shape[0])
  796. return A.from_rep(A.rep.matmul(B.rep))
  797. def _scalarmul(A, lamda, reverse):
  798. if lamda == A.domain.zero:
  799. return DomainMatrix.zeros(A.shape, A.domain)
  800. elif lamda == A.domain.one:
  801. return A.copy()
  802. elif reverse:
  803. return A.rmul(lamda)
  804. else:
  805. return A.mul(lamda)
  806. def scalarmul(A, lamda):
  807. return A._scalarmul(lamda, reverse=False)
  808. def rscalarmul(A, lamda):
  809. return A._scalarmul(lamda, reverse=True)
  810. def mul_elementwise(A, B):
  811. assert A.domain == B.domain
  812. return A.from_rep(A.rep.mul_elementwise(B.rep))
  813. def __truediv__(A, lamda):
  814. """ Method for Scalar Division"""
  815. if isinstance(lamda, int) or ZZ.of_type(lamda):
  816. lamda = DomainScalar(ZZ(lamda), ZZ)
  817. if not isinstance(lamda, DomainScalar):
  818. return NotImplemented
  819. A, lamda = A.to_field().unify(lamda)
  820. if lamda.element == lamda.domain.zero:
  821. raise ZeroDivisionError
  822. if lamda.element == lamda.domain.one:
  823. return A.to_field()
  824. return A.mul(1 / lamda.element)
  825. def pow(A, n):
  826. r"""
  827. Computes A**n
  828. Parameters
  829. ==========
  830. A : DomainMatrix
  831. n : exponent for A
  832. Returns
  833. =======
  834. DomainMatrix
  835. DomainMatrix on computing A**n
  836. Raises
  837. ======
  838. NotImplementedError
  839. if n is negative.
  840. Examples
  841. ========
  842. >>> from sympy import ZZ
  843. >>> from sympy.polys.matrices import DomainMatrix
  844. >>> A = DomainMatrix([
  845. ... [ZZ(1), ZZ(1)],
  846. ... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
  847. >>> A.pow(2)
  848. DomainMatrix([[1, 2], [0, 1]], (2, 2), ZZ)
  849. See Also
  850. ========
  851. matmul
  852. """
  853. nrows, ncols = A.shape
  854. if nrows != ncols:
  855. raise DMNonSquareMatrixError('Power of a nonsquare matrix')
  856. if n < 0:
  857. raise NotImplementedError('Negative powers')
  858. elif n == 0:
  859. return A.eye(nrows, A.domain)
  860. elif n == 1:
  861. return A
  862. elif n % 2 == 1:
  863. return A * A**(n - 1)
  864. else:
  865. sqrtAn = A ** (n // 2)
  866. return sqrtAn * sqrtAn
  867. def scc(self):
  868. """Compute the strongly connected components of a DomainMatrix
  869. Explanation
  870. ===========
  871. A square matrix can be considered as the adjacency matrix for a
  872. directed graph where the row and column indices are the vertices. In
  873. this graph if there is an edge from vertex ``i`` to vertex ``j`` if
  874. ``M[i, j]`` is nonzero. This routine computes the strongly connected
  875. components of that graph which are subsets of the rows and columns that
  876. are connected by some nonzero element of the matrix. The strongly
  877. connected components are useful because many operations such as the
  878. determinant can be computed by working with the submatrices
  879. corresponding to each component.
  880. Examples
  881. ========
  882. Find the strongly connected components of a matrix:
  883. >>> from sympy import ZZ
  884. >>> from sympy.polys.matrices import DomainMatrix
  885. >>> M = DomainMatrix([[ZZ(1), ZZ(0), ZZ(2)],
  886. ... [ZZ(0), ZZ(3), ZZ(0)],
  887. ... [ZZ(4), ZZ(6), ZZ(5)]], (3, 3), ZZ)
  888. >>> M.scc()
  889. [[1], [0, 2]]
  890. Compute the determinant from the components:
  891. >>> MM = M.to_Matrix()
  892. >>> MM
  893. Matrix([
  894. [1, 0, 2],
  895. [0, 3, 0],
  896. [4, 6, 5]])
  897. >>> MM[[1], [1]]
  898. Matrix([[3]])
  899. >>> MM[[0, 2], [0, 2]]
  900. Matrix([
  901. [1, 2],
  902. [4, 5]])
  903. >>> MM.det()
  904. -9
  905. >>> MM[[1], [1]].det() * MM[[0, 2], [0, 2]].det()
  906. -9
  907. The components are given in reverse topological order and represent a
  908. permutation of the rows and columns that will bring the matrix into
  909. block lower-triangular form:
  910. >>> MM[[1, 0, 2], [1, 0, 2]]
  911. Matrix([
  912. [3, 0, 0],
  913. [0, 1, 2],
  914. [6, 4, 5]])
  915. Returns
  916. =======
  917. List of lists of integers
  918. Each list represents a strongly connected component.
  919. See also
  920. ========
  921. sympy.matrices.matrices.MatrixBase.strongly_connected_components
  922. sympy.utilities.iterables.strongly_connected_components
  923. """
  924. rows, cols = self.shape
  925. assert rows == cols
  926. return self.rep.scc()
  927. def rref(self):
  928. r"""
  929. Returns reduced-row echelon form and list of pivots for the DomainMatrix
  930. Returns
  931. =======
  932. (DomainMatrix, list)
  933. reduced-row echelon form and list of pivots for the DomainMatrix
  934. Raises
  935. ======
  936. ValueError
  937. If the domain of DomainMatrix not a Field
  938. Examples
  939. ========
  940. >>> from sympy import QQ
  941. >>> from sympy.polys.matrices import DomainMatrix
  942. >>> A = DomainMatrix([
  943. ... [QQ(2), QQ(-1), QQ(0)],
  944. ... [QQ(-1), QQ(2), QQ(-1)],
  945. ... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ)
  946. >>> rref_matrix, rref_pivots = A.rref()
  947. >>> rref_matrix
  948. DomainMatrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]], (3, 3), QQ)
  949. >>> rref_pivots
  950. (0, 1, 2)
  951. See Also
  952. ========
  953. convert_to, lu
  954. """
  955. if not self.domain.is_Field:
  956. raise DMNotAField('Not a field')
  957. rref_ddm, pivots = self.rep.rref()
  958. return self.from_rep(rref_ddm), tuple(pivots)
  959. def columnspace(self):
  960. r"""
  961. Returns the columnspace for the DomainMatrix
  962. Returns
  963. =======
  964. DomainMatrix
  965. The columns of this matrix form a basis for the columnspace.
  966. Examples
  967. ========
  968. >>> from sympy import QQ
  969. >>> from sympy.polys.matrices import DomainMatrix
  970. >>> A = DomainMatrix([
  971. ... [QQ(1), QQ(-1)],
  972. ... [QQ(2), QQ(-2)]], (2, 2), QQ)
  973. >>> A.columnspace()
  974. DomainMatrix([[1], [2]], (2, 1), QQ)
  975. """
  976. if not self.domain.is_Field:
  977. raise DMNotAField('Not a field')
  978. rref, pivots = self.rref()
  979. rows, cols = self.shape
  980. return self.extract(range(rows), pivots)
  981. def rowspace(self):
  982. r"""
  983. Returns the rowspace for the DomainMatrix
  984. Returns
  985. =======
  986. DomainMatrix
  987. The rows of this matrix form a basis for the rowspace.
  988. Examples
  989. ========
  990. >>> from sympy import QQ
  991. >>> from sympy.polys.matrices import DomainMatrix
  992. >>> A = DomainMatrix([
  993. ... [QQ(1), QQ(-1)],
  994. ... [QQ(2), QQ(-2)]], (2, 2), QQ)
  995. >>> A.rowspace()
  996. DomainMatrix([[1, -1]], (1, 2), QQ)
  997. """
  998. if not self.domain.is_Field:
  999. raise DMNotAField('Not a field')
  1000. rref, pivots = self.rref()
  1001. rows, cols = self.shape
  1002. return self.extract(range(len(pivots)), range(cols))
  1003. def nullspace(self):
  1004. r"""
  1005. Returns the nullspace for the DomainMatrix
  1006. Returns
  1007. =======
  1008. DomainMatrix
  1009. The rows of this matrix form a basis for the nullspace.
  1010. Examples
  1011. ========
  1012. >>> from sympy import QQ
  1013. >>> from sympy.polys.matrices import DomainMatrix
  1014. >>> A = DomainMatrix([
  1015. ... [QQ(1), QQ(-1)],
  1016. ... [QQ(2), QQ(-2)]], (2, 2), QQ)
  1017. >>> A.nullspace()
  1018. DomainMatrix([[1, 1]], (1, 2), QQ)
  1019. """
  1020. if not self.domain.is_Field:
  1021. raise DMNotAField('Not a field')
  1022. return self.from_rep(self.rep.nullspace()[0])
  1023. def inv(self):
  1024. r"""
  1025. Finds the inverse of the DomainMatrix if exists
  1026. Returns
  1027. =======
  1028. DomainMatrix
  1029. DomainMatrix after inverse
  1030. Raises
  1031. ======
  1032. ValueError
  1033. If the domain of DomainMatrix not a Field
  1034. DMNonSquareMatrixError
  1035. If the DomainMatrix is not a not Square DomainMatrix
  1036. Examples
  1037. ========
  1038. >>> from sympy import QQ
  1039. >>> from sympy.polys.matrices import DomainMatrix
  1040. >>> A = DomainMatrix([
  1041. ... [QQ(2), QQ(-1), QQ(0)],
  1042. ... [QQ(-1), QQ(2), QQ(-1)],
  1043. ... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ)
  1044. >>> A.inv()
  1045. DomainMatrix([[2/3, 1/3, 1/6], [1/3, 2/3, 1/3], [0, 0, 1/2]], (3, 3), QQ)
  1046. See Also
  1047. ========
  1048. neg
  1049. """
  1050. if not self.domain.is_Field:
  1051. raise DMNotAField('Not a field')
  1052. m, n = self.shape
  1053. if m != n:
  1054. raise DMNonSquareMatrixError
  1055. inv = self.rep.inv()
  1056. return self.from_rep(inv)
  1057. def det(self):
  1058. r"""
  1059. Returns the determinant of a Square DomainMatrix
  1060. Returns
  1061. =======
  1062. S.Complexes
  1063. determinant of Square DomainMatrix
  1064. Raises
  1065. ======
  1066. ValueError
  1067. If the domain of DomainMatrix not a Field
  1068. Examples
  1069. ========
  1070. >>> from sympy import ZZ
  1071. >>> from sympy.polys.matrices import DomainMatrix
  1072. >>> A = DomainMatrix([
  1073. ... [ZZ(1), ZZ(2)],
  1074. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  1075. >>> A.det()
  1076. -2
  1077. """
  1078. m, n = self.shape
  1079. if m != n:
  1080. raise DMNonSquareMatrixError
  1081. return self.rep.det()
  1082. def lu(self):
  1083. r"""
  1084. Returns Lower and Upper decomposition of the DomainMatrix
  1085. Returns
  1086. =======
  1087. (L, U, exchange)
  1088. L, U are Lower and Upper decomposition of the DomainMatrix,
  1089. exchange is the list of indices of rows exchanged in the decomposition.
  1090. Raises
  1091. ======
  1092. ValueError
  1093. If the domain of DomainMatrix not a Field
  1094. Examples
  1095. ========
  1096. >>> from sympy import QQ
  1097. >>> from sympy.polys.matrices import DomainMatrix
  1098. >>> A = DomainMatrix([
  1099. ... [QQ(1), QQ(-1)],
  1100. ... [QQ(2), QQ(-2)]], (2, 2), QQ)
  1101. >>> A.lu()
  1102. (DomainMatrix([[1, 0], [2, 1]], (2, 2), QQ), DomainMatrix([[1, -1], [0, 0]], (2, 2), QQ), [])
  1103. See Also
  1104. ========
  1105. lu_solve
  1106. """
  1107. if not self.domain.is_Field:
  1108. raise DMNotAField('Not a field')
  1109. L, U, swaps = self.rep.lu()
  1110. return self.from_rep(L), self.from_rep(U), swaps
  1111. def lu_solve(self, rhs):
  1112. r"""
  1113. Solver for DomainMatrix x in the A*x = B
  1114. Parameters
  1115. ==========
  1116. rhs : DomainMatrix B
  1117. Returns
  1118. =======
  1119. DomainMatrix
  1120. x in A*x = B
  1121. Raises
  1122. ======
  1123. DMShapeError
  1124. If the DomainMatrix A and rhs have different number of rows
  1125. ValueError
  1126. If the domain of DomainMatrix A not a Field
  1127. Examples
  1128. ========
  1129. >>> from sympy import QQ
  1130. >>> from sympy.polys.matrices import DomainMatrix
  1131. >>> A = DomainMatrix([
  1132. ... [QQ(1), QQ(2)],
  1133. ... [QQ(3), QQ(4)]], (2, 2), QQ)
  1134. >>> B = DomainMatrix([
  1135. ... [QQ(1), QQ(1)],
  1136. ... [QQ(0), QQ(1)]], (2, 2), QQ)
  1137. >>> A.lu_solve(B)
  1138. DomainMatrix([[-2, -1], [3/2, 1]], (2, 2), QQ)
  1139. See Also
  1140. ========
  1141. lu
  1142. """
  1143. if self.shape[0] != rhs.shape[0]:
  1144. raise DMShapeError("Shape")
  1145. if not self.domain.is_Field:
  1146. raise DMNotAField('Not a field')
  1147. sol = self.rep.lu_solve(rhs.rep)
  1148. return self.from_rep(sol)
  1149. def _solve(A, b):
  1150. # XXX: Not sure about this method or its signature. It is just created
  1151. # because it is needed by the holonomic module.
  1152. if A.shape[0] != b.shape[0]:
  1153. raise DMShapeError("Shape")
  1154. if A.domain != b.domain or not A.domain.is_Field:
  1155. raise DMNotAField('Not a field')
  1156. Aaug = A.hstack(b)
  1157. Arref, pivots = Aaug.rref()
  1158. particular = Arref.from_rep(Arref.rep.particular())
  1159. nullspace_rep, nonpivots = Arref[:,:-1].rep.nullspace()
  1160. nullspace = Arref.from_rep(nullspace_rep)
  1161. return particular, nullspace
  1162. def charpoly(self):
  1163. r"""
  1164. Returns the coefficients of the characteristic polynomial
  1165. of the DomainMatrix. These elements will be domain elements.
  1166. The domain of the elements will be same as domain of the DomainMatrix.
  1167. Returns
  1168. =======
  1169. list
  1170. coefficients of the characteristic polynomial
  1171. Raises
  1172. ======
  1173. DMNonSquareMatrixError
  1174. If the DomainMatrix is not a not Square DomainMatrix
  1175. Examples
  1176. ========
  1177. >>> from sympy import ZZ
  1178. >>> from sympy.polys.matrices import DomainMatrix
  1179. >>> A = DomainMatrix([
  1180. ... [ZZ(1), ZZ(2)],
  1181. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  1182. >>> A.charpoly()
  1183. [1, -5, -2]
  1184. """
  1185. m, n = self.shape
  1186. if m != n:
  1187. raise DMNonSquareMatrixError("not square")
  1188. return self.rep.charpoly()
  1189. @classmethod
  1190. def eye(cls, shape, domain):
  1191. r"""
  1192. Return identity matrix of size n
  1193. Examples
  1194. ========
  1195. >>> from sympy.polys.matrices import DomainMatrix
  1196. >>> from sympy import QQ
  1197. >>> DomainMatrix.eye(3, QQ)
  1198. DomainMatrix({0: {0: 1}, 1: {1: 1}, 2: {2: 1}}, (3, 3), QQ)
  1199. """
  1200. if isinstance(shape, int):
  1201. shape = (shape, shape)
  1202. return cls.from_rep(SDM.eye(shape, domain))
  1203. @classmethod
  1204. def diag(cls, diagonal, domain, shape=None):
  1205. r"""
  1206. Return diagonal matrix with entries from ``diagonal``.
  1207. Examples
  1208. ========
  1209. >>> from sympy.polys.matrices import DomainMatrix
  1210. >>> from sympy import ZZ
  1211. >>> DomainMatrix.diag([ZZ(5), ZZ(6)], ZZ)
  1212. DomainMatrix({0: {0: 5}, 1: {1: 6}}, (2, 2), ZZ)
  1213. """
  1214. if shape is None:
  1215. N = len(diagonal)
  1216. shape = (N, N)
  1217. return cls.from_rep(SDM.diag(diagonal, domain, shape))
  1218. @classmethod
  1219. def zeros(cls, shape, domain, *, fmt='sparse'):
  1220. """Returns a zero DomainMatrix of size shape, belonging to the specified domain
  1221. Examples
  1222. ========
  1223. >>> from sympy.polys.matrices import DomainMatrix
  1224. >>> from sympy import QQ
  1225. >>> DomainMatrix.zeros((2, 3), QQ)
  1226. DomainMatrix({}, (2, 3), QQ)
  1227. """
  1228. return cls.from_rep(SDM.zeros(shape, domain))
  1229. @classmethod
  1230. def ones(cls, shape, domain):
  1231. """Returns a DomainMatrix of 1s, of size shape, belonging to the specified domain
  1232. Examples
  1233. ========
  1234. >>> from sympy.polys.matrices import DomainMatrix
  1235. >>> from sympy import QQ
  1236. >>> DomainMatrix.ones((2,3), QQ)
  1237. DomainMatrix([[1, 1, 1], [1, 1, 1]], (2, 3), QQ)
  1238. """
  1239. return cls.from_rep(DDM.ones(shape, domain))
  1240. def __eq__(A, B):
  1241. r"""
  1242. Checks for two DomainMatrix matrices to be equal or not
  1243. Parameters
  1244. ==========
  1245. A, B: DomainMatrix
  1246. to check equality
  1247. Returns
  1248. =======
  1249. Boolean
  1250. True for equal, else False
  1251. Raises
  1252. ======
  1253. NotImplementedError
  1254. If B is not a DomainMatrix
  1255. Examples
  1256. ========
  1257. >>> from sympy import ZZ
  1258. >>> from sympy.polys.matrices import DomainMatrix
  1259. >>> A = DomainMatrix([
  1260. ... [ZZ(1), ZZ(2)],
  1261. ... [ZZ(3), ZZ(4)]], (2, 2), ZZ)
  1262. >>> B = DomainMatrix([
  1263. ... [ZZ(1), ZZ(1)],
  1264. ... [ZZ(0), ZZ(1)]], (2, 2), ZZ)
  1265. >>> A.__eq__(A)
  1266. True
  1267. >>> A.__eq__(B)
  1268. False
  1269. """
  1270. if not isinstance(A, type(B)):
  1271. return NotImplemented
  1272. return A.domain == B.domain and A.rep == B.rep
  1273. def unify_eq(A, B):
  1274. if A.shape != B.shape:
  1275. return False
  1276. if A.domain != B.domain:
  1277. A, B = A.unify(B)
  1278. return A == B
  1279. def lll(A, delta=QQ(3, 4)):
  1280. """
  1281. Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm.
  1282. See [1]_ and [2]_.
  1283. Parameters
  1284. ==========
  1285. delta : QQ, optional
  1286. The Lovász parameter. Must be in the interval (0.25, 1), with larger
  1287. values producing a more reduced basis. The default is 0.75 for
  1288. historical reasons.
  1289. Returns
  1290. =======
  1291. The reduced basis as a DomainMatrix over ZZ.
  1292. Throws
  1293. ======
  1294. DMValueError: if delta is not in the range (0.25, 1)
  1295. DMShapeError: if the matrix is not of shape (m, n) with m <= n
  1296. DMDomainError: if the matrix domain is not ZZ
  1297. DMRankError: if the matrix contains linearly dependent rows
  1298. Examples
  1299. ========
  1300. >>> from sympy.polys.domains import ZZ, QQ
  1301. >>> from sympy.polys.matrices import DM
  1302. >>> x = DM([[1, 0, 0, 0, -20160],
  1303. ... [0, 1, 0, 0, 33768],
  1304. ... [0, 0, 1, 0, 39578],
  1305. ... [0, 0, 0, 1, 47757]], ZZ)
  1306. >>> y = DM([[10, -3, -2, 8, -4],
  1307. ... [3, -9, 8, 1, -11],
  1308. ... [-3, 13, -9, -3, -9],
  1309. ... [-12, -7, -11, 9, -1]], ZZ)
  1310. >>> assert x.lll(delta=QQ(5, 6)) == y
  1311. Notes
  1312. =====
  1313. The implementation is derived from the Maple code given in Figures 4.3
  1314. and 4.4 of [3]_ (pp.68-69). It uses the efficient method of only calculating
  1315. state updates as they are required.
  1316. See also
  1317. ========
  1318. lll_transform
  1319. References
  1320. ==========
  1321. .. [1] https://en.wikipedia.org/wiki/Lenstra–Lenstra–Lovász_lattice_basis_reduction_algorithm
  1322. .. [2] https://web.archive.org/web/20221029115428/https://web.cs.elte.hu/~lovasz/scans/lll.pdf
  1323. .. [3] Murray R. Bremner, "Lattice Basis Reduction: An Introduction to the LLL Algorithm and Its Applications"
  1324. """
  1325. return DomainMatrix.from_rep(A.rep.lll(delta=delta))
  1326. def lll_transform(A, delta=QQ(3, 4)):
  1327. """
  1328. Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm
  1329. and returns the reduced basis and transformation matrix.
  1330. Explanation
  1331. ===========
  1332. Parameters, algorithm and basis are the same as for :meth:`lll` except that
  1333. the return value is a tuple `(B, T)` with `B` the reduced basis and
  1334. `T` a transformation matrix. The original basis `A` is transformed to
  1335. `B` with `T*A == B`. If only `B` is needed then :meth:`lll` should be
  1336. used as it is a little faster.
  1337. Examples
  1338. ========
  1339. >>> from sympy.polys.domains import ZZ, QQ
  1340. >>> from sympy.polys.matrices import DM
  1341. >>> X = DM([[1, 0, 0, 0, -20160],
  1342. ... [0, 1, 0, 0, 33768],
  1343. ... [0, 0, 1, 0, 39578],
  1344. ... [0, 0, 0, 1, 47757]], ZZ)
  1345. >>> B, T = X.lll_transform(delta=QQ(5, 6))
  1346. >>> T * X == B
  1347. True
  1348. See also
  1349. ========
  1350. lll
  1351. """
  1352. reduced, transform = A.rep.lll_transform(delta=delta)
  1353. return DomainMatrix.from_rep(reduced), DomainMatrix.from_rep(transform)