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
16
17 from error import ControlSystemsError
18
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
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
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
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
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
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
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
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
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
254
255 zero = []
256
257 for i in range(order):
258 zero.append(0)
259
260 return Polynomial(zero)
261
262
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
837
838 return TransferFunction(self.num.mult(a), self.den.mult(a))
839
840
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
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
905
906 aux = TransferFunction([1], [1]) + self
907
908 return TransferFunction(self.num * aux.den, self.den * aux.num)
909
910
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
939
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
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
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
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
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
1017 self.__ss(a, b, c, [[0]])
1018
1019
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