minimize a function that contains an array parameter - python

I am trying to minimize a function that contains an array parameter.
I tried to simplify my problem in the following example. The function Func contains the parameter c2 = linspace(-1,1,10). The optimization is about thet1, phai1, thet2, and phai2 so that they are bounded by the interval (0,2*pi).
import numpy as np
import scipy.optimize as spo
a1 = 0
c1 = 0
cc2 = np.linspace(-1,1,10)
alpha1 = 1+a1/3
def Func(ang):
thet1 = ang[0]
phai1 = ang[1]
thet2 = ang[2]
phai2 = ang[3]
RhoABC = np.array([[[1-a1,0,thet1,0,0,0,0,c1],[0,alpha1,0,0,phai2,0,c2,0],[0,0,alpha1,0,0,c2,thet2,0],[0,0,0,alpha1,c2,0,0,0],[0,phai1,0,c2,alpha1,0,0,0],[0,0,c2,0,0,alpha1,0,thet2],[0,c2,0,0,0,0,alpha1,0],[c1,0,0,phai1,0,0,0,1-a1]] for c2 in cc2])
w, v = np.linalg.eig(RhoABC)
return w[1]
angs0 = [.4*np.pi,1.9*np.pi,1.2*np.pi,.7*np.pi]
bnd = (0*np.pi,2*np.pi)
bnds = (bnd,bnd,bnd,bnd)
result = spo.minimize(Func,angs0,bounds=bnds)
the output is:
ValueError: too many axes: 2 (effrank=2), expected rank=1
I just want to plot the minimized value (fun) as a function of c2. Any suggestions, please?

The function to be minimized should return a float (see doc). However, Func returns a 1d array instead of a float. That is because RhoABC is 3d, so w is 2d, which makes w[1] a 1d array.

Related

How to return the generated function to the form of mathematical expression and perform derivation and other operations on it in python?

After using curve_fitting from scipy,the scatter data is fitted by Gaussian approximation,the code is as follows:
x = np.linspace(1,len(y),len(y))
n = len(x)
mean = sum(x*y)/n
sigma = np.sqrt(sum(y*(x-mean)**2)/n)
def gaus(x,a,x0,sigma):
return a*np.exp(-(x-x0)**2/(2*sigma**2))/(sigma*np.sqrt(2*np.pi))
popt,pcov = curve_fit(gaus,x,y,maxfev = 200000)
When I call it, the generated p1 is just an array corresponding to x:
p1 = gaus(x,*popt)
,The returned array is:
[0.09933219 0.10139629 0.10350315 0.10565368 0.10784877 0.11008935
0.11237635 0.11471073 0.11709347 0.11952557 0.12200806 0.12454196
0.12712835 0.1297683 0.13246293 0.13521337 0.13802076 0.14088628
0.14381113 0.14679655 0.14984377 0.15295407 0.15612876 0.15936917
0.16267665 0.16605259 0.1694984 0.17301552 0.17660543 0.18026962
0.18400963 0.18782703 0.19172341 0.19570039 0.19975966 0.20390289
0.20813183 0.21244823 0.21685392 0.22135072 0.22594052 0.23062523
0.23540682 0.24028728 0.24526864 0.250353 0.25554246 0.26083921
0.26624545 0.27176344 0.27739549 0.28314393 0.28901118 0.29499968
0.30111193 0.30735049 0.31371794 0.32021696 0.32685025 0.33362057
0.34053076 0.34758369 0.3547823 0.36212959 0.36962863 0.37728255
0.38509452 0.39306781 0.40120574 0.4095117 0.41798914 0.42664161
0.4354727 0.4444861 0.45368554 0.46307487 0.472658 0.4824389
0.49242166 0.50261042 0.51300944 0.52362302 0.53445559 0.54551166
0.55679582]
In this case, how can I find it's first derivative expression, the second derivative and so on for the generated function?
This can be achieved using scipy.interpolate.InterpolatedUnivariateSpline.
First, you need to create a spline of your data as:
from scipy.interpolate import InterpolatedUnivariateSpline
spl = InterpolatedUnivariateSpline(x, p1)
Afterward, you can use the spl object to pass x and n (the number of derivative), to get a new spline as np.ndarray at x with its nth derivative as:
der1 = spl(x, 1)
der2 = spl(x, 2)

How to include known parameter that changes over time in solve_bvp

I am trying to use scipy's solve_bvp in python to solve differential equations that depend on a known parameter that changes over time. I have this parameter saved in a numpy array. However, when I try to use this array in the derivatives function, I get the following error ValueError: operands could not be broadcast together with shapes (10,) (11,).
Below is a simplified version of my code. I want the variable d2 to take certain values at different times according to an array, d2_set_values. The differential equations for some of the 12 variables then depend on d2. I hope it's clear from this code what I'm trying to achieve.
import numpy as np
from scipy.integrate import solve_bvp
t = np.linspace(0, 10, 11)
# Known parameter that changes over time
d2_set_values = np.zeros(t.size)
d2_set_values[:4] = 0.1
d2_set_values[4:8] = 0.2
d2_set_values[8:] = 0.1
# Initialise y vector
y = np.zeros((12, t.size))
# ODEs
def fun(x, y):
S1, I1, R1, S2, I2, R2, lamS1, lamI1, lamR1, lamS2, lamI2, lamR2 = y
d1 = 0.5*(I1 + 0.1*I2)*(lamS1 - lamI1)
d2 = d2_set_values
dS1dt = -0.5*S1*(1-d1)*(I1 + 0.1*I2)
dS2dt = -0.5*S2*(1-d2)*(I2 + 0.1*I1)
dI1dt = 0.5*S1*(1-d1)*(I1 + 0.1*I2) - 0.2*I1
dI2dt = 0.5*S2*(1-d2)*(I2 + 0.1*I1) - 0.2*I2
dR1dt = 0.2*I1
dR2dt = 0.2*I2
dlamS1dt = 0.5*(1-d1)*S1*lamS1
dlamS2dt = 0.5*(1-d2)*S2*lamS2
dlamI1dt = 0.5*(1-d1)*I1*lamI1
dlamI2dt = 0.5*(1-d2)*I2*lamI2
dlamR1dt = lamR1
dlamR2dt = lamR2
return np.vstack((dS1dt, dI1dt, dR1dt, dS2dt, dI2dt, dR2dt, dlamS1dt, dlamI1dt, dlamR1dt, dlamS2dt, dlamI2dt, dlamR2dt))
# Boundary conditions
def bc(ya, yb):
return np.array([ya[0]-0.99, ya[1]-0.01, ya[2]-0., ya[3]-1.0, ya[4]-0., ya[5]-0.,
yb[6]-0., yb[7]-1., yb[8]-0., yb[9]-0, yb[10]-0, yb[11]-0])
# Run the solver
sol = solve_bvp(fun, bc, t, y)
I have even tried reducing the size of d2_set_values by one, but that doesn't solve the issue.
Any help I can get would be much appreciated!

Multiple return using scipy.odeint method in Python

I am trying to use scipy.odeint() method in order to solve an second order partial derivative function.
I can do that for a single value of constant k, which is a constant of the function I have.
But I want to try this solution for many values of k.
To do so, I included the values that I want in a list k, and going through a loop I want to plug in these values for the final solution as arguments.
However, I am getting an error
error: Extra arguments must be in a tuple
import numpy as np
from scipy.integrate import odeint
### Code with a single value of K.THAT WORKS FINE!!!! ###
k = 1 #attributes to be changed
t = [0.1,0.2,0.3] #Data
init = [45,0] #initial values
#Function to apply an integration
def f(init, t, args=(k,)):
dOdt = init[1]
dwdt = -np.cos(init[0]) + k*dOdt
return [dOdt, dwdt]
#integrating function that returns a list of 2D numpy arrays
zCH = odeint(f,init,t)
################################################################
### Code that DOES NOT WORK!###
k = [1,2,3] #attributes to be changed
t = [0.1,0.2,0.3] #Data
init = [45,0] #initial values
#Function to apply an integration
def f(init, t, args=(k,)):
dOdt = init[1]
dwdt = -np.cos(init[0]) + k*dOdt
return [dOdt, dwdt]
solutions = []
for i in k:
#integrating function that returns a list of 2D numpy arrays
zCH = odeint(f,init,t,(k[i-1]))
solutions.append(zCH)```
It has to do with the way you are passing k into your function f().
The following changes the value of k on each iteration
k_list = [1,2,3] #attributes to be changed
t = [0.1,0.2,0.3] #Data
init = [45,0] #initial values
#Function to apply an integration
def f(init, t, args=(k,)):
dOdt = init[1]
dwdt = -np.cos(init[0]) + k*dOdt
return [dOdt, dwdt]
solutions = []
for k in k_list:
#integrating function that returns a list of 2D numpy arrays
zCH = odeint(f, init, t)
solutions.append(zCH)

How to use scipy.optimize with array function?

I created a custom-made exponential smoothing function. I want to optimize its parameters thanks to scipy.optimize.basinhopping.
If I only optimize the function for one time serie it works.
import numpy as np
from scipy.optimize import basinhopping
d = [1,2,3,4]
cols = len(d)
def simple_exp_smooth(inputs):
ini,alpha = inputs
f = np.full(cols,np.nan)
f[0] = ini
for t in range(1,cols):
f[t] = alpha*d[t-1]+(1-alpha)*f[t-1]
error = sum(abs(f[1:] - d[1:]))
return error
func = simple_exp_smooth
bounds = np.array([(0,4),(0.0, 1.0)])
x0 = (1,0.1)
res = basinhopping(func, x0, minimizer_kwargs={'bounds': bounds},stepsize=0.1,niter=45)
One of the issue of this is that it is slow if you have 1000 time series to optimize. So I have created an array version to perform the exponential smoothing of multiple time series at once.
def simple(inputs):
a0,alpha = inputs
a = np.full([rows,cols],np.nan)
a[:,0] = a0
for t in range(1,cols):
a[:,t] = alpha*d[:,t]+(1-alpha)*a[:,t-1]
MAE = abs(d - a).mean(axis=1)/d.mean(axis=1)
return sum(MAE)
d = np.array([[1,2,3,4],
[10,20,30,40]])
rows, cols = d.shape
a0_bound = np.vstack((d.min(axis=1),d.max(axis=1))).T
a0_ini = d.mean(axis=1)
bounds = ([a0_bound,(0.0, 1.0)])
x0 = (a0_ini,0.2)
res = basinhopping(simple, x0, minimizer_kwargs={'bounds': bounds},stepsize=0.1)
But now, the basinhopping gives me this error:
bounds = [(None if l == -np.inf else l, None if u == np.inf else u) for l, u in bounds]
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Is there any way for me to use basinhopping to optimize my whole array at once instead of each line one by one?

numpy contour plot with cost function

a = np.array(x)
b = np.array(y)
a_transpose = a.transpose()
a_trans_times_a = np.dot(a_transpose,a)
a_trans_times_b = np.dot(a_transpose,b)
def cost(theta):
x_times_theta = np.dot(a, theta)
_y_minus_x_theta = b - x_times_theta
_y_minus_x_theta_transpose = _y_minus_x_theta.transpose()
return np.dot(_y_minus_x_theta_transpose, _y_minus_x_theta)
n = 256
p = np.linspace(-100,100, n)
q= np.linspace(-100,100, n)
P, Q = np.meshgrid(p,q)
pl.contourf(P, Q, cost(np.array([P,Q])) ,8, alpha =0.75, cmap = 'jet')
C = pl.contour(P,Q, cost(np.array([P,Q])), 8, colors = 'black', linewidth = 0.5 )
Hi, I'm trying to make a contour plot using a cost function on two parameters, involving matrix multiplication. I've tested the cost function and it works properly in interactive session. However, running it on a linspace makes it get error "ValueError: objects are not aligned". I understand now that it has to do with how I structure P,Q. Would the solution involve writing a for loop to explicitly get an array of outputs? How would I write this?
EDIT: a,b are matrices with correct size. The cost function takes a 2-vector and outputs a number.
It's hard to know exactly without having at hand the shapes of a and b, but this error is probably caused by np.array[P,Q] being a 3-dimensional array. It seems you expect it to be 2-dimensional and for np.dot(a,theta) to perform matrix multiplication.
Presumably you want theta to be the the angular coordinate at a particular x and y value. In this case you should do
theta = np.arctan2(Q,P) #this is a 2D array of theta coordinates
costarray = cost(theta)
pl.contourf(P,Q,costarray,8,alpha=0.75,cmap='jet')

Categories

Resources