Probelm with Scipy.quad - scipy

I have been trying to work our my project using a Python code which needs integration within a sigma.
I get the following error and despite trying several ways, I was not able to solve it. Below you can find a shorter version of my code for error duplication.
The code can be run without any problem if the lower limit of the integral is zero or positive. If it's negative the code gives error...
File "C:\Users\AppData\Local\Programs\Python\Python37\lib\site-packages\scipy\integrate\quadpack.py", line 341, in quad
points)
File "C:\Users\AppData\Local\Programs\Python\Python37\lib\site-packages\scipy\integrate\quadpack.py", line 448, in _quad
return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
TypeError: must be real number, not mpc
import numpy as np
from scipy.integrate import quad
from mpmath import besselk, besseli, nsum, inf, exp, log, cos, mp
mp.dps = 3; mp.pretty = True
tt = (np.logspace(0.0001, 10, num=10)).round(2)
lenght = len(tt)
k0 = lambda u: besselk(0,u)
f = lambda u: u*exp(-2)
Zwn = lambda n: 0.5*(cos(n)*cos(2*n))
Rn = lambda u, n, xD: (1/u)*k0(xD*((f(u) + (n)**2)**0.5))
Lap_Func = lambda u: nsum(lambda n: ((quad(lambda xD: Zwn(n)*Rn(u, n, xD), -10, 10))[0]), [1, 100])
print(Lap_Func((log(2))*1/tt[3]))

Quad only deals with floats, and does not understand mpmath objects. Either drop mpmath and use directly numpy/scipy functions, or convert mpmath expressions to floats at the end of computations.

Related

How to correctly set the 'rtol' and 'atol' in scipy integration module 'solve_ivp' for solving a system of ODE with unknown analytic solution?

I was trying to reproduce some results of ode45 solver in Python using solve_ivp. Though all parameters, initial conditions, step size, and 'atol' and 'rtol' (which are 1e-6 and 1e-3) are same, I am getting different solutions. Both of the solutions are converging to a periodic solution but of different kind. As solve_ivp uses same rk4(5) method as ode45, this discrepancy in the final result is not quite understable. How can we know which one is the correct solution?
The code is included below
import sys
import numpy as np
from scipy.integrate import solve_ivp
#from scipy import integrate
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
# Pendulum rod lengths (m), bob masses (kg).
L1, L2, mu, a1 = 1, 1, 1/5, 1
m1, m2, B = 1, 1, 0.1
# The gravitational acceleration (m.s-2).
g = 9.81
# The forcing frequency,forcing amplitude
w, a_m =10, 4.5
A=(a_m*w**2)/g
A1=a_m/g
def deriv(t, y, mu, a1, B, w, A): # beware of the order of the aruments
"""Return the first derivatives of y = theta1, z1, theta2, z2, z3."""
a, c, b, d, e = y
#c, s = np.cos(theta1-theta2), np.sin(theta1-theta2)
adot = c
cdot = (-(1-A*np.sin(e))*(((1+mu)*np.sin(a))-(mu*np.cos(a-b)*np.sin(b)))-((mu/a1)*((d**2)+(a1*np.cos(a-b)*c**2))*np.sin(a-b))-(2*B*(1+(np.sin(a-b))**2)*c)-((2*B*A/w)*(2*np.sin(a)-(np.cos(a-b)*np.sin(b)))*np.cos(e)))/(1+mu*(np.sin(a-b))**2)
bdot = d
ddot = ((-a1*(1+mu)*(1-A*np.sin(e))*(np.sin(b)-(np.cos(a-b)*np.sin(a))))+(((a1*(1+mu)*c**2)+(mu*np.cos(a-b)*d**2))*np.sin(a-b))-((2*B/mu)*(((1+mu*(np.sin(a-b))**2)*d)+(a1*(1-mu)*np.cos(a-b)*c)))-((2*B*a1*A/(w*mu))*(((1+mu)*np.sin(b))-(2*mu*np.cos(a-b)*np.sin(a)))*np.cos(e)))/(1+mu*(np.sin(a-b))**2)
edot = w
return adot, cdot, bdot, ddot, edot
# Initial conditions: theta1, dtheta1/dt, theta2, dtheta2/dt.
y0 = np.array([3.15, -0.1, 3.13, 0.1, 0])
# Do the numerical integration of the equations of motion
sol = integrate.solve_ivp(deriv,[0,40000], y0, args=(mu, a1, B, w, A), method='RK45',t_eval=np.arange(0, 40000, 0.005), dense_output=True, rtol=1e-3, atol=1e-6)
T = sol.t
Y = sol.y
I am expecting similar result from ode45 in MATLAB and solve_ivp in Python. How can I exactly reproduce the result from ode45 in python? What is the reason of discrepancy?
Even if ode45and RK45use the same underlying scheme, they do not necessarily use the same exact strategy regarding the evolution of the time step and its adaptation to match the error tolerance. Thus, it is difficult to know which one is better.
The only thing you could is simply trying lower tolerances, e.g. 1e-10. Then, both solutions should end up being virtually identical... Here, your current error tolerance might be insufficiently low, so that small discrepancies in the fine details of both algorithms create a visible difference in the solution.

How to use scipy.optimize.curve_fit to use lists of variable

I have time varying data trace which I want to fit a function to. The inputs to the functions are lists and I want the curve_fit to optimize all values in the list to fit the curve. I have gotten so far-
from scipy.optimize import curve_fit
from matplotlib.pylab import plt
from numpy import exp
def ffunc2(x, a, b):
counter = 0
return_value = 0
while counter < len(a):
return_value += a[counter] * exp(b[counter] * x)
counter += 1
return return_value
# INITIAL DATA
x = [1, 2, 3, 5]
y = [1, 8, 81, 125]
number_variable = 2
# INTIAL GUESS
p0 = []
counter = 0
while counter < number_variable:
p0.append(0.0)
counter += 1
p, _ = curve_fit(ffunc2, x, y, p0=[0.0, 0.0])
I want to create a loop which iterates such that it gives me the best fit with maximum number of variables by minimizing the error.
I have found this discussion as well -
Using scipy curve_fit for a variable number of parameters
from numpy import exp
from scipy.optimize import curve_fit
def wrapper_fit_func(x, N, *args):
a, b, c = list(args[0][:N]), list(args[0][N:2*N]), list(args[0][2*N:3*N])
return fit_func(x, a, b)
def fit_func(x, a, b):
counter = 0
return_value = 0
while counter < len(a):
return_value += a[counter] * exp(b[counter] * x)
counter += 1
return return_value
x = [1, 2, 3, 5]
y = [1, 8, 81, 125]
params_0 = [0,1.0,2.0,3.0,4.0,5.0]
popt, pcov = curve_fit(lambda x, *params_0: wrapper_fit_func(x, 3, params_0), x, y, p0=params_0)
But get an error -´´´
File "C:\python\lib\site-packages\scipy\optimize\minpack.py", line 387, in leastsq
raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m))
TypeError: Improper input: N=6 must not exceed M=4
´´´
I was able to solve this be using sci_py.optimize.least_squares directly as it takes in tuple as an input and not variables directly. But I do have to define the error function. I would assume it help solve my problem as of now.
from scipy.optimize import least_squares
from matplotlib.pylab import plt
from numpy import exp
import numpy as np
# define function to fit
def ffunc2(a, x):
counter = 0
return_value = 0
while counter < len(a):
return_value += a[counter] * exp(x * a[counter + 1])
counter += 2
return return_value
def error_func(tpl, x, y):
return ffunc2(tpl,x) - y
# INPUT DATA
x = np.array([1, 2, 3, 5])
y = np.array([0.22103418, 0.24428055, 0.26997176, 0.32974425,])
# INITIAL GUESS
p0 = (1, 1)*10
output = least_squares(error_func, x0=p0, jac='2-point', bounds=(0, np.inf), method='trf', ftol=1e-08,
xtol=1e-08, gtol=1e-08, x_scale=1.0, loss='linear', f_scale=1.0, diff_step=None,
tr_solver=None,
tr_options={}, jac_sparsity=None, max_nfev=None, verbose=0, args=(x, y))
tpl_final = output.x
print (tpl_final)
final_curve = ffunc2(tpl_final,x)
plt.plot(x, y, 'r-', x, final_curve, 'g-')
plt.show()
The short answer is "No". curve_fit requires a single list/ndarray of variable parameters that correspond, in order, with the arguments of the model function you provide. That is, you would have to explicitly name all the parameters in your function and make your list of lists of variables strictly 1D.
Then again, curve_fit is not much more than a wrapper around scipy.optimize.least_squares. To use that approach, you pass in a (still strictly 1D) list/ndarray and use those values to build the array to be minimized (typically data-model) from that single array of variables. In some complex cases with many components or data sets, that can become easier to use. That is, the curve_fit approach doesn't scale very well to 50 positional variables.
Depending on the nature of your problem, you might also find lmfit (https://lmfit.github.io/lmfit-py/ -- disclaimer: I am the original author) useful. This organizes Parameters by name, not position in a list and provides more built-in ways to constrain Parameters and explore uncertainties. What may be of particular for your case, the lmfit.Model class for curve-fitting includes the ability to easily add models together into a composite (for example, 2 Gaussians + n exponential background as shown at https://lmfit.github.io/lmfit-py/examples/documentation/builtinmodels_nistgauss2.html). That might help you express what you are trying to do.
Please take a look at this post https://stackoverflow.com/a/73951825/20160627, where I propose a use of scipy.optimize.curve_fit with arbitrary number and positioning of parameters to fit or fix in a list

How to vectorize scipy.integrate.quad to calculate elementwise integral of a matrix

I want to integrate a matrix such that each element of the output matrix is integral of the corresponding element of the integrand matrix. Code snippets are as below:
import numpy as np
from scipy.integrate import quad
N=3
A = np.random.rand(N,N)
evs = np.linalg.eigvals(A)
evs = -np.sort(-evs)
Anew = A/(evs[0]+1) - np.eye(N)
B = np.eye(N)
def integrand(t,A,B):
prod = np.multiply(sp.linalg.expm(A*t),B)
return np.multiply(prod,prod.T)
This gives a square matrix with each element a function of t. I use the following to integrate:
np.vectorize(quad)(integrand,0,1,args=(Anew,B))
However, I receive the following error message:
integrand() missing 1 required positional argument: 'B'
Although this states that 'B' is missing, I don't understand it as I am providing B as an argument. I am also not sure if I am implementing vectorization correctly.
Try scipy.integrate.quad_vec. It's not yet released, so you'll need the in-development version of scipy, which is available from github. Or, wait until scipy 1.4 is released.

How to use scipy signal for MIMO systems

I am looking for a way to simulate the output of a signal for various input signals. To be more precise, I have a system defined by its transfer function H that takes one input and has one output. I generated several signals (stored in a numpy array). What I would like to do, is get the response of the system, to each input signal whithout using a for loop. Is there a way to proceed? Below is the code I wrote so far.
from __future__ import division
import numpy as np
from scipy import signal
nbr_inputs = 5
t_in = np.arange(0,10,0.2)
dim = (nbr_inputs, len(t_in))
x = np.cumsum(np.random.normal(0,2e-3, dim), axis=1)
H = signal.TransferFunction([1, 3, 3], [1, 2, 1])
t_out, y, _ = signal.lsim(H, x[0], t_in) # here, I would just like to simply write x
thanks for your help
This is not a MIMO system, it is a SISO system but you have multiple inputs.
You can create a MIMO system and apply your inputs all at once which will be computed channel by channel but simultaneously. Moreover, you can't use scipy.signal.lsim for MIMO systems yet. You can use other options such as python-control (if you have slycot extension otherwise again no MIMO) or harold if you have Python 3.6 or greater (disclaimer: I'm the author).
import numpy as np
from harold import *
import matplotlib.pyplot
nbr_inputs = 5
t_in = np.arange(0,10,0.2)
dim = (nbr_inputs, len(t_in))
x = np.cumsum(np.random.normal(0,2e-3, dim), axis=1)
# Forming a 1x5 system, common denominator will be completed automatically
H = Transfer([[[1, 3, 3]]*nbr_inputs], [1, 2, 1])
The keyword per_channel=True applies first input to first channel, second input to second and so on. Otherwise combined response is returned. You can check the shapes by playing around with it to see what I mean.
# Notice it is x.T below -> input shape = <num samples>, <num inputs>
y, t = simulate_linear_system(H, x.T, t_in, per_channel=True)
plt.plot(t, y)
This gives

BFGS Fails to Converge

The model I'm working on is a multinomial logit choice model. It's a very specific dataset so other existing MNLogit libraries don't fit with my data.
So basically, it's a very complex function which takes 11 parameters and returns a loglikelihood value. Then I need to find the optimal parameter values that can minimize the loglikelihood using scipy.optimize.minimize.
Here are the problems that I encounter with different methods:
'Nelder-Mead’: it works well, and always give me the correct answer. However, it's EXTREMELY slow. For another function with a more complicated setup, it takes 15 hours to get to the optimal point. At the same time, the same function takes only 1 hour on Matlab using fminunc (which uses BFGS by default)
‘BFGS’: This is the method used by Matlab. It works well for any simply functions. However, for the function that I have, it always fails to converge and returns 'Desired error not necessarily achieved due to precision loss.’. I've spent lots of time playing around with the options but still failed to work.
'Powell': It quickly converges successfully but returns a wrong answer. The code is printed below (x0 is the correct answer, Nelder-Mead works for whatever initial value), and you can get the data here: https://www.dropbox.com/s/aap2dhor5jyxy94/data.csv
Thanks!
import pandas as pd
import numpy as np
from scipy.optimize import minimize
# https://www.dropbox.com/s/aap2dhor5jyxy94/data.csv
df = pd.read_csv('data.csv', index_col=0)
dfhh = df.hh
B = df.ix[:,'b0':'b4'].values # NT*5
P = df.ix[:,'p1':'p4'].values # NT*4
F = df.ix[:,'f1':'f4'].values # NT*4
SDV = df.ix[:,'lagb1':'lagb4'].values
def Li(x):
b1 = x[0] # coeff on prices
b2 = x[1] # coeff on features
a = x[2:7] # take first 4 values as alpha
E = np.exp(a + b1*P + b2*F) # (1*4) + (NT*4) + (NT*4) build matrix (NT*J) for each exp()
E = np.insert(E, 0, 1, axis=1) # (NT*5)
denom = E.sum(1)
return -np.log((B * E).sum(1) / denom).sum()
x0 = np.array([-32.31028223, 0.23965953, 0.84739154, 0.25418215,-3.38757007,-0.38036966])
np.random.seed(0)
x0 = x0 + np.random.rand(6)
minL = minimize(Li, x0, method='Nelder-Mead',options={'xtol': 1e-8, 'disp': True})
# minL = minimize(Li, x0, method='BFGS')
# minL = minimize(Li, x0, method='Powell', options={'xtol': 1e-12, 'ftol': 1e-12})
print minL
Update: 03/07/14 Simpler Version of the Code
Now Powell works well with very small tolerance, however the speed of Powell is slower than Nelder-Mead in this case. BFGS still fails to work.