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?
Related
I have an array of Pe of shape (5100,5100). I am trying to find the neighbor indices using the code below but for this shape of Pe, the computational time is 100 seconds. Is there a more time efficient way to do it?
def get_neighbor_indices(position, dimensions):
'''
dimensions is a shape of np.array
'''
i, j = position
indices = [(i+1,j), (i-1,j), (i,j+1), (i,j-1)]
return [
(i,j) for i,j in indices
if i>=0 and i<dimensions[0]
and j>=0 and j<dimensions[1]
]
def iterate_array(init_i, init_j, arr, condition_func):
'''
arr is an instance of np.array
condition_func is a function (value) => boolean
'''
indices_to_check = [(init_i,init_j)]
checked_indices = set()
result = []
t0 = None
t1 = None
timestamps = []
while indices_to_check:
pos = indices_to_check.pop()
if pos in checked_indices:
continue
item = arr[pos]
checked_indices.add(pos)
if condition_func(item):
result.append(item)
t1=time.time()
if(t0==None):
t0=t1
timestamps.append(t1-t0)
indices_to_check.extend(
get_neighbor_indices(pos, arr.shape)
)
return result,timestamps
Visited_Elements,timestamps=iterate_array(0,0, Pe, lambda x : x < Pin0)
With scipy and a slight change in the way the filter condition is described this can be made rather fast:
import numpy as np
from scipy.ndimage import label
import time
def collect_array(init_i, init_j, arr, condition_arr):
t0=time.time()
if not condition_arr[init_i, init_j]:
return [], time.time() - t0
islands = label(condition_arr)[0]
mask = islands != islands[init_i, init_j]
mx = np.ma.masked_array(Pe, mask)
return mx.compressed(), time.time() - t0
# Trying it out
Pe = np.random.rand(5100, 5100)
Pin0 = 0.7
Visited_Elements, timestamp = collect_array(0,0, Pe, Pe < Pin0)
print(Visited_Elements,timestamp)
The core of the code is the label function and the fact that the condition function is replaced by a boolean array.
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.
I am trying to use scipy.optimize.minimize to fit parameters for a multivariate function, however, regardless of how many noise free data points I am providing to the optimizer, the optimizer could not converge to a correct (or close) answer.
I wonder if there is a mistake in the way I am using the optimizer but I have been scratching my head to find the mistake. I would appreciate any advice or guesses, thanks!
import numpy as np
from scipy.optimize import minimize
import math
def get_transform(ai,aj,ak,x,y,z):
i,j,k = 0, 1, 2
si, sj, sk = math.sin(ai), math.sin(aj), math.sin(ak)
ci, cj, ck = math.cos(ai), math.cos(aj), math.cos(ak)
cc, cs = ci*ck, ci*sk
sc, ss = si*ck, si*sk
M = np.identity(4)
M[i, i] = cj*ck
M[i, j] = sj*sc-cs
M[i, k] = sj*cc+ss
M[j, i] = cj*sk
M[j, j] = sj*ss+cc
M[j, k] = sj*cs-sc
M[k, i] = -sj
M[k, j] = cj*si
M[k, k] = cj*ci
M[0, 3] = x
M[1, 3] = y
M[2, 3] = z
return M
def camera_intrinsic(fx, ppx, fy, ppy):
K = np.zeros((3, 3), dtype='float64')
K[0, 0], K[0, 2] = fx, ppx
K[1, 1], K[1, 2] = fy, ppy
K[2, 2] = 1
return K
def apply_transform(p, matrix):
rotation = matrix[0:3,0:3]
T = np.array([matrix[0][3],matrix[1][3],matrix[2][3]])
transformed = (np.dot(rotation, p.T).T)+T
return transformed
def project(points_3D,internal_calibration):
points_3D = points_3D.T
projections_2d = np.zeros((2, points_3D.shape[1]), dtype='float32')
camera_projection = (internal_calibration).dot(points_3D)
projections_2d[0, :] = camera_projection[0, :]/camera_projection[2, :]
projections_2d[1, :] = camera_projection[1, :]/camera_projection[2, :]
return projections_2d.T
def error(x):
global points,pixels
transform = get_transform(x[0],x[1],x[2],x[3],x[4],x[5])
points_transfered = apply_transform(points, transform)
internal_calibration = camera_intrinsic(x[6],x[7],x[8],x[9])
projected = project(points_transfered,internal_calibration)
# print(((projected-pixels)**2).mean())
return ((projected-pixels)**2).mean()
def generate(points, x):
transform = get_transform(x[0],x[1],x[2],x[3],x[4],x[5])
points_transfered = apply_transform(points, transform)
internal_calibration = camera_intrinsic(x[6],x[7],x[8],x[9])
projected = project(points_transfered,internal_calibration)
return projected
points = np.random.rand(100,3)
x_initial = np.random.rand(10)
pixels = generate(points,x_initial)
x_guess = np.random.rand(10)
results = minimize(error,x_guess, method='nelder-mead', tol = 1e-15)
x = results.x
print(x_initial)
print(x)
You are solving least squares problem, but trying to optimize it using a solver that minimizes a scalar function. While it can possibly solve the problem, it does so very inefficiently. It can require much more iterations or can fail to converge at all.
The better way is to use least_squares instead of minimize.
For it to work properly you should modify error function by returning 1D numpy array instead of a scalar:
def error(x):
...
return (projected-pixels).flatten()
Then call least_squares:
results = least_squares(error, x_guess)
x = results.x
print(x_initial)
print(x)
print('error:', np.linalg.norm(error(x)))
Also, error(x) currently returns array of float32, because an array of float32 is created in project. It should be replaced by float64, otherwise minimization fails to converge, because most of gradients become zeros when 32 bit precision is used.
def project(points_3D,internal_calibration):
...
projections_2d = np.zeros((2, points_3D.shape[1]), dtype='float64')
With these modifications the solver converges to the solution most of the times, but can sometimes fail to do so. It happens because you generate the problem randomly, so in some cases the problem may be degenerate or make no physical sense. Such cases should be investigated on their own.
It can also help to use a robust loss, such as 'arctan', instead of linear loss:
results = least_squares(error, x_guess, loss='arctan')
Result:
[0.68589904 0.68782115 0.83299068 0.02360941 0.19367124 0.54715374
0.37609235 0.62190714 0.98824796 0.88385802]
[0.68589904 0.68782115 0.83299068 0.02360941 0.19367124 0.54715374
0.37609235 0.62190714 0.98824796 0.88385802]
error: 1.2269443642313758e-12
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)
I am coming from a java background and new to numpy and pandas.
I want to translate the following pseudo code into python.
theta[0...D] - numpy
input[1...D][0...N-1] - Pandas data frame
PSEUDO CODE:
mean = theta[0]
for(row = 0 to N-1)
for(col = 1 to D)
mean += theta[col] * input[row][col]
Implementation:
class simulator:
theta = np.array([])
stddev = 0
def __init__(self, v_coefficents, v_stddev):
self.theta = v_coefficents
self.stddev = v_stddev
def sim( self, input ):
mean = self.theta[0]
D = input.shape[0]
N = input.shape[1]
for index, row in input.iterrows():
mean = self.theta[0]
for i in range(D):
mean += self.theta[i+1] *row['y']
I am concerned with iteration in the last line of code:
mean += self.theta[i+1] *row['y'].
Since you are working with NumPy, I would suggest extracting the pandas dataframe as an array and then we would have the luxury of working with theta and the extracted version of input both as arrays.
Thus, starting off we would have the array as -
input_arr = input.values
Then, the translation of the pseudo code would be -
mean = theta[0]
for row in range(N):
for col in range(1,D+1):
mean += theta[col] * input_arr[row,col]
To perform the sum-reductions, with NumPy supporting vectorized operations and broadcasting, we would have the output with simply -
mean = theta[0] + (theta[1:D+1]*input_arr[:,1:D+1]).sum()
This could be optimized further with np.dot as a matrix-multiplication, like so -
mean = theta[0] + np.dot(input_arr[:,1:D+1], theta[1:D+1]).sum()
Please note that if you meant that input has a length of D-1, then we need few edits :
Loopy code would have : input_arr[row,col-1] instead of input_arr[row,col].
Vectorized codes would have : input_arr instead of input_arr[:,1:D+1].
Sample run based on comments -
In [71]: df = {'y' : [1,2,3,4,5]}
...: data_frame = pd.DataFrame(df)
...: test_coefficients = np.array([1,2,3,4,5,6])
...:
In [79]: input_arr = data_frame.values
...: theta = test_coefficients
...:
In [80]: theta[0] + np.dot(input_arr[:,0], theta[1:])
Out[80]: 71