Coverage for /usr/lib/python3/dist-packages/sympy/polys/matrices/domainmatrix.py: 30%

396 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1""" 

2 

3Module for the DomainMatrix class. 

4 

5A DomainMatrix represents a matrix with elements that are in a particular 

6Domain. Each DomainMatrix internally wraps a DDM which is used for the 

7lower-level operations. The idea is that the DomainMatrix class provides the 

8convenience routines for converting between Expr and the poly domains as well 

9as unifying matrices with different domains. 

10 

11""" 

12from functools import reduce 

13from typing import Union as tUnion, Tuple as tTuple 

14 

15from sympy.core.sympify import _sympify 

16 

17from ..domains import Domain 

18 

19from ..constructor import construct_domain 

20 

21from .exceptions import (DMNonSquareMatrixError, DMShapeError, 

22 DMDomainError, DMFormatError, DMBadInputError, 

23 DMNotAField) 

24 

25from .ddm import DDM 

26 

27from .sdm import SDM 

28 

29from .domainscalar import DomainScalar 

30 

31from sympy.polys.domains import ZZ, EXRAW, QQ 

32 

33 

34def DM(rows, domain): 

35 """Convenient alias for DomainMatrix.from_list 

36 

37 Examples 

38 ======= 

39 

40 >>> from sympy import ZZ 

41 >>> from sympy.polys.matrices import DM 

42 >>> DM([[1, 2], [3, 4]], ZZ) 

43 DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ) 

44 

45 See also 

46 ======= 

47 

48 DomainMatrix.from_list 

49 """ 

50 return DomainMatrix.from_list(rows, domain) 

51 

52 

53class DomainMatrix: 

54 r""" 

55 Associate Matrix with :py:class:`~.Domain` 

56 

57 Explanation 

58 =========== 

59 

60 DomainMatrix uses :py:class:`~.Domain` for its internal representation 

61 which makes it faster than the SymPy Matrix class (currently) for many 

62 common operations, but this advantage makes it not entirely compatible 

63 with Matrix. DomainMatrix are analogous to numpy arrays with "dtype". 

64 In the DomainMatrix, each element has a domain such as :ref:`ZZ` 

65 or :ref:`QQ(a)`. 

66 

67 

68 Examples 

69 ======== 

70 

71 Creating a DomainMatrix from the existing Matrix class: 

72 

73 >>> from sympy import Matrix 

74 >>> from sympy.polys.matrices import DomainMatrix 

75 >>> Matrix1 = Matrix([ 

76 ... [1, 2], 

77 ... [3, 4]]) 

78 >>> A = DomainMatrix.from_Matrix(Matrix1) 

79 >>> A 

80 DomainMatrix({0: {0: 1, 1: 2}, 1: {0: 3, 1: 4}}, (2, 2), ZZ) 

81 

82 Directly forming a DomainMatrix: 

83 

84 >>> from sympy import ZZ 

85 >>> from sympy.polys.matrices import DomainMatrix 

86 >>> A = DomainMatrix([ 

87 ... [ZZ(1), ZZ(2)], 

88 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

89 >>> A 

90 DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ) 

91 

92 See Also 

93 ======== 

94 

95 DDM 

96 SDM 

97 Domain 

98 Poly 

99 

100 """ 

101 rep: tUnion[SDM, DDM] 

102 shape: tTuple[int, int] 

103 domain: Domain 

104 

105 def __new__(cls, rows, shape, domain, *, fmt=None): 

106 """ 

107 Creates a :py:class:`~.DomainMatrix`. 

108 

109 Parameters 

110 ========== 

111 

112 rows : Represents elements of DomainMatrix as list of lists 

113 shape : Represents dimension of DomainMatrix 

114 domain : Represents :py:class:`~.Domain` of DomainMatrix 

115 

116 Raises 

117 ====== 

118 

119 TypeError 

120 If any of rows, shape and domain are not provided 

121 

122 """ 

123 if isinstance(rows, (DDM, SDM)): 

124 raise TypeError("Use from_rep to initialise from SDM/DDM") 

125 elif isinstance(rows, list): 

126 rep = DDM(rows, shape, domain) 

127 elif isinstance(rows, dict): 

128 rep = SDM(rows, shape, domain) 

129 else: 

130 msg = "Input should be list-of-lists or dict-of-dicts" 

131 raise TypeError(msg) 

132 

133 if fmt is not None: 

134 if fmt == 'sparse': 

135 rep = rep.to_sdm() 

136 elif fmt == 'dense': 

137 rep = rep.to_ddm() 

138 else: 

139 raise ValueError("fmt should be 'sparse' or 'dense'") 

140 

141 return cls.from_rep(rep) 

142 

143 def __getnewargs__(self): 

144 rep = self.rep 

145 if isinstance(rep, DDM): 

146 arg = list(rep) 

147 elif isinstance(rep, SDM): 

148 arg = dict(rep) 

149 else: 

150 raise RuntimeError # pragma: no cover 

151 return arg, self.shape, self.domain 

152 

153 def __getitem__(self, key): 

154 i, j = key 

155 m, n = self.shape 

156 if not (isinstance(i, slice) or isinstance(j, slice)): 

157 return DomainScalar(self.rep.getitem(i, j), self.domain) 

158 

159 if not isinstance(i, slice): 

160 if not -m <= i < m: 

161 raise IndexError("Row index out of range") 

162 i = i % m 

163 i = slice(i, i+1) 

164 if not isinstance(j, slice): 

165 if not -n <= j < n: 

166 raise IndexError("Column index out of range") 

167 j = j % n 

168 j = slice(j, j+1) 

169 

170 return self.from_rep(self.rep.extract_slice(i, j)) 

171 

172 def getitem_sympy(self, i, j): 

173 return self.domain.to_sympy(self.rep.getitem(i, j)) 

174 

175 def extract(self, rowslist, colslist): 

176 return self.from_rep(self.rep.extract(rowslist, colslist)) 

177 

178 def __setitem__(self, key, value): 

179 i, j = key 

180 if not self.domain.of_type(value): 

181 raise TypeError 

182 if isinstance(i, int) and isinstance(j, int): 

183 self.rep.setitem(i, j, value) 

184 else: 

185 raise NotImplementedError 

186 

187 @classmethod 

188 def from_rep(cls, rep): 

189 """Create a new DomainMatrix efficiently from DDM/SDM. 

190 

191 Examples 

192 ======== 

193 

194 Create a :py:class:`~.DomainMatrix` with an dense internal 

195 representation as :py:class:`~.DDM`: 

196 

197 >>> from sympy.polys.domains import ZZ 

198 >>> from sympy.polys.matrices import DomainMatrix 

199 >>> from sympy.polys.matrices.ddm import DDM 

200 >>> drep = DDM([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

201 >>> dM = DomainMatrix.from_rep(drep) 

202 >>> dM 

203 DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ) 

204 

205 Create a :py:class:`~.DomainMatrix` with a sparse internal 

206 representation as :py:class:`~.SDM`: 

207 

208 >>> from sympy.polys.matrices import DomainMatrix 

209 >>> from sympy.polys.matrices.sdm import SDM 

210 >>> from sympy import ZZ 

211 >>> drep = SDM({0:{1:ZZ(1)},1:{0:ZZ(2)}}, (2, 2), ZZ) 

212 >>> dM = DomainMatrix.from_rep(drep) 

213 >>> dM 

214 DomainMatrix({0: {1: 1}, 1: {0: 2}}, (2, 2), ZZ) 

215 

216 Parameters 

217 ========== 

218 

219 rep: SDM or DDM 

220 The internal sparse or dense representation of the matrix. 

221 

222 Returns 

223 ======= 

224 

225 DomainMatrix 

226 A :py:class:`~.DomainMatrix` wrapping *rep*. 

227 

228 Notes 

229 ===== 

230 

231 This takes ownership of rep as its internal representation. If rep is 

232 being mutated elsewhere then a copy should be provided to 

233 ``from_rep``. Only minimal verification or checking is done on *rep* 

234 as this is supposed to be an efficient internal routine. 

235 

236 """ 

237 if not isinstance(rep, (DDM, SDM)): 

238 raise TypeError("rep should be of type DDM or SDM") 

239 self = super().__new__(cls) 

240 self.rep = rep 

241 self.shape = rep.shape 

242 self.domain = rep.domain 

243 return self 

244 

245 

246 @classmethod 

247 def from_list(cls, rows, domain): 

248 r""" 

249 Convert a list of lists into a DomainMatrix 

250 

251 Parameters 

252 ========== 

253 

254 rows: list of lists 

255 Each element of the inner lists should be either the single arg, 

256 or tuple of args, that would be passed to the domain constructor 

257 in order to form an element of the domain. See examples. 

258 

259 Returns 

260 ======= 

261 

262 DomainMatrix containing elements defined in rows 

263 

264 Examples 

265 ======== 

266 

267 >>> from sympy.polys.matrices import DomainMatrix 

268 >>> from sympy import FF, QQ, ZZ 

269 >>> A = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], ZZ) 

270 >>> A 

271 DomainMatrix([[1, 0, 1], [0, 0, 1]], (2, 3), ZZ) 

272 >>> B = DomainMatrix.from_list([[1, 0, 1], [0, 0, 1]], FF(7)) 

273 >>> B 

274 DomainMatrix([[1 mod 7, 0 mod 7, 1 mod 7], [0 mod 7, 0 mod 7, 1 mod 7]], (2, 3), GF(7)) 

275 >>> C = DomainMatrix.from_list([[(1, 2), (3, 1)], [(1, 4), (5, 1)]], QQ) 

276 >>> C 

277 DomainMatrix([[1/2, 3], [1/4, 5]], (2, 2), QQ) 

278 

279 See Also 

280 ======== 

281 

282 from_list_sympy 

283 

284 """ 

285 nrows = len(rows) 

286 ncols = 0 if not nrows else len(rows[0]) 

287 conv = lambda e: domain(*e) if isinstance(e, tuple) else domain(e) 

288 domain_rows = [[conv(e) for e in row] for row in rows] 

289 return DomainMatrix(domain_rows, (nrows, ncols), domain) 

290 

291 

292 @classmethod 

293 def from_list_sympy(cls, nrows, ncols, rows, **kwargs): 

294 r""" 

295 Convert a list of lists of Expr into a DomainMatrix using construct_domain 

296 

297 Parameters 

298 ========== 

299 

300 nrows: number of rows 

301 ncols: number of columns 

302 rows: list of lists 

303 

304 Returns 

305 ======= 

306 

307 DomainMatrix containing elements of rows 

308 

309 Examples 

310 ======== 

311 

312 >>> from sympy.polys.matrices import DomainMatrix 

313 >>> from sympy.abc import x, y, z 

314 >>> A = DomainMatrix.from_list_sympy(1, 3, [[x, y, z]]) 

315 >>> A 

316 DomainMatrix([[x, y, z]], (1, 3), ZZ[x,y,z]) 

317 

318 See Also 

319 ======== 

320 

321 sympy.polys.constructor.construct_domain, from_dict_sympy 

322 

323 """ 

324 assert len(rows) == nrows 

325 assert all(len(row) == ncols for row in rows) 

326 

327 items_sympy = [_sympify(item) for row in rows for item in row] 

328 

329 domain, items_domain = cls.get_domain(items_sympy, **kwargs) 

330 

331 domain_rows = [[items_domain[ncols*r + c] for c in range(ncols)] for r in range(nrows)] 

332 

333 return DomainMatrix(domain_rows, (nrows, ncols), domain) 

334 

335 @classmethod 

336 def from_dict_sympy(cls, nrows, ncols, elemsdict, **kwargs): 

337 """ 

338 

339 Parameters 

340 ========== 

341 

342 nrows: number of rows 

343 ncols: number of cols 

344 elemsdict: dict of dicts containing non-zero elements of the DomainMatrix 

345 

346 Returns 

347 ======= 

348 

349 DomainMatrix containing elements of elemsdict 

350 

351 Examples 

352 ======== 

353 

354 >>> from sympy.polys.matrices import DomainMatrix 

355 >>> from sympy.abc import x,y,z 

356 >>> elemsdict = {0: {0:x}, 1:{1: y}, 2: {2: z}} 

357 >>> A = DomainMatrix.from_dict_sympy(3, 3, elemsdict) 

358 >>> A 

359 DomainMatrix({0: {0: x}, 1: {1: y}, 2: {2: z}}, (3, 3), ZZ[x,y,z]) 

360 

361 See Also 

362 ======== 

363 

364 from_list_sympy 

365 

366 """ 

367 if not all(0 <= r < nrows for r in elemsdict): 

368 raise DMBadInputError("Row out of range") 

369 if not all(0 <= c < ncols for row in elemsdict.values() for c in row): 

370 raise DMBadInputError("Column out of range") 

371 

372 items_sympy = [_sympify(item) for row in elemsdict.values() for item in row.values()] 

373 domain, items_domain = cls.get_domain(items_sympy, **kwargs) 

374 

375 idx = 0 

376 items_dict = {} 

377 for i, row in elemsdict.items(): 

378 items_dict[i] = {} 

379 for j in row: 

380 items_dict[i][j] = items_domain[idx] 

381 idx += 1 

382 

383 return DomainMatrix(items_dict, (nrows, ncols), domain) 

384 

385 @classmethod 

386 def from_Matrix(cls, M, fmt='sparse',**kwargs): 

387 r""" 

388 Convert Matrix to DomainMatrix 

389 

390 Parameters 

391 ========== 

392 

393 M: Matrix 

394 

395 Returns 

396 ======= 

397 

398 Returns DomainMatrix with identical elements as M 

399 

400 Examples 

401 ======== 

402 

403 >>> from sympy import Matrix 

404 >>> from sympy.polys.matrices import DomainMatrix 

405 >>> M = Matrix([ 

406 ... [1.0, 3.4], 

407 ... [2.4, 1]]) 

408 >>> A = DomainMatrix.from_Matrix(M) 

409 >>> A 

410 DomainMatrix({0: {0: 1.0, 1: 3.4}, 1: {0: 2.4, 1: 1.0}}, (2, 2), RR) 

411 

412 We can keep internal representation as ddm using fmt='dense' 

413 >>> from sympy import Matrix, QQ 

414 >>> from sympy.polys.matrices import DomainMatrix 

415 >>> A = DomainMatrix.from_Matrix(Matrix([[QQ(1, 2), QQ(3, 4)], [QQ(0, 1), QQ(0, 1)]]), fmt='dense') 

416 >>> A.rep 

417 [[1/2, 3/4], [0, 0]] 

418 

419 See Also 

420 ======== 

421 

422 Matrix 

423 

424 """ 

425 if fmt == 'dense': 

426 return cls.from_list_sympy(*M.shape, M.tolist(), **kwargs) 

427 

428 return cls.from_dict_sympy(*M.shape, M.todod(), **kwargs) 

429 

430 @classmethod 

431 def get_domain(cls, items_sympy, **kwargs): 

432 K, items_K = construct_domain(items_sympy, **kwargs) 

433 return K, items_K 

434 

435 def copy(self): 

436 return self.from_rep(self.rep.copy()) 

437 

438 def convert_to(self, K): 

439 r""" 

440 Change the domain of DomainMatrix to desired domain or field 

441 

442 Parameters 

443 ========== 

444 

445 K : Represents the desired domain or field. 

446 Alternatively, ``None`` may be passed, in which case this method 

447 just returns a copy of this DomainMatrix. 

448 

449 Returns 

450 ======= 

451 

452 DomainMatrix 

453 DomainMatrix with the desired domain or field 

454 

455 Examples 

456 ======== 

457 

458 >>> from sympy import ZZ, ZZ_I 

459 >>> from sympy.polys.matrices import DomainMatrix 

460 >>> A = DomainMatrix([ 

461 ... [ZZ(1), ZZ(2)], 

462 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

463 

464 >>> A.convert_to(ZZ_I) 

465 DomainMatrix([[1, 2], [3, 4]], (2, 2), ZZ_I) 

466 

467 """ 

468 if K is None: 

469 return self.copy() 

470 return self.from_rep(self.rep.convert_to(K)) 

471 

472 def to_sympy(self): 

473 return self.convert_to(EXRAW) 

474 

475 def to_field(self): 

476 r""" 

477 Returns a DomainMatrix with the appropriate field 

478 

479 Returns 

480 ======= 

481 

482 DomainMatrix 

483 DomainMatrix with the appropriate field 

484 

485 Examples 

486 ======== 

487 

488 >>> from sympy import ZZ 

489 >>> from sympy.polys.matrices import DomainMatrix 

490 >>> A = DomainMatrix([ 

491 ... [ZZ(1), ZZ(2)], 

492 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

493 

494 >>> A.to_field() 

495 DomainMatrix([[1, 2], [3, 4]], (2, 2), QQ) 

496 

497 """ 

498 K = self.domain.get_field() 

499 return self.convert_to(K) 

500 

501 def to_sparse(self): 

502 """ 

503 Return a sparse DomainMatrix representation of *self*. 

504 

505 Examples 

506 ======== 

507 

508 >>> from sympy.polys.matrices import DomainMatrix 

509 >>> from sympy import QQ 

510 >>> A = DomainMatrix([[1, 0],[0, 2]], (2, 2), QQ) 

511 >>> A.rep 

512 [[1, 0], [0, 2]] 

513 >>> B = A.to_sparse() 

514 >>> B.rep 

515 {0: {0: 1}, 1: {1: 2}} 

516 """ 

517 if self.rep.fmt == 'sparse': 

518 return self 

519 

520 return self.from_rep(SDM.from_ddm(self.rep)) 

521 

522 def to_dense(self): 

523 """ 

524 Return a dense DomainMatrix representation of *self*. 

525 

526 Examples 

527 ======== 

528 

529 >>> from sympy.polys.matrices import DomainMatrix 

530 >>> from sympy import QQ 

531 >>> A = DomainMatrix({0: {0: 1}, 1: {1: 2}}, (2, 2), QQ) 

532 >>> A.rep 

533 {0: {0: 1}, 1: {1: 2}} 

534 >>> B = A.to_dense() 

535 >>> B.rep 

536 [[1, 0], [0, 2]] 

537 

538 """ 

539 if self.rep.fmt == 'dense': 

540 return self 

541 

542 return self.from_rep(SDM.to_ddm(self.rep)) 

543 

544 @classmethod 

545 def _unify_domain(cls, *matrices): 

546 """Convert matrices to a common domain""" 

547 domains = {matrix.domain for matrix in matrices} 

548 if len(domains) == 1: 

549 return matrices 

550 domain = reduce(lambda x, y: x.unify(y), domains) 

551 return tuple(matrix.convert_to(domain) for matrix in matrices) 

552 

553 @classmethod 

554 def _unify_fmt(cls, *matrices, fmt=None): 

555 """Convert matrices to the same format. 

556 

557 If all matrices have the same format, then return unmodified. 

558 Otherwise convert both to the preferred format given as *fmt* which 

559 should be 'dense' or 'sparse'. 

560 """ 

561 formats = {matrix.rep.fmt for matrix in matrices} 

562 if len(formats) == 1: 

563 return matrices 

564 if fmt == 'sparse': 

565 return tuple(matrix.to_sparse() for matrix in matrices) 

566 elif fmt == 'dense': 

567 return tuple(matrix.to_dense() for matrix in matrices) 

568 else: 

569 raise ValueError("fmt should be 'sparse' or 'dense'") 

570 

571 def unify(self, *others, fmt=None): 

572 """ 

573 Unifies the domains and the format of self and other 

574 matrices. 

575 

576 Parameters 

577 ========== 

578 

579 others : DomainMatrix 

580 

581 fmt: string 'dense', 'sparse' or `None` (default) 

582 The preferred format to convert to if self and other are not 

583 already in the same format. If `None` or not specified then no 

584 conversion if performed. 

585 

586 Returns 

587 ======= 

588 

589 Tuple[DomainMatrix] 

590 Matrices with unified domain and format 

591 

592 Examples 

593 ======== 

594 

595 Unify the domain of DomainMatrix that have different domains: 

596 

597 >>> from sympy import ZZ, QQ 

598 >>> from sympy.polys.matrices import DomainMatrix 

599 >>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ) 

600 >>> B = DomainMatrix([[QQ(1, 2), QQ(2)]], (1, 2), QQ) 

601 >>> Aq, Bq = A.unify(B) 

602 >>> Aq 

603 DomainMatrix([[1, 2]], (1, 2), QQ) 

604 >>> Bq 

605 DomainMatrix([[1/2, 2]], (1, 2), QQ) 

606 

607 Unify the format (dense or sparse): 

608 

609 >>> A = DomainMatrix([[ZZ(1), ZZ(2)]], (1, 2), ZZ) 

610 >>> B = DomainMatrix({0:{0: ZZ(1)}}, (2, 2), ZZ) 

611 >>> B.rep 

612 {0: {0: 1}} 

613 

614 >>> A2, B2 = A.unify(B, fmt='dense') 

615 >>> B2.rep 

616 [[1, 0], [0, 0]] 

617 

618 See Also 

619 ======== 

620 

621 convert_to, to_dense, to_sparse 

622 

623 """ 

624 matrices = (self,) + others 

625 matrices = DomainMatrix._unify_domain(*matrices) 

626 if fmt is not None: 

627 matrices = DomainMatrix._unify_fmt(*matrices, fmt=fmt) 

628 return matrices 

629 

630 def to_Matrix(self): 

631 r""" 

632 Convert DomainMatrix to Matrix 

633 

634 Returns 

635 ======= 

636 

637 Matrix 

638 MutableDenseMatrix for the DomainMatrix 

639 

640 Examples 

641 ======== 

642 

643 >>> from sympy import ZZ 

644 >>> from sympy.polys.matrices import DomainMatrix 

645 >>> A = DomainMatrix([ 

646 ... [ZZ(1), ZZ(2)], 

647 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

648 

649 >>> A.to_Matrix() 

650 Matrix([ 

651 [1, 2], 

652 [3, 4]]) 

653 

654 See Also 

655 ======== 

656 

657 from_Matrix 

658 

659 """ 

660 from sympy.matrices.dense import MutableDenseMatrix 

661 elemlist = self.rep.to_list() 

662 elements_sympy = [self.domain.to_sympy(e) for row in elemlist for e in row] 

663 return MutableDenseMatrix(*self.shape, elements_sympy) 

664 

665 def to_list(self): 

666 return self.rep.to_list() 

667 

668 def to_list_flat(self): 

669 return self.rep.to_list_flat() 

670 

671 def to_dok(self): 

672 return self.rep.to_dok() 

673 

674 def __repr__(self): 

675 return 'DomainMatrix(%s, %r, %r)' % (str(self.rep), self.shape, self.domain) 

676 

677 def transpose(self): 

678 """Matrix transpose of ``self``""" 

679 return self.from_rep(self.rep.transpose()) 

680 

681 def flat(self): 

682 rows, cols = self.shape 

683 return [self[i,j].element for i in range(rows) for j in range(cols)] 

684 

685 @property 

686 def is_zero_matrix(self): 

687 return self.rep.is_zero_matrix() 

688 

689 @property 

690 def is_upper(self): 

691 """ 

692 Says whether this matrix is upper-triangular. True can be returned 

693 even if the matrix is not square. 

694 """ 

695 return self.rep.is_upper() 

696 

697 @property 

698 def is_lower(self): 

699 """ 

700 Says whether this matrix is lower-triangular. True can be returned 

701 even if the matrix is not square. 

702 """ 

703 return self.rep.is_lower() 

704 

705 @property 

706 def is_square(self): 

707 return self.shape[0] == self.shape[1] 

708 

709 def rank(self): 

710 rref, pivots = self.rref() 

711 return len(pivots) 

712 

713 def hstack(A, *B): 

714 r"""Horizontally stack the given matrices. 

715 

716 Parameters 

717 ========== 

718 

719 B: DomainMatrix 

720 Matrices to stack horizontally. 

721 

722 Returns 

723 ======= 

724 

725 DomainMatrix 

726 DomainMatrix by stacking horizontally. 

727 

728 Examples 

729 ======== 

730 

731 >>> from sympy import ZZ 

732 >>> from sympy.polys.matrices import DomainMatrix 

733 

734 >>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

735 >>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ) 

736 >>> A.hstack(B) 

737 DomainMatrix([[1, 2, 5, 6], [3, 4, 7, 8]], (2, 4), ZZ) 

738 

739 >>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ) 

740 >>> A.hstack(B, C) 

741 DomainMatrix([[1, 2, 5, 6, 9, 10], [3, 4, 7, 8, 11, 12]], (2, 6), ZZ) 

742 

743 See Also 

744 ======== 

745 

746 unify 

747 """ 

748 A, *B = A.unify(*B, fmt='dense') 

749 return DomainMatrix.from_rep(A.rep.hstack(*(Bk.rep for Bk in B))) 

750 

751 def vstack(A, *B): 

752 r"""Vertically stack the given matrices. 

753 

754 Parameters 

755 ========== 

756 

757 B: DomainMatrix 

758 Matrices to stack vertically. 

759 

760 Returns 

761 ======= 

762 

763 DomainMatrix 

764 DomainMatrix by stacking vertically. 

765 

766 Examples 

767 ======== 

768 

769 >>> from sympy import ZZ 

770 >>> from sympy.polys.matrices import DomainMatrix 

771 

772 >>> A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

773 >>> B = DomainMatrix([[ZZ(5), ZZ(6)], [ZZ(7), ZZ(8)]], (2, 2), ZZ) 

774 >>> A.vstack(B) 

775 DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8]], (4, 2), ZZ) 

776 

777 >>> C = DomainMatrix([[ZZ(9), ZZ(10)], [ZZ(11), ZZ(12)]], (2, 2), ZZ) 

778 >>> A.vstack(B, C) 

779 DomainMatrix([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]], (6, 2), ZZ) 

780 

781 See Also 

782 ======== 

783 

784 unify 

785 """ 

786 A, *B = A.unify(*B, fmt='dense') 

787 return DomainMatrix.from_rep(A.rep.vstack(*(Bk.rep for Bk in B))) 

788 

789 def applyfunc(self, func, domain=None): 

790 if domain is None: 

791 domain = self.domain 

792 return self.from_rep(self.rep.applyfunc(func, domain)) 

793 

794 def __add__(A, B): 

795 if not isinstance(B, DomainMatrix): 

796 return NotImplemented 

797 A, B = A.unify(B, fmt='dense') 

798 return A.add(B) 

799 

800 def __sub__(A, B): 

801 if not isinstance(B, DomainMatrix): 

802 return NotImplemented 

803 A, B = A.unify(B, fmt='dense') 

804 return A.sub(B) 

805 

806 def __neg__(A): 

807 return A.neg() 

808 

809 def __mul__(A, B): 

810 """A * B""" 

811 if isinstance(B, DomainMatrix): 

812 A, B = A.unify(B, fmt='dense') 

813 return A.matmul(B) 

814 elif B in A.domain: 

815 return A.scalarmul(B) 

816 elif isinstance(B, DomainScalar): 

817 A, B = A.unify(B) 

818 return A.scalarmul(B.element) 

819 else: 

820 return NotImplemented 

821 

822 def __rmul__(A, B): 

823 if B in A.domain: 

824 return A.rscalarmul(B) 

825 elif isinstance(B, DomainScalar): 

826 A, B = A.unify(B) 

827 return A.rscalarmul(B.element) 

828 else: 

829 return NotImplemented 

830 

831 def __pow__(A, n): 

832 """A ** n""" 

833 if not isinstance(n, int): 

834 return NotImplemented 

835 return A.pow(n) 

836 

837 def _check(a, op, b, ashape, bshape): 

838 if a.domain != b.domain: 

839 msg = "Domain mismatch: %s %s %s" % (a.domain, op, b.domain) 

840 raise DMDomainError(msg) 

841 if ashape != bshape: 

842 msg = "Shape mismatch: %s %s %s" % (a.shape, op, b.shape) 

843 raise DMShapeError(msg) 

844 if a.rep.fmt != b.rep.fmt: 

845 msg = "Format mismatch: %s %s %s" % (a.rep.fmt, op, b.rep.fmt) 

846 raise DMFormatError(msg) 

847 

848 def add(A, B): 

849 r""" 

850 Adds two DomainMatrix matrices of the same Domain 

851 

852 Parameters 

853 ========== 

854 

855 A, B: DomainMatrix 

856 matrices to add 

857 

858 Returns 

859 ======= 

860 

861 DomainMatrix 

862 DomainMatrix after Addition 

863 

864 Raises 

865 ====== 

866 

867 DMShapeError 

868 If the dimensions of the two DomainMatrix are not equal 

869 

870 ValueError 

871 If the domain of the two DomainMatrix are not same 

872 

873 Examples 

874 ======== 

875 

876 >>> from sympy import ZZ 

877 >>> from sympy.polys.matrices import DomainMatrix 

878 >>> A = DomainMatrix([ 

879 ... [ZZ(1), ZZ(2)], 

880 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

881 >>> B = DomainMatrix([ 

882 ... [ZZ(4), ZZ(3)], 

883 ... [ZZ(2), ZZ(1)]], (2, 2), ZZ) 

884 

885 >>> A.add(B) 

886 DomainMatrix([[5, 5], [5, 5]], (2, 2), ZZ) 

887 

888 See Also 

889 ======== 

890 

891 sub, matmul 

892 

893 """ 

894 A._check('+', B, A.shape, B.shape) 

895 return A.from_rep(A.rep.add(B.rep)) 

896 

897 

898 def sub(A, B): 

899 r""" 

900 Subtracts two DomainMatrix matrices of the same Domain 

901 

902 Parameters 

903 ========== 

904 

905 A, B: DomainMatrix 

906 matrices to subtract 

907 

908 Returns 

909 ======= 

910 

911 DomainMatrix 

912 DomainMatrix after Subtraction 

913 

914 Raises 

915 ====== 

916 

917 DMShapeError 

918 If the dimensions of the two DomainMatrix are not equal 

919 

920 ValueError 

921 If the domain of the two DomainMatrix are not same 

922 

923 Examples 

924 ======== 

925 

926 >>> from sympy import ZZ 

927 >>> from sympy.polys.matrices import DomainMatrix 

928 >>> A = DomainMatrix([ 

929 ... [ZZ(1), ZZ(2)], 

930 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

931 >>> B = DomainMatrix([ 

932 ... [ZZ(4), ZZ(3)], 

933 ... [ZZ(2), ZZ(1)]], (2, 2), ZZ) 

934 

935 >>> A.sub(B) 

936 DomainMatrix([[-3, -1], [1, 3]], (2, 2), ZZ) 

937 

938 See Also 

939 ======== 

940 

941 add, matmul 

942 

943 """ 

944 A._check('-', B, A.shape, B.shape) 

945 return A.from_rep(A.rep.sub(B.rep)) 

946 

947 def neg(A): 

948 r""" 

949 Returns the negative of DomainMatrix 

950 

951 Parameters 

952 ========== 

953 

954 A : Represents a DomainMatrix 

955 

956 Returns 

957 ======= 

958 

959 DomainMatrix 

960 DomainMatrix after Negation 

961 

962 Examples 

963 ======== 

964 

965 >>> from sympy import ZZ 

966 >>> from sympy.polys.matrices import DomainMatrix 

967 >>> A = DomainMatrix([ 

968 ... [ZZ(1), ZZ(2)], 

969 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

970 

971 >>> A.neg() 

972 DomainMatrix([[-1, -2], [-3, -4]], (2, 2), ZZ) 

973 

974 """ 

975 return A.from_rep(A.rep.neg()) 

976 

977 def mul(A, b): 

978 r""" 

979 Performs term by term multiplication for the second DomainMatrix 

980 w.r.t first DomainMatrix. Returns a DomainMatrix whose rows are 

981 list of DomainMatrix matrices created after term by term multiplication. 

982 

983 Parameters 

984 ========== 

985 

986 A, B: DomainMatrix 

987 matrices to multiply term-wise 

988 

989 Returns 

990 ======= 

991 

992 DomainMatrix 

993 DomainMatrix after term by term multiplication 

994 

995 Examples 

996 ======== 

997 

998 >>> from sympy import ZZ 

999 >>> from sympy.polys.matrices import DomainMatrix 

1000 >>> A = DomainMatrix([ 

1001 ... [ZZ(1), ZZ(2)], 

1002 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

1003 >>> B = DomainMatrix([ 

1004 ... [ZZ(1), ZZ(1)], 

1005 ... [ZZ(0), ZZ(1)]], (2, 2), ZZ) 

1006 

1007 >>> A.mul(B) 

1008 DomainMatrix([[DomainMatrix([[1, 1], [0, 1]], (2, 2), ZZ), 

1009 DomainMatrix([[2, 2], [0, 2]], (2, 2), ZZ)], 

1010 [DomainMatrix([[3, 3], [0, 3]], (2, 2), ZZ), 

1011 DomainMatrix([[4, 4], [0, 4]], (2, 2), ZZ)]], (2, 2), ZZ) 

1012 

1013 See Also 

1014 ======== 

1015 

1016 matmul 

1017 

1018 """ 

1019 return A.from_rep(A.rep.mul(b)) 

1020 

1021 def rmul(A, b): 

1022 return A.from_rep(A.rep.rmul(b)) 

1023 

1024 def matmul(A, B): 

1025 r""" 

1026 Performs matrix multiplication of two DomainMatrix matrices 

1027 

1028 Parameters 

1029 ========== 

1030 

1031 A, B: DomainMatrix 

1032 to multiply 

1033 

1034 Returns 

1035 ======= 

1036 

1037 DomainMatrix 

1038 DomainMatrix after multiplication 

1039 

1040 Examples 

1041 ======== 

1042 

1043 >>> from sympy import ZZ 

1044 >>> from sympy.polys.matrices import DomainMatrix 

1045 >>> A = DomainMatrix([ 

1046 ... [ZZ(1), ZZ(2)], 

1047 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

1048 >>> B = DomainMatrix([ 

1049 ... [ZZ(1), ZZ(1)], 

1050 ... [ZZ(0), ZZ(1)]], (2, 2), ZZ) 

1051 

1052 >>> A.matmul(B) 

1053 DomainMatrix([[1, 3], [3, 7]], (2, 2), ZZ) 

1054 

1055 See Also 

1056 ======== 

1057 

1058 mul, pow, add, sub 

1059 

1060 """ 

1061 

1062 A._check('*', B, A.shape[1], B.shape[0]) 

1063 return A.from_rep(A.rep.matmul(B.rep)) 

1064 

1065 def _scalarmul(A, lamda, reverse): 

1066 if lamda == A.domain.zero: 

1067 return DomainMatrix.zeros(A.shape, A.domain) 

1068 elif lamda == A.domain.one: 

1069 return A.copy() 

1070 elif reverse: 

1071 return A.rmul(lamda) 

1072 else: 

1073 return A.mul(lamda) 

1074 

1075 def scalarmul(A, lamda): 

1076 return A._scalarmul(lamda, reverse=False) 

1077 

1078 def rscalarmul(A, lamda): 

1079 return A._scalarmul(lamda, reverse=True) 

1080 

1081 def mul_elementwise(A, B): 

1082 assert A.domain == B.domain 

1083 return A.from_rep(A.rep.mul_elementwise(B.rep)) 

1084 

1085 def __truediv__(A, lamda): 

1086 """ Method for Scalar Division""" 

1087 if isinstance(lamda, int) or ZZ.of_type(lamda): 

1088 lamda = DomainScalar(ZZ(lamda), ZZ) 

1089 

1090 if not isinstance(lamda, DomainScalar): 

1091 return NotImplemented 

1092 

1093 A, lamda = A.to_field().unify(lamda) 

1094 if lamda.element == lamda.domain.zero: 

1095 raise ZeroDivisionError 

1096 if lamda.element == lamda.domain.one: 

1097 return A.to_field() 

1098 

1099 return A.mul(1 / lamda.element) 

1100 

1101 def pow(A, n): 

1102 r""" 

1103 Computes A**n 

1104 

1105 Parameters 

1106 ========== 

1107 

1108 A : DomainMatrix 

1109 

1110 n : exponent for A 

1111 

1112 Returns 

1113 ======= 

1114 

1115 DomainMatrix 

1116 DomainMatrix on computing A**n 

1117 

1118 Raises 

1119 ====== 

1120 

1121 NotImplementedError 

1122 if n is negative. 

1123 

1124 Examples 

1125 ======== 

1126 

1127 >>> from sympy import ZZ 

1128 >>> from sympy.polys.matrices import DomainMatrix 

1129 >>> A = DomainMatrix([ 

1130 ... [ZZ(1), ZZ(1)], 

1131 ... [ZZ(0), ZZ(1)]], (2, 2), ZZ) 

1132 

1133 >>> A.pow(2) 

1134 DomainMatrix([[1, 2], [0, 1]], (2, 2), ZZ) 

1135 

1136 See Also 

1137 ======== 

1138 

1139 matmul 

1140 

1141 """ 

1142 nrows, ncols = A.shape 

1143 if nrows != ncols: 

1144 raise DMNonSquareMatrixError('Power of a nonsquare matrix') 

1145 if n < 0: 

1146 raise NotImplementedError('Negative powers') 

1147 elif n == 0: 

1148 return A.eye(nrows, A.domain) 

1149 elif n == 1: 

1150 return A 

1151 elif n % 2 == 1: 

1152 return A * A**(n - 1) 

1153 else: 

1154 sqrtAn = A ** (n // 2) 

1155 return sqrtAn * sqrtAn 

1156 

1157 def scc(self): 

1158 """Compute the strongly connected components of a DomainMatrix 

1159 

1160 Explanation 

1161 =========== 

1162 

1163 A square matrix can be considered as the adjacency matrix for a 

1164 directed graph where the row and column indices are the vertices. In 

1165 this graph if there is an edge from vertex ``i`` to vertex ``j`` if 

1166 ``M[i, j]`` is nonzero. This routine computes the strongly connected 

1167 components of that graph which are subsets of the rows and columns that 

1168 are connected by some nonzero element of the matrix. The strongly 

1169 connected components are useful because many operations such as the 

1170 determinant can be computed by working with the submatrices 

1171 corresponding to each component. 

1172 

1173 Examples 

1174 ======== 

1175 

1176 Find the strongly connected components of a matrix: 

1177 

1178 >>> from sympy import ZZ 

1179 >>> from sympy.polys.matrices import DomainMatrix 

1180 >>> M = DomainMatrix([[ZZ(1), ZZ(0), ZZ(2)], 

1181 ... [ZZ(0), ZZ(3), ZZ(0)], 

1182 ... [ZZ(4), ZZ(6), ZZ(5)]], (3, 3), ZZ) 

1183 >>> M.scc() 

1184 [[1], [0, 2]] 

1185 

1186 Compute the determinant from the components: 

1187 

1188 >>> MM = M.to_Matrix() 

1189 >>> MM 

1190 Matrix([ 

1191 [1, 0, 2], 

1192 [0, 3, 0], 

1193 [4, 6, 5]]) 

1194 >>> MM[[1], [1]] 

1195 Matrix([[3]]) 

1196 >>> MM[[0, 2], [0, 2]] 

1197 Matrix([ 

1198 [1, 2], 

1199 [4, 5]]) 

1200 >>> MM.det() 

1201 -9 

1202 >>> MM[[1], [1]].det() * MM[[0, 2], [0, 2]].det() 

1203 -9 

1204 

1205 The components are given in reverse topological order and represent a 

1206 permutation of the rows and columns that will bring the matrix into 

1207 block lower-triangular form: 

1208 

1209 >>> MM[[1, 0, 2], [1, 0, 2]] 

1210 Matrix([ 

1211 [3, 0, 0], 

1212 [0, 1, 2], 

1213 [6, 4, 5]]) 

1214 

1215 Returns 

1216 ======= 

1217 

1218 List of lists of integers 

1219 Each list represents a strongly connected component. 

1220 

1221 See also 

1222 ======== 

1223 

1224 sympy.matrices.matrices.MatrixBase.strongly_connected_components 

1225 sympy.utilities.iterables.strongly_connected_components 

1226 

1227 """ 

1228 rows, cols = self.shape 

1229 assert rows == cols 

1230 return self.rep.scc() 

1231 

1232 def rref(self): 

1233 r""" 

1234 Returns reduced-row echelon form and list of pivots for the DomainMatrix 

1235 

1236 Returns 

1237 ======= 

1238 

1239 (DomainMatrix, list) 

1240 reduced-row echelon form and list of pivots for the DomainMatrix 

1241 

1242 Raises 

1243 ====== 

1244 

1245 ValueError 

1246 If the domain of DomainMatrix not a Field 

1247 

1248 Examples 

1249 ======== 

1250 

1251 >>> from sympy import QQ 

1252 >>> from sympy.polys.matrices import DomainMatrix 

1253 >>> A = DomainMatrix([ 

1254 ... [QQ(2), QQ(-1), QQ(0)], 

1255 ... [QQ(-1), QQ(2), QQ(-1)], 

1256 ... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ) 

1257 

1258 >>> rref_matrix, rref_pivots = A.rref() 

1259 >>> rref_matrix 

1260 DomainMatrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]], (3, 3), QQ) 

1261 >>> rref_pivots 

1262 (0, 1, 2) 

1263 

1264 See Also 

1265 ======== 

1266 

1267 convert_to, lu 

1268 

1269 """ 

1270 if not self.domain.is_Field: 

1271 raise DMNotAField('Not a field') 

1272 rref_ddm, pivots = self.rep.rref() 

1273 return self.from_rep(rref_ddm), tuple(pivots) 

1274 

1275 def columnspace(self): 

1276 r""" 

1277 Returns the columnspace for the DomainMatrix 

1278 

1279 Returns 

1280 ======= 

1281 

1282 DomainMatrix 

1283 The columns of this matrix form a basis for the columnspace. 

1284 

1285 Examples 

1286 ======== 

1287 

1288 >>> from sympy import QQ 

1289 >>> from sympy.polys.matrices import DomainMatrix 

1290 >>> A = DomainMatrix([ 

1291 ... [QQ(1), QQ(-1)], 

1292 ... [QQ(2), QQ(-2)]], (2, 2), QQ) 

1293 >>> A.columnspace() 

1294 DomainMatrix([[1], [2]], (2, 1), QQ) 

1295 

1296 """ 

1297 if not self.domain.is_Field: 

1298 raise DMNotAField('Not a field') 

1299 rref, pivots = self.rref() 

1300 rows, cols = self.shape 

1301 return self.extract(range(rows), pivots) 

1302 

1303 def rowspace(self): 

1304 r""" 

1305 Returns the rowspace for the DomainMatrix 

1306 

1307 Returns 

1308 ======= 

1309 

1310 DomainMatrix 

1311 The rows of this matrix form a basis for the rowspace. 

1312 

1313 Examples 

1314 ======== 

1315 

1316 >>> from sympy import QQ 

1317 >>> from sympy.polys.matrices import DomainMatrix 

1318 >>> A = DomainMatrix([ 

1319 ... [QQ(1), QQ(-1)], 

1320 ... [QQ(2), QQ(-2)]], (2, 2), QQ) 

1321 >>> A.rowspace() 

1322 DomainMatrix([[1, -1]], (1, 2), QQ) 

1323 

1324 """ 

1325 if not self.domain.is_Field: 

1326 raise DMNotAField('Not a field') 

1327 rref, pivots = self.rref() 

1328 rows, cols = self.shape 

1329 return self.extract(range(len(pivots)), range(cols)) 

1330 

1331 def nullspace(self): 

1332 r""" 

1333 Returns the nullspace for the DomainMatrix 

1334 

1335 Returns 

1336 ======= 

1337 

1338 DomainMatrix 

1339 The rows of this matrix form a basis for the nullspace. 

1340 

1341 Examples 

1342 ======== 

1343 

1344 >>> from sympy import QQ 

1345 >>> from sympy.polys.matrices import DomainMatrix 

1346 >>> A = DomainMatrix([ 

1347 ... [QQ(1), QQ(-1)], 

1348 ... [QQ(2), QQ(-2)]], (2, 2), QQ) 

1349 >>> A.nullspace() 

1350 DomainMatrix([[1, 1]], (1, 2), QQ) 

1351 

1352 """ 

1353 if not self.domain.is_Field: 

1354 raise DMNotAField('Not a field') 

1355 return self.from_rep(self.rep.nullspace()[0]) 

1356 

1357 def inv(self): 

1358 r""" 

1359 Finds the inverse of the DomainMatrix if exists 

1360 

1361 Returns 

1362 ======= 

1363 

1364 DomainMatrix 

1365 DomainMatrix after inverse 

1366 

1367 Raises 

1368 ====== 

1369 

1370 ValueError 

1371 If the domain of DomainMatrix not a Field 

1372 

1373 DMNonSquareMatrixError 

1374 If the DomainMatrix is not a not Square DomainMatrix 

1375 

1376 Examples 

1377 ======== 

1378 

1379 >>> from sympy import QQ 

1380 >>> from sympy.polys.matrices import DomainMatrix 

1381 >>> A = DomainMatrix([ 

1382 ... [QQ(2), QQ(-1), QQ(0)], 

1383 ... [QQ(-1), QQ(2), QQ(-1)], 

1384 ... [QQ(0), QQ(0), QQ(2)]], (3, 3), QQ) 

1385 >>> A.inv() 

1386 DomainMatrix([[2/3, 1/3, 1/6], [1/3, 2/3, 1/3], [0, 0, 1/2]], (3, 3), QQ) 

1387 

1388 See Also 

1389 ======== 

1390 

1391 neg 

1392 

1393 """ 

1394 if not self.domain.is_Field: 

1395 raise DMNotAField('Not a field') 

1396 m, n = self.shape 

1397 if m != n: 

1398 raise DMNonSquareMatrixError 

1399 inv = self.rep.inv() 

1400 return self.from_rep(inv) 

1401 

1402 def det(self): 

1403 r""" 

1404 Returns the determinant of a Square DomainMatrix 

1405 

1406 Returns 

1407 ======= 

1408 

1409 S.Complexes 

1410 determinant of Square DomainMatrix 

1411 

1412 Raises 

1413 ====== 

1414 

1415 ValueError 

1416 If the domain of DomainMatrix not a Field 

1417 

1418 Examples 

1419 ======== 

1420 

1421 >>> from sympy import ZZ 

1422 >>> from sympy.polys.matrices import DomainMatrix 

1423 >>> A = DomainMatrix([ 

1424 ... [ZZ(1), ZZ(2)], 

1425 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

1426 

1427 >>> A.det() 

1428 -2 

1429 

1430 """ 

1431 m, n = self.shape 

1432 if m != n: 

1433 raise DMNonSquareMatrixError 

1434 return self.rep.det() 

1435 

1436 def lu(self): 

1437 r""" 

1438 Returns Lower and Upper decomposition of the DomainMatrix 

1439 

1440 Returns 

1441 ======= 

1442 

1443 (L, U, exchange) 

1444 L, U are Lower and Upper decomposition of the DomainMatrix, 

1445 exchange is the list of indices of rows exchanged in the decomposition. 

1446 

1447 Raises 

1448 ====== 

1449 

1450 ValueError 

1451 If the domain of DomainMatrix not a Field 

1452 

1453 Examples 

1454 ======== 

1455 

1456 >>> from sympy import QQ 

1457 >>> from sympy.polys.matrices import DomainMatrix 

1458 >>> A = DomainMatrix([ 

1459 ... [QQ(1), QQ(-1)], 

1460 ... [QQ(2), QQ(-2)]], (2, 2), QQ) 

1461 >>> A.lu() 

1462 (DomainMatrix([[1, 0], [2, 1]], (2, 2), QQ), DomainMatrix([[1, -1], [0, 0]], (2, 2), QQ), []) 

1463 

1464 See Also 

1465 ======== 

1466 

1467 lu_solve 

1468 

1469 """ 

1470 if not self.domain.is_Field: 

1471 raise DMNotAField('Not a field') 

1472 L, U, swaps = self.rep.lu() 

1473 return self.from_rep(L), self.from_rep(U), swaps 

1474 

1475 def lu_solve(self, rhs): 

1476 r""" 

1477 Solver for DomainMatrix x in the A*x = B 

1478 

1479 Parameters 

1480 ========== 

1481 

1482 rhs : DomainMatrix B 

1483 

1484 Returns 

1485 ======= 

1486 

1487 DomainMatrix 

1488 x in A*x = B 

1489 

1490 Raises 

1491 ====== 

1492 

1493 DMShapeError 

1494 If the DomainMatrix A and rhs have different number of rows 

1495 

1496 ValueError 

1497 If the domain of DomainMatrix A not a Field 

1498 

1499 Examples 

1500 ======== 

1501 

1502 >>> from sympy import QQ 

1503 >>> from sympy.polys.matrices import DomainMatrix 

1504 >>> A = DomainMatrix([ 

1505 ... [QQ(1), QQ(2)], 

1506 ... [QQ(3), QQ(4)]], (2, 2), QQ) 

1507 >>> B = DomainMatrix([ 

1508 ... [QQ(1), QQ(1)], 

1509 ... [QQ(0), QQ(1)]], (2, 2), QQ) 

1510 

1511 >>> A.lu_solve(B) 

1512 DomainMatrix([[-2, -1], [3/2, 1]], (2, 2), QQ) 

1513 

1514 See Also 

1515 ======== 

1516 

1517 lu 

1518 

1519 """ 

1520 if self.shape[0] != rhs.shape[0]: 

1521 raise DMShapeError("Shape") 

1522 if not self.domain.is_Field: 

1523 raise DMNotAField('Not a field') 

1524 sol = self.rep.lu_solve(rhs.rep) 

1525 return self.from_rep(sol) 

1526 

1527 def _solve(A, b): 

1528 # XXX: Not sure about this method or its signature. It is just created 

1529 # because it is needed by the holonomic module. 

1530 if A.shape[0] != b.shape[0]: 

1531 raise DMShapeError("Shape") 

1532 if A.domain != b.domain or not A.domain.is_Field: 

1533 raise DMNotAField('Not a field') 

1534 Aaug = A.hstack(b) 

1535 Arref, pivots = Aaug.rref() 

1536 particular = Arref.from_rep(Arref.rep.particular()) 

1537 nullspace_rep, nonpivots = Arref[:,:-1].rep.nullspace() 

1538 nullspace = Arref.from_rep(nullspace_rep) 

1539 return particular, nullspace 

1540 

1541 def charpoly(self): 

1542 r""" 

1543 Returns the coefficients of the characteristic polynomial 

1544 of the DomainMatrix. These elements will be domain elements. 

1545 The domain of the elements will be same as domain of the DomainMatrix. 

1546 

1547 Returns 

1548 ======= 

1549 

1550 list 

1551 coefficients of the characteristic polynomial 

1552 

1553 Raises 

1554 ====== 

1555 

1556 DMNonSquareMatrixError 

1557 If the DomainMatrix is not a not Square DomainMatrix 

1558 

1559 Examples 

1560 ======== 

1561 

1562 >>> from sympy import ZZ 

1563 >>> from sympy.polys.matrices import DomainMatrix 

1564 >>> A = DomainMatrix([ 

1565 ... [ZZ(1), ZZ(2)], 

1566 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

1567 

1568 >>> A.charpoly() 

1569 [1, -5, -2] 

1570 

1571 """ 

1572 m, n = self.shape 

1573 if m != n: 

1574 raise DMNonSquareMatrixError("not square") 

1575 return self.rep.charpoly() 

1576 

1577 @classmethod 

1578 def eye(cls, shape, domain): 

1579 r""" 

1580 Return identity matrix of size n 

1581 

1582 Examples 

1583 ======== 

1584 

1585 >>> from sympy.polys.matrices import DomainMatrix 

1586 >>> from sympy import QQ 

1587 >>> DomainMatrix.eye(3, QQ) 

1588 DomainMatrix({0: {0: 1}, 1: {1: 1}, 2: {2: 1}}, (3, 3), QQ) 

1589 

1590 """ 

1591 if isinstance(shape, int): 

1592 shape = (shape, shape) 

1593 return cls.from_rep(SDM.eye(shape, domain)) 

1594 

1595 @classmethod 

1596 def diag(cls, diagonal, domain, shape=None): 

1597 r""" 

1598 Return diagonal matrix with entries from ``diagonal``. 

1599 

1600 Examples 

1601 ======== 

1602 

1603 >>> from sympy.polys.matrices import DomainMatrix 

1604 >>> from sympy import ZZ 

1605 >>> DomainMatrix.diag([ZZ(5), ZZ(6)], ZZ) 

1606 DomainMatrix({0: {0: 5}, 1: {1: 6}}, (2, 2), ZZ) 

1607 

1608 """ 

1609 if shape is None: 

1610 N = len(diagonal) 

1611 shape = (N, N) 

1612 return cls.from_rep(SDM.diag(diagonal, domain, shape)) 

1613 

1614 @classmethod 

1615 def zeros(cls, shape, domain, *, fmt='sparse'): 

1616 """Returns a zero DomainMatrix of size shape, belonging to the specified domain 

1617 

1618 Examples 

1619 ======== 

1620 

1621 >>> from sympy.polys.matrices import DomainMatrix 

1622 >>> from sympy import QQ 

1623 >>> DomainMatrix.zeros((2, 3), QQ) 

1624 DomainMatrix({}, (2, 3), QQ) 

1625 

1626 """ 

1627 return cls.from_rep(SDM.zeros(shape, domain)) 

1628 

1629 @classmethod 

1630 def ones(cls, shape, domain): 

1631 """Returns a DomainMatrix of 1s, of size shape, belonging to the specified domain 

1632 

1633 Examples 

1634 ======== 

1635 

1636 >>> from sympy.polys.matrices import DomainMatrix 

1637 >>> from sympy import QQ 

1638 >>> DomainMatrix.ones((2,3), QQ) 

1639 DomainMatrix([[1, 1, 1], [1, 1, 1]], (2, 3), QQ) 

1640 

1641 """ 

1642 return cls.from_rep(DDM.ones(shape, domain)) 

1643 

1644 def __eq__(A, B): 

1645 r""" 

1646 Checks for two DomainMatrix matrices to be equal or not 

1647 

1648 Parameters 

1649 ========== 

1650 

1651 A, B: DomainMatrix 

1652 to check equality 

1653 

1654 Returns 

1655 ======= 

1656 

1657 Boolean 

1658 True for equal, else False 

1659 

1660 Raises 

1661 ====== 

1662 

1663 NotImplementedError 

1664 If B is not a DomainMatrix 

1665 

1666 Examples 

1667 ======== 

1668 

1669 >>> from sympy import ZZ 

1670 >>> from sympy.polys.matrices import DomainMatrix 

1671 >>> A = DomainMatrix([ 

1672 ... [ZZ(1), ZZ(2)], 

1673 ... [ZZ(3), ZZ(4)]], (2, 2), ZZ) 

1674 >>> B = DomainMatrix([ 

1675 ... [ZZ(1), ZZ(1)], 

1676 ... [ZZ(0), ZZ(1)]], (2, 2), ZZ) 

1677 >>> A.__eq__(A) 

1678 True 

1679 >>> A.__eq__(B) 

1680 False 

1681 

1682 """ 

1683 if not isinstance(A, type(B)): 

1684 return NotImplemented 

1685 return A.domain == B.domain and A.rep == B.rep 

1686 

1687 def unify_eq(A, B): 

1688 if A.shape != B.shape: 

1689 return False 

1690 if A.domain != B.domain: 

1691 A, B = A.unify(B) 

1692 return A == B 

1693 

1694 def lll(A, delta=QQ(3, 4)): 

1695 """ 

1696 Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm. 

1697 See [1]_ and [2]_. 

1698 

1699 Parameters 

1700 ========== 

1701 

1702 delta : QQ, optional 

1703 The Lovász parameter. Must be in the interval (0.25, 1), with larger 

1704 values producing a more reduced basis. The default is 0.75 for 

1705 historical reasons. 

1706 

1707 Returns 

1708 ======= 

1709 

1710 The reduced basis as a DomainMatrix over ZZ. 

1711 

1712 Throws 

1713 ====== 

1714 

1715 DMValueError: if delta is not in the range (0.25, 1) 

1716 DMShapeError: if the matrix is not of shape (m, n) with m <= n 

1717 DMDomainError: if the matrix domain is not ZZ 

1718 DMRankError: if the matrix contains linearly dependent rows 

1719 

1720 Examples 

1721 ======== 

1722 

1723 >>> from sympy.polys.domains import ZZ, QQ 

1724 >>> from sympy.polys.matrices import DM 

1725 >>> x = DM([[1, 0, 0, 0, -20160], 

1726 ... [0, 1, 0, 0, 33768], 

1727 ... [0, 0, 1, 0, 39578], 

1728 ... [0, 0, 0, 1, 47757]], ZZ) 

1729 >>> y = DM([[10, -3, -2, 8, -4], 

1730 ... [3, -9, 8, 1, -11], 

1731 ... [-3, 13, -9, -3, -9], 

1732 ... [-12, -7, -11, 9, -1]], ZZ) 

1733 >>> assert x.lll(delta=QQ(5, 6)) == y 

1734 

1735 Notes 

1736 ===== 

1737 

1738 The implementation is derived from the Maple code given in Figures 4.3 

1739 and 4.4 of [3]_ (pp.68-69). It uses the efficient method of only calculating 

1740 state updates as they are required. 

1741 

1742 See also 

1743 ======== 

1744 

1745 lll_transform 

1746 

1747 References 

1748 ========== 

1749 

1750 .. [1] https://en.wikipedia.org/wiki/Lenstra–Lenstra–Lovász_lattice_basis_reduction_algorithm 

1751 .. [2] https://web.archive.org/web/20221029115428/https://web.cs.elte.hu/~lovasz/scans/lll.pdf 

1752 .. [3] Murray R. Bremner, "Lattice Basis Reduction: An Introduction to the LLL Algorithm and Its Applications" 

1753 

1754 """ 

1755 return DomainMatrix.from_rep(A.rep.lll(delta=delta)) 

1756 

1757 def lll_transform(A, delta=QQ(3, 4)): 

1758 """ 

1759 Performs the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm 

1760 and returns the reduced basis and transformation matrix. 

1761 

1762 Explanation 

1763 =========== 

1764 

1765 Parameters, algorithm and basis are the same as for :meth:`lll` except that 

1766 the return value is a tuple `(B, T)` with `B` the reduced basis and 

1767 `T` a transformation matrix. The original basis `A` is transformed to 

1768 `B` with `T*A == B`. If only `B` is needed then :meth:`lll` should be 

1769 used as it is a little faster. 

1770 

1771 Examples 

1772 ======== 

1773 

1774 >>> from sympy.polys.domains import ZZ, QQ 

1775 >>> from sympy.polys.matrices import DM 

1776 >>> X = DM([[1, 0, 0, 0, -20160], 

1777 ... [0, 1, 0, 0, 33768], 

1778 ... [0, 0, 1, 0, 39578], 

1779 ... [0, 0, 0, 1, 47757]], ZZ) 

1780 >>> B, T = X.lll_transform(delta=QQ(5, 6)) 

1781 >>> T * X == B 

1782 True 

1783 

1784 See also 

1785 ======== 

1786 

1787 lll 

1788 

1789 """ 

1790 reduced, transform = A.rep.lll_transform(delta=delta) 

1791 return DomainMatrix.from_rep(reduced), DomainMatrix.from_rep(transform)