简体   繁体   English

Pyomo - 选择域值的子集以创建最佳集合

[英]Pyomo - Selecting a subset of domain values to create an optimal set

I have a series of items, from which I would like to select an optimal subset of items, which maximise the cost based on a condition.我有一系列项目,我想从中得到 select 项目的最佳子集,它可以根据条件最大化成本。 The list of items is as follows:物品清单如下:

items = {
    0: { 'user': 1, 'cost': 100 }, 
    1: { 'user': 1, 'cost': 150 }, 
    2: { 'user': 2, 'cost': 200 }, 
    3: { 'user': 2, 'cost': 100 }, 
    4: { 'user': 3, 'cost': 150 }
}

The constraint is that, each user can only have one item selected.约束是,每个用户只能选择一个项目。 So the optimal solution for the scenario above would contain item 1, 2 and 4.因此,上述场景的最佳解决方案将包含第 1、2 和 4 项。

I have tried the following code for the problem (without the constraint, as I have not gotten that far just yet)我已经尝试了以下代码来解决这个问题(没有约束,因为我还没有走到那一步)

from pyomo.environ import (ConcreteModel, Objective, Var, Boolean, maximize, Constraint, Set, value)
from pyomo.opt.base import SolverFactory 

items = {
    0: { 'user': 1, 'cost': 100 }, 
    1: { 'user': 1, 'cost': 150 }, 
    2: { 'user': 2, 'cost': 200 }, 
    3: { 'user': 2, 'cost': 100 }, 
    4: { 'user': 3, 'cost': 150 }
}

item_selection = model = ConcreteModel()
model.selected_items = Set(initialize=[0], domain=items.keys())

model.obj = Objective(expr = sum(items[i]['cost'] for i in model.selected_items), sense=maximize)

solver = 'glpk'
solver_exe = '/opt/homebrew/Cellar/glpk/5.0/bin/glpsol'

opt = SolverFactory(solver, executable=solver_exe)
solution = opt.solve(item_selection)

solution.write()

The output to the above code is as follows上述代码的output如下

WARNING: Constant objective detected, replacing with a placeholder to prevent
    solver failure.
WARNING: Empty constraint block written in LP format - solver may error
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Name: unknown
  Lower bound: 100.0
  Upper bound: 100.0
  Number of objectives: 1
  Number of constraints: 1
  Number of variables: 1
  Number of nonzeros: 1
  Sense: maximize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Termination condition: optimal
  Statistics: 
    Branch and bound: 
      Number of bounded subproblems: 0
      Number of created subproblems: 0
  Error rc: 0
  Time: 0.032784223556518555
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0

Since in my code example, the set selected_items is a list with the domain of all item ids, I would expect selected_items = [1,2,4] (once the constraint is also applied).因为在我的代码示例中,集合 selected_items 是一个包含所有项目 id 的域的列表,我希望 selected_items = [1,2,4] (一旦也应用了约束)。

When I run当我跑

item_selection.obj()

I simply get我只是得到

100.0

As the output.作为 output。 Which is the cost for item id '0' (which I have initialised the set with).这是项目 id '0' 的成本(我已经用它初始化了集合)。 So the solver is not adding any other ids to the set.所以求解器不会向集合中添加任何其他 id。

I am new to pyomo, so any suggestions would be helpful.我是pyomo的新手,所以任何建议都会有所帮助。 Thanks谢谢

I was able to solve the problem after some research.经过一番研究,我能够解决这个问题。

import pyomo.environ as pe 
import pyomo.opt as po

solver = po.SolverFactory('glpk')

items = {
    0: { 'user': 1, 'cost': 100 }, 
    1: { 'user': 1, 'cost': 150 }, 
    2: { 'user': 2, 'cost': 200 }, 
    3: { 'user': 2, 'cost': 100 }, 
    4: { 'user': 3, 'cost': 150 }, 
}

# Get a list of unique users from the items dict 
users = list(set([items[i]['user'] for i in items.keys()]))

# Create dictionaries to initialise model parameters and variables
item_user_init = dict([(item_id, item['user']) for item_id, item in items.items()])
item_cost_init = dict([(item_id, item['cost']) for item_id, item in items.items()])
selected_users_init = dict([(item_id, 0) for item_id in items.keys()])

model = pe.ConcreteModel() 

# Indexed set for all the item keys
model.item_keys = pe.Set(initialize=items.keys())

# Model Parameters and 
model.user = pe.Param(model.item_keys, initialize=item_user_init)
model.cost = pe.Param(model.item_keys, initialize=item_cost_init)

# For each item id, assign a value of 0 in the beginning. For selected items, this value will change to 1
model.selected_users = pe.Var(model.item_keys, domain=pe.Binary, initialize=selected_users_init)

# Objective Function - to maximise sum of costs for selected items
model.obj = pe.Objective(sense=pe.maximize, expr = sum(model.cost[i]*model.selected_users[i] for i in model.item_keys))

# Constraint Function - to limit maximum one item per user
def user_constraint(model, user): 
    return sum(model.selected_users[i] for i in model.item_keys if items[i]['user'] == user) <= 1

model.user_constraint = pe.Constraint(users, expr=user_constraint)

result = solver.solve(model)

print('Selected Item IDs: {}'.format([i for i in model.item_keys if pe.value(model.selected_users[i]) > 0]))
print('Total Cost: {}'.format(pe.value(model.obj)))

The above code produces the expected output, which is as follows:上面的代码产生了预期的 output,如下:

Selected Item IDs: [1, 2, 4]
Total Cost: 500.0

As I am still new to pyomo, any suggestions to improve code quality would be appreciated.由于我还是 pyomo 的新手,任何提高代码质量的建议都将不胜感激。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM