Reputation: 890
I am new to Pyomo and I started with some small examples by understanding and testing them. Now I would like to rewrite a more complex Example using Python dictionaries the example I want to rewrite is from the Pyomo Gallery:
https://nbviewer.jupyter.org/github/Pyomo/PyomoGallery/blob/master/diet/DietProblem.ipynb
As example on how to use Python Dictionaries with pyomo I took the following:
https://pyomo.readthedocs.io/en/stable/working_abstractmodels/data/raw_dicts.html
Since two days since I started I am circling dictionaries around shifting indexes and hoping for my reimplementation to work and I just don't know what the correct approach is. Why the current approach doesn't work. Sometimes I change something about the dictionary structure and receive a different error message, then I proceed with the next one and (solve it, or just change it) and the old one pops up again.
I just will provide my current status and would appreciate your help. A working solution would probably help me a lot to understand how to model data with Python dictionaries.
from __future__ import division
import pyomo.environ as pyo
from pyomo.opt import SolverFactory
from math import inf
food_keys = ['Cheeseburger', 'Ham Sandwich', 'Hamburger', 'Fish Sandwich', 'Chicken Sandwich', 'Fries', 'Sausage Biscuit', 'Lowfat Milk', 'Orange Juice']
cost_vol_keys = ['c', 'V']
cost_vol = [[1.84, 4.0], [2.19, 7.5], [1.84, 3.5], [1.44, 5.0], [2.29, 7.3], [.77, 2.6], [1.29, 4.1], [.60, 8.0], [.72, 12.0]]
F = {food_keys[i]:
{cost_vol_keys[j]:
cost_vol[i][j]
for j in range(len(cost_vol_keys))}
for i in range(len(food_keys))}
nutrition_keys = ['Cal', 'Carbo Protein', 'VitA', 'VitC', 'Calc', 'Iron']
nutrition_req_keys = ['Nmin', 'Nmax']
nutrition_req = [
[2000, None],
[ 350, 375],
[ 55, None],
[ 100, None],
[ 100, None],
[ 100, None],
[ 100, None]
]
N = {nutrition_keys[i]:
{nutrition_req_keys[j]:
nutrition_req[i][j]
for j in range(len(nutrition_req_keys))}
for i in range(len(nutrition_keys))}
Vmax = 75.0
nutrition = [
[510, 34, 28, 15, 6, 30, 20],
[370, 35, 24, 15, 10, 20, 20],
[500, 42, 25, 6, 2, 25, 20],
[370, 38, 14, 2, 0, 15, 10],
[400, 42, 31, 8, 15, 15, 8],
[220, 26, 3, 0, 15, 0, 2],
[345, 27, 15, 4, 0, 20, 15],
[110, 12, 9, 10, 4, 30, 0],
[ 80, 20, 1, 2, 120, 2, 2]
]
a = {(food_keys[i], nutrition_keys[j]): nutrition[i][j]
for j in range(len(nutrition_keys))
for i in range(len(food_keys))}
data = {None: {
'F': {None: F},
'N': {None: N},
'a': a,
'Vmax': {None: Vmax},
}}
model = pyo.AbstractModel()
solver = 'gurobi'
solver_io = 'python'
stream_solver = False # True prints solver output to screen
keepfiles = False # True prints intermediate file names (.nl,.sol,...)
opt = SolverFactory(solver, solver_io = solver_io)
opt.options['outlev'] = 1 # tell gurobi to be verbose with output
opt.options['solnsens'] = 1
opt.options['bestbound'] = 1
# Foods
model.F = pyo.Set()
# Nutrients
model.N = pyo.Set()
# Cost of each food
model.c = pyo.Param(model.F, within = pyo.PositiveReals)
# Amount of nutrient in each food
model.a = pyo.Param(model.F, model.N, within = pyo.NonNegativeReals)
# Lower and upper bound on each nutrient
model.Nmin = pyo.Param(model.N, within = pyo.NonNegativeReals, default = 0.0)
model.Nmax = pyo.Param(model.N, within = pyo.NonNegativeReals, default = inf)
# Volume per serving of food
model.V = pyo.Param(model.F, within = pyo.PositiveReals)
# Maximum volume of food consumed
model.Vmax = pyo.Param(within = pyo.PositiveReals)
# Number of servings consumed of each food
model.x = pyo.Var(model.F, within = pyo.NonNegativeIntegers)
# Minimize the cost of food that is consumed
def cost_rule(model):
return sum(model.c[i] * model.x[i] for i in model.F)
model.cost = pyo.Objective(rule = cost_rule)
# Limit nutrient consumption for each nutrient
def nutrient_rule(model, j):
value = sum(model.a[i, j] * model.x[i] for i in model.F)
return pyo.inequality(model.Nmin[j], value, model.Nmax[j])
model.nutrient_limit = pyo.Constraint(model.N, rule = nutrient_rule)
# Limit the volume of food consumed
def volume_rule(model):
return sum(model.V[i] * model.x[i] for i in model.F) <= model.Vmax
model.volume = pyo.Constraint(rule = volume_rule)
model_instance = model.create_instance(data)
model_instance.pprint()
results = opt.solve(model_instance, keepfiles = keepfiles, tee = stream_solver)
Upvotes: 0
Views: 851
Reputation: 11938
You are running into problems here because you are asking pyomo to read your dictionaries as if they were a .dat file like the example you reference. It can't do that. It can read multi-columnar .dat files that have multiple items indexed by the same keyset, but (to my knowledge) cannot do the same thing out of a python dictionary. So you have a couple choices...
Break up your dictionaries and have individual ones for cost, value, etc. This should be a trivial fix and then update your data
model.
Just stuff your data into a separate .dat file like the example you reference, which is perfectly fine
Shift gears to a ConcreteModel()
and forget about keeping the model and data separate. Python is so good at wrangling files and data tables, that I almost always elect to let python do the wrangling and then just make a ConcreteModel()
from the dictionaries created. Without changing any of your dictionaries, you could do something like this:
model = pyo.ConcreteModel('chow time')
# Foods
model.F = pyo.Set(initialize=F.keys())
# Nutrients
model.N = pyo.Set(initialize=N.keys())
# Cost of each food
model.c = pyo.Param(model.F, within = pyo.PositiveReals, initialize = {k:v['c'] for k,v in F.items()})
# Volume per serving of food
model.V = pyo.Param(model.F, within = pyo.PositiveReals, initialize = {k:v['V'] for k,v in F.items()})
Upvotes: 1