Sympy series expansion with numerical integration - python

I want to make a series expansion for a function F(e,Eo) up to a certain power of e and integrate over the Eo variable numerically.
What I thought was using SymPy to make the power series in e, and then use MPMath for the numerical integration over Eo.
Below is an example code. I receive the message that it can not create mpf from the expression. I guess the problem has to do with the fact that with the series from SymPy has an O(e**5) term at the end, and later that I want the numerical integration to show a function of e instead of a number.
import sympy as sp
import numpy as np
from mpmath import *
e = sp.symbols('e')
Eo = sp.symbols('Eo')
expr = sp.sin(e-2*Eo).series(e, 0, 5)
F = lambda Eo : expr
I = quad(F, [0, 2*np.pi])
print(I)
It’s evident that I need a different approach, but I would still need the numerical integration for my actual code because it has much more complicated expressions that could not be integrated analytically.
Edit: I should have chosen a complex function of real variables for the example code, I am trying this (the expansion and integration) for functions such as:
expr = (cos(Eo) - e - I*sqrt(1 - e ** 2)*sin(Eo)) ** 2 * (cos(2*(Eo - e*sin(Eo))) + I*sin(2*(Eo - e*sin(Eo))))/(1 - e*cos(Eo)) ** 4

Here is an approach similar to Wrzlprmft's answer but with a different way of handling coefficients, via SymPy's polynomial module:
from sympy import sin, pi, symbols, Integral, Poly
def integrate_coeff(coeff):
partial_integral = coeff.integrate((Eo, 0, 2*pi))
return partial_integral.n() if partial_integral.has(Integral) else partial_integral
e,Eo = symbols("e Eo")
expr = sin(e-sin(2*Eo))
degree = 5
coeffs = Poly(expr.series(e, 0, degree).removeO(), e).all_coeffs()
new_coeffs = map(integrate_coeff, coeffs)
print((Poly(new_coeffs, e).as_expr() + e**degree).series(e, 0, degree))
The main code is three lines: (1) extract coefficients of e up to given degree; (2) integrate each, numerically if we must; (3) print the result, presenting it as a series rather than a polynomial (hence the trick with adding e**degree, to make SymPy aware that the series continues). Output:
-6.81273574401304e-108 + 4.80787886126883*e + 3.40636787200652e-108*e**2 - 0.801313143544804*e**3 - 2.12897992000408e-109*e**4 + O(e**5)

I want the numerical integration to show a function of e instead of a number.
This is fundamentally impossible.
Either your integration is analytical or numerical, and if it is numerical it will only handle and yield numbers for you (the words numerical and number are similar for a reason).
If you want to split the integration into numerical and analytical components, you have to do so yourself – or hope that SymPy automatically splits the integration as needed, which it unfortunately is not yet capable of.
This is how I would do it (details are commented in the code):
from sympy import sin, pi, symbols, Integral
from itertools import islice
e,Eo = symbols("e Eo")
expr = sin(e-sin(2*Eo))
# Create a generator yielding the first five summands of the series.
# This avoids the O(e**5) term.
series = islice(expr.series(e,0,None),5)
integral = 0
for power,summand in enumerate(series):
# Remove the e from the expression
Eo_part = summand/e**power
# … and ensure that it worked:
assert not Eo_part.has(e)
# Integrate the Eo part:
partial_integral = Eo_part.integrate((Eo,0,2*pi))
# If the integral cannot be evaluated analytically, …
if partial_integral.has(Integral):
# … replace it by the numerical estimate:
partial_integral = partial_integral.n()
# Re-attach the e component and add it to the sum:
integral += partial_integral*e**power
Note that I did not use NumPy or MPMath at all (though SymPy uses the latter under the hood for numerical estimates). Unless you really really know what you’re doing, mixing those two with SymPy is a bad idea as they are not even aware of SymPy expressions.

Related

Sympy not properly displaying conjugate of square root of real

I keep getting expressions like this image, despite declaring these symbols as reals.
The code to reproduce is:
import sympy as sp
delta = sp.Symbol('delta', real=True)
f = sp.sqrt(1/delta)
prod = sp.conjugate(f)*f
prod.subs(delta,delta)
I expected to get 1/delta
Also trying simplify() does not work either.
According to the official SymPy Docs for conjugate, it looks like the function is supposed to return the complex conjugate for its input. In other words, it takes the complex part of the number and flips the sign.
In your example, you are taking the square root of a variable. If delta = -1, then the resulting conjugate could be unreal and thus different than if delta was any other integer. Thus, SymPy wraps the result in a conjugate object.
If you want to tell Sympy that your variable delta is positive (and thus f must be real), then you should define it as delta = sp.Symbol('delta', real=True, positive=True).

How to solve equations in python

I try to write a script that simulates a resistor. It takes 2 arguments for example P and R and it should calculate all missing values of this resistor.
The problem is that I don't want to write every single possible equation for every value. This means I want to write something like (U=RxI, R=U/R, I=U/R , P=UxI) and the script should then complete all equation with the given values for every equation.
For example, something like this:
in R=10
in I=5
out U=R*I
out P=I**2 * R
You can use https://pypi.org/project/Equation/ Packages.
Example
>>> from Equation import Expression
>>> fn = Expression("sin(x+y^2)",["y","x"])
>>> fn
sin((x + (y ^ (2+0j))))
>>> print fn
\sin\left(\left(x + y^{(2+0j)}\right)\right)
>>> fn(3,4)
(0.42016703682664092+0j)
Sympy
Second: https://github.com/sympy/sympy/wiki
Arbitrary precision integers, rationals and floats, as well as symbolic expressions
Simplification (e.g. ( abb + 2bab ) → (3ab^2)), expansion (e.g. ((a+b)^2) → (a^2 + 2ab + b^2)), and other methods of rewriting expressions
Functions (exp, log, sin, ...)
Complex numbers (like exp(Ix).expand(complex=True) → cos(x)+Isin(x))
Taylor (Laurent) series and limits
Differentiation and integration
In vanilla python, there is no solution as general as the one you are looking for.
The typical solution would be to write an algorithm for every option (only given U, only given R) and then logically select which option to execute.
You may also want to consider using a module like SymPy, which has a solver module that may be more up your alley.

I am writing a program that accepts user input in order to differentiate a function

In order to find the maximums and minimums of a function, I am writing a program that accepts a polynomial imputed from the user and finds where the derivative = 0.
from sympy import Symbol, Derivative
from sympy import symbols, Eq, solve
import sympy as sy
import numpy as np
import math
x= Symbol('x', real = True)
function= x**4 +7*x**2 +8
deriv= Derivative(function, x)
yprime = deriv.doit()
y = symbols('x')
eq1 = Eq(yprime,0)
sol = solve(eq1, )
The only reason that the code above is able to take the derivative and find solutions is because the coefficients and exponents are integers. If I ask for user input {e.g., function = input("What is your function: ")}, then the whole thing will become a string and SymPy can not take the derivative.
Is it possible to turn the coefficients and exponents into integers and leave the rest of the function as a string so that I can take the derivative?
If your equation can be arbitrarily complicated, you might want to use eval to parse it from Python syntax. Now, eval is generally unsafe to use on untrusted user input, but a user replying to an input() prompt is probably trusted (since they're usually sitting at the computer running the code).
So one option is:
function = eval(input("What is your function: "))
If placed just below the definition of x, it will allow that variable to be used in the equation. You could also provide a namespace for eval to use, rather than letting it use your function's local namespace (e.g. function = eval(input(...), {'x': Symbol('x', real=True)})).
If your problem space is more limited, and you only need to handle equations that are polynomials with integer coefficients, then you could write your own logic to input the equation in a much more simplified form. For instance, you could loop, asking for the coefficients in order (probably from the lowest exponent to the highest). Try something like this:
import itertools
from sympy import Symbol
x = Symbol('x', real = True)
function = 0
for exponent in itertools.count():
coef = input(f"Enter coefficient for x**{exponent} (or give an empty value to be done): ")
if not coef:
break
function += int(coef) * x**exponent
print("Polynomial so far:", function)
For the example equation in your question, you'd enter 8, 0, 7, 0, 1 and finally an empty input.
The better thing to do is to turn your string into a SymPy expression. As long as the user uses SymPy/python syntax, sympyify will convert it for you:
>>> from sympy import sympify, solve
>>> user='3*x**2-1'
>>> eq = sympify(user)
>>> x = eq.free_symbols.pop() # assuming there is 1 symbol
>>> solve(eq.diff(x))
[0]

Sympy cannot evaluate an infinite sum involving gamma functions

I am using Sympy to evaluate some symbolic sums that involve manipulations of the gamma functions but I noticed that in this case it's not evaluating the sum and keeps it unevaluated.
import sympy as sp
a = sp.Symbol('a',real=True)
b = sp.Symbol('b',real=True)
d = sp.Symbol('d',real=True)
c = sp.Symbol('c',integer=True)
z = sp.Symbol('z',complex=True)
t = sp.Symbol('t',complex=True)
sp.simplify(t-sp.summation((sp.exp(-d)*(d**c)/sp.gamma(c+1))/(z-c-a*t),(c,0,sp.oo)))
I then need to lambdify this expression, and unfortunately this becomes impossible to do.
With Matlab symbolic toolbox however I get the following answer:
Matlab
>> a=sym('a')
>> b=sym('b');
>> c=sym('c')
>> d=sym('d');
>> z=sym('z');
>> t=sym('t');
>> symsum((exp(-d)*(d^c)/factorial(c))/(z-c-a*t),c,0,inf)
ans =
(-d)^(z - a*t)*exp(-d)*(gamma(a*t - z) - igamma(a*t - z, -d))
The formula involves lower incomplete gamma functions, as expected.
Any idea why of this behaviour? I thought sympy was able to do this summation symbolically.
Running your code with SymPy 1.2 results in
d**(-a*t + z)*exp(-I*pi*a*t - d + I*pi*z)*lowergamma(a*t - z, d*exp_polar(I*pi)) + t
By the way, summation already attempts to evaluate the sum (and succeeds in case of SymPy 1.2), subsequent simplification is cosmetic. (And can sometimes be harmful).
The presence of exp_polar means that SymPy found it necessary to consider the points on the Riemann surface of logarithmic function instead of regular complex numbers. (Related bit of docs). The function lower_gamma is branched and so we must distinguish between "the value at -1, if we arrive to -1 from 1 going clockwise" from "the value at -1, if we arrive to -1 from 1 going counterclockwise". The former is exp_polar(-I*pi), the latter is exp_polar(I*pi).
All this is very interesting but not really helpful when you need concrete evaluation of the expression. We have to unpolarify this expression, and from what Matlab shows, simply replacing exp_polar with exp is a correct way to do so here.
rv = sp.simplify(t-sp.summation((sp.exp(-d)*(d**c)/sp.gamma(c+1))/(z-c-a*t),(c,0,sp.oo)))
rv = rv.subs(sp.exp_polar, sp.exp)
Result: d**(-a*t + z)*exp(-I*pi*a*t - d + I*pi*z)*lowergamma(a*t - z, -d) + t
There is still something to think about here, with complex numbers and so on. Is d positive or negative? What does raising it to the power -a*t+z mean, what branch of multivalued power function do we take? The same issues are present in Matlab output, where -d is raised to a power.
I recommend testing this with floating point input (direct summation of series vs evaluation of the SymPy expression for it), and adding assumptions on the sign of d if possible.

Associated Legendre Function

Hi I am writing Python code which returns the associated Legendre function.
Using numpy poly1d function on this part,
firstTerm = (np.poly1d([-1,0,1]))**(m/2.0) # HELP!
It yields an error since it can only be raised to integer.
Is there any other alternative where I can raise the desired function to power 1/2 and etc.?
The reason you can't raise your poly1d to half-integer power is that that would not be a polynomial, since it would contain square roots.
While in principle you could orthogonalize the functions yourself, or construct the functions from something like sympy.special.legendre, but your safest bet is symbolic math. And hey, we already have sympy.functions.special.polynomials.assoc_legendre! Since symbolic math is slow, you should probably use sympy.lambdify to turn each function into a numerical one:
import sympy as sym
x = sym.symbols('x')
n = 3
m = 1
legfun_sym = sym.functions.special.polynomials.assoc_legendre(n,m,x)
legfun_num = sym.lambdify(x,legfun_sym)
print(legfun_sym)
print(legfun_num)
x0 = 0.25
print(legfun_sym.evalf(subs={x:x0}) - legfun_num(x0))
This prints
-sqrt(-x**2 + 1)*(15*x**2/2 - 3/2)
<function <lambda> at 0x7f0a091976e0>
-1.11022302462516e-16
which seems to make sense (the first is the symbolic function at x, the second shows that lambdify indeed creates a lambda from the function, and the last one is the numerical difference of the two functions at the pseudorandom point x0 = 0.25, and is clearly zero within machine precision).

Categories