# Accessing Pyomo variable values and objective function value¶

I built some pretty big mixed integer programming models in Pyomo a few years ago (back in the Pyomo 2.x and 3.x days I believe). After neglecting them for a while, I returned to them this year and, not surprisingly, tons of improvements and changes had been made to Pyomo. I'm using Pyomo 5.0.1 and spent a bunch of time updating my old models and driver scripts to be compliant with the new version. In doing so, I had a bunch of trouble doing relatively simple things that used to work in older versions. For example, accessing variable values and the objective function value after successfully solving a problem, proved to be tricky. I read the docs, snooped numerous example scripts in various GitHub repos and did a lot of reading in the Pyomo forums and StackOverflow. In doing so, I ran across several different ways to access these values. I'd like to find out which approaches are considered the "right" or Pyomothonic way. I also just wanted to create a simple notebook that illustrates this confusion in the hopes that I, and others, might learn something.

So, I'm using the simple production model example that came with Pyomo (some version ago). After loading and solving, we'll explore a bunch of different ways to access both variable values and the objective function value.

In [2]:
import pyomo.opt
from pyomo.environ import *

In [3]:
# Import model
import prod

# Create the model instance
instance = prod.model.create_instance("prod.dat")

In [4]:
instance.pprint()

1 Set Declarations
P : Dim=0, Dimen=1, Size=2, Domain=None, Ordered=False, Bounds=None
['bands', 'coils']

4 Param Declarations
a : Size=2, Index=P, Domain=Any, Default=None, Mutable=False
Key   : Value
bands :   200
coils :   140
b : Size=1, Index=None, Domain=Any, Default=None, Mutable=False
Key  : Value
None :    40
c : Size=2, Index=P, Domain=Any, Default=None, Mutable=False
Key   : Value
bands :    25
coils :    30
u : Size=2, Index=P, Domain=Any, Default=None, Mutable=False
Key   : Value
bands :  6000
coils :  4000

1 Var Declarations
X : Size=2, Index=P
Key   : Lower : Value : Upper : Fixed : Stale : Domain
bands :  None :  None :  None : False :  True :  Reals
coils :  None :  None :  None : False :  True :  Reals

1 Objective Declarations
Total_Profit : Size=1, Index=None, Active=True
Key  : Active : Sense    : Expression
None :   True : maximize : 30*X[coils] + 25*X[bands]

2 Constraint Declarations
Limit : Size=2, Index=P, Active=True
Key   : Lower : Body     : Upper  : Active
bands :   0.0 : X[bands] : 6000.0 :   True
coils :   0.0 : X[coils] : 4000.0 :   True
Time : Size=1, Index=None, Active=True
Key  : Lower : Body                                           : Upper : Active
None :  -Inf : 0.007142857142857143*X[coils] + 0.005*X[bands] :     b :   True

9 Declarations: P a b c u X Total_Profit Time Limit

In [5]:
# Setup the optimizer
opt = pyomo.opt.SolverFactory("cbc")

# Optimize
results = opt.solve(instance)

# Write the output
results.write(num=1)

# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem:
- Name: tmpma7o0956.pyomo
Lower bound: -192000.0
Upper bound: -192000.0
Number of objectives: 1
Number of constraints: 6
Number of variables: 3
Number of nonzeros: 7
Sense: minimize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver:
- Status: ok
User time: -1.0
Termination condition: optimal
Error rc: 0
Time: 0.015891075134277344
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution:
- number of solutions: 0
number of solutions displayed: 0


In Pyomo 4.x, results from solving an optimization problem are automatically loaded into the model instance. See http://www.pyomo.org/blog/2015/12/26/updating-to-pyomo-41 for more details. So, how to interrogate the instance object to get things like the objective function value or variable values?

## Variable values¶

### Invidual variable values¶

Invididual variable values can be accessed via their value property. This is the approach shown in the Pyomo documentation in Sec 18.6.2.

In [6]:
instance.X['coils'].value

Out[6]:
1400
In [7]:
for p in instance.P:
print(instance.X[p].value)

1400
6000


Variable values can also be accessed in a few other ways. I'm guessing from the documentation that the value property approach shown above is the "Pyomothonic" way to do it. However, I've seen the following two approaches used in various Pyomo problem example galleries.

In [8]:
value(instance.X['coils'])

Out[8]:
1400
In [9]:
instance.X['coils']()

Out[9]:
1400

What is type of an individual variable object? What about its string representation?

In [10]:
type(instance.X['coils'])

Out[10]:
pyomo.core.base.var._GeneralVarData
In [12]:
str(instance.X['coils'])

Out[12]:
'X[coils]'

### All variable values¶

We can loop over the collection of variable objects in the problem instance and access their values as follows. This approach is based on Section 18.6.3 in the Pyomo 4 docs. I guess I'm confused as to the need for using getattr instead of simply accessing the object variable used in the for statement. As we see below, both v and varobject have type class 'pyomo.core.base.var.IndexedVar'.

In [19]:
for v in instance.component_objects(Var, active=True):
print ("Variable component object",v)
print ("Type of component object: ", str(type(v))[1:-1]) # Stripping <> for nbconvert
varobject = getattr(instance, str(v))
print ("Type of object accessed via getattr: ", str(type(varobject))[1:-1])
for index in varobject:
print ("   ", index, varobject[index].value)

Variable component object X
Type of component object:  class 'pyomo.core.base.var.IndexedVar'
Type of object accessed via getattr:  class 'pyomo.core.base.var.IndexedVar'
coils 1400
bands 6000


Why can't we just do this?

In [20]:
for v in instance.component_objects(Var, active=True):
print ("Variable component object",v)
for index in v:
print ("   ", index, v[index].value)

Variable component object X
coils 1400
bands 6000


## Objective function value¶

I've found accessing the objective function value even less clear. I can't seem to find the suggested way to do it in the official docs and have run across various approaches in examples in various GitHub repos. Here's a bunch of examples that work to various degrees, though some appear to be deprecated. I wonder which is the Pyomothonic way.

In [21]:
# Various ways of trying to print out objective function value

print('\ninstance.Total_Profit.value')
print(instance.Total_Profit.value)

print('\ninstance.Total_Profit.value()')
print(instance.Total_Profit.value())

print('\nvalue(instance.Total_Profit)')
print(value(instance.Total_Profit))

print('\nvalue(instance.Total_Profit())')
print(value(instance.Total_Profit()))

print("\nobjective_object = getattr(instance, 'Total_Profit')")
objective_object = getattr(instance, 'Total_Profit')

print('\nobjective_object.value')
print(objective_object.value)

print('\nvalue(objective_object)')
print(value(objective_object.value))

print('\nobjective_object()')
print(objective_object())

instance.Total_Profit.value
WARNING: DEPRECATED: The .value property getter on SimpleObjective is deprecated. Use the .expr property getter instead
30*X[coils] + 25*X[bands]

instance.Total_Profit.value()
WARNING: DEPRECATED: The .value property getter on SimpleObjective is deprecated. Use the .expr property getter instead
192000

value(instance.Total_Profit)
192000

value(instance.Total_Profit())
192000

objective_object = getattr(instance, 'Total_Profit')

objective_object.value
WARNING: DEPRECATED: The .value property getter on SimpleObjective is deprecated. Use the .expr property getter instead
30*X[coils] + 25*X[bands]

value(objective_object)
WARNING: DEPRECATED: The .value property getter on SimpleObjective is deprecated. Use the .expr property getter instead
192000

objective_object()
192000


What is the right way to access the objective function value?