I have a function that returns a numpy array as follows:
import numpy as np
from scipy.misc import derivative
import sympy as sp
x, y = sp.symbols('x y')
f = x**2 + y **2
def grad(f):
exp = sp.expand(f)
dfdx = sp.diff(exp,x)
dfdy = sp.diff(exp,y)
global grad
Df = np.array([dfdx,dfdy])
return Df
I'm using the variable Df in another function and do some computations with it.
As you may have guessed, the results come out including x and y. However, I need the results to be evaluated each time with the initial values I choose for x and y instead of the symbols.
I was wondering if there was something like the .subs() in sympy but works on a numpy array rather than a function????
Sympy and numpy are two separate worlds, that aren't easy to bring together.
With sympy's lambdify, sympy expressions can be made to work on numpy arguments. When arrays are used as arguments, they all need to be 1D and of the same size. The function np_grad_1 below is how it works standard. It returns an array with two subarrays.
To get your desired functionality, a wrapper can take a 2D numpy input and convert the result back to a 2D numpy array:
import sympy as sp
import numpy as np
x, y = sp.symbols('x y')
f = x ** 2 + y ** 2
def grad(f, x, y):
exp = sp.expand(f)
dfdx = sp.diff(exp, x)
dfdy = sp.diff(exp, y)
return [dfdx, dfdy]
np_grad_1 = sp.lambdify([x, y], grad(f, x, y))
np_grad_2 = lambda points: np.array(np_grad_1(points[:, 0], points[:, 1])).T
points = np.random.uniform(-1, 1, (5, 2))
np_grad_1(points[:, 0], points[:, 1]) # returns an array with 2 subarrays
np_grad_2(points) # returns an Nx2 array
Related
My goal is to plot the function $f(x,y) = 3^{\min\{x,2\}}t_0 y$
where $t_0$
is the smallest solution of $2t^x-3t-1=0$ (it's just a MWE; I didn't check the existence of the root(s). )
I did it. But in somehow an ugly way.
My approach is to apply the function element-wise. Here is my code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
def equ(t,*data): # 2t^x - 3t -1
x = data
return 2*(t ** x) - 3*t - 1
def t0(t): # call fslove to solve the equation
return fsolve(equ, 1.5, args=(t))[0] # 1.5 is randomly choosed
xx = np.linspace(1,3,50) # range of x
yy = np.linspace(1,3,50) # range of y
ff = [] # the value of f at each (x, y)
# compute the value of f element-wise
for x in xx:
for y in yy:
f = (3 ** np.minimum(x, 2)) * t0(x) * y
ff.append(f)
fv = np.split(np.array(ff), np.size(xx)) # split ff to make it conform with meshgrid
xv, yv = np.meshgrid(xx, yy)
plt.pcolormesh(xv, yv, fv)
plt.colorbar()
plt.show()
But I think the elegancy of numpy is to avoid writing loops manually and to manipulate lists as a whole. So, is there a more elegant way to do that?
I am solving an ODE with Sympy. The equation is
ODE
To solve it, I used this little code, which returns this result.
from sympy import *
from numpy import *
import matplotlib.pyplot as plt
x = symbols('x')
y = Function('y')
f = y(x)
print(f)
edo = Eq(f.diff()+3*x**2*f, 6*x**2)
print(edo)
edoSolve = dsolve(edo, f)
print(edoSolve)
C1*exp(-x**3) + 2
My question is, how can I plot the result with x being a range from 0 to 10?
Firstly it's problematic to combine these two lines:
from sympy import *
from numpy import *
These two libraries define many functions with the same names and mixing those together will lead to problems. For clarity it is better to do something like:
import sympy as sym
import numpy as np
You can only plot a sympy expression if you give numbers for all of the symbols apart from the one that you want to plot against (i.e. x in this example). That means that you need to have a concrete value for the integration constant C1. You can get that by giving an initial conditions (ics) argument to dsolve. Also since dsolve returns an equation you need to choose a side of the equation as the expression that you want to plot. Having done that the sym.plot function will do precisely what you ask for:
In [10]: import sympy as sym
In [11]: sol = sym.dsolve(edo, f, ics={f.subs(x, 0): 1})
In [12]: sol
Out[12]:
3
-x
y(x) = 2 - ℯ
In [13]: sym.plot(sol.rhs, (x, 0, 10))
Out[13]: <sympy.plotting.plot.Plot at 0x7f346de1caf0>
If you want to show solutions for multiple values for C1 together, you could append plots:
from sympy import symbols, Function, Eq, dsolve, plot
x = symbols('x')
y = Function('y')
f = y(x)
edo = Eq(f.diff() + 3 * x ** 2 * f, 6 * x ** 2)
edoSolve = dsolve(edo, f)
plot1 = plot(show=False)
for c1 in range(-5, 6):
plotc1 = plot(edoSolve.subs('C1', c1).rhs, (x, 0, 10), show=False)
plot1.append(plotc1[0])
plot1.show()
I am trying to obtain a function of x(y) instead of y(x), but the function itself in terms of x is an implicit function. Is there a way that I can return the x value base on a range of y? Or is it possible to extrapolate a set of x,y values at x much greater than 10^7?
edit1: changed the x and y to make it more sensible
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
df = .1
a = 300
y = np.arange(1, a,df)
def x (y):
try:
x = y*(a-1)**(3/2)/(a-y)**(3/2)
return x
except ZeroDivisionError:
return 0
plt.loglog(x(y),y,'b')
Follow up:
Turns out interp1d can also be used for extrapolation
from scipy import interpolate
inte = interpolate.interp1d(x(y),y, fill_value='extrapolate')
ynew = np.arange(299,300,1e-5)
xnew = f(ynew)
plt.loglog(x(y),y,xnew,ynew)
I would like to be able to apply a generic function on either scalars numpy 1-D arrays, o numpy 2-D arrays.
The example in point is
def stress2d_lefm_cyl(KI, r, qdeg) :
"""Compute stresses in Mode I around a 2D crack, according to LEFM
q should be input in degrees"""
sfactor = KI / sqrt(2*pi*r)
q = radians(qdeg)
q12 = q/2; q32 = 3*q/2;
sq12 = sin(q12); cq12 = cos(q12);
sq32 = sin(q32); cq32 = cos(q32);
af11 = cq12 * (1 - sq12*sq32); af22 = cq12 * (1 + sq12*sq32);
af12 = cq12 * sq12 * cq32
return sfactor * np.array([af11, af22, af12])
def stress2d_lefm_rect(KI, x, y) :
"""Compute stresses in Mode I around a 2D crack, according to LEFM
"""
r = sqrt(x**2+y**2) <-- Error line
q = atan2(y, x)
return stress2d_lefm_cyl(KI, r, degrees(q))
delta = 0.5
x = np.arange(-10.0, 10.01, delta)
y = np.arange(0.0, 10.01, delta)
X, Y = np.meshgrid(x, y)
KI = 1
# I want to pass a scalar KI, and either scalar, 1D, or 2D arrays for X,Y (of the same shape, of course)
Z = stress2d_lefm_rect(KI, X, Y)
TypeError: only size-1 arrays can be converted to Python scalars
(I mean to use this for a contour plot).
If I now change to
def stress2d_lefm_rect(KI, x, y) :
"""Compute stresses in Mode I around a 2D crack, according to LEFM
"""
r = lambda x,y: x**2 + y**2 <-- Now this works
q = lambda x,y: atan2(y, x) <-- Error line
return stress2d_lefm_cyl(KI, r(x,y), degrees(q(x,y)))
Z = stress2d_lefm_rect(KI, X, Y)
TypeError: only size-1 arrays can be converted to Python scalars
which boils down to
x = np.array([1.0, 2, 3, 4, 5])
h = lambda x,y: atan2(y,x) <-- Error
print(h(0,1)) <-- Works
print(h(x, x)) <-- Error
1.5707963267948966
TypeError: only size-1 arrays can be converted to Python scalars
A "similar" question was posted, Most efficient way to map function over numpy array
The differences are:
1. I have to (or possibly more) arguments (x,y), which should have the same shape.
2. I am combining also with a scalar argument (KI).
3. atan2 seems to be less "tolerant" than **2. I mean to work with a generic function.
4. I am chaining two functions.
Can this be worked out?
Perhaps point 2 can be overcome by multiplying the result somewhere else.
You should use numpy to apply your function to every element of an array.
Ex :
import numpy as np
np.sqrt(np.square(x) + np.square(y))
I want to study symbolic functions in python. I want to create y(x) = x^2 + 2x + 3 and plot it in the range [1, 255]. I want to use the subs() function to calculate the values by using the for loop. However, when I run that I get this error:
IndexError('list index out of range')
Can you help me please?
import numpy as np
import matplotlib.pyplot as plot
from sympy import *
a = [1,2,3]
x = Symbol('x')
fx = a[0]*x**2 + a[1]*x + a[2]
t = list(range(1,256))
y = np.zeros(256)
for i in t:
y[i] = fx.subs({x:t[i]})
plot.plot(t,y)
plot.show()
Just replace with the following lines:
y = np.zeros(len(t))
for i in range(len(t)):
y[i] = fx.subs({x:t[i]})
The problem was that the length of t was only 255 but the len of y was 256 in your code because you define y = np.zeros(256), hence the Index Error because there is no t[256]. I am using y = np.zeros(len(t)) because you have as many y points as t (or x) points. By the way, you are most likely to get an error in your plot command the way it is right now because you have called import matplotlib.pyplot as plot. I would simply call it plt instead of plot
Output