(eq, func, order, constants, hint)
return eq
r"""
    Simplifies ODEs, including trying to solve for ``func`` and running
    :py:meth:`~sympy.solvers.ode.constantsimp`.

    It may use knowledge of the type of solution that the hint returns to
    apply additional simplifications.

    It also attempts to integrate any :py:class:`~sympy.integrals.Integral`\s
    in the expression, if the hint is not an ``_Integral`` hint.

    This function should have no effect on expressions returned by
    :py:meth:`~sympy.solvers.ode.dsolve`, as
    :py:meth:`~sympy.solvers.ode.dsolve` already calls
    :py:meth:`~sympy.solvers.ode.odesimp`, but the individual hint functions
    do not call :py:meth:`~sympy.solvers.ode.odesimp` (because the
    :py:meth:`~sympy.solvers.ode.dsolve` wrapper does).  Therefore, this
    function is designed for mainly internal use.

    Examples
    ========

    >>> from sympy import sin, symbols, dsolve, pprint, Function
    >>> from sympy.solvers.ode import odesimp
    >>> x , u2, C1= symbols('x,u2,C1')
    >>> f = Function('f')

    >>> eq = dsolve(x*f(x).diff(x) - f(x) - x*sin(f(x)/x), f(x),
    ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral',
    ... simplify=False)
    >>> pprint(eq, wrap_line=False)
                            x
                           ----
                           f(x)
                             /
                            |
                            |   /        1   \
                            |  -|u2 + -------|
                            |   |        /1 \|
                            |   |     sin|--||
                            |   \        \u2//
    log(f(x)) = log(C1) +   |  ---------------- d(u2)
                            |          2
                            |        u2
                            |
                           /

    >>> pprint(odesimp(eq, f(x), 1, set([C1]),
    ... hint='1st_homogeneous_coeff_subs_indep_div_dep'
    ... )) #doctest: +SKIP
        x
    --------- = C1
       /f(x)\
    tan|----|
       \2*x /
r"""
    Simplifies ODEs, including trying to solve for ``func`` and running
    :py:meth:`~sympy.solvers.ode.constantsimp`.
r Simplifies ODEs including trying to solve for func and running : py : meth : ~sympy . solvers . ode . constantsimp .
def odesimp(eq, func, order, constants, hint):
    r"""
    Simplifies ODEs, including trying to solve for ``func`` and running
    :py:meth:`~sympy.solvers.ode.constantsimp`.

    It may use knowledge of the type of solution that the hint returns to
    apply additional simplifications.

    It also attempts to integrate any :py:class:`~sympy.integrals.Integral`\s
    in the expression, if the hint is not an ``_Integral`` hint.

    This function should have no effect on expressions returned by
    :py:meth:`~sympy.solvers.ode.dsolve`, as
    :py:meth:`~sympy.solvers.ode.dsolve` already calls
    :py:meth:`~sympy.solvers.ode.odesimp`, but the individual hint functions
    do not call :py:meth:`~sympy.solvers.ode.odesimp` (because the
    :py:meth:`~sympy.solvers.ode.dsolve` wrapper does).  Therefore, this
    function is designed for mainly internal use.

    Examples
    ========

    >>> from sympy import sin, symbols, dsolve, pprint, Function
    >>> from sympy.solvers.ode import odesimp
    >>> x , u2, C1= symbols('x,u2,C1')
    >>> f = Function('f')

    >>> eq = dsolve(x*f(x).diff(x) - f(x) - x*sin(f(x)/x), f(x),
    ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral',
    ... simplify=False)
    >>> pprint(eq, wrap_line=False)
                            x
                           ----
                           f(x)
                             /
                            |
                            |   /        1   \
                            |  -|u2 + -------|
                            |   |        /1 \|
                            |   |     sin|--||
                            |   \        \u2//
    log(f(x)) = log(C1) +   |  ---------------- d(u2)
                            |          2
                            |        u2
                            |
                           /

    >>> pprint(odesimp(eq, f(x), 1, set([C1]),
    ... hint='1st_homogeneous_coeff_subs_indep_div_dep'
    ... )) #doctest: +SKIP
        x
    --------- = C1
       /f(x)\
    tan|----|
       \2*x /

    """
    x = func.args[0]
    f = func.func
    C1 = get_numbered_constants(eq, num=1)

    # First, integrate if the hint allows it.
    eq = _handle_Integral(eq, func, order, hint)
    if hint.startswith("nth_linear_euler_eq_nonhomogeneous"):
        eq = simplify(eq)
    if not isinstance(eq, Equality):
        raise TypeError("eq should be an instance of Equality")

    # Second, clean up the arbitrary constants.
    # Right now, nth linear hints can put as many as 2*order constants in an
    # expression.  If that number grows with another hint, the third argument
    # here should be raised accordingly, or constantsimp() rewritten to handle
    # an arbitrary number of constants.
    eq = constantsimp(eq, constants)

    # Lastly, now that we have cleaned up the expression, try solving for func.
    # When RootOf is implemented in solve(), we will want to return a RootOf
    # everytime instead of an Equality.

    # Get the f(x) on the left if possible.
    if eq.rhs == func and not eq.lhs.has(func):
        eq = [Eq(eq.rhs, eq.lhs)]

    # make sure we are working with lists of solutions in simplified form.
    if eq.lhs == func and not eq.rhs.has(func):
        # The solution is already solved
        eq = [eq]

        # special simplification of the rhs
        if hint.startswith("nth_linear_constant_coeff"):
            # Collect terms to make the solution look nice.
            # This is also necessary for constantsimp to remove unnecessary
            # terms from the particular solution from variation of parameters
            #
            # Collect is not behaving reliably here.  The results for
            # some linear constant-coefficient equations with repeated
            # roots do not properly simplify all constants sometimes.
            # 'collectterms' gives different orders sometimes, and results
            # differ in collect based on that order.  The
            # sort-reverse trick fixes things, but may fail in the
            # future. In addition, collect is splitting exponentials with
            # rational powers for no reason.  We have to do a match
            # to fix this using Wilds.
            global collectterms
            try:
                collectterms.sort(key=default_sort_key)
                collectterms.reverse()
            except:
                pass
            assert len(eq) == 1 and eq[0].lhs == f(x)
            sol = eq[0].rhs
            sol = expand_mul(sol)
            for i, reroot, imroot in collectterms:
                sol = collect(sol, x**i*exp(reroot*x)*sin(abs(imroot)*x))
                sol = collect(sol, x**i*exp(reroot*x)*cos(imroot*x))
            for i, reroot, imroot in collectterms:
                sol = collect(sol, x**i*exp(reroot*x))
            del collectterms

            # Collect is splitting exponentials with rational powers for
            # no reason.  We call powsimp to fix.
            sol = powsimp(sol)

            eq[0] = Eq(f(x), sol)

    else:
        # The solution is not solved, so try to solve it
        try:
            eqsol = solve(eq, func, force=True)
            if not eqsol:
                raise NotImplementedError
        except (NotImplementedError, PolynomialError):
            eq = [eq]
        else:
            def _expand(expr):
                numer, denom = expr.as_numer_denom()

                if denom.is_Add:
                    return expr
                else:
                    return powsimp(expr.expand(), combine='exp', deep=True)

            # XXX: the rest of odesimp() expects each ``t`` to be in a
            # specific normal form: rational expression with numerator
            # expanded, but with combined exponential functions (at
            # least in this setup all tests pass).
            eq = [Eq(f(x), _expand(t)) for t in eqsol]

        # special simplification of the lhs.
        if hint.startswith("1st_homogeneous_coeff"):
            for j, eqi in enumerate(eq):
                newi = logcombine(eqi, force=True)
                if newi.lhs.func is log and newi.rhs == 0:
                    newi = Eq(newi.lhs.args[0]/C1, C1)
                eq[j] = newi

    # We cleaned up the constants before solving to help the solve engine with
    # a simpler expression, but the solved expression could have introduced
    # things like -C1, so rerun constantsimp() one last time before returning.
    for i, eqi in enumerate(eq):
        eq[i] = constantsimp(eqi, constants)
        eq[i] = constant_renumber(eq[i], 'C', 1, 2*order)

    # If there is only 1 solution, return it;
    # otherwise return the list of solutions.
    if len(eq) == 1:
        eq = eq[0]
    return eq
def odesimp ( eq , func , order , constants , hint ) : x = func . args [ 0 ] f = func . func C1 = get_numbered_constants ( eq , num = 1 ) # First, integrate if the hint allows it. eq = _handle_Integral ( eq , func , order , hint ) if hint . startswith ( "nth_linear_euler_eq_nonhomogeneous" ) : eq = simplify ( eq ) if not isinstance ( eq , Equality ) : raise TypeError ( "eq should be an instance of Equality" ) # Second, clean up the arbitrary constants. # Right now, nth linear hints can put as many as 2*order constants in an # expression.  If that number grows with another hint, the third argument # here should be raised accordingly, or constantsimp() rewritten to handle # an arbitrary number of constants. eq = constantsimp ( eq , constants ) # Lastly, now that we have cleaned up the expression, try solving for func. # When RootOf is implemented in solve(), we will want to return a RootOf # everytime instead of an Equality. # Get the f(x) on the left if possible. if eq . rhs == func and not eq . lhs . has ( func ) : eq = [ Eq ( eq . rhs , eq . lhs ) ] # make sure we are working with lists of solutions in simplified form. if eq . lhs == func and not eq . rhs . has ( func ) : # The solution is already solved eq = [ eq ] # special simplification of the rhs if hint . startswith ( "nth_linear_constant_coeff" ) : # Collect terms to make the solution look nice. # This is also necessary for constantsimp to remove unnecessary # terms from the particular solution from variation of parameters # # Collect is not behaving reliably here.  The results for # some linear constant-coefficient equations with repeated # roots do not properly simplify all constants sometimes. # 'collectterms' gives different orders sometimes, and results # differ in collect based on that order.  The # sort-reverse trick fixes things, but may fail in the # future. In addition, collect is splitting exponentials with # rational powers for no reason.  We have to do a match # to fix this using Wilds. global collectterms try : collectterms . sort ( key = default_sort_key ) collectterms . reverse ( ) except : pass assert len ( eq ) == 1 and eq [ 0 ] . lhs == f ( x ) sol = eq [ 0 ] . rhs sol = expand_mul ( sol ) for i , reroot , imroot in collectterms : sol = collect ( sol , x ** i * exp ( reroot * x ) * sin ( abs ( imroot ) * x ) ) sol = collect ( sol , x ** i * exp ( reroot * x ) * cos ( imroot * x ) ) for i , reroot , imroot in collectterms : sol = collect ( sol , x ** i * exp ( reroot * x ) ) del collectterms # Collect is splitting exponentials with rational powers for # no reason.  We call powsimp to fix. sol = powsimp ( sol ) eq [ 0 ] = Eq ( f ( x ) , sol ) else : # The solution is not solved, so try to solve it try : eqsol = solve ( eq , func , force = True ) if not eqsol : raise NotImplementedError except ( NotImplementedError , PolynomialError ) : eq = [ eq ] else : def _expand ( expr ) : numer , denom = expr . as_numer_denom ( ) if denom . is_Add : return expr else : return powsimp ( expr . expand ( ) , combine = 'exp' , deep = True ) # XXX: the rest of odesimp() expects each ``t`` to be in a # specific normal form: rational expression with numerator # expanded, but with combined exponential functions (at # least in this setup all tests pass). eq = [ Eq ( f ( x ) , _expand ( t ) ) for t in eqsol ] # special simplification of the lhs. if hint . startswith ( "1st_homogeneous_coeff" ) : for j , eqi in enumerate ( eq ) : newi = logcombine ( eqi , force = True ) if newi . lhs . func is log and newi . rhs == 0 : newi = Eq ( newi . lhs . args [ 0 ] / C1 , C1 ) eq [ j ] = newi # We cleaned up the constants before solving to help the solve engine with # a simpler expression, but the solved expression could have introduced # things like -C1, so rerun constantsimp() one last time before returning. for i , eqi in enumerate ( eq ) : eq [ i ] = constantsimp ( eqi , constants ) eq [ i ] = constant_renumber ( eq [ i ] , 'C' , 1 , 2 * order ) # If there is only 1 solution, return it; # otherwise return the list of solutions. if len ( eq ) == 1 : eq = eq [ 0 ] return eq