我是否可以在不限制CPLEX模型的情况下添加最大值的kpi?

0 人关注

我正在使用cplex做一个优化模型,以优化饮食结构。现在我想添加一个额外的变量/KPI,它是模型中其他KPI的总和。然而,这个总和是一个有最大值的分数,这意味着当每个KPI超过某个上限时,总和不应该考虑超过这个值。分数只是对每种食物成分的衡量,因此,上限不是对模型的约束,只是计算分数的限制,所以应该不会影响模型的结果。最后,我想使总分最小化。

我曾尝试用mdl.sum对每个指标进行求和,并将其添加到一个列表中,然后我试图在超过上限的情况下用另一个列表中的值来替换求和。见下面的例子。

    # Decision variables, limited to be >= Food.qmin and <= Food.qmax
    ftype = mdl.integer_vartype if ints else mdl.continuous_vartype
    qty = mdl.var_dict(foods, ftype, lb=lambda f: f.qmin,ub=lambda f: f.qmax, name=lambda f: 
    "q_%s" % f.name)
    # Limit range of nutrients, and mark them as KPIs
    for c in constraints:
        amount = mdl.sum(qty[f] * food_constraints[f.name, c.name] for f in foods)
        mdl.add_range(c.qmin, amount, c.qmax)
        mdl.add_kpi(amount, publish_name="Total %s" % c.name)
    # add sum of indicators with max values:
    score1 = mdl.sum(qty[f] * f.veg for f in foods)
    score2 = mdl.sum(qty[f] * f.fruit for f in foods)
    score3 = mdl.sum(qty[f] * f.fish for f in foods)
    # add sum of indicators without max values:
    score4 = mdl.sum(qty[f] * f.sugar for f in foods)
    score5 = mdl.sum(qty[f] * f.fats for f in foods)
    Score_sum_A = [score1, score2, score3]
    max_scores = [10.6, 3.3, 9.7] 
    # cap values of Score_sum_A to max scores:
    for i in range(len(max_scores)):
        if Score_sum_A[i] >= max_scores[i]:
           Score_sum_A[i] = max_scores[i]
        else:
           Score_sum_A[i] = Score_sum_A[i] 
    Score_sum_B = [score4, score5]
    total_score_sum = sum(Score_sum_A + Score_sum_B)
    mdl.add_kpi(total_score_sum , 'Total food score')
    mdl.minimize(total_score_sum)

然而,当运行该模型时,我得到了错误。"TypeError:不能将线性约束转换为布尔值",这是因为if语句的原因。因此,似乎模型不能在模型内部操作这种if语句。

有谁知道,在cplex中是否有可能添加这样一个带有最大值的指标,而不限制模型结果?

如果有任何提示,我将非常感激。

python
cplex
constraint-programming
docplex
kpi
Caroline Herlev Gebara
Caroline Herlev Gebara
发布于 2022-07-26
1 个回答
Alex Fleischer
Alex Fleischer
发布于 2022-07-26
已采纳
0 人赞同
if Score_sum_A[i] >= max_scores[i]:
           Score_sum_A[i] = max_scores[i]

因为Score_sum_A不是一个常数。 你应该使用最大

from docplex.mp.model import Model
mdl = Model(name='buses')
nbKids=300;
buses=[30,40,50]
#decision variables
mdl.nbBus = {b: mdl.integer_var(name="nbBus"+str(b)) for b in buses}
# Constraint
mdl.add_constraint(sum(mdl.nbBus[b]*b for b in buses) >= nbKids, 'kids')
# Objective
# logical constraint is the max of all nbBus
mdl.minimize(mdl.max(mdl.nbBus[b] for b in buses)) 
mdl.solve(log_output=True,)
mdl.export("c:\\temp\\buses.lp")
for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

or if_then from docplex

from docplex.mp.model import Model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
   print(v," = ",v.solution_value)
print()
print("with if nb buses 40 more than 3  then nbBuses30 more than 7")
#if then constraint
mdl.add(mdl.if_then(nbbus40>=3,nbbus30>=7))
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve()
for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

如果我略微改变一下https://ibmdecisionoptimization.github.io/docplex-doc/mp/diet.html

def nb_products(mdl_, s_):
    qvs = mdl_.find_matching_vars(pattern="q_")
    return sum(1 for qv in qvs if s_[qv] >= 1e-5)
mdl.add_kpi(nb_products, 'Nb foods')
def nb_products(mdl_, s_):
    qvs = mdl_.find_matching_vars(pattern="q_")
    return sum(1 for qv in qvs if s_[qv] >= 1e-5)
def nb_products_capped(mdl_, s_):
    qvs = mdl_.find_matching_vars(pattern="q_")
    return mdl_.min(3,sum(1 for qv in qvs if s_[qv] >= 1e-5))
mdl.add_kpi(nb_products, 'Nb foods')
mdl.add_kpi(nb_products_capped,'Nb foods capped to 3')

then I will get

*  KPI: Nb foods             = 5.000000
*  KPI: Nb foods capped to 3 = 3.000000

在一个较小的例子中,如zookpi

from docplex.mp.model import Model
mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
nbbus=nbbus30+nbbus40
mdl.add_kpi(nbbus,"nbbus")
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)
mdl.solve(log_output=True,)
mdl.export("c:\\temp\\buses.lp")
for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)
for k in mdl.iter_kpis():
    print(k," = ",k.solution_value)
mdl.add_kpi(mdl.min(nbbus,3),"nbbuscapped3")

will give