Skip to content
Advertisement

Scipy Optimise (minimize) not giving correct results

I am trying to do a simple minimisation as below using SciPy optimise, but the expected results are NOT matching the optimiser output:

x0 = [0.2, 0.2, 0.2, 0.4]
x_expected = np.array([0., 0., 0., 1])
bounds = ((0, 1), (0, 1), (0, 1), (0, 1))
df = pd.DataFrame(
        {'t1': [1, 2, 3, 2, 1], 't2': [3, 4, 5, 6, 2], 't3': [2, 3, 2, 3, 5], 'c': [1, 1.1, 1.2, 1.3, 1.4]}).to_numpy()

    factors = pd.DataFrame(
        {"tk": ["t1", "t2", "t3", "c"], "class": ["x", "y", "x", "z"]})
    min_max_class = pd.DataFrame(
        {"class": ["x", "y", "z"], "min_allocation": [0., 0., 0.], "max_allocation": [0.0001, 0.5, 1.0000]})

    allocation = factors.join(min_max_class.set_index('class'), on='class')
    min = allocation['min_allocation'].to_numpy()
    max = allocation['max_allocation'].to_numpy()
    ineq1 = {'type': 'ineq',
             "fun": lambda x: np.greater_equal(x, min).sum() + np.less_equal(x, max).sum() - 2 * df.shape[1]}
    eq1 = {'type': 'eq', "fun": lambda x: 1 - np.sum(x)}

    result = sp.minimize(fun=lambda x: np.std(np.dot(df, x.T), ddof=1), x0=x0, bounds=bounds, method='SLSQP',
                         options={'disp': True}, constraints=[eq1, ineq1])

    print(f"final result : {np.round(result.x,2)}, objective value: {result.fun}")
    print(f" Manual : {x_expected}, objective value: {np.std(np.dot(df, x_expected.T))}")

I would expect the final results to be close to “x_expected”… but that is not the case.. any ideas ?

Advertisement

Answer

SLSQP solver failed to find the optimal value for your problem.

def obj(x):
    return np.std(np.dot(df, x.T), ddof=1)

def ineq(x):
    # Non-negative
    return np.greater_equal(x, min).sum() + np.less_equal(x, max).sum() - 2 * df.shape[1]

def eq(x):
    # Zero
    return 1 - np.sum(x)

result = scipy.optimize.minimize(fun=obj, x0=x0, bounds=bounds, method='SLSQP', options={'disp': True}, constraints=[eq1, ineq1])
> "Positive directional derivative for linesearch    (Exit mode 8)"

Therefore, inequality constraint does not hold.

x_opt = np.round(result.x,2)

print(f"opt.   result : {x_opt}, objective: {obj(x_opt)}, ineq: {ineq(x_opt)}, eq: {eq(x_opt)}")
print(f"manual result : {x_expected}, objective: {obj(x_expected)}, ineq: {ineq(x_expected)}, eq: {eq(x_expected)}")
opt.   result : [0.03 0.   0.   0.97], objective: 0.1554107460891942, ineq: -1, eq: 0.0
manual result : [0. 0. 0. 1.], objective: 0.15811388300841892, ineq: 0, eq: 0.0

Initial guess [0, 0, 0, 0] can help find the optimal value for your problem.

x0 = [0.0, 0.0, 0.0, 0.0]
bounds = ((0, 1), (0, 1), (0, 1), (0, 1))
ineq1 = {'type': 'ineq', "fun": ineq}
eq1 = {'type': 'eq', "fun": eq}

result = scipy.optimize.minimize(fun=obj, x0=x0, bounds=bounds, method='SLSQP', options={'disp': True}, constraints=[eq1, ineq1])
> Optimization terminated successfully    (Exit mode 0)

print(f"opt.   result : {x_opt}, objective: {obj(x_opt)}, ineq: {ineq(x_opt)}, eq: {eq(x_opt)}")
> opt.   result : [0. 0. 0. 1.], objective: 0.15811388300841892, ineq: 0, eq: 0.0
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement