# Control API¶

## lti¶

class sympy.physics.control.lti.TransferFunction(num, den, var)[source]

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 Symbol.

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.

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}}$$

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)


References

dc_gain()[source]

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

property den

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

eval_frequency(other)[source]

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)

expand()[source]

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)

classmethod from_coeff_lists(
num_list,
den_list,
var,
)[source]

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)

classmethod from_rational_expression(
expr,
var=None,
)[source]

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)

classmethod from_zpk(zeros, poles, gain, var)[source]

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)

property is_biproper

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

property is_proper

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

is_stable()[source]

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

property is_strictly_proper

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

property num

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)

poles()[source]

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]

to_expr()[source]

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)))

property var

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

zeros()[source]

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]

class sympy.physics.control.lti.Series(*args, evaluate=False)[source]

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.

doit(**hints)[source]

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.

property is_biproper

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

property is_proper

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

property is_strictly_proper

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

to_expr()[source]

Returns the equivalent Expr object.

property var

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

class sympy.physics.control.lti.Parallel(*args, evaluate=False)[source]

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.

doit(**hints)[source]

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)

property is_biproper

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

property is_proper

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

property is_strictly_proper

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

to_expr()[source]

Returns the equivalent Expr object.

property var

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

class sympy.physics.control.lti.Feedback(sys1, sys2=None, sign=-1)[source]

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)

property den

Returns the denominator of the closed loop feedback model.

doit(cancel=False, expand=False, **hints)[source]

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]]))

property num

Returns the numerator of the closed loop feedback system.

property sensitivity

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)

property sign

Returns the type of MIMO Feedback model. 1 for Positive and -1 for Negative.

property sys1

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)

property sys2

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))

to_expr()[source]

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

property var

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

class sympy.physics.control.lti.TransferFunctionMatrix(arg)[source]

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),)))

elem_poles()[source]

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]]]

elem_zeros()[source]

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]]]

eval_frequency(other)[source]

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]])

expand(**hints)[source]

Expands the transfer function matrix

classmethod from_Matrix(matrix, var)[source]

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]]]

property num_inputs

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

property num_outputs

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

property shape

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)

transpose()[source]

Returns the transpose of the TransferFunctionMatrix (switched input and output layers).

property var

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

class sympy.physics.control.lti.PIDController(kp=kp, ki=ki, kd=kd, tf=0, var=s)[source]

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


References

doit()[source]

Convert the PIDController into TransferFunction.

property kd

Returns the Derivative gain (kd) of the PIDController.

property ki

Returns the Integral gain (ki) of the PIDController.

property kp

Returns the Proportional gain (kp) of the PIDController.

property tf

Returns the Derivative filter time constant (tf) of the PIDController.

class sympy.physics.control.lti.MIMOSeries(*args, evaluate=False)[source]

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

doit(cancel=False, **kwargs)[source]

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))))

property num_inputs

Returns the number of input signals of the series system.

property num_outputs

Returns the number of output signals of the series system.

property shape

Returns the shape of the equivalent MIMO system.

property var

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

class sympy.physics.control.lti.MIMOParallel(*args, evaluate=False)[source]

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

doit(**hints)[source]

Returns the resultant transfer function matrix or StateSpace obtained after evaluating the MIMO systems arranged in a parallel configuration.

Examples

>>> 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))))

property num_inputs

Returns the number of input signals of the parallel system.

property num_outputs

Returns the number of output signals of the parallel system.

property shape

Returns the shape of the equivalent MIMO system.

property var

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

class sympy.physics.control.lti.MIMOFeedback(sys1, sys2, sign=-1)[source]

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]]))

doit(
cancel=True,
expand=False,
**hints,
)[source]

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}

property num_inputs

Returns the number of inputs of the system.

property num_outputs

Returns the number of outputs of the system.

property sensitivity

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    ]

property sign

Returns the type of feedback interconnection of two models. 1 for Positive and -1 for Negative.

property sys1

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}

property sys2

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}

property var

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

class sympy.physics.control.lti.StateSpace(A=None, B=None, C=None, D=None)[source]

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]]))


References

property 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]])

property B

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]])

property C

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]])

property D

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]])

append(other)[source]

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]]))

controllability_matrix()[source]
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

controllable_subspace()[source]

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]])]

dsolve(
initial_conditions=None,
input_vector=None,
var=t,
)[source]
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

property feedforward_matrix

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]])

property input_matrix

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]])

is_controllable()[source]

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

is_observable()[source]

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

property num_inputs

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

property num_outputs

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

property num_states

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

observability_matrix()[source]
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

observable_subspace()[source]

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]])]

property output_matrix

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]])

property shape

Returns the shape of the equivalent StateSpace system.

property state_matrix

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]])

sympy.physics.control.lti.gbt(tf, sample_per, alpha)[source]

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

sympy.physics.control.lti.bilinear(tf, sample_per)[source]

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)]

sympy.physics.control.lti.forward_diff(tf, sample_per)[source]

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]

sympy.physics.control.lti.backward_diff(tf, sample_per)[source]

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)]