Double integral with variable boundaries in python Scipy + sympy (?) - python

The full mathematical problem is here.
Briefly I want to integrate a function with a double integral. The inner integral has boundaries 20 and x-2, while the outer has boundaries 22 and 30.
I know that with Scipy I can compute the double integral with scipy.integrate.nquad. I would like to do something like this:
def f(x, y):
return (x ** 2 + y ** 2)
res = sp.integrate.nquad(f, [[22, 30], [20, x-2]])
Is it possible? Maybe using also sympy?

I solved with sympy:
from sympy import *
x, y = symbols("x y")
f = (x ** 2 + y ** 2)
res = integrate(f, (y, 20, x-2), (x, 22, 30))
Basically sympy.integrate is able to deal with multiple integrations, also with variable boundaries.

If you need the numerical integration and sympy is not an option. Then you could try something like the following.
For this example it seems quick, but I have a suspicion you may run into problems in general, see how well it does for your use case.Perhaps this possibly imperfect answer will prompt someone to submit something better.
I use the fact that we can do the integrations one after the other, integrating out the y first, to get a function of x, then integrating that.
from scipy.integrate import quad
def integrand(x, y):
return (x ** 2 + y ** 2)
def y_integral(x):
# Note scipy will pass args as the second argument
# we can fiddle it to work correctly, but by symmetry we don't need to here.
return quad(integrand, 20, x-2, args=(x))[0]
We then use this y_integral function as the result function of the inner integral.
res = quad(y_integral, 22, 30)
print res
You could wrap this in a function if you use it regularly.

Related

Find zeroes of function with simpy.optimize.bisect, with a complex function

I'm a student of mechanical engineering, and this is the first year I've met with the Python environment, or the distribution of it Anaconda.
I was given a task to find the zeroes of this function:
𝐷⋅sin(𝛼)cos(𝛼)+𝑙⋅cos(𝛼)sin(𝛼)2βˆ’π‘™β‹…cos(𝛼)βˆ’β„Žβ‹…sin(𝛼)=0
With the parameters:
D = 220mm,
h = 1040mm,
l = 1420mm,where
n = 81
is the number of equally distanced points on the function
and the function is limited to :
π›Όβˆˆ[0,2πœ‹] where 𝛼 is a np.array.
plotted function
The issue is, when I try to insert the function in bisect(fun, a, b), the error says
'numpy.ndarray' object is not callable
Can someone aid a noob programer ? Thanks.
The question is not clear, you should share your code and the title should say scipy, not simpy, if I am correct.
Apart from this, I do not get the same plot of the function, can you check if it is correct?
If you want to use the bisection method you should do something like this:
import numpy as np
from scipy.optimize import bisect
def fun(x, D, h, l):
return D * np.sin(x) * np.cos(x) + l * np.cos(x) * np.sin(x) * 2 - l * np.cos(x) - h * np.sin(x)
D = 220
h = 1040
l = 1420
print(bisect(lambda x: fun(x, D, h, l), 0, 2*np.pi))
Note that the bisection method only finds one zero, and this does not work at all because the two extremes of the function have the same sign. For this particular function, you could run the bisect in the intervals (0, pi) and (pi, 2pi) to find both zeros.

How to put an integral in a function in python/matplotlib

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.

How to Integrate Arc Lengths using python, numpy, and scipy?

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:

How to find root of two implicit functions in Python, without using fsolve for the set of equations

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.

solve an n-dimensional optimisation problem using iminuit

I woul like to solve an n-dimensional optimisation problem using iminuit.
So my approach is the following.
I am trying to figure out how to extend this:
def f(x,y,z):
return (x-1.)**2 + (y-2*x)**2 + (z-3.*x)**2 -1.
to a variable "x" that is a numpy.array.
I would like to do something like this:
x = [1,2,3,4,5]
y = [2,4,6,8,10]# y=2x
class StraightLineChi2:
def __init__(self,x,y):
self.x = x
self.y = y
def __call__(self,m,c): #lets try to find slope and intercept
chi2 = sum((y - m*x+c)**2 for x,y in zip(self.x,self.y))
return chi2
but in my case x is my unknown, and it is an array. Like in many optimization/minimization problems, the function is a f=f(x1,...,xn) where n can be big. x1,...,xn are the unknowns of the problem.
(These examples are taken from here)
Something similar is achieved "hacking" pyminuit2, like described here
For your example I recommend you using iminuit and probfit. Having an argument as a list of parameter is not exactly what you want to do since you will get confused which parameter is what very soon.
Here is an example taken straight from probfit tutorial. Also see the documentation
import iminuit
import probfit
x = np.linspace(0, 10, 20)
y = 3 * x + 15 + np.random.randn(len(x))
err = np.ones(len(x))
def line(x, m, c): # define it to be parabolic or whatever you like
return m * x + c
chi2 = probfit.Chi2Regression(line, x, y, err)
minuit = iminuit.Minuit(chi2)
minuit.migrad();
print(minuit.values) #{'c': 16.137947520534624, 'm': 2.8862774144823855}

Categories