def from_symbolic(exp, dR=None): r''' Method to transform a symbolic expression into a :class:`~ajpastor.dd_functions.ddFunction.DDFunction`. This method takes a symbolic expression an tries to cast it into a differentially definable function. This is closed related with the names that the module :mod:`~ajpastor.dd_functions.ddExamples` gives to the functions. This method is the inverse of :func:`symbolic`, meaning that the output of this method after applying :func:`symbolic` is always an object that is equal to the first input. INPUT: * ``exp``: symbolic expression or an object that can be casted into one. We also allow lists, tuples and sets as possible inputs, applying this method recursively to each of its elements. * ``dR``: :class:`~ajpastor.dd_functions.ddFunction.DDRing` in which hierarchy we try to include the symbolic expression ``exp``. If ``None`` is given, we compute a :class:`~ajpastor.dd_functions.ddFunction.DDRing` using `x` as a variable and all other variables appearing in ``exp`` considered as parameters. OUTPUT: A :class:`~ajpastor.dd_functions.ddFunction.DDFunction` representation of ``exp``. TODO: add more cases from ddExamples EXAMPLES:: sage: from ajpastor.dd_functions import * sage: var('y', 'm', 'n') (y, m, n) sage: from_symbolic(exp(x)) == Exp(x) True sage: sin_yx = from_symbolic(sin(y*x)) sage: parent(sin_yx) DD-Ring over (Univariate Polynomial Ring in x over Rational Field) with parameter (y) sage: sin_yx.init(6, True) [0, y, 0, -y^3, 0, y^5] sage: from_symbolic(cos(x)) == Cos(x) True sage: from_symbolic(sinh(x)) == Sinh(x) True sage: from_symbolic(cosh(x)) == Cosh(x) True sage: from_symbolic(tan(x)) == Tan(x) True sage: from_symbolic(log(x + 1)) == Log(x+1) True sage: from_symbolic(bessel_J(4, x)) == BesselD(4) True sage: from_symbolic(hypergeometric([1,2,3],[5,6,7],x)) == GenericHypergeometricFunction((1,2,3),(5,6,7)) True ''' logger.debug("Looking for a DD-finite representation of %s", exp) if (isinstance(exp, list)): logger.debug("List case: converting each element") return [from_symbolic(el, dR) for el in exp] elif (isinstance(exp, tuple)): logger.debug("Tuple case: converting each element") return tuple([from_symbolic(el, dR) for el in exp]) elif (isinstance(exp, set)): logger.debug("Set case: converting each element") return set([from_symbolic(el, dR) for el in exp]) ## If it is not from the basic structures, we required a symbolic expression exp = SR(exp) logger.debug("Deciding the starting DDRing") if (not dR is None): logger.debug("User required solution to be in the hierarchy of %s", dR) if (any(v not in list(dR.variables(True)) + list(dR.parameters(True)) for v in exp.variables())): raise TypeError("The symbolic expression has unknown variables") else: logger.debug("DDRing not specified: we compute one") params = [str(v) for v in exp.variables() if str(v) != 'x'] if (len(params) > 0): dR = ParametrizedDDRing( DFinite, [str(v) for v in exp.variables() if str(v) != 'x']) else: dR = DFinite name = None try: name = exp.operator().__name__ except AttributeError: try: name = exp.operator().name() except AttributeError: pass operands = exp.operands() if (name in ("list", "tuple")): # special tuple and list cases logger.debug("Found a symbolic list or tuple") return tuple([from_symbolic(el, dR) for el in operands]) elif (name == "set"): # special set case logger.debug("Found a symbolic set") return set([from_symbolic(el, dR) for el in operands]) elif (exp.is_rational_expression()): # rational expression case logger.debug("Rational expression found") num = exp.numerator().polynomial(ring=dR.original_ring()) den = exp.denominator().polynomial(ring=dR.original_ring()) if (den == 1): return num return num / den elif (name == "add_vararg"): logger.debug("Found an addition") return sum([from_symbolic(el, dR) for el in operands]) elif (name == "mul_vararg"): logger.debug("Found an product") return prod([from_symbolic(el, dR) for el in operands]) elif (name == "pow"): logger.debug("Found an power") if (not operands[1] in QQ): raise TypeError("The exponent has to be a rational number") if (operands[1] in ZZ): logger.debug("Integer power: simple output") return pow(from_symbolic(operands[0], dR), ZZ(operands[1])) else: base = from_symbolic(operands[0], dR) if (is_DDFunction(base)): logger.debug("Fractional power: base DDFunction") return pow(base, QQ(operands[1])) else: logger.debug("Fractional power: base rational function") params = [str(el) for el in dR.parameters()] i = 0 while ("y%d" % i in params): i += 1 R = PolynomialRing(dR.original_ring(), "y%d" % i) y = R.gens()[0] pq = QQ(operands[1]) p = pq.numerator() q = pq.denominator() poly = y**q - R(str(operands[0]))**p x = dR.variables(True)[0] init = [ dR.coeff_field(exp.derivative(x, i)(**{ str(x): 0 })) for i in range(p + q) ] return DAlgebraic(poly, init, dR.to_depth(1)) elif (name == "sin"): logger.debug("Found an sine") return Sin(operands[0]) elif (name == "cos"): logger.debug("Found an cosine") return Cos(operands[0]) elif (name == "sinh"): logger.debug("Found a hyperbolic sine") return Sinh(operands[0]) elif (name == "cosh"): logger.debug("Found an hyperbolic cosine") return Cosh(operands[0]) elif (name == "tan"): logger.debug("Found a tangent") return Tan(operands[0]) elif (name == "log"): logger.debug("Found a logarithm") return Log(operands[0]) elif (name == "exp"): logger.debug("Found an exponential") return Exp(operands[0]) elif (name == "bessel_J"): logger.debug("Found an Bessel function of first kind") if (not (operands[0] in ZZ and operands[0] >= 0)): raise ValueError( "The Bessel has to have a non-negative integer index") return BesselD(ZZ(operands[0]))(from_symbolic(operands[1], dR)) elif (name == "legendre_P"): logger.debug("Found an Legendre function of first kind") return LegendreD(operands[0], 0, 1)(from_symbolic(operands[1], dR)) elif (name == "gen_legendre_P"): logger.debug("Found an generic Legendre function of first kind") return LegendreD(operands[0], operands[1], 1)(from_symbolic(operands[1], dR)) elif (name == "legendre_Q"): logger.debug("Found an Legendre function of second kind") return LegendreD(operands[0], 0, 2)(from_symbolic(operands[1], dR)) elif (name == "gen_legendre_Q"): logger.debug("Found an generic Legendre function of second kind") return LegendreD(operands[0], operands[1], 2)(from_symbolic(operands[1], dR)) elif (name == "chebyshev_T"): logger.debug("Found an Chebyshev function of first kind") return ChebyshevD(operands[0], 1)(from_symbolic(operands[1], dR)) elif (name == "chebyshev_U"): logger.debug("Found an Chebyshev function of second kind") return ChebyshevD(operands[0], 2)(from_symbolic(operands[1], dR)) elif (name == "hypergeometric"): return GenericHypergeometricFunction(from_symbolic(operands[0], dR), from_symbolic(operands[1], dR))(from_symbolic( operands[2], dR)) else: raise NotImplementedError( "The operator %s is not valid for 'from_symbolic'" % name)