from sympy import ( Abs, atan2, cos, cosh, Function, Matrix, S, sinh, sqrt, srepr, symbols, Tuple, ) from sympy.core.function import AppliedUndef from sympy.logic import And from .faust import Delay, _is_signal, FunctionDefn, Letrec, DiffEq, symbolize from .math import ( backward_euler, FDE, function_matrix, _linearize, Newton, ODE, SolvedFDE, symbol_matrix, trapezoidal, ) from inspect import stack class Test: def __init__(self): self.failures = [] def run(self, *tests): for t in tests: t(self) return self def fail(self, number, want, got): func = stack()[1].function self.failures.append((func, number, want, got)) def report(self): for failure in self.failures: #srepr = lambda x: x print( f"{failure[0]} (case {failure[1]}):\n want {srepr(failure[2])}\n got {srepr(failure[3])}\n" ) if len(self.failures) == 0: print("ok") exit(len(self.failures)) def test_linearize(ts): t = symbols("t", real=True) k = symbols("k", integer=True) Omega, beta, f_s = symbols("Omega beta f_s", positive=True) s = symbols("s", cls=Function, real=True) y = function_matrix("y", 2, t) p = symbol_matrix("p", 2) p_0, p_1 = tuple(p) y_0, y_1 = map(lambda f: f.func, y) cases = ( { "func": Matrix( [ s(y[0] + y[1]), s(y[0]) + s(y[1]), ] ).subs(s, sinh), "state": y, "point": p, "want": Matrix( [ (y[0] - p[0]) * cosh(p[0] + p[1]) + (y[1] - p[1]) * cosh(p[0] + p[1]) + sinh(p[0] + p[1]), (y[0] - p[0]) * cosh(p[0]) + (y[1] - p[1]) * cosh(p[1]) + sinh(p[0]) + sinh(p[1]), ] ), }, { "func": Matrix( [ y_0(k - 1) - (Omega * y_1(k) - sinh(y_0(k - 1))) / (-f_s - cosh(y_0(k - 1))), (-2 * Omega * sinh(y_0(k)) + f_s * y_1(k - 1)) / (beta + f_s), ] ), "state": Matrix(list(y.subs(t, k)) + list(y.subs(t, k - 1))), "point": Matrix(list(p) + list(p)), # This is wrong because it's nonlinear with respect to delayed states. "want": Matrix( [ [ -Omega * (-p_1 + y_1(k)) / (-f_s - cosh(p_0)) + p_0 + (-p_0 + y_0(k - 1)) * ( 1 + cosh(p_0) / (-f_s - cosh(p_0)) - (Omega * p_1 - sinh(p_0)) * sinh(p_0) / (-f_s - cosh(p_0)) ** 2 ) - (Omega * p_1 - sinh(p_0)) / (-f_s - cosh(p_0)) ], [ -2 * Omega * (-p_0 + y_0(k)) * cosh(p_0) / (beta + f_s) + f_s * (-p_1 + y_1(k - 1)) / (beta + f_s) + (-2 * Omega * sinh(p_0) + f_s * p_1) / (beta + f_s) ], ] ), }, ) for i, c in enumerate(cases): got = _linearize(c["func"], c["state"], c["point"]) if got != c["want"]: ts.fail(i, c["want"], got) def test_ode_stable(ts): t = symbols("t", real=True) y = function_matrix("y", 2, t) p = symbol_matrix("p", 2) Omega, beta = symbols("Omega beta", positive=True) s = symbols("s", cls=Function, real=True) cases = ( { "ode": ODE( t, (), y, Matrix( [ y[1] - sinh(y[0]), -sinh(y[1]), ] ), ), "want": True, }, { "ode": ODE( t, (), y, Matrix( [ y[1] - y[0] ** 2, -y[1] ** 2, ] ), ), "want": And( -2 * p[0] < 0, -2 * p[1] < 0, ), }, { "ode": ODE( t, (), y, Matrix( [ y[1] + sinh(y[0]), -sinh(y[1]), ] ), ), "want": False, }, { "ode": ODE( t, (), y, Matrix( [ Omega * y[1] - s(y[0]), 2 * Omega * (-beta * y[1] / (2 * Omega) - s(y[0])), ], ).subs(s, sinh), ), "want": And( -beta / 2 - cos( atan2( 0, -8 * Omega ** 2 * cosh(p[0]) + beta ** 2 - 2 * beta * cosh(p[0]) + cosh(p[0]) ** 2, ) / 2 ) * sqrt( Abs( 8 * Omega ** 2 * cosh(p[0]) - beta ** 2 + 2 * beta * cosh(p[0]) - cosh(p[0]) ** 2 ) ) / 2 - cosh(p[0]) / 2 < 0, -beta / 2 + cos( atan2( 0, -8 * Omega ** 2 * cosh(p[0]) + beta ** 2 - 2 * beta * cosh(p[0]) + cosh(p[0]) ** 2, ) / 2 ) * sqrt( Abs( 8 * Omega ** 2 * cosh(p[0]) - beta ** 2 + 2 * beta * cosh(p[0]) - cosh(p[0]) ** 2 ) ) / 2 - cosh(p[0]) / 2 < 0, ), }, ) for i, c in enumerate(cases): got = c["ode"].stable(p) if got != c["want"]: ts.fail(i, c["want"], got) def test_fde_from_ode(ts): f_s = symbols("f_s", positive=True) t = symbols("t", real=True) y = function_matrix("y", 2, t) k = symbols("k", integer=True) yy = y.subs(t, k) yy_ = y.subs(t, k - 1) cases = ( { "ode": ODE(t, Tuple(), y, y), "discretize": backward_euler, "want": FDE(k, Tuple(), yy, -f_s * (yy - yy_) + yy), }, { "ode": ODE(t, Tuple(), y, y), "discretize": trapezoidal, "want": FDE(k, Tuple(), yy, -2 * f_s * (yy - yy_) + yy + yy_), }, ) for i, c in enumerate(cases): got = FDE.from_ode(c["ode"], c["discretize"], k, f_s) if got != c["want"]: ts.fail(i, c["want"], got) def test_fde_stable(ts): k = symbols("k", integer=True) y = function_matrix("y", 2, k) p = symbol_matrix("p", 2) cases = ( { "fde": FDE(k, (), y, y.subs(k, k - 1)), "want": False, }, { "fde": FDE(k, (), y, Matrix([0, 0])), "want": True, }, ) for i, c in enumerate(cases): got = c["fde"].stable(p) if got != c["want"]: ts.fail(i, c["want"], got) def test_solved_fde_from_fde(ts): n = symbol_matrix("n", 2) k = symbols("k", integer=True) s = symbols("s", cls=Function, real=True) y = function_matrix("y", 2, k) y_ = y.subs(k, k - 1) cases = ( { "fde": FDE(k, (), y, -y + y_), "want": SolvedFDE(k, Tuple(), y, y_), }, { "fde": FDE( k, (), y, Matrix( [ -y[0] + y_[0] + s(y[0] + y[1]), -y[1] + y_[1], ] ), ), "want": SolvedFDE( k, Tuple(), y, Matrix( [ Newton( n[0], s(y[0] + y[1]) - y[0] + y_[0], y[0], y_[0], ), y_[1], ] ), ), }, ) for i, c in enumerate(cases): got = SolvedFDE.from_fde(c["fde"], n) if got != c["want"]: ts.fail(i, c["want"], got) def test_solved_fde_stable(ts): k = symbols("k", integer=True) y = function_matrix("y", 2, k) p = symbol_matrix("p", 2) y_ = y.subs(k, k - 1) cases = ( { "fde": SolvedFDE(k, Tuple(), y, y_), "want": False, }, { "fde": SolvedFDE(k, Tuple(), y, Matrix([0, 0])), "want": True, }, ) for i, c in enumerate(cases): got = c["fde"].stable(p) if got != c["want"]: ts.fail(i, c["want"], got) def test_delay_from_time_shifts(ts): y, x = symbols("y x", cls=Function, real=True) k = symbols("k", integer=True) x_ = symbolize(x(k)) y_ = symbolize(y(k)) cases = ( { "expr": y(k), "want": Delay(y_, S(0)), }, { "expr": y(k - 1), "want": Delay(y_, S(1)), }, { "expr": y(k - 1) + x(k), "want": Delay(y_, S(1)) + Delay(x_, S(0)), }, ) for i, c in enumerate(cases): got = Delay.from_time_shifts(c["expr"], k) if got != c["want"]: ts.fail(i, c["want"], got) def test_is_signal(ts): y = symbols("y", cls=Function, real=True) k = symbols("k", integer=True) cases = ( { "expr": y(k), "want": True, }, { "expr": y(k - 1), "want": True, }, { "expr": y(k) + 1, "want": False, }, { "expr": y, "want": False, }, ) for i, c in enumerate(cases): got = _is_signal(c["expr"], k) if got != c["want"]: ts.fail(i, c["want"], got) def test_delay_from_signal(ts): y = symbols("y", cls=Function, real=True) k = symbols("k", integer=True) y_ = symbolize(y(k)) d = symbols("d") cases = ( { "sig": y(k), "want": Delay(y_, S(0)), }, { "sig": y(k - 1), "want": Delay(y_, S(1)), }, { "sig": y(k - d), "want": Delay(y_, d), }, ) for i, c in enumerate(cases): got = Delay._from_signal(c["sig"], k) if got != c["want"]: ts.fail(i, c["want"], got) def test_function_from_spec(ts): k = symbols("k", integer=True) y = function_matrix("y", 2, k) y_ = list(map(symbolize, y)) cases = ( { "params": Tuple(), "outputs": Tuple(*y), "fde": SolvedFDE(k, Tuple(), y, y.subs(k, k - 1)), "want": FunctionDefn( "func", (), Letrec( (Delay(y_[0], S(0)), Delay(y_[1], S(0))), ( DiffEq(Delay(y_[0], S(0)), Delay(y_[0], S(0))), DiffEq(Delay(y_[1], S(0)), Delay(y_[1], S(0))), ), ), ), }, ) for i, c in enumerate(cases): got = FunctionDefn.from_spec("func", c["params"], c["outputs"], c["fde"], k) if got != c["want"]: ts.fail(i, c["want"], got) Test().run( test_linearize, test_ode_stable, test_fde_from_ode, test_fde_stable, test_solved_fde_from_fde, test_solved_fde_stable, test_delay_from_time_shifts, test_is_signal, test_delay_from_signal, test_function_from_spec, ).report()