Issue
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)
Solution
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 multicolumnar .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 aConcreteModel()
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()})
Answered By – AirSquid
This Answer collected from stackoverflow, is licensed under cc bysa 2.5 , cc bysa 3.0 and cc bysa 4.0