Problems efficently computing the roots of a multiple argument function - python

Hello my dear intelligent people on the internet,
I want to compute the roots of a (non invertible) two argument function in a given interval using scipy.optimize.fsolve:
kappa = 1.4
def Av_ma(Ma):
Ma[np.where(Ma < 0)] = 0
return Ma * ((2 + (kappa - 1) * (Ma ** 2)) / (kappa + 1)) ** ((kappa + 1) / (2 * (1 - kappa)))
def Av_ma_root(Ma, _Av_e_2):
return Av_ma(Ma) - _Av_e_2
The actual funtion Av_ma has only one argument, but I want to compute the roots for values of _Av_e_2 between 0 and 1. As a result i want an array containing only the root sets for like 100 values of _Av_e_2.
This is how my code is looks so far:
Av_e_2 = np.arange(0, 1 + 1e-12, 0.01)
x0 = np.arange(0.1, 1, 0.1)
startvec = np.repeat(x0, len(Av_e_2))
lAv_e_2 = np.tile(Av_e_2, np.shape(x0)[0])
pv2 = optimize.fsolve(Av_ma_root, startvec, args=lAv_e_2)
pv2 = np.reshape(pv2, (len(x0), len(Av_e_2)))
pv2 = np.round(pv2, 6)
pv2 = np.array([np.unique(p) for p in np.transpose(pv2)])
print(pv2)
I define an array of the 'starting estimate' array with the length of the Av_e_2.
Its used to give fsolve the same starting estimate for every value of Av_e_2.
There after i define an array of the values of Av_e_2. Its used to give fsolve every single value of Av_e_2
After optimizing I reshape, round and throw out the non-unique values.
In short: my code doesn't work. It does not produce the solution i want it to
test_vec_opt.py:25: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
pv2 = np.array([np.unique(p) for p in np.transpose(pv2)])

Related

Combination of lambda function and np.array with a constant doesnt work

I defined these two functions:
function = lambda x: np.array([x, np.sin(x) + 0.01 * x ** 2]) # function
function_der_1 = lambda x: np.array([1, np.cos(x) + 0.02 * x]) # first derivative
#Defining range of x
x = np.arange(0, 10, 0.01)
#Plotting
plt.plot(*function(x))
This all works fine. But calling
x_1, y_1 = function_der_1(x)
does not work.
It returns an array of the form [1, array([...])].
Why does it not return an array of ones? I guess the reason is because the 1 is constant but it should work nevertheless, shouldn't it?
The wanted output looks like this (called with [1, 3, 4]):
[array([1. , 0.56030231]),
array([ 1. , -0.9299925]),
array([ 1. , -0.57364362])]
In function, the input x is an array and np.array is called with two array in parameter so Numpy concatenates the arrays. In function_der_1, np.array is called with a list of two completely different object: an integer 1 and an array. Numpy do not know what to do with that and decide to create an array of object which contains the two objects. This is not great to do that. In fact Numpy show a warning to say that this is likely an issue:
VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray.
To create an array of 1 with a type/shape similar to x like in function, you need to use for example np.ones_like. Note that this is better to use function like np.vstack, np.hstack or np.concatenate in such case to specify you intent to Numpy (it helps readers and produces a faster code too). Here is the resulting code:
function_der_1 = lambda x: np.vstack([np.ones_like(x), np.cos(x) + 0.02 * x])
The set up was probably not good. I rearranged to
function = lambda x: np.sin(x) + 0.01 * x ** 2
function_der_1 = lambda x: np.cos(x) + 0.02 * x
function_der_2 = lambda x: - np.sin(x) + 0.02
The derivatives of the function f(t) = (t, np.sin(t) + 0.01 * t ** 2) will alway be of the form (1, ...) so I guess as long as I don't use another parametrization it should work.

Python - Multiprocessing with multiple for-loops

I know there are other questions asked concerning this topic so I'm sorry I have to ask it again, but I cannot get it to work since I'm quite new to this topic.
I have four for-loop (nested) in which certain algbraic calculations are done (matrix operations for example). These calculations take too much time to complete, so I was hoping I could speed this up with Multiprocessing.
The code is given below. I simulated the ranges and matrix sizes here, but in my code these ranges are really used (so it's not strange that it takes so long). You should be able to run it directly when copy-paste the code.
import numpy as np
from scipy.linalg import fractional_matrix_power
import math
#Lists for the loop (and one value)
x_list = np.arange(0, 32, 1)
y_list = np.arange(0, 32, 1)
a_list = np.arange(0, 501, 1)
b_list = np.arange(0, 501, 1)
c_list = np.arange(0, 64, 1)
d_number = 32
#Matrices
Y = np.arange(2048).reshape(32, 64)
g = np.asmatrix(np.empty([d_number, 1], dtype=np.complex_))
A = np.empty([len(a_list), len(b_list), len(c_list)], dtype=np.complex_)
A_in = np.empty([len(a_list), len(b_list)], dtype=np.complex_)
for ai in range(len(a_list)):
for bi in range(len(b_list)):
for ci in range(len(c_list)):
f_k_i = c_list[ci]
X_i = np.asmatrix([Y[:, ci]]).T
for di in range(d_number):
r = math.sqrt((x_list[di] - a_list[ai])**2 + (y_list[di] - b_list[bi])**2 + 63**2)
g[di, 0] = np.exp(-2 * np.pi * 1j * f_k_i * (r / 8)) / r #g is a vector
A[-bi, -ai, ci] = ((1 / np.linalg.norm(g)**2) * (((g.conj().T * fractional_matrix_power((X_i * X_i.conj().T), (1/5)) * g) / np.linalg.norm(g)**2)**2)).item(0)
A_in[-bi, -ai] = (1 / len(c_list)) * sum(A[-bi, -ai, :])
What is the best way to approach this? If multiprocessing is the solution, how to implement this for my case (since I couldn't figure that out).
Thanks in advance.
One way to approach it would be to move the two inside loops into a function taking ai and bi as parameters and returning the indexes and the result. Then use multiprocessing.Pool.imap_unordered() to run the function on ai, bi pairs. Something like this (untested):
def partial_calc(index):
"""
This function replaces the inner two loops to calculate the value of
A_in[-bi, -ai]. index is a tuple (ai, bi).
"""
ai, bi = index
for ci in range(len(c_list)):
f_k_i = c_list[ci]
X_i = np.asmatrix([Y[:, ci]]).T
for di in range(d_number):
r = math.sqrt((x_list[di] - a_list[ai])**2 + (y_list[di] - b_list[bi])**2 + 63**2)
g[di, 0] = np.exp(-2 * np.pi * 1j * f_k_i * (r / 8)) / r #g is a vector
A[-bi, -ai, ci] = ((1 / np.linalg.norm(g)**2) * (((g.conj().T * fractional_matrix_power((X_i * X_i.conj().T), (1/5)) * g) / np.linalg.norm(g)**2)**2)).item(0)
return ai, bi, (1 / len(c_list)) * sum(A[-bi, -ai, :])
def main():
with multiprocessing.Pool(None) as p:
# this replaces the outer two loops
indices = itertools.product(range(len(a_list)), range(len(b_list)))
partial_results = p.imap_unordered(partial_calc, indices)
for ai, bi, value in partial_results:
A_in[-bi, -ai] = value
#... do something with A_in ...
if __name__ == "__main__":
main()
Or put the inner three loops into the function and generate one "row" for A_in at a time. Profile it both ways and see which is faster.
The trick will be setting up the lists (a_list, b_list, etc) and the Y matrix. And that depends on their characteristics (constant, quickly/slowly calculated, large/small, etc).

Evaluation a function stored in column major order

I am trying to evaluate a function at discretized point and stored in column-major order like this:
import numpy as np;
N = 3 ##
n = N * N
h = 1 / (N + 1) # step size
h2 = h**2 #
deltaX = np.zeros(N)
deltaY = np.zeros(N);
def Function(x, y):
output = -20. * np.pi * np.sin(2 * np.pi * x) * sin(4 * np.pi * y)
return output
## Equally spaced delta:
for i in range(1, N + 1):
deltaX[i - 1] = i * h;
deltaY[i - 1] = i * h;
### Lexicographic Row order ###
### Evaluation of function at deltaX and deltaY
feval = np.zeros((n, 1))
How could I approach to evaluate the discretization for this function?
Good news: your function properly uses numpy operations, so is completely vectorized. That means that you can evaluate it at every element of the input arrays.
The shape of the inputs don't have to match exactly. They just have to broadcast together. That means that only non-singleton dimensions need to match.
So start by creating the appropriate input arrays. Numpy provides the tools to do this elegantly without looping:
N = 3
h = 1 / (N + 1)
delta_x = np.arange(1., N + 1.) * h
delta_y = np.linspace(h, N * h, N)[:, None]
I deliberately used two different ways to create the coordinate arrays, to serve as an example. In practice, you'd want to use one of the two methods.
The index [:, None] turns delta_y into a column vector. None introduces a new singleton axis. There are any number of Other ways to do the same thing, like `delta_y = ....reshape(-1, 1).
And read the docs I linked to, and for all the functions I used.
Now that you have a column in the y direction and a row in the x, you can call Function as just
val = Function(delta_x, delta_y)
The operation of arranging the 2D matrix val into a 1D array is called raveling. By default, it uses the default row-major order that numpy uses in memory. This order is also called "C" order. The alternative arrangement is to interpret the array in column major order, like Matlab does. This is called Fortran order. It will require a copy of the data since that's not how the elements are laid out in memory.
One way to ravel in Fortran order:
feval = val.ravel(order='F')
An alternative is to transpose and use C order:
feval = val.T.ravel()
The last two lines can be combined, so you end up with 3 lines:
delta_x = h * np.arange(1., N + 1.)
delta_y = h * np.arange(1., N + 1.)[:, None]
feval = Function(delta_x, delta_y).ravel(order='F')
You could make it into a one-liner, but that's pushing it.

How to make a for loop iterate over an array with only one element (0-d array)

I have a function that takes an argument y_0 that might be a list or array, or might just be a single float. I wanted it to work in the case where y_0 was a single float, so I tried using np.asarray(y_0) in the code below, so that if there is only one item, the loop will still work. However, I got the error TypeError: iteration over a 0-d array. I could use an if statement to check to see if it is a singleton or not, and take appropriate action. However, I was curious if there is a way to make it iterate over the single object?
def vf_grapher(fn, t_0, t_n, dt, y_0, lintype='-r', sup_title=None,
title=None, xlab=None, ylab=None):
t = np.arange(t_0, t_n, dt)
y_min = .0
y_max = .0
fig, axs = plt.subplots()
fig.suptitle(sup_title)
axs.set_title(title)
axs.set_ylabel(ylab)
axs.set_xlabel(xlab)
for iv in np.asarray(y_0):
soln = rk4(dt, t, fn, iv)
plt.plot(t, soln, lintype)
if y_min > np.min(soln):
y_min = np.min(soln)
if y_max < np.max(soln):
y_max = np.max(soln)
For minimal working example, include the following function:
def rk4(dt, t, field, y_0):
"""
:param dt: float - the timestep
:param t: array - the time mesh
:param field: method - the vector field y' = f(t, y)
:param y_0: array - contains initial conditions
:return: ndarray - solution
"""
# Initialize solution matrix. Each row is the solution to the system
# for a given time step. Each column is the full solution for a single
# equation.
y = np.asarray(len(t) * [y_0])
for i in np.arange(len(t) - 1):
k1 = dt * field(t[i], y[i])
k2 = dt * field(t[i] + 0.5 * dt, y[i] + 0.5 * k1)
k3 = dt * field(t[i] + 0.5 * dt, y[i] + 0.5 * k2)
k4 = dt * field(t[i] + dt, y[i] + k3)
y[i + 1] = y[i] + (k1 + 2 * k2 + 2 * k3 + k4) / 6
return y
if __name__ == '__main__':
def f(t, x): return x**2 - x
vf_grapher(f, 0, 4, 0.1, (-0.9, 0.5, 1.01), xlab='t', ylab='x(t)',
sup_title=r'Solution Field for $\dot{x} = x^2 - x$')
You can use the ndmin argument to np.array to ensure that your array is in fact iterable:
np.array(y_0, ndmin=1, copy=False)
np.asarray is just an alias for np.array that sets some default arguments differently.
ndmin can be used to pad your shape with unit dimensions. This helps because normally zero-dimensional arrays are equivalent to scalars. One annoying side-effect of this is that they aren't iterable. ndmin=1 means that a scalar input should be treated as a one-dimensional, one-element array, which is what you're looking for.
copy=False just tells numpy to use existing arrays as-is instead of making a copy. That way, if a user passes in an actual array (as opposed to a list or a scalar), it will be used without data duplication. I often pair this argument with subok=True, which will pass through subclasses of ndarray as well without copying.
I'm not sure why you can iterate over a list of a single object but not an array of a single object, but I found a way to determine if an item is iterable or not in this answer to a different question: https://stackoverflow.com/a/1952481/3696204
Then, I used a try, except block as follows:
try:
iter(y_0)
except TypeError:
y_0 = list([y_0])
for iv in y_0:
soln = rk4(dt, t, fn, iv)
plt.plot(t, soln, lintype)
if y_min > np.min(soln):
y_min = np.min(soln)
if y_max < np.max(soln):
y_max = np.max(soln)
Thanks for the helpful comments, everyone.

leastsq trouble passing parameters Python

I am trying to fit the following function: Detrended SNR into my data. C1, C2 and h are the parameters I need to obtain from the leastsq's method. C1 and C2 are simple but the problem is that my h(t) is in reality: . What I want to obtain are the coefficients hj inside that function (in my case there are 35 different hj's ). This function is the sum of different basis B-Splines each one weighted different and the number of coefficients is equal to the number of knots of the B-Spline. As I want to obtain C1, C2 and h1..35 I do the following:
funcLine = lambda tpl, eix_x: (tpl[0]*np.sin((4*math.pi*np.sum(bsplines_evaluades * np.transpose([tpl[2],tpl[3],tpl[4],tpl[5],tpl[6],tpl[7],tpl[8],tpl[9],tpl[10],tpl[11],tpl[12],tpl[13],tpl[14],tpl[15],tpl[16],tpl[17],tpl[18],tpl[19],tpl[20],tpl[21],tpl[22],tpl[23],tpl[24],tpl[25],tpl[26],tpl[27],tpl[28],tpl[29],tpl[30],tpl[31],tpl[32],tpl[33],tpl[34],tpl[35],tpl[36],tpl[37]]) , axis=0))*eix_x/lambda1) + tpl[1]*np.cos((4*math.pi*np.sum(bsplines_evaluades * np.transpose([tpl[2],tpl[3],tpl[4],tpl[5],tpl[6],tpl[7],tpl[8],tpl[9],tpl[10],tpl[11],tpl[12],tpl[13],tpl[14],tpl[15],tpl[16],tpl[17],tpl[18],tpl[19],tpl[20],tpl[21],tpl[22],tpl[23],tpl[24],tpl[25],tpl[26],tpl[27],tpl[28],tpl[29],tpl[30],tpl[31],tpl[32],tpl[33],tpl[34],tpl[35],tpl[36],tpl[37]]) , axis=0))*eix_x/lambda1))*np.exp(-4*np.power(k, 2)*lambda_big*np.power(eix_x, 2))
func = funcLine
ErrorFunc = lambda tpl, eix_x, ydata: np.power(func(tpl, eix_x) - ydata,2)
tplFinal1, success = leastsq(ErrorFunc, [2, -2, 8.2*np.ones(35)], args=(eix_x, ydata))
tpl(0)=C1, tpl(1)=C2 and tpl(2..35)=my coefficients. bsplines_evaluades is a matrix [35,86000] where each row is the temporal function of each basis b-spline so I weight each row with its individual coefficient, 86000 is the length of eix_x. ydata(eix_x) is the function I want to aproximate. lambda1= 0.1903 ; lambda_big= 2; k=2*pi/lambda1. The output is the same initial parameters which is not logic.
Can anyone help me? I have tried with curvefit too but it does not work.
Data is in : http://www.filedropper.com/data_5>http://www.filedropper.com/download_button.png width=127 height=145 border=0/> http://www.filedropper.com >online backup storage
EDIT
The code right now is:
lambda1 = 0.1903
k = 2 * math.pi / lambda1
lambda_big = 2
def funcLine(tpl, eix_x):
C1, C2, h = tpl[0], tpl(1), tpl[2:]
hsum = np.sum(bsplines_evaluades * h, axis=1) # weight each
theta = 4 * np.pi * np.array(hsum) * np.array(eix_x) / lambda1
return (C1*np.sin(theta)+C2*np.cos(theta))*np.exp(-4*lambda_big*(k*eix_x)**2) # lambda_big = 2
if len(eix_x) != 0:
ErrorFunc = lambda tpl, eix_x, ydata: funcLine(tpl, eix_x) - ydata
param_values = 7.5 * np.ones(37)
param_values[0] = 2
param_values(1) = -2
tplFinal2, success = leastsq(ErrorFunc, param_values, args=(eix_x, ydata))
The problem is that the output parameters don't change with respect the initial ones. Data (x_axis,ydata,bsplines_evaluades):
gist.github.com/hect1995/dcd36a4237fe57791d996bd70e7a9fc7 gist.github.com/hect1995/39ae4768ebb32c27f1ddea97e24d96af gist.github.com/hect1995/bddd02de567f8fcbedc752371b47ff71
It's always helpful (to yourself as well as us) to provide a readable example that is complete enough to be runnable. Your example with lambdas and very long line is definitely not readable, making it very easy for you to miss simple mistakes. One of the points of using Python is to make code easier to read.
It is fine to have spline coefficients as fit variables, but you want to have the np.ndarray of variables to be exactly 1-dimensional. So your parameter array should be
param_values = 8.2 * np.ones(37)
param_values[0] = 2
param_values[1] = -2.
result_params, success = leastsq(errorFunc, param_values, ....)
It should be fine to use curve_fit() too. Beyond that, it's hard to give much help, as you give neither a complete runnable program (leaving lots of terms undefined), nor the output or error messages from running your code.
Several things could be wrong here: I'm not sure you're indexing your tpl array correctly (if it has 37 entries, the indexes should be 0:36). And your errorFunc should probably return the residual rather than the square residual.
Finally, I think your h-sum may be incorrect: you want to sum over the $N$ axis but not the $x$ axis, right?
You might tidy up your code as follows and see if it helps (without some data it's difficult to test for myself):
def funcLine(tpl, eix_x):
C1, C2, h = tpl[0], tpl[1], tpl[2:]
hsum = np.sum(bsplines_evaluades * h, axis=1)
theta = 4 * np.pi * hsum * eix_x / lambda1
return (C1 * np.sin(theta) + C2 * np.cos(theta)) * np.exp(-4 *lambda_big *
(k * eix_x)**2)
errorFunc = lambda tpl, eix_x, ydata: funcLine(tpl, eix_x) - ydata
tplFinal2, success = leastsq(errorFunc, [2, -2, 8.2*np.ones(35)],
args=(eix_x, ydata))

Categories