I want to take the derivative of a multivariable function using SymPy and then for a) the symbolic result to be printed and then b) the result of the derivative at a point to be printed. I'm using the following code
import math as m
import numpy
import scipy
#define constants
lambdasq = 0.09
Ca = 3
qOsq = 2
def f1(a,b,NN,ktsq,x):
return NN*x**(-a)*ktsq**b*m.exp(m.sqrt(16*Ca/9*m.log(1/x)*m.log((m.log(ktsq/lambdasq))/m.log(qOsq/lambdasq))))
from sympy import *
x = symbols('x')
def f2(NN,a,b,x,ktsq):
return -x*diff(m.log(f1),x)
This runs but I can't find a way to get the symbolic result to be printed and when I try to evaluate at a point, say e.g adding in print(f2(0.3,0.1,-0.2,0.1,3)) I get an error
TypeError: must be real number, not function
When I replace f1 with its symbolic representation, I get instead the error
ValueError:
Can't calculate 1st derivative wrt 0.100000000000000.
So I can summarise my question as follows
a) How to print out a symbolic derivative and its value at a point when I call diff(m.log(f1),x) (i.e without having to replace f1 by its actual representation)
b) If I have to use the symbolic representation in the differentiation (i.e use diff(m.log(NN*x**(-a)*ktsq**b*m.exp(m.sqrt(16*Ca/9*m.log(1/x)*m.log((m.log(ktsq\
/lambdasq))/m.log(qOsq/lambdasq))))),x) then how to print out the symbolic derivative and its value at a point?
New to Python so hopefully there is a relatively simple fix.
Thanks!
I'm posting this answer since this thread is #1 on my search engine when searching for 'simpy multivariate differentiation' and might help someone.
example 1
import sympy as sp
def f(u):
return (u[0]**2 + u[1]**10 + u[2] - 4)**2
u = sp.IndexedBase('u')
print(sp.diff(f(u), u[0]))
outputs
4*(u[0]**2 + u[1]**10 + u[2] - 4)*u[0]
This is the derivative of f(u) wrt u[0]
example 2
if we want the whole jacobian, we can do:
for i in range(3):
print(sp.diff(f(u), u[i]))
which outputs
4*(u[0]**2 + u[1]**10 + u[2] - 4)*u[0]
20*(u[0]**2 + u[1]**10 + u[2] - 4)*u[1]**9
2*u[0]**2 + 2*u[1]**10 + 2*u[2] - 8
we can define a temp function and copy paste these lines
def temp(u):
return np.array([
4*(u[0]**2 + u[1]**10 + u[2] - 4)*u[0],
20*(u[0]**2 + u[1]**10 + u[2] - 4)*u[1]**9,
2*u[0]**2 + 2*u[1]**10 + 2*u[2] - 8,
])
temp([1., 1., 1.])
this outputs array([ -4., -20., -2.])
and to verify
from autograd import grad
gradient = grad(f)
gradient([1., 1., 1.])
this outputs: [array(-4.), array(-20.), array(-2.)]
Note:This is just a simple showcase how you can do multivariate derivatives in sympy. I hope I can help someone with this
First, math functions are numeric, they cannot work with SymPy's symbols. Use the corresponding functions from SymPy (exp, log, sqrt) which you already imported with from sympy import *:
def f1(a, b, NN, ktsq, x):
return NN*x**(-a)*ktsq**b*exp(sqrt(16*Ca/9*log(1/x)*log((log(ktsq/lambdasq))/log(qOsq/lambdasq))))
Second, within f2 you are trying to differentiate f1. But f1 is a callable Python function, not a SymPy expression. You need to pass in some arguments to get a SymPy expression, which can then be differentiated.
def f2(NN, a, b, x0, ktsq):
return (-x*diff(log(f1(a, b, NN, ktsq, x)), x)).subs(x, x0)
Here the numeric arguments, except the value x0, are passed to f1, resulting in a SymPy expression containing x. That is a thing to be differentiated. After that, the numeric value x0 is substituted for x.
print(f2(0.3,0.1,-0.2,0.1,3)) # 0.366748952743614
A take-away point is that SymPy differentiates expressions, not functions. There is no concept of f' in SymPy, only f'(x).
Related
I have a function [ -4*x/sqrt(1 - (1 - 2*x^2)^2) + 2/sqrt(1 - x^2) ] that I need to evaluate at x=0. However, whenever you graph this function, for some interval of y there are many y-values at x=0. This leads me to think that the (subs) command can only return one y-value. Any help or elaboration on this? Thank you!
Here's my code if it might help:
x = symbols('x')
f = 2*asin(x) # f(x) function
g = acos(1-2*x**2) # g(x) function
eq = diff(f-g) # evaluating the derivative of f(x) - g(x)
eq.subs(x, 0) # substituting 0 for x in the derivative of f(x) - g(x)
After I run the code, it returns NaN, which I assume is because substituting in 0 for x returns not a single number, but a range of numbers.
Here is the graph of the function to be evaluated at x=0:
You should always give SymPy as many assumptions as possible. For example, it can't pull an x**2 out of a sqrt because it thinks x is complex.
A factorization an then a simplification solves the problem. SymPy can't do L'Hopital on eq = A + B since it does not know that both A and B converge. So you have to guide it a little by bringing the fractions together and then simplifying:
from sympy import *
x = symbols('x', real=True)
f = 2*asin(x) # f(x) function
g = acos(1-2*x**2) # g(x) function
eq = diff(f-g) # evaluating the derivative of f(x) - g(x)
eq = simplify(factor(eq))
print(eq)
print(limit(eq, x, 0, "+"))
print(limit(eq, x, 0, "-"))
Outputs:
(-2*x + 2*Abs(x))/(sqrt(1 - x**2)*Abs(x))
0
4
simplify, factor and expand do wonders.
I have been trying to locate a method similar to Excel's Solver where I can target a specific value for a function to converge on. I do not want a minimum or maximum optimization.
For example, if my function is:
f(x) = A^2 + cos(B) - sqrt(C)
I want f(x) = 1.86, is there a Python method that can iterate a solution for A, B, and C to get as close to 1.86 as possible? (given an acceptable error to target value?)
You need a root finding algorithm for your problem. Only a small transformation required. Find roots for g(x):
g(x) = A^2 + cos(B) - sqrt(C) - 1.86
Using scipy.optimize.root, Refer documentation:
import numpy as np
from scipy import optimize
# extra two 0's as dummy equations as root solves a system of equations
# rather than single multivariate equation
def func(x): # A,B,C represented by x ndarray
return [np.square(x[0]) + np.cos(x[1]) - np.sqrt(x[2]) - 1.86, 0, 0]
result = optimize.root(func , x0 = [0.1,0.1,0.1])
x = result.x
A, B, C = x
x
# array([ 1.09328544, -0.37977694, 0.06970678])
you can now check your solution:
np.square(x[0]) + np.cos(x[1]) - np.sqrt(x[2])
# 1.8600000000000005
So pretty much, I am aiming to achieve a function f(x)
My problem is that my function has an integral in it, and I only know how to construct definite integrals, so my question is how does one create an indefinite integral in a function (or there may be some other method I am currently unaware of)
My function is defined as :
(G is gravitational constant, although you can leave G out of your answer for simplicity, I'll add it in my code)
Here is the starting point, but I don't know how to do the integral portion
import numpy as np
def f(x):
rho = 5*(1/(1+((x**2)/(3**2))))
function_result = rho * 4 * np.pi * x**2
return function_result
Please let me know if I need to elaborate on something.
EDIT-----------------------------------------------------
I made some major progress, but I still have one little error.
Pretty much, I did this:
from sympy import *
x = Symbol('x')
rho = p0()*(1/(1+((x**2)/(rc()**2))))* 4 * np.pi * x**2
fooply = integrate(rho,x)
def f(rx):
function_result = fooply.subs({x:rx})
return function_result
Which works fine when I plug in one number for f; however, when I plug in an array (as I need to later), I get the error:
raise SympifyError(a)
sympy.core.sympify.SympifyError: SympifyError: [3, 3, 3, 3, 3]
(Here, I did print(f([3,3,3,3,3]))). Usually, the function returns an array of values. So if I did f([3,2]) it should return [f(3),f(2)]. Yet, for some reason, it doesn't for my function....
Thanks in advance
how about:
from sympy import *
x, p0, rc = symbols('x p0 rc', real=True, positive=True)
rho = p0*(1/(1+((x**2)/(rc))))* 4 * pi * x**2
fooply = integrate(rho,x)/x
rho, fooply
(4*pi*p0*x**2/(1 + x**2/rc),
4*pi*p0*rc*(-sqrt(rc)*atan(x/sqrt(rc)) + x)/x)
fooply = fooply.subs({p0: 2.0, rc: 3.0})
np_fooply = lambdify(x, fooply, 'numpy')
print(np_fooply(np.array([3,3,3,3,3])))
[ 29.81247362 29.81247362 29.81247362 29.81247362 29.81247362]
To plug in an array to a SymPy expression, you need to use lambdify to convert it to a NumPy function (f = lambdify(x, fooply)). Just using def and subs as you have done will not work.
Also, in general, when using symbolic computations, it's better to use sympy.pi instead of np.pi, as the former is symbolic and can simplify. It will automatically be converted to the numeric pi by lambdify.
On another thread, I saw someone manage to integrate the length of a arc using mathematica.They wrote:
In[1]:= ArcTan[3.05*Tan[5Pi/18]/2.23]
Out[1]= 1.02051
In[2]:= x=3.05 Cos[t];
In[3]:= y=2.23 Sin[t];
In[4]:= NIntegrate[Sqrt[D[x,t]^2+D[y,t]^2],{t,0,1.02051}]
Out[4]= 2.53143
How exactly could this be transferred to python using the imports of numpy and scipy? In particular, I am stuck on line 4 in his code with the "NIntegrate" function. Thanks for the help!
Also, if I already have the arc length and the vertical axis length, how would I be able to reverse the program to spit out the original paremeters from the known values? Thanks!
To my knowledge scipy cannot perform symbolic computations (such as symbolic differentiation). You may want to have a look at http://www.sympy.org for a symbolic computation package. Therefore, in the example below, I compute derivatives analytically (the Dx(t) and Dy(t) functions).
>>> from scipy.integrate import quad
>>> import numpy as np
>>> Dx = lambda t: -3.05 * np.sin(t)
>>> Dy = lambda t: 2.23 * np.cos(t)
>>> quad(lambda t: np.sqrt(Dx(t)**2 + Dy(t)**2), 0, 1.02051)
(2.531432761012828, 2.810454936566873e-14)
EDIT: Second part of the question - inverting the problem
From the fact that you know the value of the integral (arc) you can now solve for one of the parameters that determine the arc (semi-axes, angle, etc.) Let's assume you want to solve for the angle. Then you can use one of the non-linear solvers in scipy, to revert the equation quad(theta) - arcval == 0. You can do it like this:
>>> from scipy.integrate import quad
>>> from scipy.optimize import broyden1
>>> import numpy as np
>>> a = 3.05
>>> b = 2.23
>>> Dx = lambda t: -a * np.sin(t)
>>> Dy = lambda t: b * np.cos(t)
>>> arc = lambda theta: quad(lambda t: np.sqrt(Dx(t)**2 + Dy(t)**2), 0, np.arctan((a / b) * np.tan(np.deg2rad(theta))))[0]
>>> invert = lambda arcval: float(broyden1(lambda x: arc(x) - arcval, np.rad2deg(arcval / np.sqrt((a**2 + b**2) / 2.0))))
Then:
>>> arc(50)
2.531419526553662
>>> invert(arc(50))
50.000031008458365
If you prefer a pure numerical approach, you could use the following barebones solution. This worked well for me given that I had two input numpy.ndarrays, x and y with no functional form available.
import numpy as np
def arclength(x, y, a, b):
"""
Computes the arclength of the given curve
defined by (x0, y0), (x1, y1) ... (xn, yn)
over the provided bounds, `a` and `b`.
Parameters
----------
x: numpy.ndarray
The array of x values
y: numpy.ndarray
The array of y values corresponding to each value of x
a: int
The lower limit to integrate from
b: int
The upper limit to integrate to
Returns
-------
numpy.float64
The arclength of the curve
"""
bounds = (x >= a) & (y <= b)
return np.trapz(
np.sqrt(
1 + np.gradient(y[bounds], x[bounds])
) ** 2),
x[bounds]
)
Note: I spaced the return variables out that way just to make it more readable and clear to understand the operations taking place.
As an aside, recall that the arc-length of a curve is given by:
I am dealing with a set of several non-linear equations that I was able to reduce analytically to set of two implicit equations with two variables. Now I wish to find roots of those equations using Brent's method. I would like to pass one function as an argument to another and solve the equation for variable 2 depending on every variable 1.
In mathematical terms I wish to solve: f(x,y) and g(x,y) in this way f(x,g(x)).
Simplify example of what I wish to do can be presented here.
Instead of:
import scipy.optimize
from scipy.optimize import fsolve
def equations(p):
y,z = p
f1 = -10*z + 4*y*z - 5*y + 4*z**2 - 7
f2 = 2*y*z + 5*y - 3
return (f1,f2)
and solve it by:
y, z = fsolve(equations,[0,19])
I wish to write something like that:
def func2(x, y):
f2= 2*y*x + 5*y - 3
return brentq(f2, -5, 5)
def func(x,y):
y = func2(x,y)
return -10*x + 4*x*y - 5*y + 4*x**2 - 7
sol, = brentq(lambda x: func(x, func2), -5, 5)
I wish to ask for help in how to pass a function as an argument for this particular purpose and explain what am I doing wrong. I am new to Python and maybe there is a better way to ensure precise solution to my problem.