When I use Scipy minimize in the example below, it finds a non minimal solution - can someone please help me modify this to find the minimum?
I have four constraints: 1) guesses of guess array must sum to one, 2) guesses must be > 0, and 3) any guess from the initial guess array that is over the threshold must be set equal to the threshold and held constant.
Edit: 4th constraint - no guess can be greater than the threshold.
I didn't use constraints for the last 3 constraints I have. For constraint 2, I used a bounds. For constraint 3, I did that in the performMinimize method before calling scipy's minimize method.
from scipy.optimize import minimize
import numpy as np
from numpy import array
def objective(initialGuesses, threshold):
overThreshold = initialGuesses[np.where((threshold < initialGuesses[:]))]
underThreshold = initialGuesses[np.where((threshold >= initialGuesses[:]))]
overThreshold[:] = threshold
sumUnderThreshold = np.sum(underThreshold)
oppositeSumOverTreshold = 1 - np.sum(overThreshold)
transformedGuess = underThreshold / sumUnderThreshold * oppositeSumOverTreshold
thresholdedResults = np.concatenate((overThreshold, transformedGuess))
squaredError = np.square(thresholdedResults - initialGuesses) / initialGuesses
return np.sum(squaredError)
def performMinimizeSo(initialGuesses, threshold):
overThreshold = initialGuesses[np.where((threshold < initialGuesses[:]))]
overThreshold[:] = threshold
underThreshold = initialGuesses[np.where((threshold >= initialGuesses[:]))]
# Says one minus the sum of all variables minus the sum of weights over the threshold must be zero
cons = ({'type': 'eq', 'fun': lambda x: 1 - sum(x) - sum(overThreshold)})
minimum = minimize(objective, underThreshold, args=(threshold), method='SLSQP',
constraints=cons,
bounds=[(0, None) for i in range(len(underThreshold))],
)
allGuesses = np.append(overThreshold, minimum.x)
return allGuesses
def testCaseForSo():
initialGuesses = array([
[0.1],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.05],
[0.025],
[0.025]])
threshold = .09
output = (performMinimizeSo(initialGuesses, threshold))
print(output)
testCaseForSo()
The minimal answer, as found by excel, is this:
0.09
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.050555556
0.025277778
0.025277778
Scipy minimize thinks this is the answer, which is close but not the correct minimum:
0.09
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526315
0.050526317
0.050526317
0.050526317
0.050526317
0.050526317
0.050526317
0.050526317
0.025526317
0.025526317
Here is what the attributes of the output look like after running scipy.minimize (as you can see, scipy thinks it found the minimum even though we know it didn't):
minimum.sucess == True
minimum.status == 0
minimum.message == 'Optimization terminated successfully'