I am trying to minimize a function with 3 variables x[0], x[1] and x[2]. A, B, C, and D are dataframes with dimensions 72×33. The optimization works as intended when I don’t use constraints, however I also care for the constrainted case. For the constrainted case, I want
A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
to be equal to or greater than zero for all combinations of t and i. To achieve this, I have defined constraints in the following way:
cons=[]
def f(a):
def g(x):
return A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
return g
for t in range (72):
for i in range(33):
cons.append({'type':'ineq', 'fun': f(t)})
While I am getting the right number of constraints (i.e. len(cons) = 72*33 = 2376), the optimization results do not satisfy the constraints that I had in mind, meaning it results in values for x[0], x[1] and x[2] for which
A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
is smaller than zero for many t, i. I have ascertained that result.success = True, so the optimization suddenly stopping can be ruled out as a potential problem. While looking for a solution to this problem, I have found this case of someone trying to iterate constraints in scipy aswell, but they only iterated over one range rather than over two and I was not able to modify their solution to work for my case.
Edit:
As per request, I have added the whole optimization problem:
def objective(x):
list1 = []
for t in range(72):
k = sum([(A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33) for i in range(33)])
u = ((1+k)**(1-5))/(1-5)
list1.append(u)
list2 = [x / (-73) for x in list1]
return sum(list2)
cons=[]
def f(a):
def g(x):
return A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
return g
for t in range (72):
for i in range(33):
cons.append({'type':'ineq', 'fun': f(t)})
# For all three x variables, define 1.0 as an initial guess for the optimization algorithm to work with
n = 3
x0 = np.zeros(n)
x0[0] = 1.0
x0[1] = 1.0
x0[2] = 1.0
# Print the function value that is obtained by plugging in the initial guesses
print('Initial Function Value: ' + str(objective(x0)))
solution = minimize(objective,x0,method='SLSQP',constraints=cons)
x = solution.x
# Print the optimized function value
print('Final Function Value: ' + str(objective(x)))
# Print the x for which the function is optimized
print('Solution')
print('x1 = ' + str(x[0]))
print('x2 = ' + str(x[1]))
print('x3 = ' + str(x[2]))
>Solution :
Your function f doesn’t make any sense since the function g doesn’t depend on a. If you really want f to return a new function depending on the indices i and t, f should be a function of the two indices:
cons=[]
def f(t, i):
def g(x):
return A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
return g
for t in range (72):
for i in range(33):
cons.append({'type':'ineq', 'fun': f(t, i)})
Note also that you could easily define the constraint functions on the fly thanks to lambda expressions:
cons = []
for t in range(72):
for i in range(33):
cons.append({'type': 'ineq', 'fun': lambda x, t=t, i=i: A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33})