(self, f=None, x=None, handle_first='log', dummy=True, extension=None, rewrite_complex=False)
return
Tries to build a transcendental extension tower from f with respect to x.

        If it is successful, creates a DifferentialExtension object with, among
        others, the attributes fa, fd, D, T, Tfuncs, and backsubs such that
        fa and fd are Polys in T[-1] with rational coefficients in T[:-1],
        fa/fd == f, and D[i] is a Poly in T[i] with rational coefficients in
        T[:i] representing the derivative of T[i] for each i from 1 to len(T).
        Tfuncs is a list of Lambda objects for back replacing the functions
        after integrating.  Lambda() is only used (instead of lambda) to make
        them easier to test and debug. Note that Tfuncs corresponds to the
        elements of T, except for T[0] == x, but they should be back-substituted
        in reverse order.  backsubs is a (possibly empty) back-substitution list
        that should be applied on the completed integral to make it look more
        like the original integrand.

        If it is unsuccessful, it raises NotImplementedError.

        You can also create an object by manually setting the attributes as a
        dictionary to the extension keyword argument.  You must include at least
        D.  Warning, any attribute that is not given will be set to None. The
        attributes T, t, d, cases, case, x, and level are set automatically and
        do not need to be given.  The functions in the Risch Algorithm will NOT
        check to see if an attribute is None before using it.  This also does not
        check to see if the extension is valid (non-algebraic) or even if it is
        self-consistent.  Therefore, this should only be used for
        testing/debugging purposes.
Tries to build a transcendental extension tower from f with respect to x.
Tries to build a transcendental extension tower from f with respect to x .
def __init__(self, f=None, x=None, handle_first='log', dummy=True, extension=None, rewrite_complex=False):
        """
        Tries to build a transcendental extension tower from f with respect to x.

        If it is successful, creates a DifferentialExtension object with, among
        others, the attributes fa, fd, D, T, Tfuncs, and backsubs such that
        fa and fd are Polys in T[-1] with rational coefficients in T[:-1],
        fa/fd == f, and D[i] is a Poly in T[i] with rational coefficients in
        T[:i] representing the derivative of T[i] for each i from 1 to len(T).
        Tfuncs is a list of Lambda objects for back replacing the functions
        after integrating.  Lambda() is only used (instead of lambda) to make
        them easier to test and debug. Note that Tfuncs corresponds to the
        elements of T, except for T[0] == x, but they should be back-substituted
        in reverse order.  backsubs is a (possibly empty) back-substitution list
        that should be applied on the completed integral to make it look more
        like the original integrand.

        If it is unsuccessful, it raises NotImplementedError.

        You can also create an object by manually setting the attributes as a
        dictionary to the extension keyword argument.  You must include at least
        D.  Warning, any attribute that is not given will be set to None. The
        attributes T, t, d, cases, case, x, and level are set automatically and
        do not need to be given.  The functions in the Risch Algorithm will NOT
        check to see if an attribute is None before using it.  This also does not
        check to see if the extension is valid (non-algebraic) or even if it is
        self-consistent.  Therefore, this should only be used for
        testing/debugging purposes.
        """
        # XXX: If you need to debug this function, set the break point here

        if extension:
            if 'D' not in extension:
                raise ValueError("At least the key D must be included with "
                    "the extension flag to DifferentialExtension.")
            for attr in extension:
                setattr(self, attr, extension[attr])

            self._auto_attrs()

            return
        elif f is None or x is None:
            raise ValueError("Either both f and x or a manual extension must "
            "be given.")

        from sympy.integrals.prde import is_deriv_k

        if handle_first not in ['log', 'exp']:
            raise ValueError("handle_first must be 'log' or 'exp', not %s." %
                str(handle_first))

        # f will be the original function, self.f might change if we reset
        # (e.g., we pull out a constant from an exponential)
        self.f = f
        self.x = x
        self.reset(dummy=dummy)
        exp_new_extension, log_new_extension = True, True
        if rewrite_complex:
            rewritables = {
                (sin, cos, cot, tan, sinh, cosh, coth, tanh): exp,
                (asin, acos, acot, atan): log,
            }
        #rewrite the trigonometric components
            for candidates, rule in rewritables.items():
                self.newf = self.newf.rewrite(candidates, rule)
        else:
            if any(i.has(x) for i in self.f.atoms(sin, cos, tan, atan, asin, acos)):
                raise NotImplementedError("Trigonometric extensions are not "
                "supported (yet!)")

        def update(seq, atoms, func):
            s = set(seq)
            new = atoms - s
            s = atoms.intersection(s)
            s.update(list(filter(func, new)))
            return list(s)

        exps = set()
        pows = set()
        numpows = set()
        sympows = set()
        logs = set()
        symlogs = set()

        while True:
            restart = False
            if self.newf.is_rational_function(*self.T):
                break

            if not exp_new_extension and not log_new_extension:
                # We couldn't find a new extension on the last pass, so I guess
                # we can't do it.
                raise NotImplementedError("Couldn't find an elementary "
                    "transcendental extension for %s.  Try using a " % str(f) +
                    "manual extension with the extension flag.")

            # Pre-preparsing.
            #################
            # Get all exp arguments, so we can avoid ahead of time doing
            # something like t1 = exp(x), t2 = exp(x/2) == sqrt(t1).

            # Things like sqrt(exp(x)) do not automatically simplify to
            # exp(x/2), so they will be viewed as algebraic.  The easiest way
            # to handle this is to convert all instances of (a**b)**Rational
            # to a**(Rational*b) before doing anything else.  Note that the
            # _exp_part code can generate terms of this form, so we do need to
            # do this at each pass (or else modify it to not do that).

            ratpows = [i for i in self.newf.atoms(Pow).union(self.newf.atoms(exp))
                if (i.base.is_Pow or i.base.func is exp and i.exp.is_Rational)]

            ratpows_repl = [
                (i, i.base.base**(i.exp*i.base.exp)) for i in ratpows]
            self.backsubs += [(j, i) for i, j in ratpows_repl]
            self.newf = self.newf.xreplace(dict(ratpows_repl))

            # To make the process deterministic, the args are sorted
            # so that functions with smaller op-counts are processed first.
            # Ties are broken with the default_sort_key.

            # XXX Although the method is deterministic no additional work
            # has been done to guarantee that the simplest solution is
            # returned and that it would be affected be using different
            # variables. Though it is possible that this is the case
            # one should know that it has not been done intentionally so
            # further improvements may possible.

            # TODO: This probably doesn't need to be completely recomputed at
            # each pass.
            exps = update(exps, self.newf.atoms(exp),
                lambda i: i.exp.is_rational_function(*self.T) and
                i.exp.has(*self.T))
            pows = update(pows, self.newf.atoms(Pow),
                lambda i: i.exp.is_rational_function(*self.T) and
                i.exp.has(*self.T))
            numpows = update(numpows, set(pows),
                lambda i: not i.base.has(*self.T))
            sympows = update(sympows, set(pows) - set(numpows),
                lambda i: i.base.is_rational_function(*self.T) and
                not i.exp.is_Integer)

            # The easiest way to deal with non-base E powers is to convert them
            # into base E, integrate, and then convert back.
            for i in ordered(pows):
                old = i
                new = exp(i.exp*log(i.base))
                # If exp is ever changed to automatically reduce exp(x*log(2))
                # to 2**x, then this will break.  The solution is to not change
                # exp to do that :)
                if i in sympows:
                    if i.exp.is_Rational:
                        raise NotImplementedError("Algebraic extensions are "
                            "not supported (%s)." % str(i))
                    # We can add a**b only if log(a) in the extension, because
                    # a**b == exp(b*log(a)).
                    basea, based = frac_in(i.base, self.t)
                    A = is_deriv_k(basea, based, self)
                    if A is None:
                        # Nonelementary monomial (so far)

                        # TODO: Would there ever be any benefit from just
                        # adding log(base) as a new monomial?
                        # ANSWER: Yes, otherwise we can't integrate x**x (or
                        # rather prove that it has no elementary integral)
                        # without first manually rewriting it as exp(x*log(x))
                        continue
                    ans, u, const = A
                    newterm = exp(i.exp*(log(const) + u))
                    # Under the current implementation, exp kills terms
                    # only if they are of the form a*log(x), where a is a
                    # Number.  This case should have already been killed by the
                    # above tests.  Again, if this changes to kill more than
                    # that, this will break, which maybe is a sign that you
                    # shouldn't be changing that.  Actually, if anything, this
                    # auto-simplification should be removed.  See
                    # http://groups.google.com/group/sympy/browse_thread/thread/a61d48235f16867f

                    self.newf = self.newf.xreplace({i: newterm})

                elif i not in numpows:
                    continue
                else:
                    # i in numpows
                    newterm = new
                # TODO: Just put it in self.Tfuncs
                self.backsubs.append((new, old))
                self.newf = self.newf.xreplace({old: newterm})
                exps.append(newterm)

            atoms = self.newf.atoms(log)
            logs = update(logs, atoms,
                lambda i: i.args[0].is_rational_function(*self.T) and
                i.args[0].has(*self.T))
            symlogs = update(symlogs, atoms,
                lambda i: i.has(*self.T) and i.args[0].is_Pow and
                i.args[0].base.is_rational_function(*self.T) and
                not i.args[0].exp.is_Integer)

            # We can handle things like log(x**y) by converting it to y*log(x)
            # This will fix not only symbolic exponents of the argument, but any
            # non-Integer exponent, like log(sqrt(x)).  The exponent can also
            # depend on x, like log(x**x).
            for i in ordered(symlogs):
                # Unlike in the exponential case above, we do not ever
                # potentially add new monomials (above we had to add log(a)).
                # Therefore, there is no need to run any is_deriv functions
                # here.  Just convert log(a**b) to b*log(a) and let
                # log_new_extension() handle it from there.
                lbase = log(i.args[0].base)
                logs.append(lbase)
                new = i.args[0].exp*lbase
                self.newf = self.newf.xreplace({i: new})
                self.backsubs.append((new, i))

            # remove any duplicates
            logs = list(set(logs))

            if handle_first == 'exp' or not log_new_extension:
                exp_new_extension = self._exp_part(exps, dummy=dummy)
                if exp_new_extension is None:
                    # reset and restart
                    self.f = self.newf
                    self.reset(dummy=dummy)
                    exp_new_extension = True
                    continue

            if handle_first == 'log' or not exp_new_extension:
                log_new_extension = self._log_part(logs, dummy=dummy)

        self.fa, self.fd = frac_in(self.newf, self.t)
        self._auto_attrs()

        return
def __init__ ( self , f = None , x = None , handle_first = 'log' , dummy = True , extension = None , rewrite_complex = False ) : # XXX: If you need to debug this function, set the break point here if extension : if 'D' not in extension : raise ValueError ( "At least the key D must be included with " "the extension flag to DifferentialExtension." ) for attr in extension : setattr ( self , attr , extension [ attr ] ) self . _auto_attrs ( ) return elif f is None or x is None : raise ValueError ( "Either both f and x or a manual extension must " "be given." ) from sympy . integrals . prde import is_deriv_k if handle_first not in [ 'log' , 'exp' ] : raise ValueError ( "handle_first must be 'log' or 'exp', not %s." % str ( handle_first ) ) # f will be the original function, self.f might change if we reset # (e.g., we pull out a constant from an exponential) self . f = f self . x = x self . reset ( dummy = dummy ) exp_new_extension , log_new_extension = True , True if rewrite_complex : rewritables = { ( sin , cos , cot , tan , sinh , cosh , coth , tanh ) : exp , ( asin , acos , acot , atan ) : log , } #rewrite the trigonometric components for candidates , rule in rewritables . items ( ) : self . newf = self . newf . rewrite ( candidates , rule ) else : if any ( i . has ( x ) for i in self . f . atoms ( sin , cos , tan , atan , asin , acos ) ) : raise NotImplementedError ( "Trigonometric extensions are not " "supported (yet!)" ) def update ( seq , atoms , func ) : s = set ( seq ) new = atoms - s s = atoms . intersection ( s ) s . update ( list ( filter ( func , new ) ) ) return list ( s ) exps = set ( ) pows = set ( ) numpows = set ( ) sympows = set ( ) logs = set ( ) symlogs = set ( ) while True : restart = False if self . newf . is_rational_function ( * self . T ) : break if not exp_new_extension and not log_new_extension : # We couldn't find a new extension on the last pass, so I guess # we can't do it. raise NotImplementedError ( "Couldn't find an elementary " "transcendental extension for %s.  Try using a " % str ( f ) + "manual extension with the extension flag." ) # Pre-preparsing. ################# # Get all exp arguments, so we can avoid ahead of time doing # something like t1 = exp(x), t2 = exp(x/2) == sqrt(t1). # Things like sqrt(exp(x)) do not automatically simplify to # exp(x/2), so they will be viewed as algebraic.  The easiest way # to handle this is to convert all instances of (a**b)**Rational # to a**(Rational*b) before doing anything else.  Note that the # _exp_part code can generate terms of this form, so we do need to # do this at each pass (or else modify it to not do that). ratpows = [ i for i in self . newf . atoms ( Pow ) . union ( self . newf . atoms ( exp ) ) if ( i . base . is_Pow or i . base . func is exp and i . exp . is_Rational ) ] ratpows_repl = [ ( i , i . base . base ** ( i . exp * i . base . exp ) ) for i in ratpows ] self . backsubs += [ ( j , i ) for i , j in ratpows_repl ] self . newf = self . newf . xreplace ( dict ( ratpows_repl ) ) # To make the process deterministic, the args are sorted # so that functions with smaller op-counts are processed first. # Ties are broken with the default_sort_key. # XXX Although the method is deterministic no additional work # has been done to guarantee that the simplest solution is # returned and that it would be affected be using different # variables. Though it is possible that this is the case # one should know that it has not been done intentionally so # further improvements may possible. # TODO: This probably doesn't need to be completely recomputed at # each pass. exps = update ( exps , self . newf . atoms ( exp ) , lambda i : i . exp . is_rational_function ( * self . T ) and i . exp . has ( * self . T ) ) pows = update ( pows , self . newf . atoms ( Pow ) , lambda i : i . exp . is_rational_function ( * self . T ) and i . exp . has ( * self . T ) ) numpows = update ( numpows , set ( pows ) , lambda i : not i . base . has ( * self . T ) ) sympows = update ( sympows , set ( pows ) - set ( numpows ) , lambda i : i . base . is_rational_function ( * self . T ) and not i . exp . is_Integer ) # The easiest way to deal with non-base E powers is to convert them # into base E, integrate, and then convert back. for i in ordered ( pows ) : old = i new = exp ( i . exp * log ( i . base ) ) # If exp is ever changed to automatically reduce exp(x*log(2)) # to 2**x, then this will break.  The solution is to not change # exp to do that :) if i in sympows : if i . exp . is_Rational : raise NotImplementedError ( "Algebraic extensions are " "not supported (%s)." % str ( i ) ) # We can add a**b only if log(a) in the extension, because # a**b == exp(b*log(a)). basea , based = frac_in ( i . base , self . t ) A = is_deriv_k ( basea , based , self ) if A is None : # Nonelementary monomial (so far) # TODO: Would there ever be any benefit from just # adding log(base) as a new monomial? # ANSWER: Yes, otherwise we can't integrate x**x (or # rather prove that it has no elementary integral) # without first manually rewriting it as exp(x*log(x)) continue ans , u , const = A newterm = exp ( i . exp * ( log ( const ) + u ) ) # Under the current implementation, exp kills terms # only if they are of the form a*log(x), where a is a # Number.  This case should have already been killed by the # above tests.  Again, if this changes to kill more than # that, this will break, which maybe is a sign that you # shouldn't be changing that.  Actually, if anything, this # auto-simplification should be removed.  See # http://groups.google.com/group/sympy/browse_thread/thread/a61d48235f16867f self . newf = self . newf . xreplace ( { i : newterm } ) elif i not in numpows : continue else : # i in numpows newterm = new # TODO: Just put it in self.Tfuncs self . backsubs . append ( ( new , old ) ) self . newf = self . newf . xreplace ( { old : newterm } ) exps . append ( newterm ) atoms = self . newf . atoms ( log ) logs = update ( logs , atoms , lambda i : i . args [ 0 ] . is_rational_function ( * self . T ) and i . args [ 0 ] . has ( * self . T ) ) symlogs = update ( symlogs , atoms , lambda i : i . has ( * self . T ) and i . args [ 0 ] . is_Pow and i . args [ 0 ] . base . is_rational_function ( * self . T ) and not i . args [ 0 ] . exp . is_Integer ) # We can handle things like log(x**y) by converting it to y*log(x) # This will fix not only symbolic exponents of the argument, but any # non-Integer exponent, like log(sqrt(x)).  The exponent can also # depend on x, like log(x**x). for i in ordered ( symlogs ) : # Unlike in the exponential case above, we do not ever # potentially add new monomials (above we had to add log(a)). # Therefore, there is no need to run any is_deriv functions # here.  Just convert log(a**b) to b*log(a) and let # log_new_extension() handle it from there. lbase = log ( i . args [ 0 ] . base ) logs . append ( lbase ) new = i . args [ 0 ] . exp * lbase self . newf = self . newf . xreplace ( { i : new } ) self . backsubs . append ( ( new , i ) ) # remove any duplicates logs = list ( set ( logs ) ) if handle_first == 'exp' or not log_new_extension : exp_new_extension = self . _exp_part ( exps , dummy = dummy ) if exp_new_extension is None : # reset and restart self . f = self . newf self . reset ( dummy = dummy ) exp_new_extension = True continue if handle_first == 'log' or not exp_new_extension : log_new_extension = self . _log_part ( logs , dummy = dummy ) self . fa , self . fd = frac_in ( self . newf , self . t ) self . _auto_attrs ( ) return