K i$ddlmZddlmZmZmZmZddlmZddl m Z ddl m Z ddl mZddlmZddlmZdd lmZdd lmZdd lmZmZmZdd lmZdd lmZddlm Z m!Z!ddl"m#Z#ddl$m%Z%m&Z&ddl'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-ddl.m/Z/m0Z0ddl1m2Z2m3Z3ddl4m5Z5m6Z6ddl7m8Z8ddl9m:Z:m;Z;ddlm?Z?ddl@mAZAddlBmCZCmDZDddlEmFZFgdZGdZHdZIdZJd ZKd!ZLd"ZMd#ZNGd$d%e eZOGd&d'eOZPGd(d)eOZQePeP_ReQeQ_Rd*ZSd+ZTGd,d-ePZUGd.d/eUZVd0ZWd1ZXGd2d3ePZYd4ZZGd5d6eQZ[Gd7d8ePZ\Gd9d:eQZ]Gd;dd?eQZ`d@ZaGdAdBeQZbGdCdDeOZcyE)F)Type)IntervalnumerRationalsolveset)Add)BasicTuple) EvalfMixin)Exprexpand) fuzzy_and)Mul)Ipioo)Pow)S)DummySymbol)Abs)sympify_sympify)MatrixImmutableMatrixImmutableDenseMatrixeye ShapeErrorzeros)explog)MatMulMatAdd)Polyrootof)roots)canceldegree)limit) filldedent) linodesolve)linsolvelinear_eq_to_matrix) prec_to_dps)TransferFunction PIDControllerSeries MIMOSeriesParallel MIMOParallelFeedback MIMOFeedbackTransferFunctionMatrix StateSpacegbtbilinear forward_diff backward_diff phase_margin gain_marginct||d}t|}t||k7r#t|Dcgc]}t |||}}|Scc}w)z4 like roots, but works on higher-order polynomials. T)multiple)r(r*lenranger')polyvarrnks _/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/physics/control/lti.py_rootsrK"sN dC$'At A 1v{+08 4aVD#q ! 4 4 H 5sA c |js td|}|j}|}|jj |j }|j j |j }t|jd}tt|t|dz }tt|dddtt|D cgc]+\} } ||| z z| z|dz | zz||zdz|z || z zz-c} } } tt|dddtt|D cgc]+\} } ||| z z| z|dz | zz||zdz|z || z zz-c} } } | j |j } | j |j }|d}| Dcgc]}||z  } }|Dcgc]}||z  }}| |fScc} } wcc} } wcc}wcc}w)a  Returns falling coefficients of H(z) from numerator and denominator. Explanation =========== Where H(z) is the corresponding discretized transfer function, discretized with the generalised bilinear transformation method. H(z) is obtained from the continuous transfer function H(s) by substituting $s(z) = \frac{z-1}{T(\alpha z + (1-\alpha))}$ into H(s), where T is the sample period. Coefficients are falling, i.e. $H(z) = \frac{az+b}{cz+d}$ is returned as [a, b], [c, d]. Examples ======== >>> from sympy.physics.control.lti import TransferFunction, gbt >>> from sympy.abc import s, L, R, T >>> tf = TransferFunction(1, s*L + R, s) >>> numZ, denZ = gbt(tf, T, 0.5) >>> numZ [T/(2*(L + R*T/2)), T/(2*(L + R*T/2))] >>> denZ [1, (-L + R*T/2)/(L + R*T/2)] >>> numZ, denZ = gbt(tf, T, 0) >>> numZ [T/L] >>> denZ [1, (-L + R*T)/L] >>> numZ, denZ = gbt(tf, T, 1) >>> numZ [T/(L + R*T), 0] >>> denZ [1, -L/(L + R*T)] >>> numZ, denZ = gbt(tf, T, 0.3) >>> numZ [3*T/(10*(L + 3*R*T/10)), 7*T/(10*(L + 3*R*T/10))] >>> denZ [1, (-L + 7*R*T/10)/(L + 3*R*T/10)] References ========== .. [1] https://www.polyu.edu.hk/ama/profile/gfzhang/Research/ZCC09_IJC.pdf z!Not implemented for MIMO systems.iNr)is_SISONotImplementedErrorrFnumas_poly all_coeffsdenrlimit_denominatormaxrCrziprD)tf sample_peralphaTsznpdpNcirQrT num_coefs den_coefsparacoefs rJr;r;*sf ::!"EFFA A A   % % 'B   % % 'B UO - -d 3E CGSW!A VYZ\]a_a]aZbdijmnpjqdrVsudaQRQqSA1q(EAIME,AQqS+IIu vC VYZ\]a_a]aZbdijmnpjqdrVsudaQRQqSA1q(EAIME,AQqS+IIu vC A))+I A))+I Q G. G3c8t||tjS)a5 Returns falling coefficients of H(z) from numerator and denominator. Explanation =========== Where H(z) is the corresponding discretized transfer function, discretized with the bilinear transform method. H(z) is obtained from the continuous transfer function H(s) by substituting $s(z) = \frac{2}{T}\frac{z-1}{z+1}$ into H(s), where T is the sample period. Coefficients are falling, i.e. $H(z) = \frac{az+b}{cz+d}$ is returned as [a, b], [c, d]. Examples ======== >>> from sympy.physics.control.lti import TransferFunction, bilinear >>> from sympy.abc import s, L, R, T >>> tf = TransferFunction(1, s*L + R, s) >>> numZ, denZ = bilinear(tf, T) >>> numZ [T/(2*(L + R*T/2)), T/(2*(L + R*T/2))] >>> denZ [1, (-L + R*T/2)/(L + R*T/2)] )r;rHalfrXrYs rJr<r<v8 r:qvv &&c8t||tjS)a  Returns falling coefficients of H(z) from numerator and denominator. Explanation =========== Where H(z) is the corresponding discretized transfer function, discretized with the forward difference transform method. H(z) is obtained from the continuous transfer function H(s) by substituting $s(z) = \frac{z-1}{T}$ into H(s), where T is the sample period. Coefficients are falling, i.e. $H(z) = \frac{az+b}{cz+d}$ is returned as [a, b], [c, d]. Examples ======== >>> from sympy.physics.control.lti import TransferFunction, forward_diff >>> from sympy.abc import s, L, R, T >>> tf = TransferFunction(1, s*L + R, s) >>> numZ, denZ = forward_diff(tf, T) >>> numZ [T/L] >>> denZ [1, (-L + R*T)/L] )r;rZeroris rJr=r=rjrkc8t||tjS)a Returns falling coefficients of H(z) from numerator and denominator. Explanation =========== Where H(z) is the corresponding discretized transfer function, discretized with the backward difference transform method. H(z) is obtained from the continuous transfer function H(s) by substituting $s(z) = \frac{z-1}{Tz}$ into H(s), where T is the sample period. Coefficients are falling, i.e. $H(z) = \frac{az+b}{cz+d}$ is returned as [a, b], [c, d]. Examples ======== >>> from sympy.physics.control.lti import TransferFunction, backward_diff >>> from sympy.abc import s, L, R, T >>> tf = TransferFunction(1, s*L + R, s) >>> numZ, denZ = backward_diff(tf, T) >>> numZ [T/(L + R*T), 0] >>> denZ [1, -L/(L + R*T)] )r;rOneris rJr>r>s8 r:quu %%rkc ddlm}t|ts t dt dd}t |z}|j}t|j}|jtr td|dkDr t d |j|j|i}d tt!|d z}t#t%||t'dt(d }t|dk(r t+d } n@|d} ||t+dzt,z j|| it+dzdz} | dk\r| dz } | S)am Returns the phase margin of a continuous time system. Only applicable to Transfer Functions which can generate valid bode plots. Raises ====== NotImplementedError When time delay terms are present in the system. ValueError When a SISO LTI system is not passed. When more than one free symbol is present in the system. The only variable in the transfer function should be the variable of the Laplace transform. Examples ======== >>> from sympy.physics.control import TransferFunction, phase_margin >>> from sympy.abc import s >>> tf = TransferFunction(1, s**3 + 2*s**2 + s, s) >>> phase_margin(tf) 180*(-pi + atan((-1 + (-2*18**(1/3)/(9 + sqrt(93))**(1/3) + 12**(1/3)*(9 + sqrt(93))**(1/3))**2/36)/(-12**(1/3)*(9 + sqrt(93))**(1/3)/3 + 2*18**(1/3)/(3*(9 + sqrt(93))**(1/3)))))/pi + 180 >>> phase_margin(tf).n() 21.3863897518751 >>> tf1 = TransferFunction(s**3, s**2 + 5*s, s) >>> phase_margin(tf1) -180 + 180*(atan(sqrt(2)*(-51/10 - sqrt(101)/10)*sqrt(1 + sqrt(101))/(2*(sqrt(101)/2 + 51/2))) + pi)/pi >>> phase_margin(tf1).n() -25.1783920627277 >>> tf2 = TransferFunction(1, s + 1, s) >>> phase_margin(tf2) -180 See Also ======== gain_margin References ========== .. [1] https://en.wikipedia.org/wiki/Phase_margin r)arg1Margins are only applicable for SISO LTI systems.wTreal> D4,,- xx}!"`aa A 78 8YY D) *F SVb ! !C8CXat%DEFG G  tWaj6{1S6!"$**Bs84qv= D Sy #X Irkc zt|ts tdtdd}t|z}|j }t |j}|jtr td|dkDr td|j|j|i}dtt|d z}|}tt!t#|j%dj'|t)d t*d }t |d k(rt*} | S|d } |j|| i } | S) a Returns the gain margin of a continuous time system. Only applicable to Transfer Functions which can generate valid bode plots. Raises ====== NotImplementedError When time delay terms are present in the system. ValueError When a SISO LTI system is not passed. When more than one free symbol is present in the system. The only variable in the transfer function should be the variable of the Laplace transform. Examples ======== >>> from sympy.physics.control import TransferFunction, gain_margin >>> from sympy.abc import s >>> tf = TransferFunction(1, s**3 + 2*s**2 + s, s) >>> gain_margin(tf) 20*log(2)/log(10) >>> gain_margin(tf).n() 6.02059991327962 >>> tf1 = TransferFunction(s**3, s**2 + 5*s, s) >>> gain_margin(tf1) oo See Also ======== phase_margin References ========== https://en.wikipedia.org/wiki/Bode_plot rrrsTrtrvrMrwrxryrrz)r~rrrrrrCrrr"rPrrFr#rrrr as_real_imagr)rr) rrrrrrrphase phase_solgmwcgs rJr@r@#s*Z f5 6LMM s B R4D >> D4,,- xx}!"`aa A 78 8YY D) *F SVb ! !C EXeE$6$6$8$;$B$B$DEb(STVXfjJklmI I!  Ilhh3x  IrkcNeZdZUdZeed<fdZedZe dZ xZ S)LinearTimeInvariantzCA common class for all the Linear Time-Invariant Dynamical Systems._clstypecX|tur tdtt||g|i|S)Nz5The LTICommon class is not meant to be used directly.)rrPsuper__new__)clsrkwargs __class__s rJrzLinearTimeInvariant.__new__rs5 % %%&]^ ^(#6sNVNvNNrkc|s tdtfd|Dstdjd|Dchc]}|j}}t |dk7r!tt dt |dycc}w)Nz#At least 1 argument must be passed.c3JK|]}t|jywN)r~r).0rqrs rJ z2LinearTimeInvariant._check_args..{sAS:c3<<0As #zAll arguments must be of type .rMzw All transfer functions should use the same complex variable of the Laplace transform. z( different values found.)rall TypeErrorrrFrCr,)rargsrqvar_sets` rJ _check_argszLinearTimeInvariant._check_argswsBC CADAA.wrapper($r($;<! !(( (rkrrrs` rJ_check_other_SISOr) Nrkcfd}|S)NcFt|dtstS|i|Sr)r~rrrs rJrz"_check_other_MIMO..wrapperrrkrrs` rJ_check_other_MIMOrrrkc.eZdZdZfdZeddZedZedZe dZ e dZ e dZ d Z d Zd Zd Zd ZdZdZdZdZdZdZdZdZdZdZeZdZeZdZdZ e dZ!e dZ"e dZ#dZ$xZ%S) r1a A class for representing LTI (Linear, time-invariant) systems that can be strictly described by ratio of polynomials in the Laplace transform complex variable. The arguments are ``num``, ``den``, and ``var``, where ``num`` and ``den`` are numerator and denominator polynomials of the ``TransferFunction`` respectively, and the third argument is a complex variable of the Laplace transform used by these polynomials of the transfer function. ``num`` and ``den`` can be either polynomials or numbers, whereas ``var`` has to be a :py:class:`~.Symbol`. Explanation =========== Generally, a dynamical system representing a physical model can be described in terms of Linear Ordinary Differential Equations like - $b_{m}y^{\left(m\right)}+b_{m-1}y^{\left(m-1\right)}+\dots+b_{1}y^{\left(1\right)}+b_{0}y= a_{n}x^{\left(n\right)}+a_{n-1}x^{\left(n-1\right)}+\dots+a_{1}x^{\left(1\right)}+a_{0}x$ Here, $x$ is the input signal and $y$ is the output signal and superscript on both is the order of derivative (not exponent). Derivative is taken with respect to the independent variable, $t$. Also, generally $m$ is greater than $n$. It is not feasible to analyse the properties of such systems in their native form therefore, we use mathematical tools like Laplace transform to get a better perspective. Taking the Laplace transform of both the sides in the equation (at zero initial conditions), we get - $\mathcal{L}[b_{m}y^{\left(m\right)}+b_{m-1}y^{\left(m-1\right)}+\dots+b_{1}y^{\left(1\right)}+b_{0}y]= \mathcal{L}[a_{n}x^{\left(n\right)}+a_{n-1}x^{\left(n-1\right)}+\dots+a_{1}x^{\left(1\right)}+a_{0}x]$ Using the linearity property of Laplace transform and also considering zero initial conditions (i.e. $y(0^{-}) = 0$, $y'(0^{-}) = 0$ and so on), the equation above gets translated to - $b_{m}\mathcal{L}[y^{\left(m\right)}]+\dots+b_{1}\mathcal{L}[y^{\left(1\right)}]+b_{0}\mathcal{L}[y]= a_{n}\mathcal{L}[x^{\left(n\right)}]+\dots+a_{1}\mathcal{L}[x^{\left(1\right)}]+a_{0}\mathcal{L}[x]$ Now, applying Derivative property of Laplace transform, $b_{m}s^{m}\mathcal{L}[y]+\dots+b_{1}s\mathcal{L}[y]+b_{0}\mathcal{L}[y]= a_{n}s^{n}\mathcal{L}[x]+\dots+a_{1}s\mathcal{L}[x]+a_{0}\mathcal{L}[x]$ Here, the superscript on $s$ is **exponent**. Note that the zero initial conditions assumption, mentioned above, is very important and cannot be ignored otherwise the dynamical system cannot be considered time-independent and the simplified equation above cannot be reached. Collecting $\mathcal{L}[y]$ and $\mathcal{L}[x]$ terms from both the sides and taking the ratio $\frac{ \mathcal{L}\left\{y\right\} }{ \mathcal{L}\left\{x\right\} }$, we get the typical rational form of transfer function. The numerator of the transfer function is, therefore, the Laplace transform of the output signal (The signals are represented as functions of time) and similarly, the denominator of the transfer function is the Laplace transform of the input signal. It is also a convention to denote the input and output signal's Laplace transform with capital alphabets like shown below. $H(s) = \frac{Y(s)}{X(s)} = \frac{ \mathcal{L}\left\{y(t)\right\} }{ \mathcal{L}\left\{x(t)\right\} }$ $s$, also known as complex frequency, is a complex variable in the Laplace domain. It corresponds to the equivalent variable $t$, in the time domain. Transfer functions are sometimes also referred to as the Laplace transform of the system's impulse response. Transfer function, $H$, is represented as a rational function in $s$ like, $H(s) =\ \frac{a_{n}s^{n}+a_{n-1}s^{n-1}+\dots+a_{1}s+a_{0}}{b_{m}s^{m}+b_{m-1}s^{m-1}+\dots+b_{1}s+b_{0}}$ Parameters ========== num : Expr, Number The numerator polynomial of the transfer function. den : Expr, Number The denominator polynomial of the transfer function. var : Symbol Complex variable of the Laplace transform used by the polynomials of the transfer function. Raises ====== TypeError When ``var`` is not a Symbol or when ``num`` or ``den`` is not a number or a polynomial. ValueError When ``den`` is zero. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction(s + a, s**2 + s + 1, s) >>> tf1 TransferFunction(a + s, s**2 + s + 1, s) >>> tf1.num a + s >>> tf1.den s**2 + s + 1 >>> tf1.var s >>> tf1.args (a + s, s**2 + s + 1, s) Any complex variable can be used for ``var``. >>> tf2 = TransferFunction(a*p**3 - a*p**2 + s*p, p + a**2, p) >>> tf2 TransferFunction(a*p**3 - a*p**2 + p*s, a**2 + p, p) >>> tf3 = TransferFunction((p + 3)*(p - 1), (p - 1)*(p + 5), p) >>> tf3 TransferFunction((p - 1)*(p + 3), (p - 1)*(p + 5), p) To negate a transfer function the ``-`` operator can be prepended: >>> tf4 = TransferFunction(-a + s, p**2 + s, p) >>> -tf4 TransferFunction(a - s, p**2 + s, p) >>> tf5 = TransferFunction(s**4 - 2*s**3 + 5*s + 4, s + 4, s) >>> -tf5 TransferFunction(-s**4 + 2*s**3 - 5*s - 4, s + 4, s) You can use a float or an integer (or other constants) as numerator and denominator: >>> tf6 = TransferFunction(1/2, 4, s) >>> tf6.num 0.500000000000000 >>> tf6.den 4 >>> tf6.var s >>> tf6.args (0.5, 4, s) You can take the integer power of a transfer function using the ``**`` operator: >>> tf7 = TransferFunction(s + a, s - a, s) >>> tf7**3 TransferFunction((a + s)**3, (-a + s)**3, s) >>> tf7**0 TransferFunction(1, 1, s) >>> tf8 = TransferFunction(p + 4, p - 3, p) >>> tf8**-1 TransferFunction(p - 3, p + 4, p) Addition, subtraction, and multiplication of transfer functions can form unevaluated ``Series`` or ``Parallel`` objects. >>> tf9 = TransferFunction(s + 1, s**2 + s + 1, s) >>> tf10 = TransferFunction(s - p, s + 3, s) >>> tf11 = TransferFunction(4*s**2 + 2*s - 4, s - 1, s) >>> tf12 = TransferFunction(1 - s, s**2 + 4, s) >>> tf9 + tf10 Parallel(TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(-p + s, s + 3, s)) >>> tf10 - tf11 Parallel(TransferFunction(-p + s, s + 3, s), TransferFunction(-4*s**2 - 2*s + 4, s - 1, s)) >>> tf9 * tf10 Series(TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(-p + s, s + 3, s)) >>> tf10 - (tf9 + tf12) Parallel(TransferFunction(-p + s, s + 3, s), TransferFunction(-s - 1, s**2 + s + 1, s), TransferFunction(s - 1, s**2 + 4, s)) >>> tf10 - (tf9 * tf12) Parallel(TransferFunction(-p + s, s + 3, s), Series(TransferFunction(-1, 1, s), TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(1 - s, s**2 + 4, s))) >>> tf11 * tf10 * tf9 Series(TransferFunction(4*s**2 + 2*s - 4, s - 1, s), TransferFunction(-p + s, s + 3, s), TransferFunction(s + 1, s**2 + s + 1, s)) >>> tf9 * tf11 + tf10 * tf12 Parallel(Series(TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(4*s**2 + 2*s - 4, s - 1, s)), Series(TransferFunction(-p + s, s + 3, s), TransferFunction(1 - s, s**2 + 4, s))) >>> (tf9 + tf12) * (tf10 + tf11) Series(Parallel(TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(1 - s, s**2 + 4, s)), Parallel(TransferFunction(-p + s, s + 3, s), TransferFunction(4*s**2 + 2*s - 4, s - 1, s))) These unevaluated ``Series`` or ``Parallel`` objects can convert into the resultant transfer function using ``.doit()`` method or by ``.rewrite(TransferFunction)``. >>> ((tf9 + tf10) * tf12).doit() TransferFunction((1 - s)*((-p + s)*(s**2 + s + 1) + (s + 1)*(s + 3)), (s + 3)*(s**2 + 4)*(s**2 + s + 1), s) >>> (tf9 * tf10 - tf11 * tf12).rewrite(TransferFunction) TransferFunction(-(1 - s)*(s + 3)*(s**2 + s + 1)*(4*s**2 + 2*s - 4) + (-p + s)*(s - 1)*(s + 1)*(s**2 + 4), (s - 1)*(s + 3)*(s**2 + 4)*(s**2 + s + 1), s) See Also ======== Feedback, Series, Parallel References ========== .. [1] https://en.wikipedia.org/wiki/Transfer_function .. [2] https://en.wikipedia.org/wiki/Laplace_transform ct|t|}}t|ts td|dk(r t dt|t t ttfr|jts |jr^t|t t ttfr|jts |jrd|_ tt |7||||Std)Nz Variable input must be a Symbol.r0TransferFunction cannot have a zero denominator.FzBUnsupported type for numerator or denominator of TransferFunction.)rr~rrrr r1r3r5r is_numberis_StateSpace_objectrr)rrQrTrFrs rJrzTransferFunction.__new__qsC=(3-S#v&>? ? !8OP Pt%5vxHIcggV\obebobot%5vxHIcggV\obebobo',C $)37S#sK K`a arkct|}|g|j}t|}|dk(rt|d}n<|dk(rt t dt t dj ||j\}}|dk(s|jtjr td||||S)a Creates a new ``TransferFunction`` efficiently from a rational expression. Parameters ========== expr : Expr, Number The rational expression representing the ``TransferFunction``. var : Symbol, optional Complex variable of the Laplace transform used by the polynomials of the transfer function. Raises ====== ValueError When ``expr`` is of type ``Number`` and optional parameter ``var`` is not passed. When ``expr`` has more than one variables and an optional parameter ``var`` is not passed. ZeroDivisionError When denominator of ``expr`` is zero or it has ``ComplexInfinity`` in its numerator. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy.physics.control.lti import TransferFunction >>> expr1 = (s + 5)/(3*s**2 + 2*s + 1) >>> tf1 = TransferFunction.from_rational_expression(expr1) >>> tf1 TransferFunction(s + 5, 3*s**2 + 2*s + 1, s) >>> expr2 = (a*p**3 - a*p**2 + s*p)/(p + a**2) # Expr with more than one variables >>> tf2 = TransferFunction.from_rational_expression(expr2, p) >>> tf2 TransferFunction(a*p**3 - a*p**2 + p*s, a**2 + p, p) In case of conflict between two or more variables in a expression, SymPy will raise a ``ValueError``, if ``var`` is not passed by the user. >>> tf = TransferFunction.from_rational_expression((a + a*s)/(s**2 + s + 1)) Traceback (most recent call last): ... ValueError: Conflicting values found for positional argument `var` ({a, s}). Specify it manually. This can be corrected by specifying the ``var`` parameter manually. >>> tf = TransferFunction.from_rational_expression((a + a*s)/(s**2 + s + 1), s) >>> tf TransferFunction(a*s + a, s**2 + s + 1, s) ``var`` also need to be specified when ``expr`` is a ``Number`` >>> tf3 = TransferFunction.from_rational_expression(10, s) >>> tf3 TransferFunction(10, 1, s) rMrz Positional argument `var` not found in the TransferFunction defined. Specify it manually.zz Conflicting values found for positional argument `var` ({}). Specify it manually.r) rrrCrrr,formatas_numer_denomrrComplexInfinityZeroDivisionError)rrrF _free_symbols_len_free_symbols_num_dens rJfrom_rational_expressionz)TransferFunction.from_rational_expressions|~ ; --M #M 2  A%=)!,"a' -F"GHH!-,,2F=,A"CDD((* d 19!2!23#$VW W4s##rkcj|ddd}|ddd}tt|Dcgc]}||z }}tt|Dcgc]}||z }}tdt||D}tdt||D}|dk(r t d||||Scc}wcc}w)a[ Creates a new ``TransferFunction`` efficiently from a list of coefficients. Parameters ========== num_list : Sequence Sequence comprising of numerator coefficients. den_list : Sequence Sequence comprising of denominator coefficients. var : Symbol Complex variable of the Laplace transform used by the polynomials of the transfer function. Raises ====== ZeroDivisionError When the constructed denominator is zero. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction >>> num = [1, 0, 2] >>> den = [3, 2, 2, 1] >>> tf = TransferFunction.from_coeff_lists(num, den, s) >>> tf TransferFunction(s**2 + 2, 3*s**3 + 2*s**2 + 2*s + 1, s) >>> #Create a Transfer Function with more than one variable >>> tf1 = TransferFunction.from_coeff_lists([p, 1], [2*p, 0, 4], s) >>> tf1 TransferFunction(p*s + 1, 2*p*s**2 + 4, s) NrNc3,K|] \}}||zywrrrcoeff var_powers rJrz4TransferFunction.from_coeff_lists..[)9 59$[c3,K|] \}}||zywrrrs rJrz4TransferFunction.from_coeff_lists..rrrr)rDrCsumrWr) rnum_listden_listrFrbnum_var_powersden_var_powersrrs rJfrom_coeff_listsz!TransferFunction.from_coeff_listssLDbD>DbD>*/H *>?Q#q&??*/H *>?Q#q&??[S>=Z[[[S>=Z[[ 19#$VW W4s##@?s B+ B0c`d}d}|D] }|||z z} |D] }|||z z} |||z||S)ad Creates a new ``TransferFunction`` from given zeros, poles and gain. Parameters ========== zeros : Sequence Sequence comprising of zeros of transfer function. poles : Sequence Sequence comprising of poles of transfer function. gain : Number, Symbol, Expression A scalar value specifying gain of the model. var : Symbol Complex variable of the Laplace transform used by the polynomials of the transfer function. Examples ======== >>> from sympy.abc import s, p, k >>> from sympy.physics.control.lti import TransferFunction >>> zeros = [1, 2, 3] >>> poles = [6, 5, 4] >>> gain = 7 >>> tf = TransferFunction.from_zpk(zeros, poles, gain, s) >>> tf TransferFunction(7*(s - 3)*(s - 2)*(s - 1), (s - 6)*(s - 5)*(s - 4), s) >>> #Create a Transfer Function with variable poles and zeros >>> tf1 = TransferFunction.from_zpk([p, k], [p + k, p - k], 2, s) >>> tf1 TransferFunction(2*(-k + s)*(-p + s), (-k - p + s)*(k - p + s), s) >>> #Complex poles or zeros are acceptable >>> tf2 = TransferFunction.from_zpk([0], [1-1j, 1+1j, 2], -2, s) >>> tf2 TransferFunction(-2*s, (s - 2)*(s - 1.0 - 1.0*I)*(s - 1.0 + 1.0*I), s) rMr) rr!polesgainrFnum_polyden_polyzeropoles rJfrom_zpkzTransferFunction.from_zpks^N #D d "H # #D d "H #4=(C00rkc |jdS)a Returns the numerator polynomial of the transfer function. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction >>> G1 = TransferFunction(s**2 + p*s + 3, s - 4, s) >>> G1.num p*s + s**2 + 3 >>> G2 = TransferFunction((p + 5)*(p - 3), (p - 3)*(p + 1), p) >>> G2.num (p - 3)*(p + 5) rrrs rJrQzTransferFunction.num7$yy|rkc |jdS)a Returns the denominator polynomial of the transfer function. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction >>> G1 = TransferFunction(s + 4, p**3 - 2*p + 4, s) >>> G1.den p**3 - 2*p + 4 >>> G2 = TransferFunction(3, 4, s) >>> G2.den 4 rMrrs rJrTzTransferFunction.denKrrkc |jdS)a Returns the complex variable of the Laplace transform used by the polynomials of the transfer function. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G1.var p >>> G2 = TransferFunction(0, s - 5, s) >>> G2.var s rrs rJrFzTransferFunction.var_s&yy|rkc|jj||}|jj||}t|||j}||jk(r|S|Sr)rQrrTr1rF)roldnewarg_numarg_denargnews rJ _eval_subszTransferFunction._eval_substsQ((--S)((--S)!'7DHH=dhht2F2rkct|jj||jj||jSr)r1rQ _eval_evalfrTrF)rprecs rJrzTransferFunction._eval_evalfzs: HH  & HH  & HH rkc tt|jd|jz ddj }|d|d}}t |||j S)NrMFevaluaterr)r)rrQrTrr1rF)rrrXnum_den_s rJ_eval_simplifyzTransferFunction._eval_simplifysP C!DHH*u=e L [ [ ]UBqEddDHH55rkc|js tdt|j|j}t|j |j}|j }|j}|j}||j z }dg|z|z}|dd}tt|D cgc] } d| z|dz c} g} t|dz d} t|dz } | j| } | j| } t|d}d||dz <|}g}|dkDr+|j|||||dzz |dz}|dkDr+t|g}t|dg}t| |||Scc} w)aF Returns the equivalent space model of the transfer function model. The state space model will be returned in the controllable canonical form. Unlike the space state to transfer function model conversion, the transfer function to state space model conversion is not unique. There can be multiple state space representations of a given transfer function model. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control import TransferFunction, StateSpace >>> tf = TransferFunction(s**2 + 1, s**3 + 2*s + 10, s) >>> tf.rewrite(StateSpace) StateSpace(Matrix([ [ 0, 1, 0], [ 0, 0, 1], [-10, -2, 0]]), Matrix([ [0], [0], [1]]), Matrix([[1, 0, 1]]), Matrix([[0]])) z!Transfer Function must be proper.rrMNrN) is_properrr&rQrFrTr*rSrreversedr!rrow_joincol_joinappendr:)rrrrrH num_coeffs den_coeffsdiffa coefficienta_matvertmatABrbCDs rJ_eval_rewrite_as_StateSpacez,TransferFunction._eval_rewrite_as_StateSpaces2~~@A A$((+$((+ OO ((* ((* 8??$$SX * qrN8TU;WK"k)*Q-7WXYQqS!}!A#h MM#  JJu  !QK!A#  !e HHZ]Z]:a=%@@ A FA!e A3K JqM? #!Q1%%%Xs;Fc|tt|jt|j|jS)aK Returns the transfer function with numerator and denominator in expanded form. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> G1 = TransferFunction((a - s)**2, (s**2 + a)**2, s) >>> G1.expand() TransferFunction(a**2 - 2*a*s + s**2, a**2 + 2*a*s**2 + s**4, s) >>> G2 = TransferFunction((p + 3*b)*(p - b), (p - b)*(p + 2*b), p) >>> G2.expand() TransferFunction(-3*b**2 + 2*b*p + p**2, -2*b**2 + b*p + p**2, p) )r1rrQrTrFrs rJrzTransferFunction.expands)$ txx 0&2BDHHMMrkct|jt|jddd}t ||j dS)a Computes the gain of the response as the frequency approaches zero. The DC gain is infinite for systems with pure integrators. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction(s + 3, s**2 - 9, s) >>> tf1.dc_gain() -1/3 >>> tf2 = TransferFunction(p**2, p - 3 + p**3, p) >>> tf2.dc_gain() 0 >>> tf3 = TransferFunction(a*p**2 - b, s + b, s) >>> tf3.dc_gain() (a*p**2 - b)/b >>> tf4 = TransferFunction(1, s, s) >>> tf4.dc_gain() oo rNFrr)rrQrrTr+rF)rms rJdc_gainzTransferFunction.dc_gains62 #dhhU;e LQ!$$rkcjtt|j|j|jS)a  Returns the poles of a transfer function. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction((p + 3)*(p - 1), (p - 1)*(p + 5), p) >>> tf1.poles() [-5, 1] >>> tf2 = TransferFunction((1 - s)**2, (s**2 + 1)**2, s) >>> tf2.poles() [I, I, -I, -I] >>> tf3 = TransferFunction(s**2, a*s + p, s) >>> tf3.poles() [-p/a] )rKr&rTrFrs rJrzTransferFunction.poles$(d488TXX.99rkcjtt|j|j|jS)a Returns the zeros of a transfer function. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction((p + 3)*(p - 1), (p - 1)*(p + 5), p) >>> tf1.zeros() [-3, 1] >>> tf2 = TransferFunction((1 - s)**2, (s**2 + 1)**2, s) >>> tf2.zeros() [1, 1] >>> tf3 = TransferFunction(s**2, a*s + p, s) >>> tf3.zeros() [0, 0] )rKr&rQrFrs rJr!zTransferFunction.zerosr!rkc|jj|j|}|jj|j|}t |||jj }|j S)aA Returns the system response at any point in the real or complex plane. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy.physics.control.lti import TransferFunction >>> from sympy import I >>> tf1 = TransferFunction(1, s**2 + 2*s + 1, s) >>> omega = 0.1 >>> tf1.eval_frequency(I*omega) 1/(0.99 + 0.2*I) >>> tf2 = TransferFunction(s**2, a*s + p, s) >>> tf2.eval_frequency(2) 4/(2*a + p) >>> tf2.eval_frequency(I*2) -4/(2*I*a + p) )rQrrFrTr1rr)rotherrrrs rJeval_frequencyzTransferFunction.eval_frequencys[(((--%0((--%0!'7DHH=EEG}}rkcBtd|jDS)ao Returns True if the transfer function is asymptotically stable; else False. This would not check the marginal or conditional stability of the system. Examples ======== >>> from sympy.abc import s, p, a >>> from sympy import symbols >>> from sympy.physics.control.lti import TransferFunction >>> q, r = symbols('q, r', negative=True) >>> tf1 = TransferFunction((1 - s)**2, (s + 1)**2, s) >>> tf1.is_stable() True >>> tf2 = TransferFunction((1 - p)**2, (s**2 + 1)**2, s) >>> tf2.is_stable() False >>> tf3 = TransferFunction(4, q*s - r, s) >>> tf3.is_stable() False >>> tf4 = TransferFunction(p + 1, a*p - s**2, p) >>> tf4.is_stable() is None # Not enough info about the symbols to determine stability True c3VK|]!}|jdj#ywrN)r is_negative)rrs rJrz-TransferFunction.is_stable..Os$U**,Q/;;Us'))rrrs rJ is_stablezTransferFunction.is_stable4s6U UUUrkct|dr|jr t||St|tt t fr9|j|jk(sttdt||St|trN|j|jk(sttdt|j}t|g|Stdjt|)Nr All the transfer functions should use the same complex variable of the Laplace transform.z)TransferFunction cannot be added with {}.)hasattrrr5r~r1r3r7rFrr,rrrtyperr$arg_lists rJ__add__zTransferFunction.__add__Qs 50 1e6P6PD%( (  0&(C D88uyy( -1"233D%( ( x (88uyy( -1"233EJJ'HD,8, ,HtE{#% %rkc ||zSrrrr$s rJ__radd__zTransferFunction.__radd__es e|rkct|dr|jr t|| St|tt fr:|j |j k(sttdt|| St|tr\|j |j k(sttdt|jDcgc]}| }}t|g|Stdjt|cc}w)Nrr,z0{} cannot be subtracted from a TransferFunction.) r-rr5r~r1r3rFrr,rrrr.)rr$rbr0s rJ__sub__zTransferFunction.__sub__hs 50 1e6P6PD5&) )  0&9 :88uyy( -1"233D5&) ) x (88uyy( -1"233%)$45q5H5D,8, ,OU $& &6s Dc| |zSrrr3s rJ__rsub__zTransferFunction.__rsub__|uu}rkct|dr|jr t||St|tt t fr9|j|jk(sttdt||St|trN|j|jk(sttdt|j}t|g|Stdjt|)Nrr,z.TransferFunction cannot be multiplied with {}.)r-rr3r~r1r5r7rFrr,rrrr.r/s rJ__mul__zTransferFunction.__mul__s 50 1e6P6P$& &  0(HE F88uyy( -1"233$& & v &88uyy( -1"233EJJ'H$** *MU $& &rkc4t|trb|j|jk(stt dt |t|j |j|jSt|trst|jdk(rZt|jdtr>>"a'6~66fn&=>>HtE{#% %rkc`t|}|js td|tjurt dd|j S|dkDr|j|z|j|z}}n)t|}|j|z|j|z}}t |||j S)NzExponent must be an integer.rMr) r is_Integerrrrmr1rFrQrTabs)rprrs rJ__pow__zTransferFunction.__pow__s AJ||;< < ;#Aq$((3 3 U1dhhk$DAA1dhhk$DdDHH55rkcZt|j |j|jSrr1rQrTrFrs rJ__neg__zTransferFunction.__neg__s 488TXX>>rkct|j|jt|j|jkS)a Returns True if degree of the numerator polynomial is less than or equal to degree of the denominator polynomial, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction(b*s**2 + p**2 - a*p + s, b - p**2, s) >>> tf1.is_proper False >>> tf2 = TransferFunction(p**2 - 4*p, p**3 + 3*p + 2, p) >>> tf2.is_proper True r*rQrFrTrs rJr zTransferFunction.is_proper-&dhh)VDHHdhh-GGGrkct|j|jt|j|jkS)a Returns True if degree of the numerator polynomial is strictly less than degree of the denominator polynomial, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf1.is_strictly_proper False >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tf2.is_strictly_proper True rJrs rJis_strictly_properz#TransferFunction.is_strictly_propers-&dhh)F488TXX,FFFrkct|j|jt|j|jk(S)a Returns True if degree of the numerator polynomial is equal to degree of the denominator polynomial, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf1.is_biproper True >>> tf2 = TransferFunction(p**2, p + a, p) >>> tf2.is_biproper False rJrs rJ is_biproperzTransferFunction.is_biproperrKrkc|jdk7r.t|jt|jdddSt|jddS)a Converts a ``TransferFunction`` object to SymPy Expr. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction >>> from sympy import Expr >>> tf1 = TransferFunction(s, a*s**2 + 1, s) >>> tf1.to_expr() s/(a*s**2 + 1) >>> isinstance(_, Expr) True >>> tf2 = TransferFunction(1, (p + 3*b)*(b - p), p) >>> tf2.to_expr() 1/((b - p)*(3*b + p)) >>> tf3 = TransferFunction((s - 2)*(s - 3), (s - 1)*(s - 2)*(s - 3), s) >>> tf3.to_expr() ((s - 3)*(s - 2))/(((s - 3)*(s - 2)*(s - 1))) rMrNFr)rQrrrTrs rJrzTransferFunction.to_expr sB0 88q=txxTXXrE!BUS Stxxe4 4rkr)&rrrrrrrrrrrQrTrFrrrrrrrr!r%r*r1r4r6r8r;__rmul__r@ __rtruediv__rErHr rMrOrrrs@rJr1r1s@xrb"O$O$b0$0$d-1-1^&&(3  6 8&tN(%8:,:,2V:%(&(&(H!%FL 6?HH(GG(HH(5rkr1ceZdZdZedededdedfdZdZeZed Z ed Z ed Z ed Z d Z y)r2a A class for representing PID (Proportional-Integral-Derivative) controllers in control systems. The PIDController class is a subclass of TransferFunction, representing the controller's transfer function in the Laplace domain. The arguments are ``kp``, ``ki``, ``kd``, ``tf``, and ``var``, where ``kp``, ``ki``, and ``kd`` are the proportional, integral, and derivative gains respectively.``tf`` is the derivative filter time constant, which can be used to filter out the noise and ``var`` is the complex variable used in the transfer function. Parameters ========== kp : Expr, Number Proportional gain. Defaults to ``Symbol('kp')`` if not specified. ki : Expr, Number Integral gain. Defaults to ``Symbol('ki')`` if not specified. kd : Expr, Number Derivative gain. Defaults to ``Symbol('kd')`` if not specified. tf : Expr, Number Derivative filter time constant. Defaults to ``0`` if not specified. var : Symbol The complex frequency variable. Defaults to ``s`` if not specified. Examples ======== >>> from sympy import symbols >>> from sympy.physics.control.lti import PIDController >>> kp, ki, kd = symbols('kp ki kd') >>> p1 = PIDController(kp, ki, kd) >>> print(p1) PIDController(kp, ki, kd, 0, s) >>> p1.doit() TransferFunction(kd*s**2 + ki + kp*s, s, s) >>> p1.kp kp >>> p1.ki ki >>> p1.kd kd >>> p1.tf 0 >>> p1.var s >>> p1.to_expr() (kd*s**2 + ki + kp*s)/s See Also ======== TransferFunction References ========== .. [1] https://en.wikipedia.org/wiki/PID_controller .. [2] https://in.mathworks.com/help/control/ug/proportional-integral-derivative-pid-controllers.html kpkikdrr\c4t|t|t|t|f\}}}}||z|dzz||zz||z|zz|z||dzzz}||dzz|z}tj||||}||||f\|_|_|_|_|S)Nr)rr1r_kp_ki_kd_tf) rrTrUrVrXrFrQrTobjs rJrzPIDController.__new__fs!"x|Xb\8B<OBBeCFlRV#beCi/"4r#q&y@ai#o&&sCc:-/R^*#'37 rkc d|jd|jd|jd|jd|jd S)NzPIDController(, ))rTrUrVrXrFrs rJ__repr__zPIDController.__repr__ns>y477)2dggYb DHH:UVWWrkc|jS)zJ Returns the Proportional gain (kp) of the PIDController. )rXrs rJrTzPIDController.kps xxrkc|jS)zF Returns the Integral gain (ki) of the PIDController. )rYrs rJrUzPIDController.kizrbrkc|jS)zH Returns the Derivative gain (kd) of the PIDController. )rZrs rJrVzPIDController.kdrbrkc|jS)zX Returns the Derivative filter time constant (tf) of the PIDController. )r[rs rJrXzPIDController.tfrbrkcXt|j|j|jS)zB Convert the PIDController into TransferFunction. rGrs rJdoitzPIDController.doits $((DHH==rkN)rrrrrrr`__str__rrTrUrVrXrgrrkrJr2r2(s<zt &,1RXY\R]XG     >rkr2cg}|D];}t||r|j|j+|j|=t |Sr)r~extendrrtuple)r_cls temp_argsrqs rJ _flatten_argsrnsMI" c4   SXX &   S ! "  rkci}g}|D]5}t}|||<|j||i}|j|7||fSr)rrr)_argrF dummy_dictdummy_arg_listrq_s dummy_args rJ _dummify_argsrusXJN) W 2HHc2Y' i( ) : %%rkceZdZdZddfd ZdZeZedZdZ dZ e d Z e Z e d Zd Ze d Zd ZdZdZedZedZedZedZxZS)r3a A class for representing a series configuration of SISO systems. Parameters ========== args : SISOLinearTimeInvariant SISO systems in a series configuration. evaluate : Boolean, Keyword When passed ``True``, returns the equivalent ``Series(*args).doit()``. Set to ``False`` by default. Raises ====== ValueError When no argument is passed. ``var`` attribute is not same for every system. TypeError Any of the passed ``*args`` has unsupported type A combination of SISO and MIMO systems is passed. There should be homogeneity in the type of systems passed, SISO in this case. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy import Matrix >>> from sympy.physics.control.lti import TransferFunction, Series, Parallel, StateSpace >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tf3 = TransferFunction(p**2, p + s, s) >>> S1 = Series(tf1, tf2) >>> S1 Series(TransferFunction(a*p**2 + b*s, -p + s, s), TransferFunction(s**3 - 2, s**4 + 5*s + 6, s)) >>> S1.var s >>> S2 = Series(tf2, Parallel(tf3, -tf1)) >>> S2 Series(TransferFunction(s**3 - 2, s**4 + 5*s + 6, s), Parallel(TransferFunction(p**2, p + s, s), TransferFunction(-a*p**2 - b*s, -p + s, s))) >>> S2.var s >>> S3 = Series(Parallel(tf1, tf2), Parallel(tf2, tf3)) >>> S3 Series(Parallel(TransferFunction(a*p**2 + b*s, -p + s, s), TransferFunction(s**3 - 2, s**4 + 5*s + 6, s)), Parallel(TransferFunction(s**3 - 2, s**4 + 5*s + 6, s), TransferFunction(p**2, p + s, s))) >>> S3.var s You can get the resultant transfer function by using ``.doit()`` method: >>> S3 = Series(tf1, tf2, -tf3) >>> S3.doit() TransferFunction(-p**2*(s**3 - 2)*(a*p**2 + b*s), (-p + s)*(p + s)*(s**4 + 5*s + 6), s) >>> S4 = Series(tf2, Parallel(tf1, -tf3)) >>> S4.doit() TransferFunction((s**3 - 2)*(-p**2*(-p + s) + (p + s)*(a*p**2 + b*s)), (-p + s)*(p + s)*(s**4 + 5*s + 6), s) You can also connect StateSpace which results in SISO >>> A1 = Matrix([[-1]]) >>> B1 = Matrix([[1]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([1]) >>> A2 = Matrix([[0]]) >>> B2 = Matrix([[1]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[0]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> S5 = Series(ss1, ss2) >>> S5 Series(StateSpace(Matrix([[-1]]), Matrix([[1]]), Matrix([[-1]]), Matrix([[1]])), StateSpace(Matrix([[0]]), Matrix([[1]]), Matrix([[1]]), Matrix([[0]]))) >>> S5.doit() StateSpace(Matrix([ [-1, 0], [-1, 0]]), Matrix([ [1], [1]]), Matrix([[0, 1]]), Matrix([[0]])) Notes ===== All the transfer functions should use the same complex variable ``var`` of the Laplace transform. See Also ======== MIMOSeries, Parallel, TransferFunction, Feedback Frct|t}|rtd|Dr|djdk(rj|djdk(rXt dt |D]8}||j||dz jk7s&ttdd|_ n#tdd|_ |j|t|0|g|}|r|jS|S) Nc3tK|]0}t|txst|dxr |j2ywrNr~r:r-rrrqs rJrz!Series.__new__..sFZMP#3 3JE[8\9I030H0HJZ68rrMrNzgSystems with incompatible inputs and outputs cannot be connected in Series.TzATo use Series connection for MIMO systems use MIMOSeries instead.F)rnr3anyrrrDrCrr,_is_series_StateSpacerrrrgrrrrbr\rs rJrzSeries.__new__ sT6* CZTXZZQ""a'd2h.B.Ba.Gq#d),@AAw))T!A#Y-B-BB(5>*?@@@-1) !dee(-C % OOD !goc)D)%sxxz.3.rkcRdjd|jD}d|dS)Nr^c32K|]}t|ywrreprrrs rJrz"Series.__repr__..% F&f FzSeries(r_joinrr systems_reprs rJr`zSeries.__repr__$s)yy FDII FF a((rkc4|jdjS)a Returns the complex variable used by all the transfer functions. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, Series, Parallel >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G2 = TransferFunction(p, 4 - p, p) >>> G3 = TransferFunction(0, p**4 - 1, p) >>> Series(G1, G2).var p >>> Series(-G3, Parallel(G1, G2)).var p rrrFrs rJrFz Series.var*&yy|rkc $|jr|jd}t|ts#|j j t}|jddD]^}t|ts$|j j t}n|j }|j }||z}|cSd|jD}d|jD}t |ddi}t |ddi}t|||jS)a Returns the resultant transfer function or StateSpace obtained after evaluating the series interconnection. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Series >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> Series(tf2, tf1).doit() TransferFunction((s**3 - 2)*(a*p**2 + b*s), (-p + s)*(s**4 + 5*s + 6), s) >>> Series(-tf1, -tf2).doit() TransferFunction((2 - s**3)*(-a*p**2 - b*s), (-p + s)*(s**4 + 5*s + 6), s) Notes ===== If a series connection contains only TransferFunction components, the equivalent system returned will be a TransferFunction. However, if a StateSpace object is used in any of the arguments, the output will be a StateSpace object. rrMNc3PK|]}|jj ywr)rgrQr{s rJrzSeries.doit..g8sCHHJNN8$&c3PK|]}|jj ywr)rgrTr{s rJrzSeries.doit..hrrrT) r~rr~r:rgrewriterr1rF)rhintsresrq_num_arg_den_argres_numres_dens rJrgz Series.doit?s4  % %))A,Cc:.hhj((4yy} !#z2((*,,Z8C((*ChhjCi  9dii88dii8x/$/x/$/$((;;rkc|jr)|jjtddS|jSNrr~rgrr1rrrs rJ!_eval_rewrite_as_TransferFunctionz(Series._eval_rewrite_as_TransferFunctionms9  % %99;&&'78;A> >yy{rkc|t|tr!t|j}t|g|St||Sr)r~r5rrr/s rJr1zSeries.__add__rs9 eX &EJJ'HD,8, ,e$$rkc|| zSrrr3s rJr6zSeries.__sub__}vrkc| |zSrrr3s rJr8zSeries.__rsub__r9rkcFt|j}tg||Sr)rrr3r/s rJr;zSeries.__mul__s$ ?'x'''rkct|tr@tg|jt|j|j |j St|tr/|jt}|jt}||z St|trt|jdk(rt|jdtrt|jdtr|j |j k(sttdt|j}t|jdj}t||z }t|dk(rt||jdSt|dk(r t|g|St|t|Std)NrrrMr,z-This transfer function expression is invalid.)r~r1r3rrTrQrFrr5rCrr,setrr7)rr$tf_selftf_other self_arg_listr?rs rJr@zSeries.__truediv__sc e- .X499X&6uyy%))UYY&WX X v &ll#34G}}%56HX% %)c%**o.B5::a=*:; 5::VW=Z`@a88uyy( -1"233  NM A!3!34N}~56C3x1}ejjm44SQ+s++fcl33LM MrkcDttdd|j|SNrNrMr3r1rFrs rJrHzSeries.__neg__&r1dhh7>>rkc:td|jDddiS)'Returns the equivalent ``Expr`` object.c3<K|]}|jywrrr{s rJrz!Series.to_expr..8sS[[]8rF)rrrs rJrzSeries.to_expr8dii8I5IIrkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is less than or equal to degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Series >>> tf1 = TransferFunction(b*s**2 + p**2 - a*p + s, b - p**2, s) >>> tf2 = TransferFunction(p**2 - 4*p, p**3 + 3*s + 2, s) >>> tf3 = TransferFunction(s, s**2 + s + 1, s) >>> S1 = Series(-tf2, tf1) >>> S1.is_proper False >>> S2 = Series(tf1, tf2, tf3) >>> S2.is_proper True rgr rs rJr zSeries.is_proper.yy{$$$rkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is strictly less than degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Series >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**2 + 5*s + 6, s) >>> tf3 = TransferFunction(1, s**2 + s + 1, s) >>> S1 = Series(tf1, tf2) >>> S1.is_strictly_proper False >>> S2 = Series(tf1, tf2, tf3) >>> S2.is_strictly_proper True rgrMrs rJrMzSeries.is_strictly_proper.yy{---rkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is equal to degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Series >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(p, s**2, s) >>> tf3 = TransferFunction(s**2, 1, s) >>> S1 = Series(tf1, -tf2) >>> S1.is_biproper False >>> S2 = Series(tf2, tf3) >>> S2.is_biproper True rgrOrs rJrOzSeries.is_biproper.yy{&&&rkc|jSrr~rs rJrzSeries.is_StateSpace_object)))rk)rrrrrr`rhrrFrgrrr1r4r6r8r;r@rHrr rMrOrrrs@rJr3r3s]|&+/0)G   (,<\ %%H(( N4?J%%0..0''0**rkr3cVtfdttdz DS)z6To check whether shapes are compatible for matrix mul.c3bK|]&}|j|dzjk((yw)rMNrr)rrbrs rJrz&_mat_mul_compatible..s.WqtAw""d1Q3i&:&::Ws,/rM)rrDrCrs`rJ_mat_mul_compatiblers" WE#d)TU+DVW WWrkceZdZdZddfd ZedZedZedZedZ ed Z dd Z d Z e d ZeZe d ZdZe dZdZxZS)r4a0 A class for representing a series configuration of MIMO systems. Parameters ========== args : MIMOLinearTimeInvariant MIMO systems in a series configuration. evaluate : Boolean, Keyword When passed ``True``, returns the equivalent ``MIMOSeries(*args).doit()``. Set to ``False`` by default. Raises ====== ValueError When no argument is passed. ``var`` attribute is not same for every system. ``num_outputs`` of the MIMO system is not equal to the ``num_inputs`` of its adjacent MIMO system. (Matrix multiplication constraint, basically) TypeError Any of the passed ``*args`` has unsupported type A combination of SISO and MIMO systems is passed. There should be homogeneity in the type of systems passed, MIMO in this case. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import MIMOSeries, TransferFunctionMatrix, StateSpace >>> from sympy import Matrix, pprint >>> mat_a = Matrix([[5*s], [5]]) # 2 Outputs 1 Input >>> mat_b = Matrix([[5, 1/(6*s**2)]]) # 1 Output 2 Inputs >>> mat_c = Matrix([[1, s], [5/s, 1]]) # 2 Outputs 2 Inputs >>> tfm_a = TransferFunctionMatrix.from_Matrix(mat_a, s) >>> tfm_b = TransferFunctionMatrix.from_Matrix(mat_b, s) >>> tfm_c = TransferFunctionMatrix.from_Matrix(mat_c, s) >>> MIMOSeries(tfm_c, tfm_b, tfm_a) MIMOSeries(TransferFunctionMatrix(((TransferFunction(1, 1, s), TransferFunction(s, 1, s)), (TransferFunction(5, s, s), TransferFunction(1, 1, s)))), TransferFunctionMatrix(((TransferFunction(5, 1, s), TransferFunction(1, 6*s**2, s)),)), TransferFunctionMatrix(((TransferFunction(5*s, 1, s),), (TransferFunction(5, 1, s),)))) >>> pprint(_, use_unicode=False) # For Better Visualization [5*s] [1 s] [---] [5 1 ] [- -] [ 1 ] [- ----] [1 1] [ ] *[1 2] *[ ] [ 5 ] [ 6*s ]{t} [5 1] [ - ] [- -] [ 1 ]{t} [s 1]{t} >>> MIMOSeries(tfm_c, tfm_b, tfm_a).doit() TransferFunctionMatrix(((TransferFunction(150*s**4 + 25*s, 6*s**3, s), TransferFunction(150*s**4 + 5*s, 6*s**2, s)), (TransferFunction(150*s**3 + 25, 6*s**3, s), TransferFunction(150*s**3 + 5, 6*s**2, s)))) >>> pprint(_, use_unicode=False) # (2 Inputs -A-> 2 Outputs) -> (2 Inputs -B-> 1 Output) -> (1 Input -C-> 2 Outputs) is equivalent to (2 Inputs -Series Equivalent-> 2 Outputs). [ 4 4 ] [150*s + 25*s 150*s + 5*s] [------------- ------------] [ 3 2 ] [ 6*s 6*s ] [ ] [ 3 3 ] [ 150*s + 25 150*s + 5 ] [ ----------- ---------- ] [ 3 2 ] [ 6*s 6*s ]{t} >>> a1 = Matrix([[4, 1], [2, -3]]) >>> b1 = Matrix([[5, 2], [-3, -3]]) >>> c1 = Matrix([[2, -4], [0, 1]]) >>> d1 = Matrix([[3, 2], [1, -1]]) >>> a2 = Matrix([[-3, 4, 2], [-1, -3, 0], [2, 5, 3]]) >>> b2 = Matrix([[1, 4], [-3, -3], [-2, 1]]) >>> c2 = Matrix([[4, 2, -3], [1, 4, 3]]) >>> d2 = Matrix([[-2, 4], [0, 1]]) >>> ss1 = StateSpace(a1, b1, c1, d1) #2 inputs, 2 outputs >>> ss2 = StateSpace(a2, b2, c2, d2) #2 inputs, 2 outputs >>> S1 = MIMOSeries(ss1, ss2) #(2 inputs, 2 outputs) -> (2 inputs, 2 outputs) >>> S1 MIMOSeries(StateSpace(Matrix([ [4, 1], [2, -3]]), Matrix([ [ 5, 2], [-3, -3]]), Matrix([ [2, -4], [0, 1]]), Matrix([ [3, 2], [1, -1]])), StateSpace(Matrix([ [-3, 4, 2], [-1, -3, 0], [ 2, 5, 3]]), Matrix([ [ 1, 4], [-3, -3], [-2, 1]]), Matrix([ [4, 2, -3], [1, 4, 3]]), Matrix([ [-2, 4], [ 0, 1]]))) >>> S1.doit() StateSpace(Matrix([ [ 4, 1, 0, 0, 0], [ 2, -3, 0, 0, 0], [ 2, 0, -3, 4, 2], [-6, 9, -1, -3, 0], [-4, 9, 2, 5, 3]]), Matrix([ [ 5, 2], [ -3, -3], [ 7, -2], [-12, -3], [ -5, -5]]), Matrix([ [-4, 12, 4, 2, -3], [ 0, 1, 1, 4, 3]]), Matrix([ [-2, -8], [ 1, -1]])) Notes ===== All the transfer function matrices should use the same complex variable ``var`` of the Laplace transform. ``MIMOSeries(A, B)`` is not equivalent to ``A*B``. It is always in the reverse order, that is ``B*A``. See Also ======== Series, MIMOParallel Frc|rztd|Drhtdt|D]8}||j||dz jk7s&t t dt| |g|}d|_ nE|j|d|_ t|rt| |g|}nt t d|r|jS|S)Nc3tK|]0}t|txst|dxr |j2ywryrzr{s rJrz%MIMOSeries.__new__..sF[NQ#3 3JE[8\9I030H0HJ[r|rMzgSystems with incompatible inputs and outputs cannot be connected in MIMOSeries.TFz Number of input signals do not match the number of output signals of adjacent systems for some args.) r}rDrCrrrr,rrr~rrrgrs rJrzMIMOSeries.__new__s C[UY[[1c$i( @7%%a!e)@)@@$Z1>&?@@ @'/#--C(,C % OOD !(-C %"D)goc1D1!-L"MNN&sxxz.3.rkc4|jdjS)aL Returns the complex variable used by all the transfer functions. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, MIMOSeries, TransferFunctionMatrix >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G2 = TransferFunction(p, 4 - p, p) >>> G3 = TransferFunction(0, p**4 - 1, p) >>> tfm_1 = TransferFunctionMatrix([[G1, G2, G3]]) >>> tfm_2 = TransferFunctionMatrix([[G1], [G2], [G3]]) >>> MIMOSeries(tfm_2, tfm_1).var p rrrs rJrFzMIMOSeries.varrrkc4|jdjS)z9Returns the number of input signals of the series system.rrrrs rJrzMIMOSeries.num_inputsyy|&&&rkc4|jdjS)z:Returns the number of output signals of the series system.rNrrrs rJrzMIMOSeries.num_outputssyy}(((rkc2|j|jfSz0Returns the shape of the equivalent MIMO system.rrs rJshapezMIMOSeries.shape00rkc|jSrrrs rJrzMIMOSeries.is_StateSpace_objectrrkc |jr|jd}t|ts#|j j t}|jddD]K}t|ts$|j j t}n|j }||z}M|Sdt |jD}|r+t|ddi}tj||jSt||j\}}t|ddi}tj||j}|j|S)a Returns the resultant obtained after evaluating the MIMO systems arranged in a series configuration. For TransferFunction systems it returns a TransferFunctionMatrix and for StateSpace systems it returns the resultant StateSpace system. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, MIMOSeries, TransferFunctionMatrix >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tfm1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf2]]) >>> tfm2 = TransferFunctionMatrix([[tf2, tf1], [tf1, tf1]]) >>> MIMOSeries(tfm2, tfm1).doit() TransferFunctionMatrix(((TransferFunction(2*(-p + s)*(s**3 - 2)*(a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)**2*(s**4 + 5*s + 6)**2, s), TransferFunction((-p + s)**2*(s**3 - 2)*(a*p**2 + b*s) + (-p + s)*(a*p**2 + b*s)**2*(s**4 + 5*s + 6), (-p + s)**3*(s**4 + 5*s + 6), s)), (TransferFunction((-p + s)*(s**3 - 2)**2*(s**4 + 5*s + 6) + (s**3 - 2)*(a*p**2 + b*s)*(s**4 + 5*s + 6)**2, (-p + s)*(s**4 + 5*s + 6)**3, s), TransferFunction(2*(s**3 - 2)*(a*p**2 + b*s), (-p + s)*(s**4 + 5*s + 6), s)))) rrMNc3PK|]}|jj ywrrg _expr_matr{s rJrz"MIMOSeries.doit..sD $$DrrT) r~rr~r:rgrr r$r9 from_MatrixrFrur) rr)rrrqrp _dummy_args _dummy_dicttemp_tfms rJrgzMIMOSeries.doits&  % %))A,Cc:.hhj((4yy} !#z2((*,,Z8C((*CCi  JD0CD $..C)55c488D D#0txx#@ [k1D1)55c488D}}[))rkc|jr#|jjtS|jSrrrs rJ'_eval_rewrite_as_TransferFunctionMatrixz2MIMOSeries._eval_rewrite_as_TransferFunctionMatrixs/  % %99;&&'78 8yy{rkc|t|tr!t|j}t|g|St||Srr~r6rrr/s rJr1zMIMOSeries.__add__s9 e\ *EJJ'H0x0 0D%((rkc|| zSrrr3s rJr6zMIMOSeries.__sub__rrkc| |zSrrr3s rJr8zMIMOSeries.__rsub__r9rkct|tr7t|j}t|j}tg||St|j}t|g|Srr~r4rr)rr$rr?r0s rJr;zMIMOSeries.__mul__sY eZ ( OM!%**-N>~> > > ?%+(++rkcNt|j}|d |d<t|Sr)rrr4)rr0s rJrHzMIMOSeries.__neg__ s) ?{l 8$$rk)FrrrrrrrFrrrrrgrrr1r4r6r8r;rHrrs@rJr4r4s~~&+/2  (''))11**)*V ))H,,%rkr4ceZdZdZddfd ZdZeZedZdZ dZ e d Z e Z e d Zd Ze d Zd ZdZedZedZedZedZxZS)r5a A class for representing a parallel configuration of SISO systems. Parameters ========== args : SISOLinearTimeInvariant SISO systems in a parallel arrangement. evaluate : Boolean, Keyword When passed ``True``, returns the equivalent ``Parallel(*args).doit()``. Set to ``False`` by default. Raises ====== ValueError When no argument is passed. ``var`` attribute is not same for every system. TypeError Any of the passed ``*args`` has unsupported type A combination of SISO and MIMO systems is passed. There should be homogeneity in the type of systems passed. Examples ======== >>> from sympy import Matrix >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Parallel, Series, StateSpace >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tf3 = TransferFunction(p**2, p + s, s) >>> P1 = Parallel(tf1, tf2) >>> P1 Parallel(TransferFunction(a*p**2 + b*s, -p + s, s), TransferFunction(s**3 - 2, s**4 + 5*s + 6, s)) >>> P1.var s >>> P2 = Parallel(tf2, Series(tf3, -tf1)) >>> P2 Parallel(TransferFunction(s**3 - 2, s**4 + 5*s + 6, s), Series(TransferFunction(p**2, p + s, s), TransferFunction(-a*p**2 - b*s, -p + s, s))) >>> P2.var s >>> P3 = Parallel(Series(tf1, tf2), Series(tf2, tf3)) >>> P3 Parallel(Series(TransferFunction(a*p**2 + b*s, -p + s, s), TransferFunction(s**3 - 2, s**4 + 5*s + 6, s)), Series(TransferFunction(s**3 - 2, s**4 + 5*s + 6, s), TransferFunction(p**2, p + s, s))) >>> P3.var s You can get the resultant transfer function by using ``.doit()`` method: >>> Parallel(tf1, tf2, -tf3).doit() TransferFunction(-p**2*(-p + s)*(s**4 + 5*s + 6) + (-p + s)*(p + s)*(s**3 - 2) + (p + s)*(a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(p + s)*(s**4 + 5*s + 6), s) >>> Parallel(tf2, Series(tf1, -tf3)).doit() TransferFunction(-p**2*(a*p**2 + b*s)*(s**4 + 5*s + 6) + (-p + s)*(p + s)*(s**3 - 2), (-p + s)*(p + s)*(s**4 + 5*s + 6), s) Parallel can be used to connect SISO ``StateSpace`` systems together. >>> A1 = Matrix([[-1]]) >>> B1 = Matrix([[1]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([1]) >>> A2 = Matrix([[0]]) >>> B2 = Matrix([[1]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[0]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> P4 = Parallel(ss1, ss2) >>> P4 Parallel(StateSpace(Matrix([[-1]]), Matrix([[1]]), Matrix([[-1]]), Matrix([[1]])), StateSpace(Matrix([[0]]), Matrix([[1]]), Matrix([[1]]), Matrix([[0]]))) ``doit()`` can be used to find ``StateSpace`` equivalent for the system containing ``StateSpace`` objects. >>> P4.doit() StateSpace(Matrix([ [-1, 0], [ 0, 0]]), Matrix([ [1], [1]]), Matrix([[-1, 1]]), Matrix([[1]])) >>> P4.rewrite(TransferFunction) TransferFunction(s*(s + 1) + 1, s*(s + 1), s) Notes ===== All the transfer functions should use the same complex variable ``var`` of the Laplace transform. See Also ======== Series, TransferFunction, Feedback Frct|t}|r7td|Dr%td|Drd|_n#t dd|_|j |t| |g|}|r|jS|S)Nc3tK|]0}t|txst|dxr |j2ywryrzr{s rJrz#Parallel.__new__..zsFgZ]#3 3VE[8\9U.}s/33;;/sTzETo use Parallel connection for MIMO systems use MIMOParallel instead.F) rnr5r}r_is_parallel_StateSpacerrrrrgrrrr\rs rJrzParallel.__new__vsT8, Cgaegg/$//.2+ !hii*/C ' OOD !goc)D)%sxxz.3.rkcRdjd|jD}d|dS)Nr^c32K|]}t|ywrrrs rJrz$Parallel.__repr__..rrz Parallel(r_rrs rJr`zParallel.__repr__s)yy FDII FF <.**rkc4|jdjS)a Returns the complex variable used by all the transfer functions. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, Parallel, Series >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G2 = TransferFunction(p, 4 - p, p) >>> G3 = TransferFunction(0, p**4 - 1, p) >>> Parallel(G1, G2).var p >>> Parallel(-G3, Series(G1, G2)).var p rrrs rJrFz Parallel.varrrkc |jr|jdj}t|ts|j t}|jddD]:}t|ts#|jj t}||z }<|Sd|jD}t |j}tg||jS)a Returns the resultant transfer function or state space obtained by parallel connection of transfer functions or state space objects. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Parallel >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> Parallel(tf2, tf1).doit() TransferFunction((-p + s)*(s**3 - 2) + (a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s) >>> Parallel(-tf1, -tf2).doit() TransferFunction((2 - s**3)*(-p + s) + (-a*p**2 - b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s) rrMNc3XK|]"}|jj$ywrrgrr{s rJrz Parallel.doit..s: ""$:s(*) rrrgr~r:rrrr1rFrrrrqrps rJrgz Parallel.doits$  ' '))A,##%Cc:.kk*-yy} !#z2((*,,Z8Cs  J: :4j'')//dhh//rkc|jr)|jjtddS|jSrrrgrr1rs rJrz*Parallel._eval_rewrite_as_TransferFunctions9  ' '99;&&'78;A> >yy{rkcFt|j}tg||Sr)rrr5rr$rs rJr1zParallel.__add__s$TYY ....rkc|| zSrrr3s rJr6zParallel.__sub__rrkc| |zSrrr3s rJr8zParallel.__rsub__r9rkc|t|tr!t|j}t|g|St||Sr)r~r3rrr/s rJr;zParallel.__mul__s9 eV $EJJ'H$** *dE""rkcDttdd|j|Srrrs rJrHzParallel.__neg__rrkc:td|jDddiS)rc3<K|]}|jywrrr{s rJrz#Parallel.to_expr..rrrF)rrrs rJrzParallel.to_exprrrkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is less than or equal to degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Parallel >>> tf1 = TransferFunction(b*s**2 + p**2 - a*p + s, b - p**2, s) >>> tf2 = TransferFunction(p**2 - 4*p, p**3 + 3*s + 2, s) >>> tf3 = TransferFunction(s, s**2 + s + 1, s) >>> P1 = Parallel(-tf2, tf1) >>> P1.is_proper False >>> P2 = Parallel(tf2, tf3) >>> P2.is_proper True rrs rJr zParallel.is_properrrkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is strictly less than degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Parallel >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tf3 = TransferFunction(s, s**2 + s + 1, s) >>> P1 = Parallel(tf1, tf2) >>> P1.is_strictly_proper False >>> P2 = Parallel(tf2, tf3) >>> P2.is_strictly_proper True rrs rJrMzParallel.is_strictly_proper rrkc6|jjS)a Returns True if degree of the numerator polynomial of the resultant transfer function is equal to degree of the denominator polynomial of the same, else False. Examples ======== >>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, Parallel >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(p**2, p + s, s) >>> tf3 = TransferFunction(s, s**2 + s + 1, s) >>> P1 = Parallel(tf1, -tf2) >>> P1.is_biproper True >>> P2 = Parallel(tf2, tf3) >>> P2.is_biproper False rrs rJrOzParallel.is_biproper rrkc|jSrrrs rJrzParallel.is_StateSpace_object3 +++rk)rrrrrr`rhrrFrgrrr1r4r6r8r;rHrr rMrOrrrs@rJr5r5s`D&+/$+G   (0B // H##?J%%0..0''0,,rkr5ceZdZdZddfd ZedZedZedZedZ ed Z d Z d Z e d ZeZe d ZdZe dZdZxZS)r6aU A class for representing a parallel configuration of MIMO systems. Parameters ========== args : MIMOLinearTimeInvariant MIMO Systems in a parallel arrangement. evaluate : Boolean, Keyword When passed ``True``, returns the equivalent ``MIMOParallel(*args).doit()``. Set to ``False`` by default. Raises ====== ValueError When no argument is passed. ``var`` attribute is not same for every system. All MIMO systems passed do not have same shape. TypeError Any of the passed ``*args`` has unsupported type A combination of SISO and MIMO systems is passed. There should be homogeneity in the type of systems passed, MIMO in this case. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunctionMatrix, MIMOParallel, StateSpace >>> from sympy import Matrix, pprint >>> expr_1 = 1/s >>> expr_2 = s/(s**2-1) >>> expr_3 = (2 + s)/(s**2 - 1) >>> expr_4 = 5 >>> tfm_a = TransferFunctionMatrix.from_Matrix(Matrix([[expr_1, expr_2], [expr_3, expr_4]]), s) >>> tfm_b = TransferFunctionMatrix.from_Matrix(Matrix([[expr_2, expr_1], [expr_4, expr_3]]), s) >>> tfm_c = TransferFunctionMatrix.from_Matrix(Matrix([[expr_3, expr_4], [expr_1, expr_2]]), s) >>> MIMOParallel(tfm_a, tfm_b, tfm_c) MIMOParallel(TransferFunctionMatrix(((TransferFunction(1, s, s), TransferFunction(s, s**2 - 1, s)), (TransferFunction(s + 2, s**2 - 1, s), TransferFunction(5, 1, s)))), TransferFunctionMatrix(((TransferFunction(s, s**2 - 1, s), TransferFunction(1, s, s)), (TransferFunction(5, 1, s), TransferFunction(s + 2, s**2 - 1, s)))), TransferFunctionMatrix(((TransferFunction(s + 2, s**2 - 1, s), TransferFunction(5, 1, s)), (TransferFunction(1, s, s), TransferFunction(s, s**2 - 1, s))))) >>> pprint(_, use_unicode=False) # For Better Visualization [ 1 s ] [ s 1 ] [s + 2 5 ] [ - ------] [------ - ] [------ - ] [ s 2 ] [ 2 s ] [ 2 1 ] [ s - 1] [s - 1 ] [s - 1 ] [ ] + [ ] + [ ] [s + 2 5 ] [ 5 s + 2 ] [ 1 s ] [------ - ] [ - ------] [ - ------] [ 2 1 ] [ 1 2 ] [ s 2 ] [s - 1 ]{t} [ s - 1]{t} [ s - 1]{t} >>> MIMOParallel(tfm_a, tfm_b, tfm_c).doit() TransferFunctionMatrix(((TransferFunction(s**2 + s*(2*s + 2) - 1, s*(s**2 - 1), s), TransferFunction(2*s**2 + 5*s*(s**2 - 1) - 1, s*(s**2 - 1), s)), (TransferFunction(s**2 + s*(s + 2) + 5*s*(s**2 - 1) - 1, s*(s**2 - 1), s), TransferFunction(5*s**2 + 2*s - 3, s**2 - 1, s)))) >>> pprint(_, use_unicode=False) [ 2 2 / 2 \ ] [ s + s*(2*s + 2) - 1 2*s + 5*s*\s - 1/ - 1] [ -------------------- -----------------------] [ / 2 \ / 2 \ ] [ s*\s - 1/ s*\s - 1/ ] [ ] [ 2 / 2 \ 2 ] [s + s*(s + 2) + 5*s*\s - 1/ - 1 5*s + 2*s - 3 ] [--------------------------------- -------------- ] [ / 2 \ 2 ] [ s*\s - 1/ s - 1 ]{t} ``MIMOParallel`` can also be used to connect MIMO ``StateSpace`` systems. >>> A1 = Matrix([[4, 1], [2, -3]]) >>> B1 = Matrix([[5, 2], [-3, -3]]) >>> C1 = Matrix([[2, -4], [0, 1]]) >>> D1 = Matrix([[3, 2], [1, -1]]) >>> A2 = Matrix([[-3, 4, 2], [-1, -3, 0], [2, 5, 3]]) >>> B2 = Matrix([[1, 4], [-3, -3], [-2, 1]]) >>> C2 = Matrix([[4, 2, -3], [1, 4, 3]]) >>> D2 = Matrix([[-2, 4], [0, 1]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> p1 = MIMOParallel(ss1, ss2) >>> p1 MIMOParallel(StateSpace(Matrix([ [4, 1], [2, -3]]), Matrix([ [ 5, 2], [-3, -3]]), Matrix([ [2, -4], [0, 1]]), Matrix([ [3, 2], [1, -1]])), StateSpace(Matrix([ [-3, 4, 2], [-1, -3, 0], [ 2, 5, 3]]), Matrix([ [ 1, 4], [-3, -3], [-2, 1]]), Matrix([ [4, 2, -3], [1, 4, 3]]), Matrix([ [-2, 4], [ 0, 1]]))) ``doit()`` can be used to find ``StateSpace`` equivalent for the system containing ``StateSpace`` objects. >>> p1.doit() StateSpace(Matrix([ [4, 1, 0, 0, 0], [2, -3, 0, 0, 0], [0, 0, -3, 4, 2], [0, 0, -1, -3, 0], [0, 0, 2, 5, 3]]), Matrix([ [ 5, 2], [-3, -3], [ 1, 4], [-3, -3], [-2, 1]]), Matrix([ [2, -4, 4, 2, -3], [0, 1, 1, 4, 3]]), Matrix([ [1, 6], [1, 0]])) Notes ===== All the transfer function matrices should use the same complex variable ``var`` of the Laplace transform. See Also ======== Parallel, MIMOSeries FrcXttr. sFSFI#3 3BE[8\9A(+(@(@BSr|c3K|]>}|jdjk7xs|jdjk7@ywr()rrrrqrs rJrz'MIMOParallel.__new__.. sH(>>T!W%7%77a3??dSTgNaNa;aa(sAArMzQSystems with incompatible inputs and outputs cannot be connected in MIMOParallel.Tc3VK|] }|jdjk7"ywr()rrs rJrz'MIMOParallel.__new__.. s">#399Q ->s&)z#Shape of all the args is not equal.F) rnr6r}r rrrrrrgrs ` rJrzMIMOParallel.__new__ sT<0 CSMQSS("12h(( ">??*.C ' OOD !>>> EFF*/C 'goc)D)%sxxz.3.rkc4|jdjS)a Returns the complex variable used by all the systems. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOParallel >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G2 = TransferFunction(p, 4 - p, p) >>> G3 = TransferFunction(0, p**4 - 1, p) >>> G4 = TransferFunction(p**2, p**2 - 1, p) >>> tfm_a = TransferFunctionMatrix([[G1, G2], [G3, G4]]) >>> tfm_b = TransferFunctionMatrix([[G2, G1], [G4, G3]]) >>> MIMOParallel(tfm_a, tfm_b).var p rrrs rJrFzMIMOParallel.var s(yy|rkc4|jdjS)z;Returns the number of input signals of the parallel system.rrrs rJrzMIMOParallel.num_inputs rrkc4|jdjS)z>> from sympy.abc import s, p, a, b >>> from sympy.physics.control.lti import TransferFunction, MIMOParallel, TransferFunctionMatrix >>> tf1 = TransferFunction(a*p**2 + b*s, s - p, s) >>> tf2 = TransferFunction(s**3 - 2, s**4 + 5*s + 6, s) >>> tfm_1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) >>> tfm_2 = TransferFunctionMatrix([[tf2, tf1], [tf1, tf2]]) >>> MIMOParallel(tfm_1, tfm_2).doit() TransferFunctionMatrix(((TransferFunction((-p + s)*(s**3 - 2) + (a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s), TransferFunction((-p + s)*(s**3 - 2) + (a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s)), (TransferFunction((-p + s)*(s**3 - 2) + (a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s), TransferFunction((-p + s)*(s**3 - 2) + (a*p**2 + b*s)*(s**4 + 5*s + 6), (-p + s)*(s**4 + 5*s + 6), s)))) rrMNc3PK|]}|jj ywrrr{s rJrz$MIMOParallel.doit.. s: $$:rrT) rrr~r:rgrr%r9rrFrs rJrgzMIMOParallel.doit s$  ' '))A,Cc:.hhj((4yy} !#z2((*,,Z8C((*Cs    J: :d*T*%11#txx@@rkc|jr#|jjtS|jSrrrs rJrz4MIMOParallel._eval_rewrite_as_TransferFunctionMatrix s/  ' '99;&&'78 8yy{rkcFt|j}tg||Srrrr6rs rJr1zMIMOParallel.__add__$ s$TYY 2]2E22rkc|| zSrrr3s rJr6zMIMOParallel.__sub__, rrkc| |zSrrr3s rJr8zMIMOParallel.__rsub__0 r9rkc~t|tr"t|j}tg||St||Srrr/s rJr;zMIMOParallel.__mul__3 s< eZ (EJJ'H.x.. .%&&rkcbt|jDcgc]}| }}t|Scc}wrr)rrqr0s rJrHzMIMOParallel.__neg__< s/$(O4SSD44X&&5s ,rrs@rJr6r68 sDL&+/*  *''((11,, AD 33 H'''rkr6ceZdZdZdfd ZdZeZedZedZ edZ edZ edZ ed Z ed Zdd Zd Zd ZdZxZS)r7a1 A class for representing closed-loop feedback interconnection between two SISO input/output systems. The first argument, ``sys1``, is the feedforward part of the closed-loop system or in simple words, the dynamical model representing the process to be controlled. The second argument, ``sys2``, is the feedback system and controls the fed back signal to ``sys1``. Both ``sys1`` and ``sys2`` can either be ``Series``, ``StateSpace`` or ``TransferFunction`` objects. Parameters ========== sys1 : Series, StateSpace, TransferFunction The feedforward path system. sys2 : Series, StateSpace, TransferFunction, optional The feedback path system (often a feedback controller). It is the model sitting on the feedback path. If not specified explicitly, the sys2 is assumed to be unit (1.0) transfer function. sign : int, optional The sign of feedback. Can either be ``1`` (for positive feedback) or ``-1`` (for negative feedback). Default value is `-1`. Raises ====== ValueError When ``sys1`` and ``sys2`` are not using the same complex variable of the Laplace transform. When a combination of ``sys1`` and ``sys2`` yields zero denominator. TypeError When either ``sys1`` or ``sys2`` is not a ``Series``, ``StateSpace`` or ``TransferFunction`` object. Examples ======== >>> from sympy import Matrix >>> from sympy.abc import s >>> from sympy.physics.control.lti import StateSpace, TransferFunction, Feedback >>> plant = TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> controller = TransferFunction(5*s - 10, s + 7, s) >>> F1 = Feedback(plant, controller) >>> F1 Feedback(TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s), TransferFunction(5*s - 10, s + 7, s), -1) >>> F1.var s >>> F1.args (TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s), TransferFunction(5*s - 10, s + 7, s), -1) You can get the feedforward and feedback path systems by using ``.sys1`` and ``.sys2`` respectively. >>> F1.sys1 TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> F1.sys2 TransferFunction(5*s - 10, s + 7, s) You can get the resultant closed loop transfer function obtained by negative feedback interconnection using ``.doit()`` method. >>> F1.doit() TransferFunction((s + 7)*(s**2 - 4*s + 2)*(3*s**2 + 7*s - 3), ((s + 7)*(s**2 - 4*s + 2) + (5*s - 10)*(3*s**2 + 7*s - 3))*(s**2 - 4*s + 2), s) >>> G = TransferFunction(2*s**2 + 5*s + 1, s**2 + 2*s + 3, s) >>> C = TransferFunction(5*s + 10, s + 10, s) >>> F2 = Feedback(G*C, TransferFunction(1, 1, s)) >>> F2.doit() TransferFunction((s + 10)*(5*s + 10)*(s**2 + 2*s + 3)*(2*s**2 + 5*s + 1), (s + 10)*((s + 10)*(s**2 + 2*s + 3) + (5*s + 10)*(2*s**2 + 5*s + 1))*(s**2 + 2*s + 3), s) To negate a ``Feedback`` object, the ``-`` operator can be prepended: >>> -F1 Feedback(TransferFunction(-3*s**2 - 7*s + 3, s**2 - 4*s + 2, s), TransferFunction(10 - 5*s, s + 7, s), -1) >>> -F2 Feedback(Series(TransferFunction(-1, 1, s), TransferFunction(2*s**2 + 5*s + 1, s**2 + 2*s + 3, s), TransferFunction(5*s + 10, s + 10, s)), TransferFunction(-1, 1, s), -1) ``Feedback`` can also be used to connect SISO ``StateSpace`` systems together. >>> A1 = Matrix([[-1]]) >>> B1 = Matrix([[1]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([1]) >>> A2 = Matrix([[0]]) >>> B2 = Matrix([[1]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[0]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> F3 = Feedback(ss1, ss2) >>> F3 Feedback(StateSpace(Matrix([[-1]]), Matrix([[1]]), Matrix([[-1]]), Matrix([[1]])), StateSpace(Matrix([[0]]), Matrix([[1]]), Matrix([[1]]), Matrix([[0]])), -1) ``doit()`` can be used to find ``StateSpace`` equivalent for the system containing ``StateSpace`` objects. >>> F3.doit() StateSpace(Matrix([ [-1, -1], [-1, -1]]), Matrix([ [1], [1]]), Matrix([[-1, -1]]), Matrix([[1]])) We can also find the equivalent ``TransferFunction`` by using ``rewrite(TransferFunction)`` method. >>> F3.rewrite(TransferFunction) TransferFunction(s, s + 2, s) See Also ======== MIMOFeedback, Series, Parallel c,|stdd|j}t|tttt fs t dt|tttt fs t d|j|jcxk(r/|jcxk(r|jcxk(rdk(stdtd|dvrttd|js |jrd|_ nxt|j|jj|k(r td|j|jk7rttd d |_ tt |G|||t%|S) NrMz(Unsupported type for `sys1` in Feedback.z(Unsupported type for `sys2` in Feedback.zaTo use Feedback connection for MIMO systems use MIMOFeedback instead.rNrM Unsupported type for feedback. `sign` arg should either be 1 (positive feedback loop) or -1 (negative feedback loop).Tz1The equivalent system will have zero denominator.zQBoth `sys1` and `sys2` should be using the same complex variable.F)r1rFr~r3r:r7rrrrr,rrrsimplifyrrrrrsys1sys2signrs rJrzFeedback.__new__ ss#Aq$((3D$!16:x PQFG G$!16:x PQFG G4#3#3&t&  &$%&9: :&9: : w Z)-./ /  $ $(A(A'+C $4<<>4<<>2;;=E !TUUxx488# -*"+,,',C $,c:3dHUYN[[rkcVd|jd|jd|jdS)Nz Feedback(r^r_)rrrrs rJr`zFeedback.__repr__ s)499+R {"TYYKqAArkc |jdS)a Returns the feedforward system of the feedback interconnection. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction, Feedback >>> plant = TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> controller = TransferFunction(5*s - 10, s + 7, s) >>> F1 = Feedback(plant, controller) >>> F1.sys1 TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> G = TransferFunction(2*s**2 + 5*s + 1, p**2 + 2*p + 3, p) >>> C = TransferFunction(5*p + 10, p + 10, p) >>> P = TransferFunction(1 - s, p + 2, p) >>> F2 = Feedback(TransferFunction(1, 1, p), G*C*P) >>> F2.sys1 TransferFunction(1, 1, p) rrrs rJrz Feedback.sys1 .yy|rkc |jdS)ao Returns the feedback controller of the feedback interconnection. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction, Feedback >>> plant = TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> controller = TransferFunction(5*s - 10, s + 7, s) >>> F1 = Feedback(plant, controller) >>> F1.sys2 TransferFunction(5*s - 10, s + 7, s) >>> G = TransferFunction(2*s**2 + 5*s + 1, p**2 + 2*p + 3, p) >>> C = TransferFunction(5*p + 10, p + 10, p) >>> P = TransferFunction(1 - s, p + 2, p) >>> F2 = Feedback(TransferFunction(1, 1, p), G*C*P) >>> F2.sys2 Series(TransferFunction(2*s**2 + 5*s + 1, p**2 + 2*p + 3, p), TransferFunction(5*p + 10, p + 10, p), TransferFunction(1 - s, p + 2, p)) rMrrs rJrz Feedback.sys2 r rkc.|jjS)a Returns the complex variable of the Laplace transform used by all the transfer functions involved in the feedback interconnection. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction, Feedback >>> plant = TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> controller = TransferFunction(5*s - 10, s + 7, s) >>> F1 = Feedback(plant, controller) >>> F1.var s >>> G = TransferFunction(2*s**2 + 5*s + 1, p**2 + 2*p + 3, p) >>> C = TransferFunction(5*p + 10, p + 10, p) >>> P = TransferFunction(1 - s, p + 2, p) >>> F2 = Feedback(TransferFunction(1, 1, p), G*C*P) >>> F2.var p rrFrs rJrFz Feedback.var s0yy}}rkc |jdS)zn Returns the type of MIMO Feedback model. ``1`` for Positive and ``-1`` for Negative. rrrs rJrz Feedback.sign)  yy|rkc|jS)zK Returns the numerator of the closed loop feedback system. )rrs rJrQz Feedback.num1 s yyrkc\tdd|j}t|jtrt |jj n |jg}|jdk(r!t|t |jg| St|t |jg|S)zL Returns the denominator of the closed loop feedback model. rM) r1rFr~rr3rrrr5r)runitr0s rJrTz Feedback.den8 s  1dhh/+5dii+H4 'tyyk 99>D6$))#?h#?"?@ @fTYY::;;rkcdd|j|jjz|jjzz z S)a Returns the sensitivity function of the feedback loop. Sensitivity of a Feedback system is the ratio of change in the open loop gain to the change in the closed loop gain. .. note:: This method would not return the complementary sensitivity function. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, Feedback >>> C = TransferFunction(5*p + 10, p + 10, p) >>> P = TransferFunction(1 - p, p + 2, p) >>> F_1 = Feedback(P, C) >>> F_1.sensitivity 1/((1 - p)*(5*p + 10)/((p + 2)*(p + 10)) + 1) rM)rrrrrs rJ sensitivityzFeedback.sensitivityC s<4!dii 1 1 33DII4E4E4GGGHHrkc |jr|jjjt}|j jjt}|j |j|j|jf\}}}} |j |j|j|jf\} } } } t|j}t|j}||j| z| zz }|j}|| z}|| z}||j| z|zz}||j|z| zz}tj tj"||j|z|z|zz|j|z|ztj"| |z|z| |j| z| z|zz}tj ||z| | z|z}tj"||z|j| z|z}| |z}t ||||St%|jt&rt)|jj*n |jg}|jjt-dd|jj.}}|jdk(r/t1|t'|j g|j}n/t1|t'|j g| j}t-|j2|j4z|j4|j2z|j.}|r|j7}|r|j9}|S)aa Returns the resultant transfer function or state space obtained by feedback connection of transfer functions or state space objects. Examples ======== >>> from sympy.abc import s >>> from sympy import Matrix >>> from sympy.physics.control.lti import TransferFunction, Feedback, StateSpace >>> plant = TransferFunction(3*s**2 + 7*s - 3, s**2 - 4*s + 2, s) >>> controller = TransferFunction(5*s - 10, s + 7, s) >>> F1 = Feedback(plant, controller) >>> F1.doit() TransferFunction((s + 7)*(s**2 - 4*s + 2)*(3*s**2 + 7*s - 3), ((s + 7)*(s**2 - 4*s + 2) + (5*s - 10)*(3*s**2 + 7*s - 3))*(s**2 - 4*s + 2), s) >>> G = TransferFunction(2*s**2 + 5*s + 1, s**2 + 2*s + 3, s) >>> F2 = Feedback(G, TransferFunction(1, 1, s)) >>> F2.doit() TransferFunction((s**2 + 2*s + 3)*(2*s**2 + 5*s + 1), (s**2 + 2*s + 3)*(3*s**2 + 7*s + 4), s) Use kwarg ``expand=True`` to expand the resultant transfer function. Use ``cancel=True`` to cancel out the common terms in numerator and denominator. >>> F2.doit(cancel=True, expand=True) TransferFunction(2*s**2 + 5*s + 1, 3*s**2 + 7*s + 4, s) >>> F2.doit(expand=True) TransferFunction(2*s**4 + 9*s**3 + 17*s**2 + 17*s + 3, 3*s**4 + 13*s**3 + 27*s**2 + 29*s + 12, s) If the connection contain any ``StateSpace`` object then ``doit()`` will return the equivalent ``StateSpace`` object. >>> A1 = Matrix([[-1.5, -2], [1, 0]]) >>> B1 = Matrix([0.5, 0]) >>> C1 = Matrix([[0, 1]]) >>> A2 = Matrix([[0, 1], [-5, -2]]) >>> B2 = Matrix([0, 3]) >>> C2 = Matrix([[0, 1]]) >>> ss1 = StateSpace(A1, B1, C1) >>> ss2 = StateSpace(A2, B2, C2) >>> F3 = Feedback(ss1, ss2) >>> F3.doit() StateSpace(Matrix([ [-1.5, -2, 0, -0.5], [ 1, 0, 0, 0], [ 0, 0, 0, 1], [ 0, 3, -5, -2]]), Matrix([ [0.5], [ 0], [ 0], [ 0]]), Matrix([[0, 1, 0, 0]]), Matrix([[0]])) rMrN)rrrgrr:rrrrrrrrrinvrvstackhstackr~r3rrr1rFr5rQrTrr)rr)rrsys1_sssys2_ssA1B1C1D1A2B2C2D2I_inputs I_outputsFEE_D2E_C2T1T2rrrrr0F_nr(F_d _resultant_tfs rJrgz Feedback.doit_ sl  $ $iinn&..z:Giinn&..z:G$YY 799giiGNBB$YY 799giiGNBB4??+HD,,-I499r>B..AAr6Dr6DTYY^d22BDII,r11B MM"tyy2~4r99499r>D;P Q MM"r'B,TYY^b-@4-G(G HA b2grBw|4A b2gtyy2~'<=ARAaAq) )+5dii+H4 'tyykIINN$&6q!TYY]]&KT 99?4 !=H!=>CCEC4&">X">!>?DDFC(377):CGGcgg >yy{rkc>|jjS)a Converts a ``Feedback`` object to SymPy Expr. Examples ======== >>> from sympy.abc import s, a, b >>> from sympy.physics.control.lti import TransferFunction, Feedback >>> from sympy import Expr >>> tf1 = TransferFunction(a+s, 1, s) >>> tf2 = TransferFunction(b+s, 1, s) >>> fd1 = Feedback(tf1, tf2) >>> fd1.to_expr() (a + s)/((a + s)*(b + s) + 1) >>> isinstance(_, Expr) True rrs rJrzFeedback.to_expr s&yy{""$$rkc\t|j |j |jSr)r7rrrrs rJrHzFeedback.__neg__ s! TYYJ ::rkr)FF)rrrrrr`rhrrrrFrrQrTr*rgrrrHrrs@rJr7r7A stj\BBG 002 <<II6bH %*;rkr7ct|j||jjz|jjzz }|j }|dk7S)zV Checks whether a given pair of MIMO systems passed is invertible or not. r)rrrgrdet)rbr_mat_dets rJ_is_invertiblerL sN q}} affh&8&8 91668;M;M N ND 88:D 19rkceZdZdZd fd ZedZedZedZedZ edZ edZ ed Z dd Z d Zd ZxZS)r8a A class for representing closed-loop feedback interconnection between two MIMO input/output systems. Parameters ========== sys1 : MIMOSeries, TransferFunctionMatrix, StateSpace The MIMO system placed on the feedforward path. sys2 : MIMOSeries, TransferFunctionMatrix, StateSpace The system placed on the feedback path (often a feedback controller). sign : int, optional The sign of feedback. Can either be ``1`` (for positive feedback) or ``-1`` (for negative feedback). Default value is `-1`. Raises ====== ValueError When ``sys1`` and ``sys2`` are not using the same complex variable of the Laplace transform. Forward path model should have an equal number of inputs/outputs to the feedback path outputs/inputs. When product of ``sys1`` and ``sys2`` is not a square matrix. When the equivalent MIMO system is not invertible. TypeError When either ``sys1`` or ``sys2`` is not a ``MIMOSeries``, ``TransferFunctionMatrix`` or a ``StateSpace`` object. Examples ======== >>> from sympy import Matrix, pprint >>> from sympy.abc import s >>> from sympy.physics.control.lti import StateSpace, TransferFunctionMatrix, MIMOFeedback >>> plant_mat = Matrix([[1, 1/s], [0, 1]]) >>> controller_mat = Matrix([[10, 0], [0, 10]]) # Constant Gain >>> plant = TransferFunctionMatrix.from_Matrix(plant_mat, s) >>> controller = TransferFunctionMatrix.from_Matrix(controller_mat, s) >>> feedback = MIMOFeedback(plant, controller) # Negative Feedback (default) >>> pprint(feedback, use_unicode=False) / [1 1] [10 0 ] \-1 [1 1] | [- -] [-- - ] | [- -] | [1 s] [1 1 ] | [1 s] |I + [ ] *[ ] | * [ ] | [0 1] [0 10] | [0 1] | [- -] [- --] | [- -] \ [1 1]{t} [1 1 ]{t}/ [1 1]{t} To get the equivalent system matrix, use either ``doit`` or ``rewrite`` method. >>> pprint(feedback.doit(), use_unicode=False) [1 1 ] [-- -----] [11 121*s] [ ] [0 1 ] [- -- ] [1 11 ]{t} To negate the ``MIMOFeedback`` object, use ``-`` operator. >>> neg_feedback = -feedback >>> pprint(neg_feedback.doit(), use_unicode=False) [-1 -1 ] [--- -----] [11 121*s] [ ] [ 0 -1 ] [ - --- ] [ 1 11 ]{t} ``MIMOFeedback`` can also be used to connect MIMO ``StateSpace`` systems. >>> A1 = Matrix([[4, 1], [2, -3]]) >>> B1 = Matrix([[5, 2], [-3, -3]]) >>> C1 = Matrix([[2, -4], [0, 1]]) >>> D1 = Matrix([[3, 2], [1, -1]]) >>> A2 = Matrix([[-3, 4, 2], [-1, -3, 0], [2, 5, 3]]) >>> B2 = Matrix([[1, 4], [-3, -3], [-2, 1]]) >>> C2 = Matrix([[4, 2, -3], [1, 4, 3]]) >>> D2 = Matrix([[-2, 4], [0, 1]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> F1 = MIMOFeedback(ss1, ss2) >>> F1 MIMOFeedback(StateSpace(Matrix([ [4, 1], [2, -3]]), Matrix([ [ 5, 2], [-3, -3]]), Matrix([ [2, -4], [0, 1]]), Matrix([ [3, 2], [1, -1]])), StateSpace(Matrix([ [-3, 4, 2], [-1, -3, 0], [ 2, 5, 3]]), Matrix([ [ 1, 4], [-3, -3], [-2, 1]]), Matrix([ [4, 2, -3], [1, 4, 3]]), Matrix([ [-2, 4], [ 0, 1]])), -1) ``doit()`` can be used to find ``StateSpace`` equivalent for the system containing ``StateSpace`` objects. >>> F1.doit() StateSpace(Matrix([ [ 3, -3/4, -15/4, -37/2, -15], [ 7/2, -39/8, 9/8, 39/4, 9], [ 3, -41/4, -45/4, -51/2, -19], [-9/2, 129/8, 73/8, 171/4, 36], [-3/2, 47/8, 31/8, 85/4, 18]]), Matrix([ [-1/4, 19/4], [ 3/8, -21/8], [ 1/4, 29/4], [ 3/8, -93/8], [ 5/8, -35/8]]), Matrix([ [ 1, -15/4, -7/4, -21/2, -9], [1/2, -13/8, -13/8, -19/4, -3]]), Matrix([ [-1/4, 11/4], [ 1/8, 9/8]])) See Also ======== Feedback, MIMOSeries, MIMOParallel c~t|tttfs t dt|tttfs t d|j |j k7s|j |j k7rttd|dvrttd|js |jrd|_ nt|||s tdd|_ |js-|j|jk7rttd t|5|||t|S) Nz-Unsupported type for `sys1` in MIMO Feedback.z-Unsupported type for `sys2` in MIMO Feedback.zY Product of `sys1` and `sys2` must yield a square matrix.rrTzNon-Invertible system inputted.Fzb Both `sys1` and `sys2` should be using the same complex variable.)r~r9r4r:rrrrr,rrLrFrrrrs rJrzMIMOFeedback.__new__v s&$!7Z PQKL L$!7Z PQKL L ??d.. .    /Z)*+, , w Z)-./ /  $ $(A(A'+C $!$d3 !BCC',C $''DHH,@Z)*+, ,wsD$??rkc |jdS)a Returns the system placed on the feedforward path of the MIMO feedback interconnection. Examples ======== >>> from sympy import pprint >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOFeedback >>> tf1 = TransferFunction(s**2 + s + 1, s**2 - s + 1, s) >>> tf2 = TransferFunction(1, s, s) >>> tf3 = TransferFunction(1, 1, s) >>> sys1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) >>> sys2 = TransferFunctionMatrix([[tf3, tf3], [tf3, tf2]]) >>> F_1 = MIMOFeedback(sys1, sys2, 1) >>> F_1.sys1 TransferFunctionMatrix(((TransferFunction(s**2 + s + 1, s**2 - s + 1, s), TransferFunction(1, s, s)), (TransferFunction(1, s, s), TransferFunction(s**2 + s + 1, s**2 - s + 1, s)))) >>> pprint(_, use_unicode=False) [ 2 ] [s + s + 1 1 ] [---------- - ] [ 2 s ] [s - s + 1 ] [ ] [ 2 ] [ 1 s + s + 1] [ - ----------] [ s 2 ] [ s - s + 1]{t} rrrs rJrzMIMOFeedback.sys1 sByy|rkc |jdS)a+ Returns the feedback controller of the MIMO feedback interconnection. Examples ======== >>> from sympy import pprint >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOFeedback >>> tf1 = TransferFunction(s**2, s**3 - s + 1, s) >>> tf2 = TransferFunction(1, s, s) >>> tf3 = TransferFunction(1, 1, s) >>> sys1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) >>> sys2 = TransferFunctionMatrix([[tf1, tf3], [tf3, tf2]]) >>> F_1 = MIMOFeedback(sys1, sys2) >>> F_1.sys2 TransferFunctionMatrix(((TransferFunction(s**2, s**3 - s + 1, s), TransferFunction(1, 1, s)), (TransferFunction(1, 1, s), TransferFunction(1, s, s)))) >>> pprint(_, use_unicode=False) [ 2 ] [ s 1] [---------- -] [ 3 1] [s - s + 1 ] [ ] [ 1 1] [ - -] [ 1 s]{t} rMrrs rJrzMIMOFeedback.sys2 s>yy|rkc.|jjS)a Returns the complex variable of the Laplace transform used by all the transfer functions involved in the MIMO feedback loop. Examples ======== >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOFeedback >>> tf1 = TransferFunction(p, 1 - p, p) >>> tf2 = TransferFunction(1, p, p) >>> tf3 = TransferFunction(1, 1, p) >>> sys1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) >>> sys2 = TransferFunctionMatrix([[tf1, tf3], [tf3, tf2]]) >>> F_1 = MIMOFeedback(sys1, sys2, 1) # Positive feedback >>> F_1.var p r#rs rJrFzMIMOFeedback.var s*yy}}rkc |jdS)z Returns the type of feedback interconnection of two models. ``1`` for Positive and ``-1`` for Negative. rrrs rJrzMIMOFeedback.sign r%rkc|jjj}|jjj}t |jj |j |z|zz jS)a Returns the sensitivity function matrix of the feedback loop. Sensitivity of a closed-loop system is the ratio of change in the open loop gain to the change in the closed loop gain. .. note:: This method would not return the complementary sensitivity function. Examples ======== >>> from sympy import pprint >>> from sympy.abc import p >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOFeedback >>> tf1 = TransferFunction(p, 1 - p, p) >>> tf2 = TransferFunction(1, p, p) >>> tf3 = TransferFunction(1, 1, p) >>> sys1 = TransferFunctionMatrix([[tf1, tf2], [tf2, tf1]]) >>> sys2 = TransferFunctionMatrix([[tf1, tf3], [tf3, tf2]]) >>> F_1 = MIMOFeedback(sys1, sys2, 1) # Positive feedback >>> F_2 = MIMOFeedback(sys1, sys2) # Negative feedback >>> pprint(F_1.sensitivity, use_unicode=False) [ 4 3 2 5 4 2 ] [- p + 3*p - 4*p + 3*p - 1 p - 2*p + 3*p - 3*p + 1 ] [---------------------------- -----------------------------] [ 4 3 2 5 4 3 2 ] [ p + 3*p - 8*p + 8*p - 3 p + 3*p - 8*p + 8*p - 3*p] [ ] [ 4 3 2 3 2 ] [ p - p - p + p 3*p - 6*p + 4*p - 1 ] [ -------------------------- -------------------------- ] [ 4 3 2 4 3 2 ] [ p + 3*p - 8*p + 8*p - 3 p + 3*p - 8*p + 8*p - 3 ] >>> pprint(F_2.sensitivity, use_unicode=False) [ 4 3 2 5 4 2 ] [p - 3*p + 2*p + p - 1 p - 2*p + 3*p - 3*p + 1] [------------------------ --------------------------] [ 4 3 5 4 2 ] [ p - 3*p + 2*p - 1 p - 3*p + 2*p - p ] [ ] [ 4 3 2 4 3 ] [ p - p - p + p 2*p - 3*p + 2*p - 1 ] [ ------------------- --------------------- ] [ 4 3 4 3 ] [ p - 3*p + 2*p - 1 p - 3*p + 2*p - 1 ] )rrgrrrrrr,)r _sys1_mat _sys2_mats rJr*zMIMOFeedback.sensitivity sefIINN$.. IINN$.. DII(() IIi  )*+.35 1rkc.|jjS)z+Returns the number of inputs of the system.)rrrs rJrzMIMOFeedback.num_inputs3 syy###rkc.|jjS)z,Returns the number of outputs of the system.)rrrs rJrzMIMOFeedback.num_outputs8 syy$$$rkc 2|jr|jjjt}|j jjt}|j |j|j|jf\}}}} |j |j|j|jf\} } } } t|j}t|j}||j| z| zz }|j}|| z}|| z}||j| z|zz}||j|z| zz}tj tj"||j|z|z|zz|j|z|ztj"| |z|z| |j| z| z|zz}tj ||z| | z|z}tj"||z|j| z|z}| |z}t ||||S|j$|jjj&z}t)||j*}|r|j-}|r|j/}|S)a Returns the resultant transfer function matrix obtained by the feedback interconnection. Examples ======== >>> from sympy import pprint >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, MIMOFeedback >>> tf1 = TransferFunction(s, 1 - s, s) >>> tf2 = TransferFunction(1, s, s) >>> tf3 = TransferFunction(5, 1, s) >>> tf4 = TransferFunction(s - 1, s, s) >>> tf5 = TransferFunction(0, 1, s) >>> sys1 = TransferFunctionMatrix([[tf1, tf2], [tf3, tf4]]) >>> sys2 = TransferFunctionMatrix([[tf3, tf5], [tf5, tf5]]) >>> F_1 = MIMOFeedback(sys1, sys2, 1) >>> pprint(F_1, use_unicode=False) / [ s 1 ] [5 0] \-1 [ s 1 ] | [----- - ] [- -] | [----- - ] | [1 - s s ] [1 1] | [1 - s s ] |I - [ ] *[ ] | * [ ] | [ 5 s - 1] [0 0] | [ 5 s - 1] | [ - -----] [- -] | [ - -----] \ [ 1 s ]{t} [1 1]{t}/ [ 1 s ]{t} >>> pprint(F_1.doit(), use_unicode=False) [ -s s - 1 ] [------- ----------- ] [6*s - 1 s*(6*s - 1) ] [ ] [5*s - 5 (s - 1)*(6*s + 24)] [------- ------------------] [6*s - 1 s*(6*s - 1) ]{t} If the user wants the resultant ``TransferFunctionMatrix`` object without canceling the common factors then the ``cancel`` kwarg should be passed ``False``. >>> pprint(F_1.doit(cancel=False), use_unicode=False) [ s*(s - 1) s - 1 ] [ ----------------- ----------- ] [ (1 - s)*(6*s - 1) s*(6*s - 1) ] [ ] [s*(25*s - 25) + 5*(1 - s)*(6*s - 1) s*(s - 1)*(6*s - 1) + s*(25*s - 25)] [----------------------------------- -----------------------------------] [ (1 - s)*(6*s - 1) 2 ] [ s *(6*s - 1) ]{t} If the user wants the expanded form of the resultant transfer function matrix, the ``expand`` kwarg should be passed as ``True``. >>> pprint(F_1.doit(expand=True), use_unicode=False) [ -s s - 1 ] [------- -------- ] [6*s - 1 2 ] [ 6*s - s ] [ ] [ 2 ] [5*s - 5 6*s + 18*s - 24] [------- ----------------] [6*s - 1 2 ] [ 6*s - s ]{t} )rrrgrr:rrrrrrrrrr,rr-r.r*r_to_TFMrFrr)rr)rrr/r0r1r2r3r4r5r6r7r8r9r:r;r<r=r>r?r@rrrrrJ_resultant_tfms rJrgzMIMOFeedback.doit= s6B  $ $iinn&..z:Giinn&..z:G$YY 799giiGNBB$YY 799giiGNBB4??+HD,,-I499r>B..AAr6Dr6DTYY^d22BDII,r11B MM"tyy2~4r99499r>D;P Q MM"r'B,TYY^b-@4-G(G HA b2grBw|4A b2gtyy2~'<=ARAaAq) )$)).."2"<"<< txx0 +446N +224Nrkc "|jSr)rg)rrrrrs rJrz4MIMOFeedback._eval_rewrite_as_TransferFunctionMatrix syy{rkc\t|j |j |jSr)r8rrrrs rJrHzMIMOFeedback.__neg__ s!TYYJ DII>>rk)rN)TF)rrrrrrrrrFrr*rrrgrrHrrs@rJr8r8 sHR@B  D@,6161p$$%%gR?rkr8c fd}|jDcgc]}|Dcgc] }|| c}}}}t|Scc}wcc}}w)zOPrivate method to convert ImmutableMatrix to TransferFunctionMatrix efficientlyc0tj|Srr1r)rrFs rJz_to_TFM.. s)BB4Mrk)tolistr9)rrFto_tfrowrrqs ` rJrYrY sC ME47JJL ASC (DE$K ( AC A !# && ) As A A A A ceZdZdZfdZedZedZedZ edZ edZ dZ e d Ze d Ze d Zd Zd ZdZdZdZdZdZdZdZxZS)r9aF A class for representing the MIMO (multiple-input and multiple-output) generalization of the SISO (single-input and single-output) transfer function. It is a matrix of transfer functions (``TransferFunction``, SISO-``Series`` or SISO-``Parallel``). There is only one argument, ``arg`` which is also the compulsory argument. ``arg`` is expected to be strictly of the type list of lists which holds the transfer functions or reducible to transfer functions. Parameters ========== arg : Nested ``List`` (strictly). Users are expected to input a nested list of ``TransferFunction``, ``Series`` and/or ``Parallel`` objects. Examples ======== .. note:: ``pprint()`` can be used for better visualization of ``TransferFunctionMatrix`` objects. >>> from sympy.abc import s, p, a >>> from sympy import pprint >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, Series, Parallel >>> tf_1 = TransferFunction(s + a, s**2 + s + 1, s) >>> tf_2 = TransferFunction(p**4 - 3*p + 2, s + p, s) >>> tf_3 = TransferFunction(3, s + 2, s) >>> tf_4 = TransferFunction(-a + p, 9*s - 9, s) >>> tfm_1 = TransferFunctionMatrix([[tf_1], [tf_2], [tf_3]]) >>> tfm_1 TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(3, s + 2, s),))) >>> tfm_1.var s >>> tfm_1.num_inputs 1 >>> tfm_1.num_outputs 3 >>> tfm_1.shape (3, 1) >>> tfm_1.args (((TransferFunction(a + s, s**2 + s + 1, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(3, s + 2, s),)),) >>> tfm_2 = TransferFunctionMatrix([[tf_1, -tf_3], [tf_2, -tf_1], [tf_3, -tf_2]]) >>> tfm_2 TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s), TransferFunction(-3, s + 2, s)), (TransferFunction(p**4 - 3*p + 2, p + s, s), TransferFunction(-a - s, s**2 + s + 1, s)), (TransferFunction(3, s + 2, s), TransferFunction(-p**4 + 3*p - 2, p + s, s)))) >>> pprint(tfm_2, use_unicode=False) # pretty-printing for better visualization [ a + s -3 ] [ ---------- ----- ] [ 2 s + 2 ] [ s + s + 1 ] [ ] [ 4 ] [p - 3*p + 2 -a - s ] [------------ ---------- ] [ p + s 2 ] [ s + s + 1 ] [ ] [ 4 ] [ 3 - p + 3*p - 2] [ ----- --------------] [ s + 2 p + s ]{t} TransferFunctionMatrix can be transposed, if user wants to switch the input and output transfer functions >>> tfm_2.transpose() TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s), TransferFunction(p**4 - 3*p + 2, p + s, s), TransferFunction(3, s + 2, s)), (TransferFunction(-3, s + 2, s), TransferFunction(-a - s, s**2 + s + 1, s), TransferFunction(-p**4 + 3*p - 2, p + s, s)))) >>> pprint(_, use_unicode=False) [ 4 ] [ a + s p - 3*p + 2 3 ] [---------- ------------ ----- ] [ 2 p + s s + 2 ] [s + s + 1 ] [ ] [ 4 ] [ -3 -a - s - p + 3*p - 2] [ ----- ---------- --------------] [ s + 2 2 p + s ] [ s + s + 1 ]{t} >>> tf_5 = TransferFunction(5, s, s) >>> tf_6 = TransferFunction(5*s, (2 + s**2), s) >>> tf_7 = TransferFunction(5, (s*(2 + s**2)), s) >>> tf_8 = TransferFunction(5, 1, s) >>> tfm_3 = TransferFunctionMatrix([[tf_5, tf_6], [tf_7, tf_8]]) >>> tfm_3 TransferFunctionMatrix(((TransferFunction(5, s, s), TransferFunction(5*s, s**2 + 2, s)), (TransferFunction(5, s*(s**2 + 2), s), TransferFunction(5, 1, s)))) >>> pprint(tfm_3, use_unicode=False) [ 5 5*s ] [ - ------] [ s 2 ] [ s + 2] [ ] [ 5 5 ] [---------- - ] [ / 2 \ 1 ] [s*\s + 2/ ]{t} >>> tfm_3.var s >>> tfm_3.shape (2, 2) >>> tfm_3.num_outputs 2 >>> tfm_3.num_inputs 2 >>> tfm_3.args (((TransferFunction(5, s, s), TransferFunction(5*s, s**2 + 2, s)), (TransferFunction(5, s*(s**2 + 2), s), TransferFunction(5, 1, s))),) To access the ``TransferFunction`` at any index in the ``TransferFunctionMatrix``, use the index notation. >>> tfm_3[1, 0] # gives the TransferFunction present at 2nd Row and 1st Col. Similar to that in Matrix classes TransferFunction(5, s*(s**2 + 2), s) >>> tfm_3[0, 0] # gives the TransferFunction present at 1st Row and 1st Col. TransferFunction(5, s, s) >>> tfm_3[:, 0] # gives the first column TransferFunctionMatrix(((TransferFunction(5, s, s),), (TransferFunction(5, s*(s**2 + 2), s),))) >>> pprint(_, use_unicode=False) [ 5 ] [ - ] [ s ] [ ] [ 5 ] [----------] [ / 2 \] [s*\s + 2/]{t} >>> tfm_3[0, :] # gives the first row TransferFunctionMatrix(((TransferFunction(5, s, s), TransferFunction(5*s, s**2 + 2, s)),)) >>> pprint(_, use_unicode=False) [5 5*s ] [- ------] [s 2 ] [ s + 2]{t} To negate a transfer function matrix, ``-`` operator can be prepended: >>> tfm_4 = TransferFunctionMatrix([[tf_2], [-tf_1], [tf_3]]) >>> -tfm_4 TransferFunctionMatrix(((TransferFunction(-p**4 + 3*p - 2, p + s, s),), (TransferFunction(a + s, s**2 + s + 1, s),), (TransferFunction(-3, s + 2, s),))) >>> tfm_5 = TransferFunctionMatrix([[tf_1, tf_2], [tf_3, -tf_1]]) >>> -tfm_5 TransferFunctionMatrix(((TransferFunction(-a - s, s**2 + s + 1, s), TransferFunction(-p**4 + 3*p - 2, p + s, s)), (TransferFunction(-3, s + 2, s), TransferFunction(a + s, s**2 + s + 1, s)))) ``subs()`` returns the ``TransferFunctionMatrix`` object with the value substituted in the expression. This will not mutate your original ``TransferFunctionMatrix``. >>> tfm_2.subs(p, 2) # substituting p everywhere in tfm_2 with 2. TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s), TransferFunction(-3, s + 2, s)), (TransferFunction(12, s + 2, s), TransferFunction(-a - s, s**2 + s + 1, s)), (TransferFunction(3, s + 2, s), TransferFunction(-12, s + 2, s)))) >>> pprint(_, use_unicode=False) [ a + s -3 ] [---------- ----- ] [ 2 s + 2 ] [s + s + 1 ] [ ] [ 12 -a - s ] [ ----- ----------] [ s + 2 2 ] [ s + s + 1] [ ] [ 3 -12 ] [ ----- ----- ] [ s + 2 s + 2 ]{t} >>> pprint(tfm_2, use_unicode=False) # State of tfm_2 is unchanged after substitution [ a + s -3 ] [ ---------- ----- ] [ 2 s + 2 ] [ s + s + 1 ] [ ] [ 4 ] [p - 3*p + 2 -a - s ] [------------ ---------- ] [ p + s 2 ] [ s + s + 1 ] [ ] [ 4 ] [ 3 - p + 3*p - 2] [ ----- --------------] [ s + 2 p + s ]{t} ``subs()`` also supports multiple substitutions. >>> tfm_2.subs({p: 2, a: 1}) # substituting p with 2 and a with 1 TransferFunctionMatrix(((TransferFunction(s + 1, s**2 + s + 1, s), TransferFunction(-3, s + 2, s)), (TransferFunction(12, s + 2, s), TransferFunction(-s - 1, s**2 + s + 1, s)), (TransferFunction(3, s + 2, s), TransferFunction(-12, s + 2, s)))) >>> pprint(_, use_unicode=False) [ s + 1 -3 ] [---------- ----- ] [ 2 s + 2 ] [s + s + 1 ] [ ] [ 12 -s - 1 ] [ ----- ----------] [ s + 2 2 ] [ s + s + 1] [ ] [ 3 -12 ] [ ----- ----- ] [ s + 2 s + 2 ]{t} Users can reduce the ``Series`` and ``Parallel`` elements of the matrix to ``TransferFunction`` by using ``doit()``. >>> tfm_6 = TransferFunctionMatrix([[Series(tf_3, tf_4), Parallel(tf_3, tf_4)]]) >>> tfm_6 TransferFunctionMatrix(((Series(TransferFunction(3, s + 2, s), TransferFunction(-a + p, 9*s - 9, s)), Parallel(TransferFunction(3, s + 2, s), TransferFunction(-a + p, 9*s - 9, s))),)) >>> pprint(tfm_6, use_unicode=False) [-a + p 3 -a + p 3 ] [-------*----- ------- + -----] [9*s - 9 s + 2 9*s - 9 s + 2]{t} >>> tfm_6.doit() TransferFunctionMatrix(((TransferFunction(-3*a + 3*p, (s + 2)*(9*s - 9), s), TransferFunction(27*s + (-a + p)*(s + 2) - 27, (s + 2)*(9*s - 9), s)),)) >>> pprint(_, use_unicode=False) [ -3*a + 3*p 27*s + (-a + p)*(s + 2) - 27] [----------------- ----------------------------] [(s + 2)*(9*s - 9) (s + 2)*(9*s - 9) ]{t} >>> tf_9 = TransferFunction(1, s, s) >>> tf_10 = TransferFunction(1, s**2, s) >>> tfm_7 = TransferFunctionMatrix([[Series(tf_9, tf_10), tf_9], [tf_10, Parallel(tf_9, tf_10)]]) >>> tfm_7 TransferFunctionMatrix(((Series(TransferFunction(1, s, s), TransferFunction(1, s**2, s)), TransferFunction(1, s, s)), (TransferFunction(1, s**2, s), Parallel(TransferFunction(1, s, s), TransferFunction(1, s**2, s))))) >>> pprint(tfm_7, use_unicode=False) [ 1 1 ] [---- - ] [ 2 s ] [s*s ] [ ] [ 1 1 1] [ -- -- + -] [ 2 2 s] [ s s ]{t} >>> tfm_7.doit() TransferFunctionMatrix(((TransferFunction(1, s**3, s), TransferFunction(1, s, s)), (TransferFunction(1, s**2, s), TransferFunction(s**2 + s, s**3, s)))) >>> pprint(_, use_unicode=False) [1 1 ] [-- - ] [ 3 s ] [s ] [ ] [ 2 ] [1 s + s] [-- ------] [ 2 3 ] [s s ]{t} Addition, subtraction, and multiplication of transfer function matrices can form unevaluated ``Series`` or ``Parallel`` objects. - For addition and subtraction: All the transfer function matrices must have the same shape. - For multiplication (C = A * B): The number of inputs of the first transfer function matrix (A) must be equal to the number of outputs of the second transfer function matrix (B). Also, use pretty-printing (``pprint``) to analyse better. >>> tfm_8 = TransferFunctionMatrix([[tf_3], [tf_2], [-tf_1]]) >>> tfm_9 = TransferFunctionMatrix([[-tf_3]]) >>> tfm_10 = TransferFunctionMatrix([[tf_1], [tf_2], [tf_4]]) >>> tfm_11 = TransferFunctionMatrix([[tf_4], [-tf_1]]) >>> tfm_12 = TransferFunctionMatrix([[tf_4, -tf_1, tf_3], [-tf_2, -tf_4, -tf_3]]) >>> tfm_8 + tfm_10 MIMOParallel(TransferFunctionMatrix(((TransferFunction(3, s + 2, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a - s, s**2 + s + 1, s),))), TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a + p, 9*s - 9, s),)))) >>> pprint(_, use_unicode=False) [ 3 ] [ a + s ] [ ----- ] [ ---------- ] [ s + 2 ] [ 2 ] [ ] [ s + s + 1 ] [ 4 ] [ ] [p - 3*p + 2] [ 4 ] [------------] + [p - 3*p + 2] [ p + s ] [------------] [ ] [ p + s ] [ -a - s ] [ ] [ ---------- ] [ -a + p ] [ 2 ] [ ------- ] [ s + s + 1 ]{t} [ 9*s - 9 ]{t} >>> -tfm_10 - tfm_8 MIMOParallel(TransferFunctionMatrix(((TransferFunction(-a - s, s**2 + s + 1, s),), (TransferFunction(-p**4 + 3*p - 2, p + s, s),), (TransferFunction(a - p, 9*s - 9, s),))), TransferFunctionMatrix(((TransferFunction(-3, s + 2, s),), (TransferFunction(-p**4 + 3*p - 2, p + s, s),), (TransferFunction(a + s, s**2 + s + 1, s),)))) >>> pprint(_, use_unicode=False) [ -a - s ] [ -3 ] [ ---------- ] [ ----- ] [ 2 ] [ s + 2 ] [ s + s + 1 ] [ ] [ ] [ 4 ] [ 4 ] [- p + 3*p - 2] [- p + 3*p - 2] + [--------------] [--------------] [ p + s ] [ p + s ] [ ] [ ] [ a + s ] [ a - p ] [ ---------- ] [ ------- ] [ 2 ] [ 9*s - 9 ]{t} [ s + s + 1 ]{t} >>> tfm_12 * tfm_8 MIMOSeries(TransferFunctionMatrix(((TransferFunction(3, s + 2, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a - s, s**2 + s + 1, s),))), TransferFunctionMatrix(((TransferFunction(-a + p, 9*s - 9, s), TransferFunction(-a - s, s**2 + s + 1, s), TransferFunction(3, s + 2, s)), (TransferFunction(-p**4 + 3*p - 2, p + s, s), TransferFunction(a - p, 9*s - 9, s), TransferFunction(-3, s + 2, s))))) >>> pprint(_, use_unicode=False) [ 3 ] [ ----- ] [ -a + p -a - s 3 ] [ s + 2 ] [ ------- ---------- -----] [ ] [ 9*s - 9 2 s + 2] [ 4 ] [ s + s + 1 ] [p - 3*p + 2] [ ] *[------------] [ 4 ] [ p + s ] [- p + 3*p - 2 a - p -3 ] [ ] [-------------- ------- -----] [ -a - s ] [ p + s 9*s - 9 s + 2]{t} [ ---------- ] [ 2 ] [ s + s + 1 ]{t} >>> tfm_12 * tfm_8 * tfm_9 MIMOSeries(TransferFunctionMatrix(((TransferFunction(-3, s + 2, s),),)), TransferFunctionMatrix(((TransferFunction(3, s + 2, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a - s, s**2 + s + 1, s),))), TransferFunctionMatrix(((TransferFunction(-a + p, 9*s - 9, s), TransferFunction(-a - s, s**2 + s + 1, s), TransferFunction(3, s + 2, s)), (TransferFunction(-p**4 + 3*p - 2, p + s, s), TransferFunction(a - p, 9*s - 9, s), TransferFunction(-3, s + 2, s))))) >>> pprint(_, use_unicode=False) [ 3 ] [ ----- ] [ -a + p -a - s 3 ] [ s + 2 ] [ ------- ---------- -----] [ ] [ 9*s - 9 2 s + 2] [ 4 ] [ s + s + 1 ] [p - 3*p + 2] [ -3 ] [ ] *[------------] *[-----] [ 4 ] [ p + s ] [s + 2]{t} [- p + 3*p - 2 a - p -3 ] [ ] [-------------- ------- -----] [ -a - s ] [ p + s 9*s - 9 s + 2]{t} [ ---------- ] [ 2 ] [ s + s + 1 ]{t} >>> tfm_10 + tfm_8*tfm_9 MIMOParallel(TransferFunctionMatrix(((TransferFunction(a + s, s**2 + s + 1, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a + p, 9*s - 9, s),))), MIMOSeries(TransferFunctionMatrix(((TransferFunction(-3, s + 2, s),),)), TransferFunctionMatrix(((TransferFunction(3, s + 2, s),), (TransferFunction(p**4 - 3*p + 2, p + s, s),), (TransferFunction(-a - s, s**2 + s + 1, s),))))) >>> pprint(_, use_unicode=False) [ a + s ] [ 3 ] [ ---------- ] [ ----- ] [ 2 ] [ s + 2 ] [ s + s + 1 ] [ ] [ ] [ 4 ] [ 4 ] [p - 3*p + 2] [ -3 ] [p - 3*p + 2] + [------------] *[-----] [------------] [ p + s ] [s + 2]{t} [ p + s ] [ ] [ ] [ -a - s ] [ -a + p ] [ ---------- ] [ ------- ] [ 2 ] [ 9*s - 9 ]{t} [ s + s + 1 ]{t} These unevaluated ``Series`` or ``Parallel`` objects can convert into the resultant transfer function matrix using ``.doit()`` method or by ``.rewrite(TransferFunctionMatrix)``. >>> (-tfm_8 + tfm_10 + tfm_8*tfm_9).doit() TransferFunctionMatrix(((TransferFunction((a + s)*(s + 2)**3 - 3*(s + 2)**2*(s**2 + s + 1) - 9*(s + 2)*(s**2 + s + 1), (s + 2)**3*(s**2 + s + 1), s),), (TransferFunction((p + s)*(-3*p**4 + 9*p - 6), (p + s)**2*(s + 2), s),), (TransferFunction((-a + p)*(s + 2)*(s**2 + s + 1)**2 + (a + s)*(s + 2)*(9*s - 9)*(s**2 + s + 1) + (3*a + 3*s)*(9*s - 9)*(s**2 + s + 1), (s + 2)*(9*s - 9)*(s**2 + s + 1)**2, s),))) >>> (-tfm_12 * -tfm_8 * -tfm_9).rewrite(TransferFunctionMatrix) TransferFunctionMatrix(((TransferFunction(3*(-3*a + 3*p)*(p + s)*(s + 2)*(s**2 + s + 1)**2 + 3*(-3*a - 3*s)*(p + s)*(s + 2)*(9*s - 9)*(s**2 + s + 1) + 3*(a + s)*(s + 2)**2*(9*s - 9)*(-p**4 + 3*p - 2)*(s**2 + s + 1), (p + s)*(s + 2)**3*(9*s - 9)*(s**2 + s + 1)**2, s),), (TransferFunction(3*(-a + p)*(p + s)*(s + 2)**2*(-p**4 + 3*p - 2)*(s**2 + s + 1) + 3*(3*a + 3*s)*(p + s)**2*(s + 2)*(9*s - 9) + 3*(p + s)*(s + 2)*(9*s - 9)*(-3*p**4 + 9*p - 6)*(s**2 + s + 1), (p + s)**2*(s + 2)**3*(9*s - 9)*(s**2 + s + 1), s),))) See Also ======== TransferFunction, MIMOSeries, MIMOParallel, Feedback c2g} |ddj}|D]}g}|D]h}t |t sttd||jk7rttd|j |jj|j |t |tttfrtd|Dddi}tt|7||}t||_d|_|S#t$rttdwxYw)Nrz `arg` param in TransferFunctionMatrix should strictly be a nested list containing TransferFunction objects.zr Each element is expected to be of type `SISOLinearTimeInvariant`.z Conflicting value(s) found for `var`. All TransferFunction instances in TransferFunctionMatrix should use the same complex variable in Laplace domain.c32K|]}t|ddiyw)rFNr )rrGs rJrz1TransferFunctionMatrix.__new__..4s@q%2E2@rrF)rFrrr,r~rrrrkrr rr9rrrr) rrq expr_mat_argrFrctempelementr\rs rJrzTransferFunctionMatrix.__new__s5  a&)--C  &CD /!'+BC#J0;%<=='++%$Z1?&@AA  GOO-. /    % &" cE4/ 0@C@P%PC*C8cB' 5 #(  ; Z)  s C88Dct||S)a Creates a new ``TransferFunctionMatrix`` efficiently from a SymPy Matrix of ``Expr`` objects. Parameters ========== matrix : ``ImmutableMatrix`` having ``Expr``/``Number`` elements. var : Symbol Complex variable of the Laplace transform which will be used by the all the ``TransferFunction`` objects in the ``TransferFunctionMatrix``. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunctionMatrix >>> from sympy import Matrix, pprint >>> M = Matrix([[s, 1/s], [1/(s+1), s]]) >>> M_tf = TransferFunctionMatrix.from_Matrix(M, s) >>> pprint(M_tf, use_unicode=False) [ s 1] [ - -] [ 1 s] [ ] [ 1 s] [----- -] [s + 1 1]{t} >>> M_tf.elem_poles() [[[], [0]], [[-1], []]] >>> M_tf.elem_zeros() [[[0], []], [[], [0]]] )rY)rmatrixrFs rJrz"TransferFunctionMatrix.from_Matrix;sFvs##rkc@|jdddjS)a Returns the complex variable used by all the transfer functions or ``Series``/``Parallel`` objects in a transfer function matrix. Examples ======== >>> from sympy.abc import p, s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix, Series, Parallel >>> G1 = TransferFunction(p**2 + 2*p + 4, p - 6, p) >>> G2 = TransferFunction(p, 4 - p, p) >>> G3 = TransferFunction(0, p**4 - 1, p) >>> G4 = TransferFunction(s + 1, s**2 + s + 1, s) >>> S1 = Series(G1, G2) >>> S2 = Series(-G3, Parallel(G2, -G1)) >>> tfm1 = TransferFunctionMatrix([[G1], [G2], [G3]]) >>> tfm1.var p >>> tfm2 = TransferFunctionMatrix([[-S1, -S2], [S1, S2]]) >>> tfm2.var p >>> tfm3 = TransferFunctionMatrix([[G4]]) >>> tfm3.var s rrrs rJrFzTransferFunctionMatrix.var`s!8yy|Aq!%%%rkc4|jjdS)a- Returns the number of inputs of the system. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix >>> G1 = TransferFunction(s + 3, s**2 - 3, s) >>> G2 = TransferFunction(4, s**2, s) >>> G3 = TransferFunction(p**2 + s**2, p - 3, s) >>> tfm_1 = TransferFunctionMatrix([[G2, -G1, G3], [-G2, -G1, -G3]]) >>> tfm_1.num_inputs 3 See Also ======== num_outputs rMrrrs rJrz!TransferFunctionMatrix.num_inputs~s.~~##A&&rkc4|jjdS)a* Returns the number of outputs of the system. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunctionMatrix >>> from sympy import Matrix >>> M_1 = Matrix([[s], [1/s]]) >>> TFM = TransferFunctionMatrix.from_Matrix(M_1, s) >>> print(TFM) TransferFunctionMatrix(((TransferFunction(s, 1, s),), (TransferFunction(1, s, s),))) >>> TFM.num_outputs 2 See Also ======== num_inputs rrnrs rJrz"TransferFunctionMatrix.num_outputss0~~##A&&rkc.|jjS)a Returns the shape of the transfer function matrix, that is, ``(# of outputs, # of inputs)``. Examples ======== >>> from sympy.abc import s, p >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix >>> tf1 = TransferFunction(p**2 - 1, s**4 + s**3 - p, p) >>> tf2 = TransferFunction(1 - p, p**2 - 3*p + 7, p) >>> tf3 = TransferFunction(3, 4, p) >>> tfm1 = TransferFunctionMatrix([[tf1, -tf2]]) >>> tfm1.shape (1, 2) >>> tfm2 = TransferFunctionMatrix([[-tf2, tf3], [tf1, -tf1]]) >>> tfm2.shape (2, 2) rnrs rJrzTransferFunctionMatrix.shapes*~~###rkcH|j }t||jSr)rrYrF)rnegs rJrHzTransferFunctionMatrix.__neg__s~~osDHH%%rkc|t|ts t||St|j}t|g|Srrr>s rJr1zTransferFunctionMatrix.__add__s9%.e, ,ejj)D2>22rkc|| zSrrr3s rJr6zTransferFunctionMatrix.__sub__rrkc~t|ts t||St|j}tg||Srrr>s rJr;zTransferFunctionMatrix.__mul__s<%,eT* *ejj)0>0400rkc|jj|}t|trt ||j St j||j Sr)r __getitem__r~rrYrFr1r)rkeytruncs rJrwz"TransferFunctionMatrix.__getitem__sH**3/ e_ -5$((+ +88IIrkcb|jj}t||jS)z[Returns the transpose of the ``TransferFunctionMatrix`` (switched input and output layers).)r transposerYrF)rtransposed_mats rJr{z TransferFunctionMatrix.transposes%113~txx00rkc |jjdDcgc] }|Dcgc]}|jc}"c}}Scc}wcc}}w)a Returns the poles of each element of the ``TransferFunctionMatrix``. .. note:: Actual poles of a MIMO system are NOT the poles of individual elements. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix >>> tf_1 = TransferFunction(3, (s + 1), s) >>> tf_2 = TransferFunction(s + 6, (s + 1)*(s + 2), s) >>> tf_3 = TransferFunction(s + 3, s**2 + 3*s + 2, s) >>> tf_4 = TransferFunction(s + 2, s**2 + 5*s - 10, s) >>> tfm_1 = TransferFunctionMatrix([[tf_1, tf_2], [tf_3, tf_4]]) >>> tfm_1 TransferFunctionMatrix(((TransferFunction(3, s + 1, s), TransferFunction(s + 6, (s + 1)*(s + 2), s)), (TransferFunction(s + 3, s**2 + 3*s + 2, s), TransferFunction(s + 2, s**2 + 5*s - 10, s)))) >>> tfm_1.elem_poles() [[[-1], [-2, -1]], [[-2, -1], [-5/2 + sqrt(65)/2, -sqrt(65)/2 - 5/2]]] See Also ======== elem_zeros r)rgrrrrcris rJ elem_polesz!TransferFunctionMatrix.elem_poles@8AE @P@PQR@ST4W4TT4T AA A Ac |jjdDcgc] }|Dcgc]}|jc}"c}}Scc}wcc}}w)a Returns the zeros of each element of the ``TransferFunctionMatrix``. .. note:: Actual zeros of a MIMO system are NOT the zeros of individual elements. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix >>> tf_1 = TransferFunction(3, (s + 1), s) >>> tf_2 = TransferFunction(s + 6, (s + 1)*(s + 2), s) >>> tf_3 = TransferFunction(s + 3, s**2 + 3*s + 2, s) >>> tf_4 = TransferFunction(s**2 - 9*s + 20, s**2 + 5*s - 10, s) >>> tfm_1 = TransferFunctionMatrix([[tf_1, tf_2], [tf_3, tf_4]]) >>> tfm_1 TransferFunctionMatrix(((TransferFunction(3, s + 1, s), TransferFunction(s + 6, (s + 1)*(s + 2), s)), (TransferFunction(s + 3, s**2 + 3*s + 2, s), TransferFunction(s**2 - 9*s + 20, s**2 + 5*s - 10, s)))) >>> tfm_1.elem_zeros() [[[], [-6]], [[-3], [4, 5]]] See Also ======== elem_poles r)rgrr!r~s rJ elem_zerosz!TransferFunctionMatrix.elem_zeros rrcn|jj|j|}|jS)ao Evaluates system response of each transfer function in the ``TransferFunctionMatrix`` at any point in the real or complex plane. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction, TransferFunctionMatrix >>> from sympy import I >>> tf_1 = TransferFunction(3, (s + 1), s) >>> tf_2 = TransferFunction(s + 6, (s + 1)*(s + 2), s) >>> tf_3 = TransferFunction(s + 3, s**2 + 3*s + 2, s) >>> tf_4 = TransferFunction(s**2 - 9*s + 20, s**2 + 5*s - 10, s) >>> tfm_1 = TransferFunctionMatrix([[tf_1, tf_2], [tf_3, tf_4]]) >>> tfm_1 TransferFunctionMatrix(((TransferFunction(3, s + 1, s), TransferFunction(s + 6, (s + 1)*(s + 2), s)), (TransferFunction(s + 3, s**2 + 3*s + 2, s), TransferFunction(s**2 - 9*s + 20, s**2 + 5*s - 10, s)))) >>> tfm_1.eval_frequency(2) Matrix([ [ 1, 2/3], [5/12, 3/2]]) >>> tfm_1.eval_frequency(I*2) Matrix([ [ 3/5 - 6*I/5, -I], [3/20 - 11*I/20, -101/74 + 23*I/74]]) )rrrFr)rr$rs rJr%z%TransferFunctionMatrix.eval_frequency's*4nn!!$((E2zz|rkcZ|jdDcgc] }|D]}| c}}Scc}}w)z8Returns flattened list of args in TransferFunctionMatrixrr)rtupelems rJ_flatzTransferFunctionMatrix._flatDs* $ ! ======s'ct||jjfd}t||jS)zGCalls evalf() on each transfer function in the transfer function matrixc(|jS)NrH)evalf)rdpss rJr`z4TransferFunctionMatrix._eval_evalf..Ks3rk)r0r applyfuncrYrF)rrrrs @rJrz"TransferFunctionMatrix._eval_evalfHs3$nn&&'?@sDHH%%rkc f|jjd}t||jS)z'Simplifies the transfer function matrixct|dS)NFr)r))rs rJr`z7TransferFunctionMatrix._eval_simplify..Ps fQu6Mrk)rrrYrF)rrsimp_mats rJrz%TransferFunctionMatrix._eval_simplifyNs(>>++,MNx**rkc f|jjdi|}t||jS)z$Expands the transfer function matrixr)rrrYrF)rr expand_mats rJrzTransferFunctionMatrix.expandSs,*T^^**3U3 z488,,rk)rrrrrrrrrFrrrrHrr1r6r;rwr{rrr%rrrrrrs@rJr9r9 saD "H"$"$H&&:''0''2$$,&3311J 1 U<U<:>& + -rkr9c4eZdZdZdfd ZedZedZedZedZ eZ eZ eZ e Z edZed Zed Zed Zdded fd ZdZdZdZdZdZdZdZdZdZdZdZdZdZ dZ!dZ"dZ#dZ$xZ%S) r:ak State space model (ssm) of a linear, time invariant control system. Represents the standard state-space model with A, B, C, D as state-space matrices. This makes the linear control system: (1) x'(t) = A * x(t) + B * u(t); x in R^n , u in R^k (2) y(t) = C * x(t) + D * u(t); y in R^m where u(t) is any input signal, y(t) the corresponding output, and x(t) the system's state. Parameters ========== A : Matrix The State matrix of the state space model. B : Matrix The Input-to-State matrix of the state space model. C : Matrix The State-to-Output matrix of the state space model. D : Matrix The Feedthrough matrix of the state space model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace The easiest way to create a StateSpaceModel is via four matrices: >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> StateSpace(A, B, C, D) StateSpace(Matrix([ [1, 2], [1, 0]]), Matrix([ [1], [1]]), Matrix([[0, 1]]), Matrix([[0]])) One can use less matrices. The rest will be filled with a minimum of zeros: >>> StateSpace(A, B) StateSpace(Matrix([ [1, 2], [1, 0]]), Matrix([ [1], [1]]), Matrix([[0, 0]]), Matrix([[0]])) See Also ======== TransferFunction, TransferFunctionMatrix References ========== .. [1] https://en.wikipedia.org/wiki/State-space_representation .. [2] https://in.mathworks.com/help/control/ref/ss.html Nc&| td}|t|jd}|td|j}| t|j|j}t|}t|}t|}t|}t |t rjt |t rYt |t rHt |t r7|j|jk7r t d|j|jk7r t d|j|jk7r t d|j|jk7r t d|j|jk7r t dtt|'|||||}||_ ||_ ||_ ||_ |j}|j}|dk(r|dk(rd|_t|_nd|_t"|_d|_|St'd ) NrMz!Matrix A must be a square matrix.z3Matrices A and B must have the same number of rows.z3Matrices C and D must have the same number of rows.z6Matrices A and C must have the same number of columns.z6Matrices B and D must have the same number of columns.TFz0A, B, C and D inputs must all be sympy Matrices.)r!rowscolsrr~rr rr:r_A_B_C_Drrrrrr) rrrrrr\rrrs rJrzStateSpace.__new__s 9aA 9affa A 9a A 9affaff%A QK QK QK QK q. /JqBV4W q. /JqBV4Wvv !DEEvv !VWWvv !VWWvv !YZZvv !YZZ C0aAqACCFCFCFCF&&KJQ;!#3# 6 $ 6 '+C $JNO Orkc|jS)a Returns the state matrix of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.state_matrix Matrix([ [1, 2], [1, 0]]) )rrs rJ state_matrixzStateSpace.state_matrix *wwrkc|jS)a Returns the input matrix of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.input_matrix Matrix([ [1], [1]]) )rrs rJ input_matrixzStateSpace.input_matrixrrkc|jS)a Returns the output matrix of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.output_matrix Matrix([[0, 1]]) )rrs rJ output_matrixzStateSpace.output_matrix &wwrkc|jS)a Returns the feedforward matrix of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.feedforward_matrix Matrix([[0]]) )rrs rJfeedforward_matrixzStateSpace.feedforward_matrixrrkc.|jjS)a Returns the number of states of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.num_states 2 )rrrs rJ num_stateszStateSpace.num_states/&ww||rkc.|jjS)a Returns the number of inputs of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.num_inputs 1 )rrrs rJrzStateSpace.num_inputsDrrkc.|jjS)a Returns the number of outputs of the model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[1, 2], [1, 0]]) >>> B = Matrix([1, 1]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.num_outputs 1 )rrrs rJrzStateSpace.num_outputsYrrkc2|j|jfS)z6Returns the shape of the equivalent StateSpace system.rrs rJrzStateSpace.shapeorrktct|ts td|s$t|jj dd}n3|j |jj ddfk7r t d|s$t|jj dd}n3|j |jj ddfk7r t dt|j||j|zdd}t|}|j|d}|jj|jjz|jz}|j}t||z } t|| \} } t| || zf} tt!| } t#| D]\}}|j| ||}|j$|z|j&|zz}|S) a Returns `y(t)` or output of StateSpace given by the solution of equations: x'(t) = A * x(t) + B * u(t) y(t) = C * x(t) + D * u(t) Parameters ============ initial_conditions : Matrix The initial conditions of `x` state vector. If not provided, it defaults to a zero vector. input_vector : Matrix The input vector for state space. If not provided, it defaults to a zero vector. var : Symbol The symbol representing time. If not provided, it defaults to `t`. Examples ========== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-2, 0], [1, -1]]) >>> B = Matrix([[1], [0]]) >>> C = Matrix([[2, 1]]) >>> ip = Matrix([5]) >>> i = Matrix([0, 0]) >>> ss = StateSpace(A, B, C) >>> ss.dsolve(input_vector=ip, initial_conditions=i).simplify() Matrix([[15/2 - 5*exp(-t) - 5*exp(-2*t)/2]]) If no input is provided it defaults to solving the system with zero initial conditions and zero input. >>> ss.dsolve() Matrix([[0]]) References ========== .. [1] https://web.mit.edu/2.14/www/Handouts/StateSpaceResponse.pdf .. [2] https://docs.sympy.org/latest/modules/solvers/ode.html#sympy.solvers.ode.systems.linodesolve z0Variable for representing time must be a Symbol.rrMzQInitial condition vector should have the same number of rows as the state matrix.zHInput vector should have the same number of columns as the input matrix.type2T)rrrIr.rg)r~rrr!rrr rr-rreplacerrr/r.nextiter enumeraterr)rinitial_conditions input_vectorrFsolmat1mat2free1free2 dummy_symbolsr1r2r\ res_tupleindvrs rJdsolvezStateSpace.dsolvetsT#v&OP P!!&tww}}Q'7!;   % %$''--*:A)> >9: : q!115L   DGGMM!$4a#8 8<= =DGGsdggl.BW[\c{||C#$$tww';';;l>W>WW!!U5[) $T=9B b,R/0 1aM  * 7FC<< c 2A6D 7ggdlTWW\11 rkct|}t|jj||jj||j j||j j|S)zr Returns state space model where numerical expressions are evaluated into floating point numbers. r)r0r:rrrrr)rrrs rJrzStateSpace._eval_evalfsc$ GGMMcM " GGMMcM " GGMMcM " GGMMcM " $ $rkc  td |jjd}t|}|j |z|jz j |j z|jz}|j} fd}|jDcgc]}|Dcgc] }|| c}}}}|Scc}wcc}}w)a Returns the equivalent Transfer Function of the state space model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import TransferFunction, StateSpace >>> A = Matrix([[-5, -1], [3, -1]]) >>> B = Matrix([2, 5]) >>> C = Matrix([[1, 2]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss.rewrite(TransferFunction) [[TransferFunction(12*s + 59, s**2 + 6*s + 8, s)]] r\rc0tj|Srr_)rr\s rJr`z>StateSpace._eval_rewrite_as_TransferFunction..s-FFtQOrk) rrrrrsolverrrra) rrrHrGrbsublistrtf_matr\s @rJrz,StateSpace._eval_rewrite_as_TransferFunctions$ 3K GGMM!  F GGQqS477]))$''2 2TWW < JJLOCD88:N7345;3NN 4Ns C'C 6CCctttttfrD|j }|j }|j}|jjfd}nrtts td|jjk7s|jjk7r td|j jt!|j j"dj j"d}t!j j"d|j j"djj }|j%|}|j j%j }|jjj}|jjz}t||||S)a Add two State Space systems (parallel connection). Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A1 = Matrix([[1]]) >>> B1 = Matrix([[2]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([[-2]]) >>> A2 = Matrix([[-1]]) >>> B2 = Matrix([[-2]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[2]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> ss1 + ss2 StateSpace(Matrix([ [1, 0], [0, -1]]), Matrix([ [ 2], [-2]]), Matrix([[-1, 1]]), Matrix([[0]])) c|zSrrrir$s rJr`z$StateSpace.__add__..s 'E/rkz4Addition is only supported for 2 State Space models.z=Systems with incompatible inputs and outputs cannot be added.rrN)r~intfloatcomplexrrrrrrr:rrrr r r!rr rr$rrrrm1m2s ` rJr1zStateSpace.__add__s\8 ec5'6: ;AAA!!"ABAeZ0 !WXX??e&6&66D>> from sympy.physics.control import StateSpace >>> s = StateSpace() >>> 5 + s StateSpace(Matrix([[0]]), Matrix([[0]]), Matrix([[0]]), Matrix([[5]])) rr3s rJr4zStateSpace.__radd__se|rkc|| zS)a Subtract two State Space systems. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A1 = Matrix([[1]]) >>> B1 = Matrix([[2]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([[-2]]) >>> A2 = Matrix([[-1]]) >>> B2 = Matrix([[-2]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[2]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> ss1 - ss2 StateSpace(Matrix([ [1, 0], [0, -1]]), Matrix([ [ 2], [-2]]), Matrix([[-1, -1]]), Matrix([[-4]])) rr3s rJr6zStateSpace.__sub__#s6vrkc|| zS)a Right subtract two tate Space systems. Examples ======== >>> from sympy.physics.control import StateSpace >>> s = StateSpace() >>> 5 - s StateSpace(Matrix([[0]]), Matrix([[0]]), Matrix([[0]]), Matrix([[5]])) rr3s rJr8zStateSpace.__rsub__@srkcrt|j|j|j |j S)a  Returns the negation of the state space model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-5, -1], [3, -1]]) >>> B = Matrix([2, 5]) >>> C = Matrix([[1, 2]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> -ss StateSpace(Matrix([ [-5, -1], [ 3, -1]]), Matrix([ [2], [5]]), Matrix([[-1, -2]]), Matrix([[0]])) )r:rrrrrs rJrHzStateSpace.__neg__Os),$''477TWWHtwwh??rkctttttfrV|j }|j }|jjfd}|jjfd}nRtts td|jjk7r tdj jt!j j"d|j j"d}|j jzj|j }|j%|}j j%|j jz}|jjzj|j}|jjz}t||||S)a Multiplication of two State Space systems (serial connection). Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-5, -1], [3, -1]]) >>> B = Matrix([2, 5]) >>> C = Matrix([[1, 2]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> ss*5 StateSpace(Matrix([ [-5, -1], [ 3, -1]]), Matrix([ [2], [5]]), Matrix([[5, 10]]), Matrix([[0]])) c|zSrrrs rJr`z$StateSpace.__mul__.. '%-rkc|zSrrrs rJr`z$StateSpace.__mul__..rrkz:Multiplication is only supported for 2 State Space models.zBSystems with incompatible inputs and outputs cannot be multiplied.rrM)r~rrrrrrrrrr:rrrr r r!rr rs ` rJr;zStateSpace.__mul__gsR. ec5'6: ;AA!!"?@A!!"?@AeZ0 !]^^E$5$55 !eff(($$U588>>!+N%OPB''EHH$..tww7B BA##DGGehh$67A588#--dgg6A%(("A!Q1%%rkctttttfrb|j }|j }|jjfd}|jjfd}t||||S|zS)a Right multiply two tate Space systems. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-5, -1], [3, -1]]) >>> B = Matrix([2, 5]) >>> C = Matrix([[1, 2]]) >>> D = Matrix([0]) >>> ss = StateSpace(A, B, C, D) >>> 5*ss StateSpace(Matrix([ [-5, -1], [ 3, -1]]), Matrix([ [10], [25]]), Matrix([[1, 2]]), Matrix([[0]])) c|zSrrrs rJr`z%StateSpace.__rmul__..rrkc|zSrrrs rJr`z%StateSpace.__rmul__..rrk) r~rrrrrrrrrr:)rr$rrrrs ` rJrQzStateSpace.__rmul__so, ec5'6: ;AA!!"?@A!!"?@AaAq) ): rkc |jj}|jj}|jj}|jj}d|d|d|d|d S)Nz StateSpace( z, r_)rr`rrr)rA_strB_strC_strD_strs rJr`zStateSpace.__repr__sk  "  "  "  "ugU5'ugU5'KKrkc|j|jz}|j|jz}|j|jz}t||}t||}t||}t||}|j|d|jd|jf<|j||jd|jdf<|j |d|jd|jf<|j ||jd|jdf<|j |d|jd|jf<|j ||jd|jdf<|j|d|jd|jf<|j||jd|jdf<t||||S)a Returns the first model appended with the second model. The order is preserved. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A1 = Matrix([[1]]) >>> B1 = Matrix([[2]]) >>> C1 = Matrix([[-1]]) >>> D1 = Matrix([[-2]]) >>> A2 = Matrix([[-1]]) >>> B2 = Matrix([[-2]]) >>> C2 = Matrix([[1]]) >>> D2 = Matrix([[2]]) >>> ss1 = StateSpace(A1, B1, C1, D1) >>> ss2 = StateSpace(A2, B2, C2, D2) >>> ss1.append(ss2) StateSpace(Matrix([ [1, 0], [0, -1]]), Matrix([ [2, 0], [0, -2]]), Matrix([ [-1, 0], [ 0, 1]]), Matrix([ [-2, 0], [ 0, 2]])) N) rrrr!rrrrr:) rr$rHrrDrrrrs rJrzStateSpace.appends> OOe.. . OOe.. .   u00 0 !QK !QK !QK !QK04 4?? ,T__, ,-05$// DOO, ,-04 4?? ,T__, ,-05$// DOO, ,-15 4   -doo- -.16$   T__- -.15 4   -doo- -.16$   T__- -.!Q1%%rkc|j}|j}td|D]-}|j|j|j|zz}/|S)a Returns the observability matrix of the state space model: [C, C * A^1, C * A^2, .. , C * A^(n-1)]; A in R^(n x n), C in R^(m x k) Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> ob = ss.observability_matrix() >>> ob Matrix([ [0, 1], [1, 0]]) References ========== .. [1] https://in.mathworks.com/help/control/ref/statespacemodel.obsv.html rM)rrrDr r)rrHobrbs rJobservability_matrixzStateSpace.observability_matrixsQ4 OO WWq 3ATWWtwwz12B 3 rkc>|jjS)a Returns the observable subspace of the state space model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> ob_subspace = ss.observable_subspace() >>> ob_subspace [Matrix([ [0], [1]]), Matrix([ [1], [0]])] )r columnspacers rJobservable_subspacezStateSpace.observable_subspaces.((*6688rkcX|jj|jk(S)a Returns if the state space model is observable. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> ss.is_observable() True )rrankrrs rJ is_observablezStateSpace.is_observable*s%$((*//1T__DDrkc|j}|jjd}td|D]-}|j |j|z|jz}/|S)a Returns the controllability matrix of the system: [B, A * B, A^2 * B, .. , A^(n-1) * B]; A in R^(n x n), B in R^(n x m) Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> ss.controllability_matrix() Matrix([ [0.5, -0.75], [ 0, 0.5]]) References ========== .. [1] https://in.mathworks.com/help/control/ref/statespacemodel.ctrb.html rrM)rrrrDr )rcorHrbs rJcontrollability_matrixz!StateSpace.controllability_matrix>sZ2WW GGMM! q! 7Atwwldgg56B 7 rkc>|jjS)a/ Returns the controllable subspace of the state space model. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> co_subspace = ss.controllable_subspace() >>> co_subspace [Matrix([ [0.5], [ 0]]), Matrix([ [-0.75], [ 0.5]])] )rrrs rJcontrollable_subspacez StateSpace.controllable_subspace^s.**,88::rkcX|jj|jk(S)a Returns if the state space model is controllable. Examples ======== >>> from sympy import Matrix >>> from sympy.physics.control import StateSpace >>> A = Matrix([[-1.5, -2], [1, 0]]) >>> B = Matrix([0.5, 0]) >>> C = Matrix([[0, 1]]) >>> D = Matrix([1]) >>> ss = StateSpace(A, B, C, D) >>> ss.is_controllable() True )rrrrs rJis_controllablezStateSpace.is_controllablews%$**,113tFFrk)NNNN)&rrrrrrrrrrrrrrrrrrrrrrr1r4r6r8rHr;rQr`rrrrrrrrrs@rJr:r:Xs6>~8Pt,,(( AAAA ((*11)-4VC[DL $62&h : @0-&^>L0&dB92E(@;2Grkr:N)dtypingrsympyrrrrsympy.core.addrsympy.core.basicr sympy.core.containersr sympy.core.evalfr sympy.core.exprr sympy.core.functionrsympy.core.logicrsympy.core.mulrsympy.core.numbersrrrsympy.core.powerrsympy.core.singletonrsympy.core.symbolrrr}rsympy.core.sympifyrrsympy.matricesrrrrr r!&sympy.functions.elementary.exponentialr"r#sympy.matrices.expressionsr$r% sympy.polysr&r'sympy.polys.polyrootsr(sympy.polys.polytoolsr)r* sympy.seriesr+sympy.utilities.miscr,sympy.solvers.ode.systemsr-sympy.solvers.solvesetr.r/mpmath.libmp.libmpfr0__all__rKr;r<r=r>r?r@rrrrrrr1r2rnrur3rr4r5r6r7rLr8rYr9r:rrkrJr s55"'' &&(( "+0``=5$'2+1@+ # J X'<'<&$k>\ &K* $K*Z X Q%(Q%hb,&b,J F'*F'R];&];@ ~?*~?B'b -4b -Hq G$q Grk