def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+2)/(x+1), 1/(1 + x) - 1/(2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+1)/(x+5), -1/(5 + x)/4 + 1/(1 + x)/4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x+2)/(x-pi)*(x-1), x) == \ 2 - E + E*pi + E*x - (2 + E*pi)*(-pi + 1)/(x - pi) assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) raises(NotImplementedError, "apart(1/(x + 1)/(y + 2))")
def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+2)/(x+1), 1/(1 + x) - 1/(2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+1)/(x+5), -1/(5 + x)/4 + 1/(1 + x)/4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x+2)/(x-pi)*(x-1), x) == \ 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) raises(NotImplementedError, "apart(1/(x + 1)/(y + 2))")
def test_noncommutative_pseudomultivariate(): class foo(Expr): is_commutative=False e = x/(x + x*y) c = 1/(1 + y) assert apart(e + foo(e)) == c + foo(c) assert apart(e*foo(e)) == c*foo(c)
def test_apart_extension(): f = 2 / (x**2 + 1) g = I / (x + I) - I / (x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g f = x / ((x - 2) * (x + I)) assert factor(together(apart(f)).expand()) == f # https://github.com/sympy/sympy/issues/18531 from sympy.core import Mul def mul2(expr): # 2-arg mul hack... return Mul(2, expr, evaluate=False) f = ((x**2 + 1)**3 / ((x - 1)**2 * (x + 1)**2 * (-x**2 + 2 * x + 1) * (x**2 + 2 * x - 1))) g = (1 / mul2(x - sqrt(2) + 1) - 1 / mul2(x - sqrt(2) - 1) + 1 / mul2(x + 1 + sqrt(2)) - 1 / mul2(x - 1 + sqrt(2)) + 1 / mul2( (x + 1)**2) + 1 / mul2((x - 1)**2)) assert apart(f, x, extension={sqrt(2)}) == g
def test_apart_symbolic(): f = a*x**4 + (2*b + 2*a*c)*x**3 + (4*b*c - a**2 + a*c**2)*x**2 + (-2*a*b + 2*b*c**2)*x - b**2 g = a**2*x**4 + (2*a*b + 2*c*a**2)*x**3 + (4*a*b*c + b**2 + a**2*c**2)*x**2 + (2*c*b**2 + 2*a*b*c**2)*x + b**2*c**2 assert apart(f/g, x) == 1/a - 1/(x + c)**2 - b**2/(a*(a*x + b)**2) assert apart(1/((x + a)*(x + b)*(x + c)), x) == \ 1/((a - c)*(b - c)*(c + x)) - 1/((a - b)*(b - c)*(b + x)) + 1/((a - b)*(a - c)*(a + x))
def test_noncommutative_pseudomultivariate(): # apart doesn't go inside noncommutative expressions class foo(Expr): is_commutative=False e = x/(x + x*y) c = 1/(1 + y) assert apart(e + foo(e)) == c + foo(c) assert apart(e*foo(e)) == c*foo(c)
def test_noncommutative_pseudomultivariate(): class foo(Expr): is_commutative = False e = x / (x + x * y) c = 1 / (1 + y) assert apart(e + foo(e)) == c + foo(c) assert apart(e * foo(e)) == c * foo(c)
def test_apart_extension(): f = 2 / (x**2 + 1) g = I / (x + I) - I / (x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g f = x / ((x - 2) * (x + I)) assert factor(together(apart(f)).expand()) == f
def test_apart_extension(): f = 2/(x**2 + 1) g = I/(x + I) - I/(x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g f = x/((x - 2)*(x + I)) assert factor(together(apart(f))) == f
def test_issue_20163(): assert apart(1/(x**6+1), extension=[sqrt(3), I]) == \ (sqrt(3) + I)/(2*x + sqrt(3) + I)/6 + \ (sqrt(3) - I)/(2*x + sqrt(3) - I)/6 - \ (sqrt(3) - I)/(2*x - sqrt(3) + I)/6 - \ (sqrt(3) + I)/(2*x - sqrt(3) - I)/6 + \ I/(x + I)/6 - I/(x - I)/6
def test_noncommutative(): class foo(Expr): is_commutative = False e = x / (x + x * y) c = 1 / (1 + y) assert apart(e + foo()) == c + foo()
def test_apart_matrix(): M = Matrix(2, 2, lambda i, j: 1 / (x + i + 1) / (x + j)) assert apart(M) == Matrix([ [1 / x - 1 / (x + 1), (x + 1)**(-2)], [1 / (2 * x) - (S.Half) / (x + 2), 1 / (x + 1) - 1 / (x + 2)], ])
def test_apart_matrix(): M = Matrix(2, 2, lambda i, j: 1/(x + i + 1)/(x + j)) assert apart(M) == Matrix([ [1/x - 1/(x + 1), (x + 1)**(-2) ], [1/(2*x) - (S(1)/2)/(x + 2), 1/(x + 1) - 1/(x + 2)], ])
def test_apart_extension(): f = 2 / (x**2 + 1) g = I / (x + I) - I / (x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g f = x / ((x - 2) * (x + I)) assert factor(together(apart(f)).expand()) == f f, g = _make_extension_example() # XXX: Only works with dotprodsimp. See test_apart_extension_xfail below from sympy.matrices import dotprodsimp with dotprodsimp(True): assert apart(f, x, extension={sqrt(2)}) == g
def test_apart_full(): f = 1 / (x**2 + 1) assert apart(f, full=False) == f assert (apart( f, full=True) == -RootSum(x**2 + 1, Lambda(a, a / (x - a)), auto=False) / 2) f = 1 / (x**3 + x + 1) assert apart(f, full=False) == f assert apart(f, full=True) == RootSum( x**3 + x + 1, Lambda( a, (a**2 * Rational(6, 31) - a * Rational(9, 31) + Rational(4, 31)) / (x - a), ), auto=False, ) f = 1 / (x**5 + 1) assert apart(f, full=False) == (Rational(-1, 5)) * ( (x**3 - 2 * x**2 + 3 * x - 4) / (x**4 - x**3 + x**2 - x + 1)) + (Rational(1, 5)) / (x + 1) assert apart(f, full=True) == -RootSum( x**4 - x**3 + x**2 - x + 1, Lambda(a, a / (x - a)), auto=False) / 5 + ( Rational(1, 5)) / (x + 1)
def test_apart_full(): f = 1/(x**2 + 1) assert apart(f, full=False) == f assert apart(f, full=True) == -RootSum(x**2 + 1, Lambda(a, a/(x - a)), auto=False)/2 f = 1/(x**3 + x + 1) assert apart(f, full=False) == f assert apart(f, full=True) == RootSum(x**3 + x + 1, Lambda(a, (6*a**2/31 - 9*a/31 + S(4)/31)/(x - a)), auto=False) f = 1/(x**5 + 1) assert apart(f, full=False) == \ (-S(1)/5)*((x**3 - 2*x**2 + 3*x - 4)/(x**4 - x**3 + x**2 - x + 1)) + (S(1)/5)/(x + 1) assert apart(f, full=True) == \ -RootSum(x**4 - x**3 + x**2 - x + 1, Lambda(a, a/(x - a)), auto=False)/5 + (S(1)/5)/(x + 1)
def test_apart_full(): f = 1 / (x**2 + 1) assert apart(f, full=False) == f assert apart(f, full=True) == \ -RootSum(x**2 + 1, Lambda(a, a/(x - a)), auto=False)/2 f = 1 / (x**3 + x + 1) assert apart(f, full=False) == f assert apart(f, full=True) == \ RootSum(x**3 + x + 1, Lambda(a, (6*a**2/31 - 9*a/31 + S(4)/31)/(x - a)), auto=False) f = 1 / (x**5 + 1) assert apart(f, full=False) == \ (-S(1)/5)*((x**3 - 2*x**2 + 3*x - 4)/(x**4 - x**3 + x**2 - x + 1)) + (S(1)/5)/(x + 1) assert apart(f, full=True) == \ -RootSum(x**4 - x**3 + x**2 - x + 1, Lambda(a, a/(x - a)), auto=False)/5 + (S(1)/5)/(x + 1)
def test_apart_extension_xfail(): f, g = _make_extension_example() assert apart(f, x, extension={sqrt(2)}) == g
def partFrac(expr, Ndigits): expr = expr.cancel() expr = apart(adjustCoeff(expr), s, full=True).doit() return sp.N(expr, Ndigits)
def test_issue_5798(): assert apart( 2*x/(x**2 + 1) - (x - 1)/(2*(x**2 + 1)) + 1/(2*(x + 1)) - 2/x) == \ (3*x + 1)/(x**2 + 1)/2 + 1/(x + 1)/2 - 2/x
def _run(self, num_str, denum_str, in_type): in_allowed = ['real', 'pwl'] assert in_type in in_allowed, 'Allowed input stimulus should be among %s' % in_allowed # create symbols rsvd_var = ['t', 's', 'si', 'si_a', 'si_b'] expr = S('(' + num_str + ')/(' + denum_str + ')') for x in expr.atoms(): if str(x) not in rsvd_var: try: float(x) except: vars()[str(x)] = Symbol(str(x), real=True) t, s, si, si_a, si_b = symbols(' '.join(rsvd_var), real=True) # transfer function num = eval(num_str) denum = eval(denum_str) TF = num / denum # input if in_type == 'pwl': xin = si_a / s + si_b / s**2 in_str = 'ramp' else: xin = si / s in_str = 'step' # forced response Ysf = (TF * xin) # forced respose # natural response ais_d, order_denum = self.__get_order(denum) ais_n, order_num = self.__get_order(num) yos = ['yo%d' % x for x in range(order_denum)] for v in yos: vars()[v] = Symbol(v, real=True) xis = ['xi%d' % x for x in range(order_denum)] for v in xis: vars()[v] = Symbol(v, real=True) s = Symbol('s', real=True) # Why does 's' symbol disappear init_yo = self.__get_init_expr(ais_d, order_denum, 'yo') init_xi = self.__get_init_expr(ais_n, order_num, 'xi') init_expr = init_yo if init_xi != '': init_expr = init_expr + '-' + init_xi Ysn = eval( init_expr ) / denum # I hate eval(), but S() and parse_expr() screw me Ys = Ysf + Ysn # Complete Transfer function Ys_pfd = apart(Ys, s) yt, yt_basis = self.__inv_laplace( Ys_pfd.as_ordered_terms()) # complete time-domain response # sort out basis yt_basis.sort(key=lambda tup: tup[0]) self.__print_fn('Transfer Function', TF) self.__print_fn('Forced response in s-domain to %s input' % in_str, Ysf) self.__print_fn('natural response in s-domain', Ysn) self.__print_fn( 'Complete response in s-domain after partial fraction decomposition', Ys_pfd) self.__print_fn( 'Complete response in time domain to %s input' % in_str, yt) print '=' * 40 print 'Nonpretty expression for y(t)' print '=' * 40 print 'y(t)= ', yt return yt_basis
def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1) / (x + 1), 2 / (x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1 / (x + 2) / (x + 1), 1 / (1 + x) - 1 / (2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1 / (x + 1) / (x + 5), -1 / (5 + x) / 4 + 1 / (1 + x) / 4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x + 2)/(x - pi)*(x - 1), x) == \ 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) assert apart(Eq((x**2 + 1) / (x + 1), x), x) == Eq(x - 1 + 2 / (x + 1), x) assert apart(x / 2, y) == x / 2 f, g = (x + y) / (2 * x - y), Rational(3, 2) * y / (2 * x - y) + S.Half assert apart(f, x, full=False) == g assert apart(f, x, full=True) == g f, g = (x + y) / (2 * x - y), 3 * x / (2 * x - y) - 1 assert apart(f, y, full=False) == g assert apart(f, y, full=True) == g raises(NotImplementedError, lambda: apart(1 / (x + 1) / (y + 2)))
def test_apart_extension_xfail(): if ON_TRAVIS: skip('Too slow for Travis') f, g = _make_extension_example() assert apart(f, x, extension={sqrt(2)}) == g
def test_apart_extension(): f = 2 / (x**2 + 1) g = I / (x + I) - I / (x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g
def test_apart_extension(): f = 2/(x**2 + 1) g = I/(x + I) - I/(x - I) assert apart(f, extension=I) == g assert apart(f, gaussian=True) == g
def step_response_numerical_data(system, prec=8, lower_limit=0, upper_limit=10, **kwargs): """ Returns the numerical values of the points in the step response plot of a SISO continuous-time system. By default, adaptive sampling is used. If the user wants to instead get an uniformly sampled response, then ``adaptive`` kwarg should be passed ``False`` and ``nb_of_points`` must be passed as additional kwargs. Refer to the parameters of class :class:`sympy.plotting.plot.LineOver1DRangeSeries` for more details. Parameters ========== system : SISOLinearTimeInvariant The system for which the unit step response data is to be computed. prec : int, optional The decimal point precision for the point coordinate values. Defaults to 8. lower_limit : Number, optional The lower limit of the plot range. Defaults to 0. upper_limit : Number, optional The upper limit of the plot range. Defaults to 10. kwargs : Additional keyword arguments are passed to the underlying :class:`sympy.plotting.plot.LineOver1DRangeSeries` class. Returns ======= tuple : (x, y) x = Time-axis values of the points in the step response. NumPy array. y = Amplitude-axis values of the points in the step response. NumPy array. Raises ====== NotImplementedError When a SISO LTI system is not passed. When time delay terms are present in the system. ValueError When more than one free symbol is present in the system. The only variable in the transfer function should be the variable of the Laplace transform. When ``lower_limit`` parameter is less than 0. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction >>> from sympy.physics.control.control_plots import step_response_numerical_data >>> tf1 = TransferFunction(s, s**2 + 5*s + 8, s) >>> step_response_numerical_data(tf1) # doctest: +SKIP ([0.0, 0.025413462339411542, 0.0484508722725343, ... , 9.670250533855183, 9.844291913708725, 10.0], [0.0, 0.023844582399907256, 0.042894276802320226, ..., 6.828770759094287e-12, 6.456457160755703e-12]) See Also ======== step_response_plot """ if lower_limit < 0: raise ValueError("Lower limit of time must be greater " "than or equal to zero.") _check_system(system) _x = Dummy("x") expr = system.to_expr() / (system.var) expr = apart(expr, system.var, full=True) _y = _fast_inverse_laplace(expr, system.var, _x).evalf(prec) return LineOver1DRangeSeries(_y, (_x, lower_limit, upper_limit), **kwargs).get_points()
def impulse_response_numerical_data(system, prec=8, lower_limit=0, upper_limit=10, **kwargs): """ Returns the numerical values of the points in the impulse response plot of a SISO continuous-time system. By default, adaptive sampling is used. If the user wants to instead get an uniformly sampled response, then ``adaptive`` kwarg should be passed ``False`` and ``nb_of_points`` must be passed as additional kwargs. Refer to the parameters of class :class:`sympy.plotting.plot.LineOver1DRangeSeries` for more details. Parameters ========== system : SISOLinearTimeInvariant The system for which the impulse response data is to be computed. prec : int, optional The decimal point precision for the point coordinate values. Defaults to 8. lower_limit : Number, optional The lower limit of the plot range. Defaults to 0. upper_limit : Number, optional The upper limit of the plot range. Defaults to 10. kwargs : Additional keyword arguments are passed to the underlying :class:`sympy.plotting.plot.LineOver1DRangeSeries` class. Returns ======= tuple : (x, y) x = Time-axis values of the points in the impulse response. NumPy array. y = Amplitude-axis values of the points in the impulse response. NumPy array. Raises ====== NotImplementedError When a SISO LTI system is not passed. When time delay terms are present in the system. ValueError When more than one free symbol is present in the system. The only variable in the transfer function should be the variable of the Laplace transform. When ``lower_limit`` parameter is less than 0. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction >>> from sympy.physics.control.control_plots import impulse_response_numerical_data >>> tf1 = TransferFunction(s, s**2 + 5*s + 8, s) >>> impulse_response_numerical_data(tf1) # doctest: +SKIP ([0.0, 0.06616480200395854,... , 9.854500743565858, 10.0], [0.9999999799999999, 0.7042848373025861,...,7.170748906965121e-13, -5.1901263495547205e-12]) See Also ======== impulse_response_plot """ if lower_limit < 0: raise ValueError("Lower limit of time must be greater " "than or equal to zero.") _check_system(system) _x = Dummy("x") expr = system.to_expr() expr = apart(expr, system.var, full=True) _y = _fast_inverse_laplace(expr, system.var, _x).evalf(prec) return LineOver1DRangeSeries(_y, (_x, lower_limit, upper_limit), **kwargs).get_points()
def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x + 2)/(x + 1), 1/(1 + x) - 1/(2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x + 1)/(x + 5), -1/(5 + x)/4 + 1/(1 + x)/4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x + 2)/(x - pi)*(x - 1), x) == \ 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) assert apart(x/2, y) == x/2 f, g = (x+y)/(2*x - y), Rational(3/2)*y/((2*x - y)) + Rational(1/2) assert apart(f, x, full=False) == g assert apart(f, x, full=True) == g f, g = (x+y)/(2*x - y), 3*x/(2*x - y) - 1 assert apart(f, y, full=False) == g assert apart(f, y, full=True) == g raises(NotImplementedError, lambda: apart(1/(x + 1)/(y + 2)))
def ramp_response_numerical_data(system, slope=1, prec=8, lower_limit=0, upper_limit=10, **kwargs): """ Returns the numerical values of the points in the ramp response plot of a SISO continuous-time system. By default, adaptive sampling is used. If the user wants to instead get an uniformly sampled response, then ``adaptive`` kwarg should be passed ``False`` and ``nb_of_points`` must be passed as additional kwargs. Refer to the parameters of class :class:`sympy.plotting.plot.LineOver1DRangeSeries` for more details. Parameters ========== system : SISOLinearTimeInvariant The system for which the ramp response data is to be computed. slope : Number, optional The slope of the input ramp function. Defaults to 1. prec : int, optional The decimal point precision for the point coordinate values. Defaults to 8. lower_limit : Number, optional The lower limit of the plot range. Defaults to 0. upper_limit : Number, optional The upper limit of the plot range. Defaults to 10. kwargs : Additional keyword arguments are passed to the underlying :class:`sympy.plotting.plot.LineOver1DRangeSeries` class. Returns ======= tuple : (x, y) x = Time-axis values of the points in the ramp response plot. NumPy array. y = Amplitude-axis values of the points in the ramp response plot. NumPy array. Raises ====== NotImplementedError When a SISO LTI system is not passed. When time delay terms are present in the system. ValueError When more than one free symbol is present in the system. The only variable in the transfer function should be the variable of the Laplace transform. When ``lower_limit`` parameter is less than 0. When ``slope`` is negative. Examples ======== >>> from sympy.abc import s >>> from sympy.physics.control.lti import TransferFunction >>> from sympy.physics.control.control_plots import ramp_response_numerical_data >>> tf1 = TransferFunction(s, s**2 + 5*s + 8, s) >>> ramp_response_numerical_data(tf1) # doctest: +SKIP (([0.0, 0.12166980856813935,..., 9.861246379582118, 10.0], [1.4504508011325967e-09, 0.006046440489058766,..., 0.12499999999568202, 0.12499999999661349])) See Also ======== ramp_response_plot """ if slope < 0: raise ValueError("Slope must be greater than or equal" " to zero.") if lower_limit < 0: raise ValueError("Lower limit of time must be greater " "than or equal to zero.") _check_system(system) _x = Dummy("x") expr = (slope * system.to_expr()) / ((system.var)**2) expr = apart(expr, system.var, full=True) _y = _fast_inverse_laplace(expr, system.var, _x).evalf(prec) return LineOver1DRangeSeries(_y, (_x, lower_limit, upper_limit), **kwargs).get_points()