Package controlsystems :: Module types
[hide private]

Source Code for Module controlsystems.types

   1  """Data Types for Control Systems 
   2   
   3  This module implements some data types for the Control Systems. For 
   4  example: Transfer Functions, State-Space models and others. 
   5   
   6  """ 
   7   
   8  __all__ = [ 
   9      'Polynomial', 
  10      'Matrix', 'ZerosMatrix', 'IdentityMatrix', 
  11      'TransferFunction', 
  12      'StateSpace', 
  13  ] 
  14   
  15  #TODO: implement zero-pole data type 
  16   
  17  from error import ControlSystemsError 
  18   
19 -class Polynomial(list):
20 """Polynomial type 21 22 This class implements the Polynomial type, based on the Python lists. 23 The Polynomial object is a list of coeficients. For example: 24 25 >>> a = Polynomial([1, 2, 3]) 26 >>> print a 27 x^2 + 2x + 3 28 29 """ 30 31 var = 'x' 32
33 - def __str__(self):
34 """String representation 35 36 This method returns the string representation of polynomials. 37 For example: 38 39 x^2 + 2x + 3 40 41 """ 42 43 #TODO: fix bug of first term negative 44 45 poly = self[:] 46 poly.reverse() 47 48 response = '' 49 50 for order in range(len(self) - 1, -1, -1): 51 52 coefficient = poly.pop() 53 54 if coefficient == 0: 55 continue 56 57 if order != len(self) - 1: 58 59 if coefficient > 0: 60 response += ' + ' 61 else: 62 response += ' - ' 63 64 if abs(coefficient) > 1 or order == 0: 65 response += str(abs(coefficient)) 66 67 if order > 0: 68 response += self.var 69 70 if order > 1: 71 response += '^' + str(order) 72 73 return response
74 75
76 - def __add__(self, term):
77 """Operation of addition 78 79 This method returns a Polynomial object with the result of the 80 addition of the Polynomial 'self' and the Polynomial 'term'. 81 For example: 82 83 >>> a = Polynomial([1, 2, 3]) 84 >>> b = Polynomial([2, 3, 4]) 85 >>> c = a + b 86 >>> print c 87 3x^2 + 5x + 7 88 >>> type(c) 89 <class 'controlsystems.types.Polynomial'> 90 91 """ 92 93 if not isinstance(term, Polynomial): 94 raise ControlSystemsError('Operands must be polynomials') 95 96 a = self[:] 97 a.reverse() 98 99 b = term[:] 100 b.reverse() 101 102 order = (len(a) > len(b)) and len(a) or len(b) 103 result = [0 for x in range(order)] 104 105 for x in range(order): 106 try: 107 result[x] += a[x] 108 except IndexError: 109 pass 110 try: 111 result[x] += b[x] 112 except IndexError: 113 pass 114 115 result.reverse() 116 117 return Polynomial(result)
118 119
120 - def __sub__(self, term):
121 """Operation of subtraction 122 123 This method returns a Polynomial object with the result of the 124 subtraction of the Polynomial 'self' and the Polynomial 'term'. 125 For example: 126 127 >>> a = Polynomial([2, 3, 4]) 128 >>> b = Polynomial([1, 2, 3]) 129 >>> c = a - b 130 >>> print c 131 x^2 + x + 1 132 >>> type(c) 133 <class 'controlsystems.types.Polynomial'> 134 135 This method is based on __add__ method 136 137 """ 138 139 term_aux = Polynomial([-x for x in term]) 140 141 return self.__add__(term_aux)
142 143
144 - def __mul__(self, term):
145 """Operation of multiplication of polynomials 146 147 This method returns a Polynomial object with the result of the 148 multiplication of the Polynomial 'self' and the Polynomial 149 'term'. For example: 150 151 >>> a = Polynomial([1, 2, 3]) 152 >>> b = Polynomial([2, 3, 4]) 153 >>> c = a * b 154 >>> print c 155 2x^4 + 7x^3 + 16x^2 + 17x + 12 156 >>> type(c) 157 <class 'controlsystems.types.Polynomial'> 158 159 """ 160 161 if not isinstance(term, Polynomial): 162 raise ControlSystemsError('Operands must be polynomials') 163 164 a = self[:] 165 b = term[:] 166 result = [] 167 j = 0 168 169 for x in a: 170 i = 0 171 for y in b: 172 result.append((i + j, x * y)) 173 i += 1 174 j += 1 175 176 ord_res = 0 177 178 for x, y in result: 179 if x > ord_res: 180 ord_res = x 181 182 resp = [0 for x in range(ord_res + 1)] 183 184 for x in range(ord_res + 1): 185 resp[x] = 0 186 for c, d in result: 187 if c == x: 188 resp[x] += d 189 190 return Polynomial(resp)
191 192
193 - def __div__(self, term):
194 """Operation of division of polynomials 195 196 This method returns a Polynomial object with the result of the 197 multiplication of the Polynomial 'self' and the Polynomial 198 'term'. 199 200 Not implemented yet. 201 202 """ 203 204 #TODO: implement division of polynomials 205 206 if not isinstance(term, Polynomial): 207 raise ControlSystemsError('Operands must be polynomials') 208 209 if len(term) > len(self): 210 raise ControlSystemsError('Invalid sizes to division') 211 212 raise NotImplementedError
213 214
215 - def mult(self, val):
216 """Operation of multiplication between numbers and polynomials 217 218 This method returns a Polynomial object with the result of the 219 multiplication of the Polynomial 'self' and the number 'val'. 220 For example: 221 222 >>> a = Polynomial([1, 2, 3]) 223 >>> b = a.mult(5) 224 >>> print b 225 5x^2 + 10x + 15 226 >>> type(b) 227 <class 'controlsystems.types.Polynomial'> 228 229 """ 230 231 #TODO: check if 'val' is a Real number 232 233 x = [] 234 235 for i in range(len(self)): 236 x.append(self[i] * val) 237 238 return Polynomial(x)
239 240
241 - def Zero(self, order):
242 """Auxiliary method 243 244 This method returns a Polynomial object initialized with zeros. 245 For example: 246 247 >>> a = Polynomial() 248 >>> a.Zero(4) 249 [0, 0, 0, 0] 250 251 """ 252 253 #TODO: implement it in a better way 254 255 zero = [] 256 257 for i in range(order): 258 zero.append(0) 259 260 return Polynomial(zero)
261 262
263 -class Matrix(list):
264 """Matrix type 265 266 This class implements the Matrix type, based on the Python lists. 267 The matrix object is a list of lists and have 2 properties ('cols' 268 and 'rows'), that store the sizes of the matrix. For example: 269 270 >>> a = Matrix([ 271 ... [1, 2, 3], 272 ... [2, 3, 4], 273 ... [3, 4, 5], 274 ... ]) 275 >>> 276 >>> print a 277 1 2 3 278 2 3 4 279 3 4 5 280 >>> 281 >>> a.rows 282 3 283 >>> a.cols 284 3 285 286 """ 287
288 - def __init__(self, mat):
289 """Initialization of Matrix object 290 291 This method initialize a Matrix object, calculating the values 292 of the properties 'cols' and 'rows'. 293 294 """ 295 296 list.__init__(self, mat) 297 self.rows = len(self) 298 self.cols = [] 299 300 for i in range(self.rows): 301 302 try: 303 if len(self[i]) == self.cols or self.cols == []: 304 self.cols = len(self[i]) 305 else: 306 raise ControlSystemsError('Invalid Matrix size') 307 except: 308 raise ControlSystemsError('Invalid Matrix size')
309 310
311 - def __str__(self):
312 """String representation 313 314 This method returns the string representation of matrices. For 315 example: 316 317 1 2 3 318 2 3 4 319 3 4 5 320 321 """ 322 323 ret = '' 324 325 for i in range(self.rows): 326 for j in range(self.cols): 327 ret += '%s\t' % self[i][j] 328 if i < (self.rows - 1): 329 ret += '\n' 330 331 return ret
332 333
334 - def __call__(self, row, col=None):
335 """Callable object 336 337 This method returns a row Matrix object if a parameter is used 338 and a number if two parameters are used. For example: 339 340 >>> a = Matrix([ 341 ... [1, 2], 342 ... [3, 4], 343 ... ]) 344 >>> 345 >>> b = a(1) 346 >>> print b 347 3 4 348 >>> type(b) 349 <class 'controlsystems.types.Matrix'> 350 >>> 351 >>> c = a(0, 0) 352 >>> print c 353 1 354 >>> type(c) 355 <type 'int'> 356 357 """ 358 359 if col == None: 360 return Matrix([self[row]]) 361 else: 362 return self[row][col]
363 364
365 - def __add__(self, mat):
366 """Operation of addition 367 368 This method returns a Matrix object with the result of the 369 addition of the Matrix 'self' and the Matrix 'mat'. For example: 370 371 >>> a = Matrix([ 372 ... [1, 2], 373 ... [3, 4], 374 ... ]) 375 >>> 376 >>> b = Matrix([ 377 ... [2, 3], 378 ... [4, 5], 379 ... ]) 380 >>> 381 >>> c = a + b 382 >>> print c 383 3 5 384 7 9 385 >>> type(c) 386 <class 'controlsystems.types.Matrix'> 387 388 """ 389 390 if not isinstance(mat, Matrix): 391 raise ControlSystemsError('Operands must be matrices') 392 393 rows = self.rows > mat.rows and self.rows or mat.rows 394 cols = self.cols > mat.cols and self.cols or mat.cols 395 396 res = ZerosMatrix(rows, cols) 397 398 for i in range(rows): 399 for j in range(cols): 400 try: 401 res[i][j] += self[i][j] 402 except IndexError: 403 pass 404 try: 405 res[i][j] += mat[i][j] 406 except IndexError: 407 pass 408 409 return res
410 411
412 - def __sub__(self, mat):
413 """Operation of subtraction 414 415 This method returns a Matrix object with the result of the 416 subtraction of the Matrix 'self' and the Matrix 'mat'. For 417 example: 418 419 >>> a = Matrix([ 420 ... [2, 3], 421 ... [4, 5], 422 ... ]) 423 >>> 424 >>> b = Matrix([ 425 ... [1, 2], 426 ... [3, 4], 427 ... ]) 428 >>> 429 >>> c = a - b 430 >>> print c 431 1 1 432 1 1 433 >>> 434 >>> type(c) 435 <class 'controlsystems.types.Matrix'> 436 437 This method is based on __add__ method 438 439 """ 440 441 aux = mat.mult(-1) 442 443 return self.__add__(aux)
444 445
446 - def __mul__(self, mat):
447 """Operation of multiplication of polynomials 448 449 This method returns a Matrix object with the result of the 450 multiplication of the Matrix 'self' and the Matrix 'mat'. For 451 example: 452 453 >>> a = Matrix([ 454 ... [1, 2], 455 ... [3, 4], 456 ... ]) 457 >>> 458 >>> b = Matrix([ 459 ... [2, 3], 460 ... [4, 5], 461 ... ]) 462 >>> 463 >>> c = a * b 464 >>> print c 465 10 13 466 22 29 467 >>> 468 >>> type(c) 469 <class 'controlsystems.types.Matrix'> 470 471 """ 472 473 if not isinstance(mat, Matrix): 474 raise ControlSystemsError('Operands must be matrices') 475 476 if self.cols != mat.rows: 477 raise ControlSystemsError('Invalid Matrices size for mult.') 478 479 res = ZerosMatrix(self.rows, mat.cols) 480 481 for i in range(self.rows): 482 for j in range(mat.cols): 483 for aux in range(self.cols): 484 res[i][j] += self[i][aux] * mat[aux][j] 485 486 return res
487 488
489 - def mult(self, num):
490 """Operation of multiplication between numbers and matrices 491 492 This method returns a Matrix object with the result of the 493 multiplication of the Matrix 'self' and the number 'num'. For 494 example: 495 496 >>> a = Matrix([ 497 ... [1, 2], 498 ... [3, 4], 499 ... ]) 500 >>> 501 >>> b = a.mult(5) 502 >>> print b 503 5 10 504 15 20 505 >>> 506 >>> type(b) 507 <class 'controlsystems.types.Matrix'> 508 509 """ 510 511 #TODO: check if 'num' is a Real number 512 513 aux = ZerosMatrix(self.rows, self.cols) 514 515 for i in range(self.rows): 516 for j in range(self.cols): 517 aux[i][j] = self[i][j] * num 518 519 return aux
520
521 - def transpose(self):
522 """Transpose of matrix 523 524 This method returns a Matrix object with the transpose of 525 the Matrix 'self'. For example: 526 527 >>> a = Matrix([ 528 ... [1, 2], 529 ... [3, 4], 530 ... ]) 531 >>> 532 >>> b = a.transpose() 533 >>> print b 534 1 3 535 2 4 536 >>> 537 >>> type(b) 538 <class 'controlsystems.types.Matrix'> 539 540 """ 541 542 aux = ZerosMatrix(self.cols, self.rows) 543 544 for i in range(self.rows): 545 for j in range(self.cols): 546 aux[j][i] = self[i][j] 547 548 return aux
549 550
551 -def ZerosMatrix(rows, cols=None):
552 """Matrix of zeros 553 554 This method returns a Matrix object filled by zeros. For example: 555 556 >>> a = ZerosMatrix(2, 4) 557 >>> print a 558 0 0 0 0 559 0 0 0 0 560 >>> 561 >>> b = ZerosMatrix(2) 562 >>> print b 563 0 0 564 0 0 565 >>> 566 >>> type(b) 567 <class 'controlsystems.types.Matrix'> 568 569 """ 570 571 if cols == None: 572 cols = rows 573 574 aux = [] 575 576 for i in range(rows): 577 aux.append([]) 578 for j in range(cols): 579 aux[i].append(0) 580 581 return Matrix(aux)
582 583
584 -def IdentityMatrix(order):
585 """Matrix Identity 586 587 This method returns a Matrix object with zeros, and ones only on 588 main diagonal. For example: 589 590 >>> a = IdentityMatrix(4) 591 >>> print a 592 1 0 0 0 593 0 1 0 0 594 0 0 1 0 595 0 0 0 1 596 >>> 597 >>> type(a) 598 <class 'controlsystems.types.Matrix'> 599 600 """ 601 602 aux = [] 603 604 for i in range(order): 605 aux.append([]) 606 for j in range(order): 607 if i == j: 608 aux[i].append(1) 609 else: 610 aux[i].append(0) 611 612 return Matrix(aux)
613 614
615 -class TransferFunction(object):
616 """TransferFunction type 617 618 This class implements the TransferFunction type, based on the 619 Polynomial type. The TransferFunction object uses 2 polynomials 620 to store the numerator and the denominator. For example: 621 622 >>> a = TransferFunction([1], [1, 2, 3]) 623 >>> print a 624 Transfer Function: 625 . 626 . 1 627 ------------ 628 s^2 + 2s + 3 629 630 """ 631
632 - def __init__(self, num, den):
633 """Initialization of TransferFunction object 634 635 This method initialize a TransferFunction object. 636 637 """ 638 639 self.num = Polynomial(num) 640 self.num.var = 's' 641 642 self.den = Polynomial(den) 643 self.den.var = 's'
644 645
646 - def __str__(self):
647 """String representation 648 649 This method returns the string representation of the transfer 650 functions. For example: 651 652 Transfer Function: 653 . 654 . 1 655 ------------ 656 s^2 + 2s + 3 657 658 """ 659 660 len_max = (len(str(self.num)) > len(str(self.den))) and \ 661 len(str(self.num)) or len(str(self.den)) 662 663 response = 'Transfer Function:\n\n' 664 response += str(self.num).center(len_max) + '\n' 665 response += '-' * len_max + '\n' 666 response += str(self.den).center(len_max) + '\n' 667 668 return response
669 670
671 - def __add__(self, tf):
672 """Operation of addition 673 674 This method returns a TransferFunction object with the result 675 of the addition of the TransferFunction 'self' and the 676 TransferFunction 'tf'. For example: 677 678 >>> a = TransferFunction([1], [1, 2, 3]) 679 >>> b = TransferFunction([1], [2, 3, 4]) 680 >>> c = a + b 681 >>> print c 682 Transfer Function: 683 . 684 . 3s^2 + 5s + 7 685 ------------------------------ 686 2s^4 + 7s^3 + 16s^2 + 17s + 12 687 . 688 >>> type(c) 689 <class 'controlsystems.types.TransferFunction'> 690 691 """ 692 693 num = (self.num*tf.den) + (tf.num*self.den) 694 den = self.den * tf.den 695 return TransferFunction(num, den)
696 697
698 - def __sub__(self, tf):
699 """Operation of subtraction 700 701 This method returns a TransferFunction object with the result 702 of the subtraction of the TransferFunction 'self' and the 703 TransferFunction 'tf'. For example: 704 705 >>> a = TransferFunction([1], [1, 2, 3]) 706 >>> b = TransferFunction([1], [2, 3, 4]) 707 >>> c = a - b 708 >>> print c 709 Transfer Function: 710 . 711 . s^2 + s + 1 712 ------------------------------ 713 2s^4 + 7s^3 + 16s^2 + 17s + 12 714 . 715 >>> type(c) 716 <class 'controlsystems.types.TransferFunction'> 717 718 """ 719 720 num = (self.num*tf.den) - (tf.num*self.den) 721 den = self.den * tf.den 722 723 return TransferFunction(num, den)
724 725
726 - def __mul__(self, tf):
727 """Operation of multiplication of polynomials 728 729 This method returns a TransferFunction object with the result 730 of the multiplication of the TransferFunction 'self'and the 731 TransferFunction 'tf'. for example: 732 733 >>> a = TransferFunction([1], [1, 2, 3]) 734 >>> b = TransferFunction([1], [2, 3, 4]) 735 >>> c = a * b 736 >>> print c 737 Transfer Function: 738 . 739 . 1 740 ------------------------------ 741 2s^4 + 7s^3 + 16s^2 + 17s + 12 742 . 743 >>> type(c) 744 <class 'controlsystems.types.TransferFunction'> 745 746 """ 747 748 num = self.num * tf.num 749 den = self.den * tf.den 750 751 return TransferFunction(num, den)
752 753
754 - def simplify(self):
755 """Simplify Transfer Functions 756 757 This method returns a TransferFunction object with the transfer 758 function simplified. For example: 759 760 >>> a = TransferFunction([3], [3, 6, 9]) 761 >>> print a 762 Transfer Function: 763 . 764 . 3 765 ------------- 766 3s^2 + 6s + 9 767 . 768 >>> b = a.simplify() 769 >>> print b 770 Transfer Function: 771 . 772 . 1 773 ------------ 774 s^2 + 2s + 3 775 . 776 >>> type(b) 777 <class 'controlsystems.types.TransferFunction'> 778 779 Attention: This method is far from perfect, and don't simplify 780 all possible expressions, but it's usable. 781 782 """ 783 784 #TODO: improve this method 785 786 num = self.num[:] 787 den = self.den[:] 788 789 mdc = 1 790 791 for i in range(1, int(max([max(num), max(den)]))): 792 793 die = 0 794 795 if len(self.num) == 1: 796 mdc = self.num[0] 797 798 else: 799 800 for j in range(len(num)): 801 if num[j] % i != 0: 802 die = 1 803 804 for j in range(len(den)): 805 if den[j] % i != 0: 806 die = 1 807 808 if not die: 809 mdc = i 810 811 return TransferFunction(num, den).div(mdc)
812 813
814 - def mult(self, a):
815 """Operation of multiplication between numbers and transfer 816 functions 817 818 This method returns a TransferFunction object with the result 819 of the multiplication of the TransferFunction 'self' and the 820 number 'a'. For example: 821 822 >>> a = TransferFunction([1], [1, 2, 3]) 823 >>> b = a.mult(5) 824 >>> print b 825 Transfer Function: 826 . 827 . 5 828 --------------- 829 5s^2 + 10s + 15 830 831 >>> type(b) 832 <class 'controlsystems.types.TransferFunction'> 833 834 """ 835 836 #TODO: check if 'a' is a Real number 837 838 return TransferFunction(self.num.mult(a), self.den.mult(a))
839 840
841 - def div(self, a):
842 """Operation of division of a transfer function per a number 843 844 This method returns a TransferFunction object with the result 845 of the division of the coefficients of the TransferFunction 846 'self' and the number 'a'. For example: 847 848 >>> a = TransferFunction([3], [3, 6, 9]) 849 >>> print a 850 Transfer Function: 851 . 852 . 3 853 ------------- 854 3s^2 + 6s + 9 855 . 856 >>> b = a.div(3) 857 >>> print b 858 Transfer Function: 859 . 860 . 1 861 ------------ 862 s^2 + 2s + 3 863 . 864 >>> type(b) 865 <class 'controlsystems.types.TransferFunction'> 866 867 """ 868 869 num = self.num[:] 870 den = self.den[:] 871 872 for i in range(len(num)): 873 num[i] /= a 874 875 for i in range(len(den)): 876 den[i] /= a 877 878 return TransferFunction(num, den)
879 880
881 - def feedback_unit(self):
882 """Feedback with unit gain 883 884 This method returns a TransferFunction object with the result 885 of the unit gain feedback of the transfer function. For example: 886 887 >>> a = TransferFunction([1], [1, 2, 3]) 888 >>> b = a.feedback_unit() 889 >>> print b 890 Transfer Function: 891 . 892 . s^2 + 2s + 3 893 ----------------------------- 894 s^4 + 4s^3 + 11s^2 + 14s + 12 895 . 896 >>> type(b) 897 <class 'controlsystems.types.TransferFunction'> 898 899 Attention: This method is far from perfect, and don't simplify 900 the expressions, but it's usable. 901 902 """ 903 904 #TODO: improve this method 905 906 aux = TransferFunction([1], [1]) + self 907 908 return TransferFunction(self.num * aux.den, self.den * aux.num)
909 910
911 -class StateSpace(object):
912 """StateSpace type 913 914 This class implements the StateSpace type, based on the Matrix type. 915 The state-space object uses 4 matrices, like: 916 917 >>> tf = TransferFunction([1], [1, 2, 3]) 918 >>> a = StateSpace(tf) 919 >>> print a 920 State-Space model: 921 . 922 Matrix A: 923 0 1 924 -3 -2 925 . 926 Matrix B: 927 0 928 1 929 . 930 Matrix C: 931 1 0 932 . 933 Matrix D: 934 0 935 936 """ 937 938 #TODO: fix D matrix behaviour 939 #TODO: improve state-space operations 940
941 - def __init__(self, a, b = None, c = None, d = [[0]]):
942 """Initialization of StateSpace object 943 944 This method initialize a StateSpace object, using matrices or 945 a TransferFunction object. 946 947 """ 948 949 if isinstance(a, TransferFunction): 950 951 if len(a.num) > len(a.den): 952 raise ControlSystemsError('More zeros than poles.') 953 954 self.__tf2ss(a) 955 956 else: 957 self.__ss(a, b, c, d)
958 959
960 - def __ss(self, a, b, c, d):
961 """Initialization of StateSpace object using matrices 962 963 This method initialize a StateSpace object, using 4 matrices. 964 965 """ 966 967 self.a = Matrix(a) 968 self.b = Matrix(b) 969 self.c = Matrix(c) 970 self.d = Matrix(d)
971 972
973 - def __tf2ss(self, tf):
974 """Initialization of StateSpace object using transfer function 975 976 This method initialize a StateSpace object, using a 977 TransferFunction object. 978 979 """ 980 981 if len(tf.num) == 0 or len(tf.den) == 0: 982 raise ControlSystemsError('Invalid Transfer Function') 983 984 #preparing A 985 order = len(tf.den) - 1 986 a = ZerosMatrix(order) 987 for i in range(order-1): 988 for j in range(1, order): 989 if (i+1) == j: 990 a[i][j] = 1 991 den = tf.den[:] 992 den.reverse() 993 for i in range(order): 994 a[order-1][i] = -den[i] 995 996 #preparing B 997 b = ZerosMatrix(order, 1) 998 if len(tf.num) == 1: 999 b[order-1][0] = tf.num[0] 1000 else: 1001 b[order-1][0] = 1 1002 1003 #preparing C 1004 c = ZerosMatrix(1, order) 1005 if len(tf.num) == 1: 1006 c[0][0] = 1 1007 else: 1008 num = tf.num[:] 1009 num.reverse() 1010 for i in range(order): 1011 try: 1012 c[0][i] = num[i] 1013 except IndexError: 1014 pass 1015 1016 #TODO: fix D 1017 self.__ss(a, b, c, [[0]])
1018 1019
1020 - def __str__(self):
1021 """String representation 1022 1023 This method returns the string representation of the state-space 1024 models. For example: 1025 1026 State-Space model: 1027 . 1028 Matrix A: 1029 0 1 1030 -3 -2 1031 . 1032 Matrix B: 1033 0 1034 1 1035 . 1036 Matrix C: 1037 1 0 1038 . 1039 Matrix D: 1040 0 1041 1042 """ 1043 1044 ret = 'State-Space model:\n\nMatrix A:\n' 1045 ret += str(self.a) + '\n\n' 1046 ret += 'Matrix B:\n' 1047 ret += str(self.b) + '\n\n' 1048 ret += 'Matrix C:\n' 1049 ret += str(self.c) + '\n\n' 1050 ret += 'Matrix D:\n' 1051 ret += str(self.d) + '\n' 1052 1053 return ret
1054