非线性约束的优化问题

DPN*_*DPN 0 python linear-programming pulp mixed-integer-programming scipy-optimize

我是优化问题的新手,正在研究一个简单的最大化问题,我可以在 Excel 中非常简单地解决这个问题。但是,我需要在 Python 中扩展它并需要一些帮助。

我有一份不同食物的菜单,我需要最大限度地提高我的能量输出。例子:

食物 卡路里 活力
蛋白质 100 60
蛋白质 羊肉 200 40
蛋白质 200 38
碳水化合物 香蕉 200 25
碳水化合物 土豆 200 30
碳水化合物 200 40
胖的 牛油果 450 50
胖的 奶酪 400 60
胖的 奶油 500 55

鉴于以下限制,我需要最大化能量(e):

  1. 每个 Macros(m) 只能消耗 1 个食物项目 (i)。所以我需要一个指示变量 (0/1) 来从 m - 蛋白质、脂肪和碳水化合物中的每一个中只选择 1 个。
  2. 卡路里总数 (c) 不应超过一个恒定值假设每个项目有 1 份(对此没有限制)

问题表述:

变量: X (m,i) ? 二元变量 = {1 ,如果宏 m 和项目 i 被选择,0 否则}

最大化 e(m,i) * X(m,i)

参数:卡路里 (C) -> 每个卡路里(宏,食物)

受约束:对于每个 m,? X (m,i) <= 1(每个宏只能选择一项)?c(m,i) * X(m,i)/ X(i) <= N(卡路里消耗量限制为常数 N)

到目前为止,我认为这是一个具有非线性约束的混合整数问题。

  1. 我曾尝试使用 Pulp,但由于非线性约束而失败。如果我去除非线性,它工作正常。
  2. 我尝试使用 Scipy Optimize,但 Scipy 不允许创建整数变量。

如何使用 Python 解决这个问题?我在这里误解了这个问题吗?

更新: 上面缺少由于mean. 我将问题从对 的约束更新为totalmean. 在非数学术语中,我取所有宏相乘后得到的数字的平均值,因为我希望我的平均卡路里小于常数 N。

所以在数学上, ? c(m,i) * X(m,i)/ X(i) <= N(平均卡路里消耗量限制为常数 N)

jon*_*oni 5

正如您已经提到的,scipy.optimize.minimize无法处理混合整数问题 (MIP)。最多可以做的是尝试通过惩罚方法来解决 MIP,即向目标添加一个惩罚函数,例如1.0/eps * np.sum(x*(1 - x)),其中eps > 0是给定的惩罚参数和xa np.ndarray

但是,使用 MIP 求解器解决问题要方便得多。由于您的问题具有众所周知的类似背包的结构,因此您甚至可以期待非商业 MIP 求解器(PuLp 默认使用 CBC)来利用您问题的底层结构。在这里,我推荐以下公式:

Binary variables:
x[i] = 1 if fooditem i is chosen, 0 otherwise

Parameters:
a[i][m] = 1 if fooditem i covers macro m, 0 otherwise
c[i]        calories for fooditem i
e[i]        energy for fooditem i
N           total calories limit

Model:

max ? (e[i] * a[i][m] * x[i],  ? i ? Fooditems, m ? Macros)

s.t. ? (a[i][m] * x[i], ? i ? Fooditems) <= 1  ? m ? Macros. (1)
     ? (c[i] * x[i], ? i ? Fooditems)    <= N                (2)
Run Code Online (Sandbox Code Playgroud)

可以像这样建模和解决:

import pulp

fooditems = {
    'Fish':    {'macro': 'Protein', 'calorie': 100, 'energy': 60},
    'Lamb':    {'macro': 'Protein', 'calorie': 200, 'energy': 40},
    'Egg':     {'macro': 'Protein', 'calorie': 200, 'energy': 38},
    'Banana':  {'macro': 'Carbs',   'calorie': 200, 'energy': 25},
    'Potato':  {'macro': 'Carbs',   'calorie': 200, 'energy': 30},
    'Rice':    {'macro': 'Carbs',   'calorie': 200, 'energy': 40},
    'Avocado': {'macro': 'Fat',     'calorie': 450, 'energy': 50},
    'Cheese':  {'macro': 'Fat',     'calorie': 400, 'energy': 60},
    'Cream':   {'macro': 'Fat',     'calorie': 500, 'energy': 55},
}

# parameters
macros = list({fooditems[i]['macro'] for i in fooditems})
a = {item: {m: 1 if m == fooditems[item]['macro']
            else 0 for m in macros} for item in fooditems}
c = {item: fooditems[item]['calorie'] for item in fooditems}
e = {item: fooditems[item]['energy'] for item in fooditems}
N = 1000

# pulp model
mdl = pulp.LpProblem("bla", pulp.LpMaximize)

# binary variables
x = pulp.LpVariable.dicts("x", fooditems, cat="Binary")

# objective
mdl += pulp.lpSum([e[i] * a[i][m] * x[i] for m in macros for i in fooditems])

# constraints (1)
for m in macros:
    mdl += (pulp.lpSum([a[i][m]*x[i] for i in fooditems]) <= 1)

# constraints (2)
mdl += (pulp.lpSum([x[i]*c[i] for i in fooditems]) <= N)

# solve the problem
mdl.solve()

print(f"Status: {pulp.LpStatus[mdl.status]}")
for var in mdl.variables():
    print(f"{var.name} = {var.varValue:.0f}")
print(f"energy: {mdl.objective.value()}")
Run Code Online (Sandbox Code Playgroud)

这产生

Status: Optimal
x_Avocado = 0.0
x_Banana = 0.0
x_Cheese = 1.0
x_Cream = 0.0
x_Egg = 0.0
x_Fish = 1.0
x_Lamb = 0.0
x_Potato = 0.0
x_Rice = 1.0
Energy: 160.0
Run Code Online (Sandbox Code Playgroud)