Define function from SymPy symbols - python

I am using SymPy for analytical handling of variables (e.g. matrix multiplication etc.).
After doing so, I end up with a new SymPy expression which I would like to use for plotting with Matplotlib. So far, my only way to do so, was to print the SymPy expression and paste it into a newly defined function manually.
How can I directly convert the SymPy expression into a function for numerical interpretation without relying on copying and pasting?
A minimal working example is the following:
import sympy as sp
import matplotlib.pyplot as plt
import numpy as np
sp.var('x A B') # x will later be the variable, A and B will be constants
temp = A*x+B # intermediate calculations
f = temp*x # function in reality, it will be more complicated.
print(f) # I printed f in order to find the expression and copy-pasting it into the function below.
def func(x,A,B):
return x*(A*x + B) # This statement was copy-pasted. This line should be changed, s.t. copy-pasting is not necessary!
#Now we can handle the function numerically for plotting (using matplotlib and numpy)
xspace = np.linspace(0,5)
plt.plot(xspace,func(x=xspace,A=1,B=2))

Sympy’s lambdify exists exactly for this. Just replace your definition of func with:
func = sp.lambdify((x,A,B),f)

Related

Why sympy can't calculate?

import sympy as sp
import matplotlib.pyplot as plt
# set
Cd = 0.25
g = 9.81
pf = 10**(-6) # Perturbation Fraction
t = 4
v = 36
xr = [int(input('initial guess : '))]
i = 0
Ea = 1
Es = 0.01
# 함수 정의
def f(m):
return sp.sqrt(g * m / Cd) * sp.tanh(sp.sqrt(g * Cd / m) * t) - v
# real root
x = sp.Symbol('x')
ans = sp.solve(f(x)) # sp.solve()로 해 구하기
print(ans)
i want to get real root of f(x).
but this code have some problem in line for # real root
i can't figure out
You should not expect sympy to do miracles. Beyond relatively simple symbolic manipulations, sympy just get stuck, sometimes even returning wrong answers. You have to turn to commercial tools such as Maple or Mathematica in order to crack tough nuts.
Your alternative in most practical cases is to use scipy and get a good numeric solution, which is what you want most of the time rather than a closed form solution.
Sympy is a symbolic math library, trying to find exact symbolic solutions. As such, it doesn't work well with floats, as they are necessarily imprecise.
If your equations are fully numeric, it is usually recommended to employ numeric libraries such as numpy and scipy. If you're already doing symbolic manipulations (e.g. calculating differentials), sympy provides nsolve which calls a numeric solver. As such, it also needs a seed to start its numeric search. In your case it would look like:
# ....
xr = 1
ans = sp.nsolve(f(x), xr)
Result: 142.737633108449
Sympy also has a way to convert a sympy function to numpy format (in numpy things work much faster, but there are no symbolic expressions). sp.lambdify(x, f(x)) creates such a numpy function. Here is how it would look like with your example:
import matplotlib.pyplot as plt
import numpy as np
f_np = sp.lambdify(x, f(x))
xi = np.linspace(1, 1000, 2000)
plt.plot(xi, f_np(xi))
In an interactive environment, you can add a question mark to display the numpy source of the function:
>>> f_np?
Signature: f_np(x)
Docstring:
Created with lambdify. Signature:
func(x)
Expression:
6.26418390534633*sqrt(x)*tanh(6.26418390534633*sqrt(1/x)) - 36
Source code:
def _lambdifygenerated(x):
return (6.26418390534633*sqrt(x)*tanh(6.26418390534633*sqrt(x**(-1.0))) - 36)
If you look at your expression for f(x) you will see that it is highly non-linear (as also shown in the plot that JoahnC showed you):
6.26418390534633*sqrt(x)*tanh(6.26418390534633*sqrt(1/x)) - 36
SymPy cannot give an analytical solution for something that has no such solution. It can, however, give numerical approximations for univariate expressions. That's what nsolve is for. It needs an initial guess for the solution (as you anticipated by asking for xr).
>>> sp.nsolve(f(x), 100)
142.737633108449

python: Plotting and optimizing the same function

Lets say I have the following function:
def f(x):
return log(3*exp(3*x) + 7*exp(7*x))
I want to do two things:
1) plot the function over a range of x-values
2) find the root of the function using the Newton method from scipy
My problem is that it seems that plotting is best done with a numpy array x=np.linspace(-2,2,1000), but then evaluating the function results in erros TypeError: only size-1 arrays can be converted to Python scalars. I can fix this by simply changing log and exp to np.log and np.exp, respectively.
But doing so then makes scipy.optimize.newton unhappy.
It seems like I need to define the function twice, once for use in plotting (with np. ...) and once for optimizing in the form given above.
I can't imagine that this is actually the case. Any hints would be greatly appreciated.
Seems legit, you just need to use numpy functions instead of base math functions:
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
%matplotlib inline
def f(x):
return np.log(3*np.exp(3*x) + 7*np.exp(7*x))
x = np.linspace(-2,2,1000)
y = f(x)
plt.scatter(x, y)
optimize.root(f, 1)

How to use Numba to perform multiple integration in SciPy with an arbitrary number of variables and parameters?

I'd like to use Numba to decorate the integrand of a multiple integral so that it can be called by SciPy's Nquad function as a LowLevelCallable. Ideally, the decorator should allow for an arbitrary number of variables, and an arbitrary number of additional parameters from the Nquad's args argument. This is built off an excellent Q&A from earlier this year, but extended to the case of multiple variables and parameters.
As an example, suppose the following multiple integral with N variables and K parameters:
The following code works, but only for two variables and two parameters (N=2,K=2). It does not work for the more general case. This is because some of the arguments in the decorator are manually enumerated (xx[0],xx[1],xx[2],xx[3] inside the wrapped function). The decorator would have to be edited for every different number of variables or parameters. I'd like to avoid that, if possible. Note that the integrand function itself takes advantage of Numpy objects and methods and so does not have this problem.
import numpy as np
import scipy.integrate as si
import numba
from numba import cfunc,carray
from numba.types import intc, CPointer, float64
from scipy import LowLevelCallable
def jit_integrand_function(integrand_function):
jitted_function = numba.jit(integrand_function, nopython=True)
#cfunc(float64(intc, CPointer(float64)))
def wrapped(n, xx):
return jitted_function(xx[0], xx[1], xx[2], xx[3])
#xx = carray(xx,len(xx))
#return jitted_function(xx)
return LowLevelCallable(wrapped.ctypes)
#jit_integrand_function
def integrand(*args):
d = np.array([args])
return -np.exp(d.prod())
#Two variable, two parameter example
parms = np.array([2,3])
print si.nquad(integrand,[[0,1],[0,1]],parms)
The ideal code would use just one decorator on the integrand function to also run:
#Three variable, three parameter example
parms2 = np.array([1,2,3])
print si.nquad(integrand,[[0,1],[0,1],[0,1]],parms2)
The Numba documents refer to a carray function that ought to return a Numpy array when given the low-level pointer and size of an array in the callback. Possibly, this could be used to generalize the code beyond the two-variable-two-parameter case. My (unsuccessful) attempt to implement this is in the two commented-out lines of code.
Help would be appreciated. Indeed, one of the Numba developers pointed out that SciPy integration was one of the reasons Numba was written, but that documentation and examples in this area are lacking.
The following code works:
import numpy as np
import scipy.integrate as si
import numba
from numba import cfunc,carray
from numba.types import intc, CPointer, float64
from scipy import LowLevelCallable
def jit_integrand_function(integrand_function):
jitted_function = numba.jit(integrand_function, nopython=True)
#cfunc(float64(intc, CPointer(float64)))
def wrapped(n, xx):
values = carray(xx,n)
return jitted_function(values)
return LowLevelCallable(wrapped.ctypes)
#jit_integrand_function
def integrand(args):
return -np.exp(args.prod())
#Two variable, two parameter example
parms = np.array([2,3])
print si.nquad(integrand,[[0,1],[0,1]],parms)
#Three variable, three parameter example
parms2 = np.array([1,2,3])
print si.nquad(integrand,[[0,1],[0,1],[0,1]],parms2)

Sympy: using a symbolic expression as a numerical integrand

I need to manipulate a function symbolically, and then numerically integrate the function. How do I correctly use my expression f in the integrand function. How do I use lambdify correctly if that is even the sensible way to do it? Many thanks.
from sympy import *
import scipy.integrate as integrate
r = symbols('r') #define symbol
f = diff(r*r) #carry out symbolic manipulation
def integrand(x): #define function to integrate
return lambdify(x, f) #swap variable x into f
result = integrate.quad(integrand, 0, 5) #integrate numerically
print(result)
lambdify returns a function object, there is no need to use a wrapper function. Also note that the first argument of lambdify should be a tuple of variables representing sympy symbols (in this case, r) that are included in the sympy expression (in this case, f_sym) provided as its second argument.
import sympy as sp
from scipy.integrate import quad
r = sp.symbols('r')
f_sym = sp.diff(r*r, r)
f_lam = sp.lambdify(r, f_sym)
result = quad(f_lam, 0, 5)
print(result)
(25.0, 2.7755575615628914e-13)

Python Numpy - Complex Numbers - Is there a function for Polar to Rectangular conversion?

Is there a built-in Numpy function to convert a complex number in polar form, a magnitude and an angle (degrees) to one in real and imaginary components?
Clearly I could write my own but it seems like the type of thing for which there is an optimised version included in some module?
More specifically, I have an array of magnitudes and an array of angles:
>>> a
array([1, 1, 1, 1, 1])
>>> b
array([120, 121, 120, 120, 121])
And what I would like is:
>>> c
[(-0.5+0.8660254038j),(-0.515038074+0.8571673007j),(-0.5+0.8660254038j),(-0.5+0.8660254038j),(-0.515038074+0.8571673007j)]
There isn't a function to do exactly what you want, but there is angle, which does the hardest part. So, for example, one could define two functions:
def P2R(radii, angles):
return radii * exp(1j*angles)
def R2P(x):
return abs(x), angle(x)
These functions are using radians for input and output, and for degrees, one would need to do the conversion to radians in both functions.
In the numpy reference there's a section on handling complex numbers, and this is where the function you're looking for would be listed (so since they're not there, I don't think they exist within numpy).
There's an error in the previous answer that uses numpy.vectorize - cmath.rect is not a module that can be imported. Numpy also provides the deg2rad function that provides a cleaner piece of code for the angle conversion. Another version of that code could be:
import numpy as np
from cmath import rect
nprect = np.vectorize(rect)
c = nprect(a, np.deg2rad(b))
The code uses numpy's vectorize function to return a numpy style version of the standard library's cmath.rect function that can be applied element wise across numpy arrays.
I used cmath with itertools:
from cmath import rect,pi
from itertools import imap
b = b*pi/180 # convert from deg to rad
c = [x for x in imap(rect,a,b)]
import numpy as np
import cmath.rect
nprect = np.vectorize(rect)
c = nprect(a,b*np.pi/180)
tom10 answer works fine... you can also expand the Euler's formula to:
def P2R(A, phi):
return A * ( np.cos(phi) + np.sin(phi)*1j )

Categories

Resources