Is it possible to compute double integral using scipy.integrate.fixed_quad? - python

I am trying to compute a double integral (over a triangle with nodes at (0,0), (0,1), (1,0)) using Gaussian quadrature of order n. However, running
import scipy.integrate as integrate
f = lambda x,y: x+y
inside = lambda x: integrate.fixed_quad(f, 0, 1-x, args=(x,), n=5)[0]
outside = integrate.fixed_quad(inside, 0, 1, n=5)
gives
Traceback (most recent call last):
File "", line 1, in
File "/Users/username/anaconda/lib/python3.5/site-packages/scipy/integrate/quadrature.py", line 82, in fixed_quad
return (b-a)/2.0 * np.sum(w*func(y, *args), axis=0), None
File "", line 1, in
File "/Users/username/anaconda/lib/python3.5/site-packages/scipy/integrate/quadrature.py", line 78, in fixed_quad
if np.isinf(a) or np.isinf(b):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
This is the second part of the question Can scipy.integrate.fixed_quad compute integral with functional boundaries?.

The answer to your question is, yes, under certain conditions.
For demonstration purposes, I first choose different bounds than you (11 instead of 1 - x).
Generally, one can solve these types of integrals using dblquad:
area_dblquad = integrate.dblquad(lambda x, y: x + y, 0, 1, lambda x: 0, lambda x: 11)[0]
which here returns 66. That is not an option as you mentioned in the comments.
One can now do this integration stepwise and it works fine for quad as well as fixed_quad:
def integrand(x, y):
return x + y
def fint_quad(x):
return integrate.quad(integrand, 0, 11, args=(x, ))[0]
def fint_fixed_quad(x):
return integrate.fixed_quad(integrand, 0, 11, args=(x, ), n=5)[0]
res_quad = integrate.quad(lambda x: fint_quad(x), 0, 1)
res_fixed_quad = integrate.fixed_quad(lambda x: fint_fixed_quad(x), 0, 1, n=5)
They both return 66 as well, as expected. That shows that it can work to compute double integrals using scipy.integrate.fixed_quad.
However, when one now changes the upper bound back to the one you had (from 11 to 1 - x), it still works for quad but crashes for fixed_quad:
area_dblquad = integrate.dblquad(lambda x, y: x + y, 0, 1, lambda x: 0, lambda x: 1 - x)[0]
res_quad = integrate.quad(lambda x: fint_quad(x), 0, 1)
both return 0.333333..., the call with fixed_quad results in the error you received. One can understand the error by looking on the source code:
x, w = _cached_roots_legendre(n)
x = np.real(x)
if np.isinf(a) or np.isinf(b):
raise ValueError("Gaussian quadrature is only available for "
"finite limits.")
y = (b-a)*(x+1)/2.0 + a
return (b-a)/2.0 * np.sum(w*func(y, *args), axis=-1), None
When one prints a and b one gets:
a: 0
b: 1
a: 0
b: [ 0.95308992 0.76923466 0.5 0.23076534 0.04691008]
So for the call with 1-x, b is actually a numpy array with n elements and one cannot compare an array to infinity, that's why it crashes. Whether that is an intended behavior or a bug, I can't answer; might be worth opening an issue on github.

fixed_quad requires f to accept vector inputs. And the result should be the mapped values for the inputs (i.e. something like map(f, xs)). With this in mind, just ensure your inside function returns mapped values, and you're ready to go.
import scipy.integrate as integrate
f = lambda y,x: x+y
inside = lambda xs, n: np.array([integrate.fixed_quad(f, 0, 1-x, args=(x,), n=n)[0]
for x in np.array(xs)])
order = 5
outside = integrate.fixed_quad(inside, 0, 1, n=order, args=(order,))
Also, be careful with the order of arguments for your integrand. Judging from your code arg=(x,), it seems that you want the inner integral to be done along y dimension. The first argument of the integrand is the dimension along which it is integrated. So it should be lambda y,x instead (note that this is also the shape of integrand expected by dblquad).

Related

Show Math Addition in two Arrays Python

how do i write a code to show the addition operation between two arrays (row wise), i don't want the result of the addition but want to illustrate the operation. Here is what I have, however my code is not giving me the right output
import numpy as np
Grid = np.random.randint(-50,50, size=(5,4))
iList =np.array([[1, -1, 2, -2]])
result = (Grid.astype(str), iList.astype(str))
print(result)
the output needs to be something to this effect
([3+1 4-1 4+2 5-2]
[6+1 9-1 7+2 8-2]
etc.
thank you.
You basically want to apply a function to two numpy arrays of different sizes, making use of numpy's broadcasting capability.
This works:
import numpy as np
grid = np.random.randint(-50, 50, size=(5, 4))
i_list = np.array([[1, -1, 2, -2]])
def sum_text(x: int, y: int):
return f'{x}+{y}'
# create a ufunc, telling numpy that it takes 2 arguments and returns 1 value
np_sum_text = np.frompyfunc(sum_text, 2, 1)
result = np_sum_text(grid, i_list)
print(result)
Result:
[['46+1' '-27+-1' '35+2' '-3+-2']
['-5+1' '6+-1' '2+2' '22+-2']
['6+1' '-45+-1' '-21+2' '31+-2']
['25+1' '-4+-1' '-24+2' '3+-2']
['-32+1' '-10+-1' '-19+2' '28+-2']]
Or maybe you don't need to reuse that function and like one-liners:
print(np.frompyfunc(lambda x, y: f'{x}+{y}', 2, 1)(grid, i_list))
Getting rid of the + before a negative integer is trivial:
def sum_text(x: int, y: int):
return f'{x}+{y}' if y >= 0 else f'{x}{y}'

Solving set of coupled differential equations with sympy

I have the following set of coupled differential equations. I want to get an analytical solution with sympy.
from sympy import *
import numpy as np
init_printing(use_unicode=True)
x, y, z, t, w, V=symbols('x y z t omega V')
c1=Function('c1')
c2=Function('c2')
hq=symbols('hbar',positive=True)
g1=Eq(c2(t)*hq*V*exp(-I*w*t),I*hq*Derivative(c1(t),t))
g2=Eq(c1(t)*hq*V*exp(+I*w*t),I*hq*Derivative(c2(t),t))
eq=(g1,g2)
dsolve(eq,hint='all',ics={c1(0):1,c2(0):0})
When I try to solve the equation system, I get the error:
ValueError: The function cannot be automatically detected for nan.
Unfortunately I can not see my mistake.
Edit:
classify_ode(g1) returns the following hints:
('factorable', 'nth_algebraic', 'separable', '1st_exact', '1st_linear', 'Bernoulli', '1st_power_series', 'lie_group', 'nth_linear_constant_coeff_variation_of_parameters', 'nth_linear_euler_eq_nonhomogeneous_variation_of_parameters', 'nth_algebraic_Integral', 'separable_Integral', '1st_exact_Integral', '1st_linear_Integral', 'Bernoulli_Integral', 'nth_linear_constant_coeff_variation_of_parameters_Integral', 'nth_linear_euler_eq_nonhomogeneous_variation_of_parameters_Integral')
Every one of them produces the error mentioned above.
Manually, one can solve this system by applying the Laplace transform to c1 and c2. This turns the system of ODEs in a system of purely algebraic equations, which is solvable by rearranging and eliminating the coupling between equations. The result is then the transform of the solution, so one has to transform back using the inverse Laplace transform.
Edit 2:
classify_sysode(eq) returns the following:
{'no_of_equation': 2,
'eq': [-c1(t)*exp(-I*omega*t) + I*Derivative(c2(t), t),
-c2(t)*exp(I*omega*t) + I*Derivative(c1(t), t)],
'func': [c2(t), c1(t)],
'order': {c1(t): 1, c2(t): 1},
'func_coeff': {(0, c2(t), 0): 0,
(0, c2(t), 1): I,
(0, c1(t), 0): -exp(-I*omega*t),
(0, c1(t), 1): 0,
(1, c2(t), 0): -exp(I*omega*t),
(1, c2(t), 1): 0,
(1, c1(t), 0): 0,
(1, c1(t), 1): I},
'is_linear': True,
'type_of_equation': 'type6'}
This means that the solver that will be used is Linear, 2 equations, Order 1, Type 6, for a system of the form:
x' = f(t) x + g(t) y
y' = a [f(t) + a h(t)] x + a [g(t) - h(t)] y
But our system looks more like a Type 7, that is, of the form:
x' = f(t) x + g(t) y
y' = h(t) x + p(t) y
with f(t) and p(t) being zero. The suggested method of solution for a type 7 mentioned in the documentation also resembles what Lutz Lehmann mentions in his comment.
For completeness this is the error, which seems to originate from an exception raised by _preprocess:
Traceback (most recent call last):
File "problem_ode.py", line 21, in <module>
dsolve(eq,hint='all',ics={c1(0):1,c2(0):0})
File "/home/quoniam/anaconda3/envs/data/lib/python3.8/site-packages/sympy/solvers/ode.py", line 634, in dsolve
sols = solvefunc(match)
File "/home/quoniam/anaconda3/envs/data/lib/python3.8/site-packages/sympy/solvers/ode.py", line 7405, in sysode_linear_2eq_order1
sol = _linear_2eq_order1_type6(x, y, t, r, eq)
File "/home/quoniam/anaconda3/envs/data/lib/python3.8/site-packages/sympy/solvers/ode.py", line 7731, in _linear_2eq_order1_type6
hint1 = classify_ode(equ)[1]
File "/home/quoniam/anaconda3/envs/data/lib/python3.8/site-packages/sympy/solvers/ode.py", line 976, in classify_ode
eq, func_ = _preprocess(eq, func)
File "/home/quoniam/anaconda3/envs/data/lib/python3.8/site-packages/sympy/solvers/deutils.py", line 84, in _preprocess
raise ValueError('The function cannot be '
ValueError: The function cannot be automatically detected for nan.

defining a fuction to be used in solving equation from a data file

I am completely new to python and in fact any fundamental programming language, I use Mathematica for my all my symbolic and numeric calculations. I am learning to work with python and finding it really awesome! Here is a problem I am trying to solve but stuck without a clue!
I have a data file for example
0. 1.
0.01 0.9998000066665778
0.02 0.9992001066609779
... ..
Which just the {t, Cos[2t]}.
I want to define a function out of this data and use it in solving an equation in python. My Mathematica intuition tells me that I should define the function like:
iFunc[x_] = Interpolation[iData, x]
and rest of the job is easy. for instance
NDSolve[{y''[x] + iFunc[x] y[x] == 0, y[0] == 1, y[1] == 0}, y, {x, 0, 1}]
Solves the equation easily. (I have not tried with more complicated cases though).
Now how to do the job in python and also accuracy is an important issue for me. So, now I would like to ask two questions.
1. Is this the most accurate method in Mathematica?
2. And what is the equivalent of more accurate way to do the problem in python?
Here is my humble attempt to solve the problem (with a lot of input from StackOverflow) where the definition with cos(2t) works:
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
from math import cos
from scipy import interpolate
data = np.genfromtxt('cos2t.dat')
T = data[:,0] #first column
phi = data[:,1] #second column
f = interpolate.interp1d(T, phi)
tmin = 0.0# There should be a better way to define from the data
dt = 0.01
tmax = 2*np.pi
t = np.arange(tmin, tmax, dt)
phinew = f(t) # use interpolation function returned by `interp1d`
"""
def fun(z, t):
x, y = z
return np.array([y, -(cos(2*t))*x ])
"""
def fun(z, t):
x, y = z
return np.array([y, -(phinew(t))*x ])
sol1 = odeint(fun, [1, 0], t)[..., 0]
# for checking the plots
plt.plot(t, sol1, label='sol')
plt.show()
*When I run the code with interpolated function from cos(2t) data, is not working...the error message tell
Traceback (most recent call last): File "testde.py", line 30,
in <module> sol1 = odeint(fun, [1, 0], t)[..., 0]
File "/home/archimedes/anaconda3/lib/python3.6/site-packages/scip‌​y/integrate/odepack.‌​py",
line 215, in odeint ixpr, mxstep, mxhnil, mxordn, mxords)
File "testde.py",
line 28, in fun return np.array([y, -(phinew(t))*x ])
TypeError: 'numpy.ndarray' object is not callable.
I really can't decipher them. Please help...
In Mathematica, the usual way is simply
iFunc = Interpolation[iData]
Interpolation[iData] already returns a function.
To sub-question 2
With
t = np.arange(tmin, tmax, dt)
phinew = f(t) # use interpolation function returned by `interp1d`
equivalent to
phinew = np.array([ f(s) for s in t])
you construct phinew not as callable function but as array of values, closing the circle array to interpolation function to array. Use f which is a scalar function directly in the derivatives function,
def fun(z, t):
x, y = z
return np.array([y, -f(t)*x ])

Scipy analytically integrate piecewise function

Is there a method in scipy for analytic integration of a piecewise function? For example I have:
xrange_one, xrange_two = np.arange(0,4), np.arange(3,7)
part_one = lambda x: x + 3
part_two = lambda x: -2*x + 2
I'd like to integrate the first moment of this piecewise function:
func_one = lambda x: x * (x + 3)
func_two = lambda x: x * (-2*x + 2)
Is there a way with scipy integrate.quad or some other analytical integration function that do something like this:
total = integrate.quad(func_one, 0, 3, func_two, 3, 6)
I don't want to just integrate the two pieces separately.
Scipy will not perform analytical integration for you, since it is made for solving numerical problems. Sympy, on the other hand, can handle simple symbolic problems exactly:
>>> import sympy as sym
>>> x = sym.symbols('x')
>>> f = sym.Piecewise((x*(x+3),x<3), (x*(-2*x+2),True))
>>> sym.integrate(f,(x,0,6))
-153/2
Compare
>>> import scipy.integrate as integrate
>>> integrate.quad(lambda x:x*(x+3),0,3)[0] + integrate.quad(lambda x:x*(-2*x+2),3,6)[0]
-76.5
>>> -153/2.
-76.5
You could also define your original piecewise function first, then multiply it with the symbolic x, then integrate this new function analytically.
Another alternative, perhaps closer to the spirit of your question, might be to define the piecewise function numerically, and using scipy after all. This will still save you some work, but won't be strictly analytical:
>>> f = lambda x: x*(x+3) if x<3 else x*(-2*x+2)
>>> integrate.quad(f,0,6)[0]
-76.5
The most complete setup with this approach:
>>> f = lambda x: x+3 if x<3 else -2*x+2
>>> xf = lambda x: x*f(x)
>>> first_mom = integrate.quad(xf,0,6)[0]
>>> print(first_mom)
-76.5
First we define the piecewise lambda for f, then the integrand of the first moment, multiplying it with x. Then we do the integration.
Note that it is frowned upon by many to bind lambdas to variables. If you want to do this properly, you should probably define a named function for your piecewise function, and only use a lambda inside the integration (if otherwise you wouldn't use that integrand):
import scipy.integrate as integrate
def f(x):
return x+3 if x<3 else -2*x+2
first_mom = integrate.quad(lambda x: x*f(x),0,6)[0]
After playing with the numpy poly functions, I came up with:
integrate.quad(lambda x:np.piecewise(x, [x < 3, x >= 3],
[lambda x: np.polyval([1,3,0],x),
lambda x: np.polyval([-2,2,0],x)]),
0,6)
evaluates to:
(-76.5, 1.3489209749195652e-12)
There is a polyint to do a polynomial integration
In [1523]: np.polyint([1,3,0])
Out[1523]: array([ 0.33333333, 1.5 , 0. , 0. ])
In [1524]: np.polyint([-2,2,0])
Out[1524]: array([-0.66666667, 1. , 0. , 0. ])
That is
x*(x+3) => x**2 + 3*x => np.poly1d([1,3,0]) => 1/3 x**3 + 3/2 x**2
So the analytical solution is the appropriate end point differences for these two polyint objects:
In [1619]: np.diff(np.polyval(np.polyint([1,3,0]),[0,3])) +
np.diff(np.polyval(np.polyint([-2,2,0]),[3,6]))
Out[1619]: array([-76.5])
In [1621]: [np.polyval(np.polyint([1,3,0]),[0,3]),
np.polyval(np.polyint([-2,2,0]),[3,6])]
Out[1621]: [array([ 0. , 22.5]), array([ -9., -108.])]

root minuit2 contours with parameter limits

I'm trying to produce contour plots for parameters with physical limits using the Minuit2 minimizer which is a part of the ROOT data analysis framework. Unfortunately, Minuit2 seems intent on drifting the parameters into regions outside of their limits when I try to produce contour plots:
>>> from minuit2 import Minuit2
>>> def f(x,y):
... if x < 0 or y < 0:
... print 'x = %.2f, y = %.2f' % (x,y)
... raise Exception
... return x**2 + y**2
...
>>> m = Minuit2(f)
>>> m.limits['x'] = 0, 10
>>> m.limits['y'] = 0, 10
>>> m.migrad()
>>> xy = m.contour('x','y',3)
Info in <Minuit2>: MnMinos UP value has changed, need to update FunctionMinimum class
x = -9.95, y = 0.00
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in f
Exception
Has anybody else dealt with this or a similar problem? Are there any workarounds?
I've already asked this question on the ROOT forums, but I thought there might also be some stack overflow users who have dealt with this or a similar issue.
Try your example without raising an exception
def f(x,y):
return x ** 2 + y ** 2
and you will get reasonable xy contour points (i.e. within 1e-3 of the true contour).
Note that the parameter sigmas=3 in your contour call m.contour('x', 'y', 3) means that the contour for sigmas ** 2 == 9 will be computed and that contour points along the parameter limits are computed. As far as I can see this is not mentioned in the contour() pyminuit documentation).
In your example the contour starts at (0, 0), goes up to (3, 0), along the circle to (0, 3), and back to (0, 0).
A common method is to implement parameter limits (arbitrary shapes, not only min / max) in your cost function by returning very high values for excluded parameters:
def f(x,y):
if x < 0 or y < 0:
return 1e10
return x ** 2 + y ** 2
This does throw the optimizer out of the forbidden regions, but it does not prevent it to probe them sometimes (i.e. evaluate f there).
I don't know why contour() should strictly respect the limits you set via
m.limits['x'] = 0, 10
m.limits['y'] = 0, 10
Here's a short description of the contour algorithm used by Minuit (and Minuit2) and here is the documentation for the Minuit2 code in ROOT, I did not manage to find the actual C file showing the implementation.

Categories

Resources