Reputation: 2024
I am struggling with the following setup.
My data is as follows:
Group ID Wt Coeff Coeff*Wt
------ --- ------ ------- -------
Group1 A 10.00% 1.00000 0.100
Group1 B 10.00% 1.00000 0.100
Group1 C 10.00% 3.00005 0.300
Group2 D 10.00% 1.00000 0.100
Group2 E 10.00% 1.00000 0.100
Group2 F 10.00% 1.00000 0.100
Group2 G 10.00% 7.80016 0.780
Group3 H 10.00% 7.80485 0.780
Group3 I 10.00% 1.00000 0.100
Group3 J 10.00% 0.39529 0.040
Objective function: Fmin = mimimize(sum of weights * coeff)
I need to implement following constraints:
Sum of Weights*Coeff of Group1 = 20% of total minimized fmin
Sum of Weights*Coeff of Group1 = 45% of total minimized fmin
Sum of Weights*Coeff of Group1 = 35% of total minimized fmin
And the following bounding conditions:
Weights <=10% and Weights > 0.30%
And
Sum of weights = 100%
I am trying to acomplish this with the following code.
I don't know why this is not working:
from scipy.optimize import linprog
c = [ 1.0000 ,1.0000 ,3.0001 ,1.0000 ,1.0000 ,1.0000 ,7.8002 ,7.8049 ,1.0000 ,0.3953 ]
groupPerID = ['Group1','Group1','Group1','Group2','Group2','Group2','Group2','Group3','Group3','Group3']
groupList = ['Group1','Group2','Group3']
groupUpperBound = [0.20,0.45,0.40]
A_eq_list = []
A_eq_list.append([1]*len(c))
b_eq_list = [1]
for idx,currentGroup in enumerate(groupList):
matches = [i for i in range(len(groupPerID)) if groupPerID[i] == currentGroup]
currentGroupUB = groupUpperBound[idx]
x_list = [float(-1*currentGroupUB*coeff) for coeff in c]
for idx in matches:
x_list[idx] = float((1-currentGroupUB)*c[idx])
A_eq_list.append(x_list)
b_eq_list.extend([0]*len(groupUpperBound))
res = linprog(c, A_eq=A_eq_list, b_eq=b_eq_list,bounds =(0.003,0.1),options={'tol':0.05})
print(res)
Can someone please point out what mistake I am making?
Upvotes: 0
Views: 406
Reputation: 2325
So I implemented it in my scipy
wrapper symfit
which takes care of all the boiler plate code. It now works, except for the fact that I did not yet implement your bounds on the weights. However, I think those are wrong as stated in your question, because the only way to meet the constraint that all the weight should sum up to 1, is to set them all to the upper limit of 0.1. Other than that, here is my attempt:
from symfit import parameters, Minimize, Variable, Eq
import numpy as np
# Make 10 weight parameters w_i to optimize
weights = parameters(','.join('w_{}'.format(i) for i in range(1, 11)))
c = np.array([1.0000, 1.0000, 3.0001, 1.0000, 1.0000, 1.0000, 7.8002, 7.8049, 1.0000, 0.3953])
f = Variable()
for w_i in weights:
w_i.min = 0.003
w_i.max = 1.0
w_i.value = 0.1
sum_of_group_1 = sum(c_i * w_i for c_i, w_i in zip(c, weights)[0:3])
sum_of_group_2 = sum(c_i * w_i for c_i, w_i in zip(c, weights)[3:7])
sum_of_group_3 = sum(c_i * w_i for c_i, w_i in zip(c, weights)[7:10])
# Function to minimize
model = {f: sum_of_group_1 + sum_of_group_2 + sum_of_group_3}
constraints = [
Eq(0.20 * sum_of_group_1, 0.45 * sum_of_group_2),
Eq(0.20 * sum_of_group_1, 0.35 * sum_of_group_3),
Eq(sum(weights), 1)
]
fit = Minimize(model, constraints=constraints)
fit.eval_jacobian = None # Workaround needed because f is just a scalar, not an array
fit_result = fit.execute()
print(fit_result)
print(sum(fit_result.value(w) for w in weights)) # >>> 1.0
You can read more in the docs here.
Upvotes: 1