0

As a simple web developer :) i'm having trouble figuring out a more data science focused problem when using SciPy Optimize minimize function. Given a set of bounds and a series of constraints i'm trying to find the values of 8 ingredients in this case (who's total must equal to 100).

The constraints set out min/max values for overall macronutrient values ex. must be at least 40% solids, or must have an rs value (relative sweetness) that equals 13.0 etc.

I have set up my constraints (i hope correctly) and a series of bounds for each of these ingredients (some of which can be between 0 - 100) but with others with the upper bound and lower bound the same ie. fixed?. In the answer provided, i was confused as I got a successful run of the optimization BUT some of the values of the ingredients are NOT falling within the set up bounds.

See below my code so far. And Thanks so much for your help!


cream = {
    "rs": 0.0,
    "se": 0.0,
    "solids": 0.454,
    "water": 0.0,
    "fat": 0.4,
    "saturated": 0.4,
    "unsaturated": 0.26,
    "msnf": 0.14,
    "protein": 0.054,
    "brix": 0.019,
    "acid": 0.0
}

whole_milk = {
    "rs": 0.0,
    "se": 0.0,
    "solids": 0.128,
    "water": 0.872,
    "fat": 0.037,
    "saturated": 0.024,
    "unsaturated": 0.013,
    "msnf": 0.091,
    "protein": 0.035,
    "brix": 0.049,
    "acid": 0.0
}

sucrose = {
    "rs": 1.0,
    "se": 1.0,
    "solids": 1.0,
    "water": 0.0,
    "fat": 0.0,
    "saturated": 0.0,
    "unsaturated": 0.0,
    "msnf": 0.0,
    "protein": 0.0,
    "brix": 1.0,
    "acid": 0.0
}

skim_milk_powder = {
    "rs": 0.0,
    "se": 0.0,
    "solids": 0.97,
    "water": 0.0,
    "fat": 0.07,
    "saturated": 0.05,
    "unsaturated": 0.02,
    "msnf": 0.963,
    "protein": 0.38,
    "brix": 0.54,
    "acid": 0.0
}

buttermilk = {
    "rs": 0.0,
    "se": 0.0,
    "solids": 0.099,
    "water": 0.901,
    "fat": 0.009,
    "saturated": 0.005,
    "unsaturated": 0.003,
    "msnf": 0.095,
    "protein": 0.033,
    "brix": 0.048,
    "acid": 0.0
}

tapioca_starch = {
    "rs": 0.0,
    "se": 0.0,
    "solids": 0.867,
    "water": 0.133,
    "fat": 0.0,
    "saturated": 0.0,
    "unsaturated": 0.0,
    "msnf": 0.0,
    "protein": 0.0,
    "brix": 0.0,
    "acid": 0.0
}

salt = {
    "rs": 0.0,
    "se": 5.9,
    "solids": 1.0,
    "water": 0.0,
    "fat": 0.0,
    "saturated": 0.0,
    "unsaturated": 0.0,
    "msnf": 0.0,
    "protein": 0.0,
    "brix": 0.0,
    "acid": 0.0
}

sugared_egg_yolk = {
    "rs": 0.1,
    "se": 0.1,
    "solids": 0.529,
    "water": 0.471,
    "fat": 0.239,
    "saturated": 0.09,
    "unsaturated": 0.143,
    "msnf": 0.0,
    "protein": 0.143,
    "brix": 0.105,
    "acid": 0.0
}

import numpy as np
from scipy.optimize import minimize

initial_guess = np.array([12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5])
x0 = np.zeros(8)

fat_value = 15.0
rs_value = 13.0
solids_value = 40.0
msnf_value = 11.0

def optimize_function(x):
    return x[0] + x[2] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7]

def total_amount_constraint(x):
    return x[0] + x[2] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7] - 100

def total_fat_constraint(x):
    return (x[0] * cream["fat"] + \
    x[2] * whole_milk["fat"] + \
    x[2] * sucrose["fat"] + \
    x[3] * skim_milk_powder["fat"] + \
    x[4] * buttermilk["fat"] + \
    x[5] * tapioca_starch["fat"]+ \
    x[6] * salt["fat"] + \
    x[7] * sugared_egg_yolk["fat"]) - fat_value

def total_rs_constraint(x):
    return (x[0] * cream["rs"] + \
    x[2] * whole_milk["rs"] + \
    x[2] * sucrose["rs"] + \
    x[3] * skim_milk_powder["rs"] + \
    x[4] * buttermilk["rs"] + \
    x[5] * tapioca_starch["rs"]+ \
    x[6] * salt["rs"] + \
    x[7] * sugared_egg_yolk["rs"]) - rs_value

def total_solids_constraint(x):
    return (x[0] * cream["solids"] + \
    x[2] * whole_milk["solids"] + \
    x[2] * sucrose["solids"] + \
    x[3] * skim_milk_powder["solids"] + \
    x[4] * buttermilk["solids"] + \
    x[5] * tapioca_starch["solids"]+ \
    x[6] * salt["solids"] + \
    x[7] * sugared_egg_yolk["solids"]) - solids_value

def total_msnf_constraint(x):
    return (x[0] * cream["msnf"] + \
    x[2] * whole_milk["msnf"] + \
    x[2] * sucrose["msnf"] + \
    x[3] * skim_milk_powder["msnf"] + \
    x[4] * buttermilk["msnf"] + \
    x[5] * tapioca_starch["msnf"]+ \
    x[6] * salt["msnf"] + \
    x[7] * sugared_egg_yolk["msnf"]) - msnf_value

b = (0.0, 100.0)
salt_bound = (0.1, 0.1)
buttermilk_bound = (9.0, 9.0)
tapioca_starch_bound = (1.0, 1.0)
sugared_egg_yolk_bound = (7.77, 7.77)

bnds = (b, b, b, b, buttermilk_bound, tapioca_starch_bound, salt_bound, sugared_egg_yolk_bound)

cons = [
    {'type': 'eq', 'fun': total_amount_constraint},
    {'type': 'eq', 'fun': total_fat_constraint},
    {'type': 'ineq', 'fun': total_solids_constraint},
    {'type': 'eq', 'fun': total_rs_constraint},
    {'type': 'eq', 'fun': total_msnf_constraint},
]

options = {
    'maxiter': 1000
}

response = minimize(optimize_function, initial_guess, method="trust-constr", tol=1e-10, constraints = cons, bounds = bnds, options = options)

print(response)```

The solution set given is the following:
```x: array([29.12370093, 37.03694111, 12.45159589,  3.03213572,  8.99735476,
        1.2025148 ,  0.33409969,  7.82503797])```

Given that I set the bounds of 9, 1, 0.1 and 7.77 for the last 4 entries in this solution set, I would expect the last 4 values in the above array to be equal to that fixed bound value.


  [1]: https://i.stack.imgur.com/MDDa4.png
  [2]: https://i.stack.imgur.com/7aMgy.png
merv
  • 67,214
  • 13
  • 180
  • 245
  • 1
    That's a linear optimization problem with linear constraints **and** linear objective. Thus you should use [linprog](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linprog.html#scipy.optimize.linprog) instead. – joni Aug 25 '19 at 08:56
  • This question should be rephrased in a much more [minimal](https://stackoverflow.com/help/minimal-reproducible-example) way. – gspr Aug 25 '19 at 16:37

0 Answers0