英文:
Pyomo Objective trying to maximize the sum of the product of consecutive indexed variables
问题
我使用Pyomo在SolverStudio中为调度任务创建了一些代码。解决方案的总体目标是在每周工作的日期(model.Dates)的数量受到约束的情况下,最大化工作的连续周数,其中约束包括每周工作的人数(doctorsequalteamsRule)以及一个人可以工作的时间段内的周数(docsworktocontractRule)。可能会有不同数量的团队,一个人一次只能在一个团队工作(onedocatatimeRule)。model.NamesLarge是一种索引工作人员姓名和他们可能工作的各种(最多10个)团队的笨拙方式(1-10是工作人员1,11-20是工作人员2,21-30...)。
from pyomo.environ import * # 对于Pyomo 4.0及更高版本
# from coopr.pyomo import * # 对于早期版本
model = AbstractModel()
## 定义集合
model.NamesSmall = Set()
model.NamesLarge = Set()
model.Dates = Set()
model.Teams = Set()
## 定义参数
# 每年医生工作的周数
model.doctorsperweek = Param(model.NamesSmall)
# 每周工作的团队/医生数量
model.numberworked = Param(model.Dates)
model.abletowork = Param(model.NamesSmall, model.Dates)
model.mustwork = Param(model.NamesSmall, model.Dates)
model.maxweeksinarow = Param(model.NamesSmall, model.Dates)
model.teamsperweek = Param(model.Teams, model.Dates)
## 定义变量
model.variables = Var(model.NamesLarge, model.Dates, within=Binary, initialize=0)
## 定义目标函数
def costRule(model):
return sum(model.variables[nm, qm] * model.variables[nm, qm + 1] for nm in model.NamesLarge for qm in range(1, len(model.Dates)))
model.SolverResult = Objective(rule=costRule, sense=maximize)
## 约束条件
# 可能需要一个约束条件,如果一个变量= 1,则在10个变量块中的其他变量在下一个日期不能为1
# 需要一个约束条件,不同的团队在变量中具体反映出来
# 确保医生按照他们的合同工作的周数
blahg = []
def docsworktocontractRule(model, tr):
for mr in model.NamesSmall:
blah = sum(model.variables[(10 * mr) - p, bo] for bo in model.Dates for p in range(10))
blahg.append(blah)
return blahg[tr - 1] == model.doctorsperweek[tr]
model.docsworktocontractConstraint = Constraint(model.NamesSmall, rule=docsworktocontractRule)
# 确保每周的医生数与团队数相等
def doctorsequalteamsRule(model, i):
return sum(model.variables[m, i] for m in model.NamesLarge) == value(model.numberworked[i])
model.doctorsequalteamsConstraint = Constraint(model.Dates, rule=doctorsequalteamsRule)
def onedocatatimeRule(model, fe, da):
grapesof = []
grape = sum(model.variables[(10 * fe) - p, da] for p in range(0, 10))
grapesof.append(grape)
return sum(grapesof[yh - 1] for yh in range(len(grapesof))) <= 1
model.onedocatatimeConstraint = Constraint(model.NamesSmall, model.Dates, rule=onedocatatimeRule)
约束条件可以工作,但是目标函数解决起来需要极大的时间,即使只有6个人、2个团队和10周(我需要解决大约40个人、52周和7-8个团队的情况)。
有没有办法使目标函数更高效地求解?
我考虑过,也许不要使用*(其中0x0=0,0x1=0,1x0=0,1x1=1),而是使用+,然后过滤出只对等于2的变量求和,但这不起作用,因为在创建实例时变量是未知的?
我感到困惑,也许我错过了一些明显的东西,或者我完全没有有效地设置整个系统?
英文:
I made some code for a scheduling task using Pyomo in SolverStudio. The overall goal of the solve is to maximise the amount of consecutive weeks (model.Dates) that are worked while being constrained by the amount of people working per week (doctorsequalteamsRule) and the amount of weeks in the time period that a person can work (docsworktocontractRule). There may be a varying number of teams and a person can only work on one team at a time (onedocatatimeRule). The model.NamesLarge is a stupid way to index the worker's names and the various (up to 10) teams they might work on (1-10 is worker 1, 11-20 is worker 2, 21-30...).
from pyomo.environ import * # For Pyomo 4.0 & later
# from coopr.pyomo import * # For earlier versions
model = AbstractModel()
## Define sets
model.NamesSmall = Set()
model.NamesLarge = Set()
model.Dates = Set()
model.Teams = Set()
## Define parameters
#how many weeks a doc works in the year
model.doctorsperweek = Param(model.NamesSmall)
#how many teams/docs are on each week
model.numberworked = Param(model.Dates)
model.abletowork = Param(model.NamesSmall, model.Dates)
model.mustwork = Param(model.NamesSmall, model.Dates)
model.maxweeksinarow = Param(model.NamesSmall, model.Dates)
model.teamsperweek = Param(model.Teams, model.Dates)
## Define variables
model.variables = Var(model.NamesLarge, model.Dates, within = Binary, initialize=0)
## Define Objective Function
def costRule(model):
return sum(model.variables[nm,qm]*model.variables[nm,qm+1] for nm in model.NamesLarge for qm in range(1,len(model.Dates)))
model.SolverResult = Objective(rule=costRule, sense=maximize)
## Constraints
#Maybe need a constraint that if a var = 1 then the next date other var in the block of 10 cant be 1
#Need a constraint that the different teams are specifically reflected in the variables
#Ensures the docs are working the amount of weeks in their contract
blahg = []
def docsworktocontractRule(model,tr):
for mr in model.NamesSmall:
blah = sum(model.variables[(10*mr)-p,bo] for bo in model.Dates for p in range(10))
blahg.append(blah)
return blahg[tr-1] == model.doctorsperweek[tr]
model.docsworktocontractConstraint = Constraint(model.NamesSmall, rule=docsworktocontractRule)
#Ensures same amount of doctors as teams per week
def doctorsequalteamsRule(model,i):
return sum(model.variables[m,i] for m in model.NamesLarge) == value(model.numberworked[i])
model.doctorsequalteamsConstraint = Constraint(model.Dates, rule=doctorsequalteamsRule)
def onedocatatimeRule(model,fe,da):
grapesof = []
grape = sum(model.variables[(10*fe)-p,da] for p in range(0,10))
grapesof.append(grape)
return sum(grapesof[yh-1] for yh in range(len(grapesof))) <= 1
model.onedocatatimeConstraint = Constraint(model.NamesSmall, model.Dates, rule=onedocatatimeRule)
The constraints work, but the objective takes an incredible amount of time to solve even only for 6 people, 2 teams and 10 weeks (I'll need to solve for ~40 people, 52 weeks and 7-8 teams).
Is there any way to make the objective solve more efficiently?
I was thinking maybe instead to using * (where 0x0 = 0, 0x1 = 0, 1x0 = 0 and 1x1 = 1) I could use + and then filter out to only sum the sum of variables that == 2, but this doesn't work as the variables are unknown at the time of instance creation?
I'm at a loss, maybe I'm missing something obvious or have I completely set the whole thing up inefficiently?
答案1
得分: 1
你已经通过将变量相乘使这成为了一个非线性模型(无意中?)。 它可以被变成线性的,这样更容易求解。 如果线性/非线性/整数问题让你感到困惑,你应该参考线性规划文本或教程,了解这些类型的模型是如何求解的,以及为什么非线性模型非常不同。
另外,这里有一个示例,展示了通过在分配变量上进行一些整数数学运算来计算连续周的线性构造。
- 旁注:如果你要发布你的代码并寻求帮助,你可能需要再花几分钟来整理一下。一个叫做
variables
的变量?不一致的(非标准的)大写,变量叫做blah
和grape
?拜托了…在工作中表现一些自豪心吧。;)
代码:
import pyomo.environ as pyo
### 数据
weeks = 8
doc_limits = { 'bob': 3, # doc : max assignments
'sam': 4,
'sally': 4,
'xavier': 2}
requirements = { 1: 2, # week : number of docs
2: 2,
3: 1,
4: 0,
5: 2,
6: 2,
7: 2,
8: 2}
### 模型
m = pyo.ConcreteModel('doc_sked')
m.W = pyo.Set(initialize=range(1, weeks+1), ordered=True, doc='week')
m.W2 = pyo.Set(initialize=range(2, weeks+1), ordered=True, doc='week 2 plus')
m.D = pyo.Set(initialize=doc_limits.keys(), doc='doc')
m.assign = pyo.Var(m.D, m.W, domain=pyo.Binary)
m.consecutive_assign = pyo.Var(m.D, m.W2, domain=pyo.Binary)
### 目标
m.obj = pyo.Objective(expr=pyo.sum_product(m.consecutive_assign), sense=pyo.maximize)
### 约束
# 不要超过最大分配
def max_assignments(m, d):
return sum(m.assign[d, w] for w in m.W) <= doc_limits[d]
m.C1 = pyo.Constraint(m.D, rule=max_assignments)
# 不要超过要求
def requirement_limit(m, w):
return sum(m.assign[d, w] for d in m.D) <= requirements[w]
m.C2 = pyo.Constraint(m.W, rule=requirement_limit)
# 将分配与连续分配指示符关联
def link(m, d, w):
return m.consecutive_assign[d, w] <= (m.assign[d, w] + m.assign[d, m.W.prev(w)])/2
m.C3 = pyo.Constraint(m.D, m.W2, rule=link)
m.pprint()
solver = pyo.SolverFactory('cbc')
res = solver.solve(m)
print(res)
m.assign.display()
英文:
You have (inadvertently?) made this a non-linear model by multiplying variables together. It can be made linear, which is much simpler to solve. If the linear/non-linear/integer issue are confusing, you should consult a linear programming text or tutorial on how those types of models are solved and why non-linear is quite different.
That aside, here is an example that shows a linear construct for counting sequential weeks by doing a little integer math on the assignment variables.
A side note: If you are going to post your code and ask for help, you might want to take a few more minutes and clean it up. A variable called variables
? Inconsistent (and non-standard) capitalization, variables called blah
and grape
? C'mon.... show some pride in the work.
Code:
import pyomo.environ as pyo
### DATA
weeks = 8
doc_limits = { 'bob': 3, # doc : max assignments
'sam': 4,
'sally': 4,
'xavier': 2}
requirements = { 1: 2, # week : number of docs
2: 2,
3: 1,
4: 0,
5: 2,
6: 2,
7: 2,
8: 2}
### MODEL
m = pyo.ConcreteModel('doc_sked')
m.W = pyo.Set(initialize=range(1, weeks+1), ordered=True, doc='week')
m.W2 = pyo.Set(initialize=range(2, weeks+1), ordered=True, doc='week 2 plus')
m.D = pyo.Set(initialize=doc_limits.keys(), doc='doc')
m.assign = pyo.Var(m.D, m.W, domain=pyo.Binary)
m.consecutive_assign = pyo.Var(m.D, m.W2, domain=pyo.Binary)
### OBJ
m.obj = pyo.Objective(expr=pyo.sum_product(m.consecutive_assign), sense=pyo.maximize)
### CONSTRAINTS
# don't bust max assignments
def max_assignments(m, d):
return sum(m.assign[d, w] for w in m.W) <= doc_limits[d]
m.C1 = pyo.Constraint(m.D, rule=max_assignments)
# don't bust requirement
def requirement_limit(m, w):
return sum(m.assign[d, w] for d in m.D) <= requirements[w]
m.C2 = pyo.Constraint(m.W, rule=requirement_limit)
# link assignment to consecutive assignment indicator
def link(m, d, w):
return m.consecutive_assign[d, w] <= (m.assign[d, w] + m.assign[d, m.W.prev(w)])/2
m.C3 = pyo.Constraint(m.D, m.W2, rule=link)
m.pprint()
solver = pyo.SolverFactory('cbc')
res = solver.solve(m)
print(res)
m.assign.display()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论