连续变量和整数的乘积

Product of a continuous variable and an integer

我目前正在 python 中编写代码来解决劳动力规划问题。现在,我在这个问题中实施工人生产力的概念时遇到了一些问题。我用从 1 到 100 的数字表示概率(因此它是一个整数,而不是连续变量)。当我尝试 运行 我的程序时,仍然看到以下错误消息:"GurobiError: Unable to retrieve attribute 'X'"。在我看来,问题发生在第一个约束中。您可以在下面找到代码。

#define sets
periods = ("Period1", "Period2", "Period3", "Period4", "Period5", "Period6", "Period7")
skillPositions = ("SP1", "SP2", "SP3", "SP4", "SP5", "SP6", "SP7", "SP8")
tasks = ("T1", "T2", "T3", "T4", "T5", "T6", "T7", "T8", "T9", "T10", "T11", "T12", "T13", "T14", "T15")

#define parameters
initialProductivity = {"SP1": {"T1":0, "T2":80,"T3":0, "T4":0, "T5":0, "T6":0, "T7":70, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0}, 
                       "SP2": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":80, "T6":0, "T7":0, "T8":0, "T9":70, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0}, 
                       "SP3": {"T1":90, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":60, "T14":0, "T15":0},
                       "SP4": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":90, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0},
                       "SP5": {"T1":0, "T2":0,"T3":60, "T4":0, "T5":0, "T6":0, "T7":0, "T8":70, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0},
                       "SP6": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":80, "T11":0, "T12":0, "T13":0, "T14":0, "T15":80},
                       "SP7": {"T1":0, "T2":0,"T3":0, "T4":70, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":80, "T15":0},
                       "SP8": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":70, "T12":60, "T13":0, "T14":0, "T15":0}}

learningParameter = {"SP1": {"T1":0, "T2":5,"T3":0, "T4":0, "T5":0, "T6":0, "T7":4, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0}, 
                     "SP2": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":7, "T6":0, "T7":0, "T8":0, "T9":5, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0}, 
                     "SP3": {"T1":1, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":2, "T14":0, "T15":0},
                     "SP4": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":6, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0},
                     "SP5": {"T1":0, "T2":0,"T3":6, "T4":0, "T5":0, "T6":0, "T7":0, "T8":7, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":0, "T15":0},
                     "SP6": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":5, "T11":0, "T12":0, "T13":0, "T14":0, "T15":5},
                     "SP7": {"T1":0, "T2":0,"T3":0, "T4":6, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":0, "T12":0, "T13":0, "T14":6, "T15":0},
                     "SP8": {"T1":0, "T2":0,"T3":0, "T4":0, "T5":0, "T6":0, "T7":0, "T8":0, "T9":0, "T10":0, "T11":2, "T12":2, "T13":0, "T14":0, "T15":0}}

demand = {"Period1": {"T1":3, "T2":1386,"T3":1169, "T4":650, "T5":2923, "T6":693, "T7":4482, "T8":130, "T9":346, "T10":87, "T11":346, "T12":346, "T13":693, "T14":173, "T15":1}, 
       "Period2": {"T1":1, "T2":1252,"T3":1120, "T4":645, "T5":2788, "T6":670, "T7":4348, "T8":80, "T9":212, "T10":69, "T11":325, "T12":325, "T13":687, "T14":168, "T15":1}, 
       "Period3": {"T1":1, "T2":1302,"T3":1095, "T4":650, "T5":2838, "T6":650, "T7":4398, "T8":55, "T9":262, "T10":39, "T11":330, "T12":330, "T13":689, "T14":173, "T15":1}, 
       "Period4": {"T1":1, "T2":1297,"T3":1169, "T4":645, "T5":2788, "T6":693, "T7":4393, "T8":130, "T9":212, "T10":77, "T11":335, "T12":335, "T13":669, "T14":168, "T15":1}, 
       "Period5": {"T1":1, "T2":1452,"T3":1170, "T4":650, "T5":3038, "T6":700, "T7":4548, "T8":130, "T9":462, "T10":87, "T11":340, "T12":340, "T13":689, "T14":173, "T15":1}, 
       "Period6": {"T1":1, "T2":1502,"T3":1220, "T4":660, "T5":3088, "T6":700, "T7":4598, "T8":180, "T9":512, "T10":89, "T11":345, "T12":344, "T13":689, "T14":183, "T15":1}, 
       "Period7": {"T1":1, "T2":1602,"T3":1270, "T4":670, "T5":3188, "T6":700, "T7":4698, "T8":230, "T9":612, "T10":99, "T11":349, "T12":350, "T13":689, "T14":193, "T15":1}}


salaryCost = {"Period1": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period2": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period3": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period4": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period5": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period6": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200},
           "Period7": {"SP1":2296, "SP2":2207, "SP3":1900, "SP4":2199, "SP5":2586, "SP6":2276, "SP7":2390, "SP8":2200}}

#define decision variables
numberRequired = model.addVars(periods, skillPositions, tasks, vtype=GRB.INTEGER, name = "numberRequired")
productivity = model.addVars(periods, skillPositions, tasks, vtype=GRB.INTEGER, lb=0, ub=100, name= "productivity")
differenceProductivity = model.addVars(periods, skillPositions, tasks, vtype= GRB.INTEGER, lb=0, ub=100, name= "differenceProductivity")

#define constraints
#THIS FIRST CONSTRAINT IS WHERE THE ERROR MESSAGE COMES FROM
model.addConstrs(demand[period][taskx] <= quicksum(math.floor((numberRequired[period, skillPositionJ, taskx]*availableRegularHours*productivity[period, skillPositionJ, taskx])/100) for skillPositionJ in skillPositions) for period in periods for taskx in tasks)
model.addConstrs(productivity[period, skillPosition, task] == initialProductivity[period][skillPosition][task] + differenceProductivity[period, skillPosition, task] * (1-math.exp(-(sum(possibleCombinations[currentP][skillPosition][task] for currentP in periods[:int(period[-1])])/learningParameter[period][skillPosition][task])) if learningParameter[period][skillPosition][task] > 0 else 0) for period in periods for skillPosition in skillPositions for task in tasks) 
model.addConstrs(differenceProductivity[period, skillPosition, task] == 100-initialProductivity[period][skillPosition][task] for period in periods for skillPosition in skillPositions for task in tasks)

#define objective
obj = (quicksum(salaryCost[period][skillPositionJ]*numberRequired[periods[period_index-1], skillPositionJ, taskx] for period_index, period in enumerate(periods) for skillPositionJ in skillPositions for taskx in tasks)
model.setObjective(obj, GRB.MINIMIZE)

#solving the model
model.optimize()
model.printAttr('X')

任何人都可以在这段代码中找到我的错误吗?它与生产力变量有关,因为它 运行 没有这个。先感谢您!

亲切的问候

此错误代码表明您正在尝试获取不可行模型的解值。请仔细检查求解器的输出,并在查询解值之前始终检查解状态:

if model.status == GRB.OPTIMAL:
    print('Optimal objective: %g' % model.objVal)
    model.printAttr('X')
elif model.status != GRB.INFEASIBLE:
    print('Optimization was stopped with status %d' % model.status)

我看到的一件事是 math.floor 的使用。首先,Gurobi 并不知道这个功能。而且:它会使模型以一种令人讨厌的方式非线性(不可微分)。

构造 y=floor(x) 可以线性化为:

  y <= x
  y >= x-1+0.0001
  y integer

模型中仍有二次项。 Gurobi 可以处理凸和非凸二次模型,但通常最好尽可能坚持使用线性模型。

保持约束尽可能简单也很重要。这意味着我会预先计算如下内容:

(1-math.exp(-(sum(possibleCombinations[currentP][skillPosition][task] for currentP in periods[:int(period[-1])])/learningParameter[period] [skillPosition][task])) if learningParameter[period][skillPosition][task] > 0 else 0) 

如果我是正确的,约束就是:

productivity=initialProductivity+differenceProductivity*coefficient

但是我们陷入了复杂性,使得约束变得不可读。

最后,我的建议是在开始 Python 编码之前先开发一个数学模型并与您的主管讨论。与一堆复杂的 Python 代码相比,对紧凑数学模型的重新表述进行推理要有效得多。