Exemple #1
0
def dsolve_system(eqs, funcs=None, t=None, ics=None, doit=False):
    r"""
    Solves any(supported) system of Ordinary Differential Equations

    Explanation
    ===========

    This function takes a system of ODEs as an input, determines if the
    it is solvable by this function, and returns the solution if found any.

    This function can handle:
    1. Linear, First Order, Constant coefficient homogeneous system of ODEs
    2. Linear, First Order, Constant coefficient non-homogeneous system of ODEs
    3. Linear, First Order, non-constant coefficient homogeneous system of ODEs
    4. Linear, First Order, non-constant coefficient non-homogeneous system of ODEs
    5. Any implicit system which can be divided into system of ODEs which is of the above 4 forms

    The types of systems described above aren't limited by the number of equations, i.e. this
    function can solve the above types irrespective of the number of equations in the system passed.

    This function returns a list of solutions. Each solution is a list of equations where LHS is
    the dependent variable and RHS is an expression in terms of the independent variable.

    Parameters
    ==========

    eqs : List
        system of ODEs to be solved
    funcs : List or None
        List of dependent variables that make up the system of ODEs
    t : Symbol
        Independent variable in the system of ODEs
    ics : Dict or None
        Set of initial boundary/conditions for the system of ODEs
    doit : Boolean
        Evaluate the solutions if True. Default value is False

    Examples
    ========

    >>> from sympy import symbols, Eq, Function
    >>> from sympy.solvers.ode.systems import dsolve_system
    >>> f, g = symbols("f g", cls=Function)
    >>> x = symbols("x")

    >>> eqs = [Eq(f(x).diff(x), g(x)), Eq(g(x).diff(x), f(x))]
    >>> dsolve_system(eqs)
    [[Eq(f(x), -C1*exp(-x) + C2*exp(x)), Eq(g(x), C1*exp(-x) + C2*exp(x))]]

    You can also pass the initial conditions for the system of ODEs:
    >>> dsolve_system(eqs, ics={f(0): 1, g(0): 0})
    [[Eq(f(x), exp(x)/2 + exp(-x)/2), Eq(g(x), exp(x)/2 - exp(-x)/2)]]

    Optionally, you can pass the dependent variables and the independent
    variable for which the system is to be solved:
    >>> funcs = [f(x), g(x)]
    >>> dsolve_system(eqs, funcs=funcs, t=x)
    [[Eq(f(x), -C1*exp(-x) + C2*exp(x)), Eq(g(x), C1*exp(-x) + C2*exp(x))]]

    Lets look at an implicit system of ODEs:
    >>> eqs = [Eq(f(x).diff(x)**2, g(x)**2), Eq(g(x).diff(x), g(x))]
    >>> dsolve_system(eqs)
    [[Eq(f(x), C1 - C2*exp(x)), Eq(g(x), C2*exp(x))], [Eq(f(x), C1 + C2*exp(x)), Eq(g(x), C2*exp(x))]]

    Returns
    =======

    List of List of Equations

    Raises
    ======

    NotImplementedError
        When the system of ODEs is not solvable by this function.
    ValueError
        When the parameters passed aren't in the required form.

    """
    from sympy.solvers.ode.ode import solve_ics, _extract_funcs, constant_renumber

    if not iterable(eqs):
        raise ValueError(
            filldedent('''
            List of equations should be passed. The input is not valid.
        '''))

    eqs = _preprocess_eqs(eqs)

    if funcs is not None and not isinstance(funcs, list):
        raise ValueError(
            filldedent('''
            Input to the funcs should be a list of functions.
        '''))

    if funcs is None:
        funcs = _extract_funcs(eqs)

    if any(len(func.args) != 1 for func in funcs):
        raise ValueError(
            filldedent('''
            dsolve_system can solve a system of ODEs with only one independent
            variable.
        '''))

    if len(eqs) != len(funcs):
        raise ValueError(
            filldedent('''
            Number of equations and number of functions don't match
        '''))

    if t is not None and not isinstance(t, Symbol):
        raise ValueError(
            filldedent('''
            The indepedent variable must be of type Symbol
        '''))

    if t is None:
        t = list(list(eqs[0].atoms(Derivative))[0].atoms(Symbol))[0]

    sys_order = _get_func_order(eqs, funcs)

    # To add higher order to first order reduction later
    if not all(sys_order[func] == 1 for func in funcs):
        raise NotImplementedError(
            filldedent('''
            Higher order ODEs aren't solvable by dsolve_system
        '''))

    canon_eqs = canonical_odes(eqs, funcs, t)
    sols = []

    for canon_eq in canon_eqs:
        sol = _strong_component_solver(canon_eq, funcs, t)
        if sol is None:
            sol = _component_solver(canon_eq, funcs, t)
        sols.append(sol)

    if sols:
        final_sols = []

        for sol in sols:

            # To preserve the order corresponding to the
            # funcs list.
            sol_dict = {s.lhs: s.rhs for s in sol}
            sol = [Eq(var, sol_dict[var]) for var in funcs]

            variables = Tuple(*eqs).free_symbols
            sol = constant_renumber(sol, variables=variables)

            if ics:
                constants = Tuple(*sol).free_symbols - variables
                solved_constants = solve_ics(sol, funcs, constants, ics)
                sol = [s.subs(solved_constants) for s in sol]

            if doit:
                sol = [s.doit() for s in sol]

            final_sols.append(sol)

        sols = final_sols

    return sols
Exemple #2
0
def test_solve_ics():
    # Basic tests that things work from dsolve.
    assert dsolve(f(x).diff(x) - 1/f(x), f(x), ics={f(1): 2}) == \
        Eq(f(x), sqrt(2 * x + 2))
    assert dsolve(f(x).diff(x) - f(x), f(x), ics={f(0): 1}) == Eq(f(x), exp(x))
    assert dsolve(f(x).diff(x) - f(x), f(x), ics={f(x).diff(x).subs(x, 0):
                                                  1}) == Eq(f(x), exp(x))
    assert dsolve(f(x).diff(x, x) + f(x),
                  f(x),
                  ics={
                      f(0): 1,
                      f(x).diff(x).subs(x, 0): 1
                  }) == Eq(f(x),
                           sin(x) + cos(x))
    assert dsolve([f(x).diff(x) - f(x) + g(x),
                   g(x).diff(x) - g(x) - f(x)], [f(x), g(x)],
                  ics={
                      f(0): 1,
                      g(0): 0
                  }) == [Eq(f(x),
                            exp(x) * cos(x)),
                         Eq(g(x),
                            exp(x) * sin(x))]

    # Test cases where dsolve returns two solutions.
    eq = (x**2 * f(x)**2 - x).diff(x)
    assert dsolve(eq, f(x), ics={f(1): 0}) == [
                                     Eq(f(x), -sqrt(x - 1) / x),
                                     Eq(f(x),
                                        sqrt(x - 1) / x)
                                 ]
    assert dsolve(eq, f(x), ics={f(x).diff(x).subs(x, 1): 0}) == [
                                     Eq(f(x), -sqrt(x - S.Half) / x),
                                     Eq(f(x),
                                        sqrt(x - S.Half) / x)
                                 ]

    eq = cos(f(x)) - (x * sin(f(x)) - f(x)**2) * f(x).diff(x)
    assert dsolve(eq, f(x), ics={f(0): 1}, hint='1st_exact',
                  simplify=False) == Eq(x * cos(f(x)) + f(x)**3 / 3,
                                        Rational(1, 3))
    assert dsolve(eq, f(x), ics={f(0): 1}, hint='1st_exact',
                  simplify=True) == Eq(x * cos(f(x)) + f(x)**3 / 3,
                                       Rational(1, 3))

    assert solve_ics([Eq(f(x), C1 * exp(x))], [f(x)], [C1], {f(0): 1}) == {
                                                                 C1: 1
                                                             }
    assert solve_ics([Eq(f(x),
                         C1 * sin(x) + C2 * cos(x))], [f(x)], [C1, C2], {
                             f(0): 1,
                             f(pi / 2): 1
                         }) == {
                             C1: 1,
                             C2: 1
                         }

    assert solve_ics([Eq(f(x),
                         C1 * sin(x) + C2 * cos(x))], [f(x)], [C1, C2], {
                             f(0): 1,
                             f(x).diff(x).subs(x, 0): 1
                         }) == {
                             C1: 1,
                             C2: 1
                         }

    assert solve_ics([Eq(f(x), C1*sin(x) + C2*cos(x))], [f(x)], [C1, C2], {f(0): 1}) == \
        {C2: 1}

    # Some more complicated tests Refer to PR #16098

    assert set(dsolve(f(x).diff(x)*(f(x).diff(x, 2)-x), ics={f(0):0, f(x).diff(x).subs(x, 1):0})) == \
        {Eq(f(x), 0), Eq(f(x), x ** 3 / 6 - x / 2)}
    assert set(dsolve(f(x).diff(x)*(f(x).diff(x, 2)-x), ics={f(0):0})) == \
        {Eq(f(x), 0), Eq(f(x), C2*x + x**3/6)}

    K, r, f0 = symbols('K r f0')
    sol = Eq(
        f(x),
        K * f0 * exp(r * x) / ((-K + f0) * (f0 * exp(r * x) / (-K + f0) - 1)))
    assert (dsolve(Eq(f(x).diff(x),
                      r * f(x) * (1 - f(x) / K)),
                   f(x),
                   ics={f(0): f0})) == sol

    #Order dependent issues Refer to PR #16098
    assert set(dsolve(f(x).diff(x)*(f(x).diff(x, 2)-x), ics={f(x).diff(x).subs(x,0):0, f(0):0})) == \
        {Eq(f(x), 0), Eq(f(x), x ** 3 / 6)}
    assert set(dsolve(f(x).diff(x)*(f(x).diff(x, 2)-x), ics={f(0):0, f(x).diff(x).subs(x,0):0})) == \
        {Eq(f(x), 0), Eq(f(x), x ** 3 / 6)}

    # XXX: Ought to be ValueError
    raises(
        ValueError,
        lambda: solve_ics([Eq(f(x),
                              C1 * sin(x) + C2 * cos(x))], [f(x)], [C1, C2], {
                                  f(0): 1,
                                  f(pi): 1
                              }))

    # Degenerate case. f'(0) is identically 0.
    raises(
        ValueError, lambda: solve_ics([Eq(f(x), sqrt(C1 - x**2))], [f(x)],
                                      [C1], {f(x).diff(x).subs(x, 0): 0}))

    EI, q, L = symbols('EI q L')

    # eq = Eq(EI*diff(f(x), x, 4), q)
    sols = [
        Eq(f(x), C1 + C2 * x + C3 * x**2 + C4 * x**3 + q * x**4 / (24 * EI))
    ]
    funcs = [f(x)]
    constants = [C1, C2, C3, C4]
    # Test both cases, Derivative (the default from f(x).diff(x).subs(x, L)),
    # and Subs
    ics1 = {
        f(0): 0,
        f(x).diff(x).subs(x, 0): 0,
        f(L).diff(L, 2): 0,
        f(L).diff(L, 3): 0
    }
    ics2 = {
        f(0): 0,
        f(x).diff(x).subs(x, 0): 0,
        Subs(f(x).diff(x, 2), x, L): 0,
        Subs(f(x).diff(x, 3), x, L): 0
    }

    solved_constants1 = solve_ics(sols, funcs, constants, ics1)
    solved_constants2 = solve_ics(sols, funcs, constants, ics2)
    assert solved_constants1 == solved_constants2 == {
        C1: 0,
        C2: 0,
        C3: L**2 * q / (4 * EI),
        C4: -L * q / (6 * EI)
    }
Exemple #3
0
def dsolve_system(eqs, funcs=None, t=None, ics=None, doit=False):
    r"""
    Solves any(supported) system of Ordinary Differential Equations

    Explanation
    ===========

    This function takes a system of ODEs as an input, determines if the
    it is solvable by this function, and returns the solution if found any.

    This function can handle:
    1. Linear, First Order, Constant coefficient homogeneous system of ODEs
    2. Linear, First Order, Constant coefficient non-homogeneous system of ODEs
    3. Linear, First Order, non-constant coefficient homogeneous system of ODEs
    4. Linear, First Order, non-constant coefficient non-homogeneous system of ODEs
    5. Any implicit system which can be divided into system of ODEs which is of the above 4 forms

    The types of systems described above aren't limited by the number of equations, i.e. this
    function can solve the above types irrespective of the number of equations in the system passed.

    This function returns a list of solutions. Each solution is a list of equations where LHS is
    the dependent variable and RHS is an expression in terms of the independent variable.

    Parameters
    ==========

    eqs : List
        system of ODEs to be solved
    funcs : List or None
        List of dependent variables that make up the system of ODEs
    t : Symbol
        Independent variable in the system of ODEs
    ics : Dict or None
        Set of initial boundary/conditions for the system of ODEs
    doit : Boolean
        Evaluate the solutions if True. Default value is False

    Examples
    ========

    >>> from sympy import symbols, Eq, Function
    >>> from sympy.solvers.ode.systems import dsolve_system
    >>> f, g = symbols("f g", cls=Function)
    >>> x = symbols("x")

    >>> eqs = [Eq(f(x).diff(x), g(x)), Eq(g(x).diff(x), f(x))]
    >>> dsolve_system(eqs)
    [[Eq(f(x), -C1*exp(-x) + C2*exp(x)), Eq(g(x), C1*exp(-x) + C2*exp(x))]]

    You can also pass the initial conditions for the system of ODEs:
    >>> dsolve_system(eqs, ics={f(0): 1, g(0): 0})
    [[Eq(f(x), exp(x)/2 + exp(-x)/2), Eq(g(x), exp(x)/2 - exp(-x)/2)]]

    Optionally, you can pass the dependent variables and the independent
    variable for which the system is to be solved:
    >>> funcs = [f(x), g(x)]
    >>> dsolve_system(eqs, funcs=funcs, t=x)
    [[Eq(f(x), -C1*exp(-x) + C2*exp(x)), Eq(g(x), C1*exp(-x) + C2*exp(x))]]

    Lets look at an implicit system of ODEs:
    >>> eqs = [Eq(f(x).diff(x)**2, g(x)**2), Eq(g(x).diff(x), g(x))]
    >>> dsolve_system(eqs)
    [[Eq(f(x), C1 - C2*exp(x)), Eq(g(x), C2*exp(x))], [Eq(f(x), C1 + C2*exp(x)), Eq(g(x), C2*exp(x))]]

    Returns
    =======

    List of List of Equations

    Raises
    ======

    NotImplementedError
        When the system of ODEs is not solvable by this function.
    ValueError
        When the parameters passed aren't in the required form.

    """
    from sympy.solvers.ode.ode import solve_ics, _extract_funcs

    if not iterable(eqs):
        raise ValueError(filldedent('''
            List of equations should be passed. The input is not valid.
        '''))

    eqs = _preprocess_eqs(eqs)

    if funcs is not None and not isinstance(funcs, list):
        raise ValueError(filldedent('''
            Input to the funcs should be a list of functions.
        '''))

    # Note: This is added to solve a major problem encountered.
    # Might be best to make a function for this functions
    # extraction later.
    if funcs is None:
        funcs = _extract_funcs(eqs)
    if len(eqs) != len(funcs):
        raise ValueError(filldedent('''
            Number of equations and number of functions don't match
        '''))

    if t is not None and not isinstance(t, Symbol):
        raise ValueError(filldedent('''
            The indepedent variable must be of type Symbol
        '''))

    if t is None:
        t = list(list(eqs[0].atoms(Derivative))[0].atoms(Symbol))[0]

    match = neq_nth_linear_constant_coeff_match(eqs, funcs, t)

    sols = []

    if match is None or match.get('is_implicit', False):
        canon_eqs = [eqs] if match is None else match['canon_eqs']

        # Note: Assuming a canon_eq has a single
        # solution.
        for canon_eq in canon_eqs:
            sols.append(_component_solver(canon_eq, funcs, t))

    # Note: It is advantageous to
    # divide a system of ODEs since smaller
    # the matrices, faster the solution
    # computation. Has to be considered in
    # future PR when component division function
    # is added.
    elif match.get('is_general', False):
        if match.get('is_linear', False):
            match['t'] = t
            sols.append(_linear_ode_solver(match))

    if sols:
        final_sols = []

        # This is assuming that all the solutions
        # have the same funcs. This may have to
        # be changed when system division is
        # added.
        funcs = [s.lhs for s in sols[0]]

        for sol in sols:
            sol = _replace_dummies(eqs, sol)

            if ics:
                constants = Tuple(*sol).free_symbols - Tuple(*eqs).free_symbols
                solved_constants = solve_ics(sol, funcs, constants, ics)
                sol = [s.subs(solved_constants) for s in sol]

            if doit:
                sol = [s.doit() for s in sol]

            final_sols.append(sol)

        sols = final_sols

    return sols