I have matrices where elements can be defined as arithmetic expressions and have written Python code to optimise parameters in these expressions in order to minimize particular eigenvalues of the matrix. I have used scipy to do this, but was wondering if it is possible with NLopt as I would like to try a few more algorithms which it has (derivative free variants).
In scipy I would do something like this:
import numpy as np
from scipy.linalg import eig
from scipy.optimize import minimize
def my_func(x):
y, w = x
arr = np.array([[y+w,-2],[-2,w-2*(w+y)]])
ev, ew=eig(arr)
return ev[0]
x0 = np.array([10, 3.45]) # Initial guess
minimize(my_func, x0)
In NLopt I have tried this:
import numpy as np
from scipy.linalg import eig
import nlopt
def my_func(x,grad):
arr = np.array([[x[0]+x[1],-2],[-2,x[1]-2*(x[1]+x[0])]])
ev, ew=eig(arr)
return ev[0]
opt = nlopt.opt(nlopt.LN_BOBYQA, 2)
opt.set_lower_bounds([1.0,1.0])
opt.set_min_objective(my_func)
opt.set_xtol_rel(1e-7)
x = opt.optimize([10.0, 3.5])
minf = opt.last_optimum_value()
print "optimum at ", x[0],x[1]
print "minimum value = ", minf
print "result code = ", opt.last_optimize_result()
This returns:
ValueError: nlopt invalid argument
Is NLopt able to process this problem?
my_func should return double, posted sample return complex
print(type(ev[0]))
None
<class 'numpy.complex128'>
ev[0]
(13.607794065928395+0j)
correct version of my_func:
def my_func(x, grad):
arr = np.array([[x[0]+x[1],-2],[-2,x[1]-2*(x[1]+x[0])]])
ev, ew=eig(arr)
return ev[0].real
updated sample returns:
optimum at [ 1. 1.]
minimum value = 2.7015621187164243
result code = 4
Related
I am having trouble using the Jacobian from JAX with scipy.root. In the below example, the root works without the Jacobian, while it fails with the Jacobian. Any ideas on what I need to rewrite in order to get the code below working with the Jacobian?
from jax import jacfwd
from scipy.optimize import root
import numpy as np
def objectFunction(valuesEndo, varNamesEndo, valuesExo, varNamesExo, equations):
for i in range(len(varNamesExo)):
exec("%s = %.10f" %(varNamesExo[i], valuesExo[i]))
for i in range(len(varNamesEndo)):
exec("%s = %.10f" %(varNamesEndo[i], valuesEndo[i]))
equationVector = np.zeros(len(equations))
for i in range(len(equations)):
exec('equationVector[%d] = eval(equations[%d])' %(i, i))
return equationVector
varNamesEndo = ['x', 'y']
valuesEndoInitialGuess = [1., 1.]
varNamesExo = ['a', 'b']
valuesExo = [1., 1.]
equations = ['a*x+b*y**2-4',
'np.exp(x) + x*y - 3']
equations = ['a*x**2 + b*y**2',
'a*x**2 - b*y**2']
# Without Jacobian
sol1 = root(fun=objectFunction,
x0=valuesEndoInitialGuess,
args=(varNamesEndo, valuesExo, varNamesExo, equations))
#----> Works
# With Jacobian
jac = jacfwd(objectFunction)
sol2 = root(fun=objectFunction,
x0=valuesEndoInitialGuess,
args=(varNamesEndo, valuesExo, varNamesExo, equations),
jac=jac)
#----> Not woring
At least there seems to be problems with the line
for i in range(len(varNamesEndo)):
exec("%s = %.10f" %(varNamesEndo[i], valuesEndo[i]))
There are two issues:
to perform automatic differentiation, JAX relies on replacing values with tracers. This means your approach of printing and evaluating the string representation of the value will not work.
additionally, you are attempting to assign traced values to a standard numpy array. You should use a JAX array instead, as it knows how to handle traced values.
With this in mind, you can rewrite your function this way and it should work, so long as your equations only use Python arithmetic operations and jax functions (not things like np.exp):
import jax.numpy as jnp
def objectFunction(valuesEndo, varNamesEndo, valuesExo, varNamesExo, equations):
for i in range(len(varNamesExo)):
exec("%s = valuesExo[%d]" %(varNamesExo[i], i))
for i in range(len(varNamesEndo)):
exec("%s = valuesEndo[%d]" %(varNamesEndo[i], i))
equationVector = jnp.zeros(len(equations))
for i in range(len(equations)):
equationVector = equationVector.at[i].set(eval(equations[i]))
return equationVector
Side-note: this kind of approach based on setting variable names using exec is really brittle and error-prone; I'd suggest an approach based on building explicit namespaces for evaluating your equations. For example something like this:
def objectFunction(valuesEndo, varNamesEndo, valuesExo, varNamesExo, equations):
namespace = {
**dict(zip(varNamesEndo, valuesEndo)),
**dict(zip(varNamesExo, valuesExo))
}
return jnp.array([eval(eqn, namespace) for eqn in equations])
I am trying to minimize the quadratic weighted kappa function using scipy minimize fmin Powell function.
The two functions digitize_train and digitize_train2 gives 100% EXACT same results.
However, when I tried to use these functions with scipy minimize the second method fails.
I have been trying to debug the problem for hours, to my surprise despite the two functions being exact same the bumpy digitize function fails to give fmin Powell mimimization.
How to fix the error?
Question
How to use numpy.digitize in scipy fmin_powell?
SETUP
# imports
import numpy as np
import pandas as pd
import seaborn as sns
from scipy.optimize import fmin_powell
from sklearn import metrics
# data
train_labels = [1,1,8,7,6,5,3,2,4,4]
train_preds = [0.1,1.2,8.9, 7.6, 5.5, 5.5, 2.99, 2.4, 3.5, 4.0]
guess_lst = (1.5,2.9,3.1,4.5,5.5,6.1,7.1)
# functions
# here I am trying the convert real numbers -inf to +inf to integers 1 to 8
def digitize_train(train_preds, guess_lst):
(x1,x2,x3,x4,x5,x6,x7) = list(guess_lst)
res = []
for y in list(train_preds):
if y < x1:
res.append(1)
elif y < x2:
res.append(2)
elif y < x3:
res.append(3)
elif y < x4:
res.append(4)
elif y < x5:
res.append(5)
elif y < x6:
res.append(6)
elif y < x7:
res.append(7)
else: res.append(8)
return res
def digitize_train2(train_preds, guess_lst):
return np.digitize(train_preds,guess_lst) + 1
# compare two functions
df = pd.DataFrame({'train_labels': train_labels,
'train_preds': train_preds,
'method_1': digitize_train(train_preds, guess_lst),
'method_2': digitize_train2(train_preds, guess_lst)
})
df
** NOTE: The two functions are exact same**
Method 1: without numpy digitize runs fine
# using fmin_powel for method 1
def get_offsets_minimizing_train_preds_kappa(guess_lst):
res = digitize_train(train_preds, guess_lst)
return - metrics.cohen_kappa_score(train_labels, res,weights='quadratic')
offsets = fmin_powell(get_offsets_minimizing_train_preds_kappa, guess_lst, disp = True)
print(offsets)
Method 2: using numpy digitize fails
# using fmin_powell for method 2
def get_offsets_minimizing_train_preds_kappa2(guess_lst):
res = digitize_train2(train_preds, guess_lst)
return -metrics.cohen_kappa_score(train_labels, res,weights='quadratic')
offsets = fmin_powell(get_offsets_minimizing_train_preds_kappa2, guess_lst, disp = True)
print(offsets)
How to use numpy digitize method?
Update
As per suggestions I tried pandas cut, but still gives error.
ValueError: bins must increase monotonically.
# using fmin_powell for method 3
def get_offsets_minimizing_train_preds_kappa3(guess_lst):
res = pd.cut(train_preds, bins=[-np.inf] + list(guess_lst) + [np.inf],
right=False)
res = pd.Series(res).cat.codes + 1
res = res.to_numpy()
return -metrics.cohen_kappa_score(train_labels, res,weights='quadratic')
offsets = fmin_powell(get_offsets_minimizing_train_preds_kappa3, guess_lst, disp = True)
print(offsets)
It seems that during the minimization process, the value in guest_lst are not monotonically increasing anymore, one work around is to pass the sorted of guest_lst in digitize like:
def digitize_train2(train_preds, guess_lst):
return np.digitize(train_preds,sorted(guess_lst)) + 1
and you get
# using fmin_powell for method 2
def get_offsets_minimizing_train_preds_kappa2(guess_lst):
res = digitize_train2(train_preds, guess_lst)
return -metrics.cohen_kappa_score(train_labels, res,weights='quadratic')
offsets = fmin_powell(get_offsets_minimizing_train_preds_kappa2, guess_lst, disp = True)
print(offsets)
Optimization terminated successfully.
Current function value: -0.990792
Iterations: 2
Function evaluations: 400
[1.5 2.7015062 3.1 4.50379942 4.72643334 8.12463415
7.13652301]
x' = f(x,y,t)
y' = g(x,y,t)
Initial conditions have been given as x0 and y0 with t0. Find the solution graph in the range t0 to a.
I have tried doing this for non-coupled equations but there seems to be a problem there as well. I have to solve this exactly using this function so other functions are not the options.
from numpy import *
from matplotlib import pyplot as plt
def f(t,x):
return -x
import scipy
from scipy import integrate as inte
solution = inte.RK45(f, 0 , [1] , 10 ,1, 0.001, e**-6)
print (solution)
I expect the output to be an array of all the values.
But <scipy.integrate._ivp.rk.RK45 at 0x1988ba806d8> is what I get.
You need to collect data with calling step() function:
from math import e
from scipy import integrate as inte
def f(t,x):
return -x
solution = inte.RK45(f, 0 , [1] , 10 ,1, 0.001, e**-6)
# collect data
t_values = []
y_values = []
for i in range(100):
# get solution step state
solution.step()
t_values.append(solution.t)
y_values.append(solution.y[0])
# break loop after modeling is finished
if solution.status == 'finished':
break
data = zip(t_values, y_values)
Output:
(0.12831714796342164, 0.879574381033538)
(1.1283171479634215, 0.3239765636806864)
(2.1283171479634215, 0.11933136762238628)
(3.1283171479634215, 0.043953720407578944)
(4.128317147963422, 0.01618962035012491)
(5.128317147963422, 0.005963176828962677)
(6.128317147963422, 0.002196436798667919)
(7.128317147963422, 0.0008090208875093502)
(8.128317147963422, 0.00029798936023261037)
(9.128317147963422, 0.0001097594143523445)
(10, 4.5927433621121034e-05)
I want to integrate a function that has no closed form solution with an unknown variable and then plot vs the unknown variable. To try a simpler test, I tried to use the integral of f(x,c) = (x^2+c), integrated with respect to x and plot with different values of c. However, the code below gets the error
only size-1 arrays can be converted to Python scalars
even though the integral of a number, e.g. integral(5), seems to return the correct scalar value.
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
def f(x,c):
return x**2+c
def integral(c):
return integrate.quad(f,0,10, args = (c,))[0]
y = np.linspace(0,20,200)
plt.plot(y, integral(y))
You pass a numpy array as the argument c while you wanted to integrate over x for all the items of c. Therefore you can use this:
def f(x,c):
return x**2+c
def integrate_f(c):
result = np.zeros(len(c))
counter = 0
for item in c:
result[counter] = integrate.quad(f,0,10, args = (item))[0]
counter +=1
return result
c_array = np.linspace(0,1,200)
plt.plot(c_array, integrate_f(c_array))
onno was a bit faster. But here is my similar solution. You need to loop over all the different c:
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
def f(x,c):
return x**2+c
def getIntegral(c_list):
result = []
for c in c_list:
integral = integrate.quad(f,0,10,args = c)[0]
result.append(integral)
return result
if __name__ == "__main__":
c_list = np.linspace(0,20,200)
plt.plot(c_list, getIntegral(c_list))
plt.show()
I'm trying to optimize parameters using data for my model with scipy optimize but scipy fails to minimize the function and find values of the parameters. It just returns the initial guess which the user gives as input.Also, it gives the following error: RuntimeWarning: invalid value encountered in reduce.
import pandas as pd
import numpy as np
from math import log10
import math
import scipy.optimize as op
from scipy.integrate import odeint
df1 = pd.read_csv('dataset1.csv')
z=df1.loc[: , "z"]
za=z.as_matrix(columns=None)
mu=df1.loc[: , "mu"]
mua=mu.as_matrix(columns=None)
si=df1.loc[: , "sig"]
sia=si.as_matrix(columns=None)
c = 299792.458;
H0 = 70;
m_t=0.3
d_t=0.7
mu0 = 25 + 5*log10(c/H0);
def model(x,t,m,d):
dydt = 1/(math.sqrt((((1+x)**2)*(1+m*x))-(x*d*(2+x))))
return dydt
def Io(zb,m,d):
return odeint(model,0,zb, args=(m,d))
def lnlike(theta,zb, mub,sib):
m, d = theta
isia2 = 1.0/np.square(sib)
return 0.5*(np.sum(((((5*(np.log10((1+zb)*Io(zb,m,d)))+mu0)-mub)**2)*isia2)- np.log(isia2)))
nll = lambda *args: -lnlike(*args)
result = op.minimize(nll, [m_t, d_t], args=(za, mua,sia))
m_ml, d_ml = result["x"]
print(m_ml, d_ml)
I think scipy is not able to handle illegal values generated due to the square root.If so, how can one bypass the illegal values?
the dataset1 file can be found at the link:https://drive.google.com/file/d/1HDzQ7rz_u9y63ECNkhtB49T2KBvu0qu6/view?usp=sharing