1

I tried to create a LP model by using pyomo.environ. However, I'm having a hard time on creating sets. For my problem, I have to create two sets. One set is from a bunch of nodes, and the other one is from several arcs between nodes. I create a network by using Networkx to store my nodes and arcs. The node data is saved like (Longitude, Latitude) in tuple form. The arcs are saved as (nodeA, nodeB), where nodeA and nodeB are both coordinates in tuple.

So, a node is something like:

(-97.97516252657978, 30.342243012086083)

And, an arc is something like:

((-97.97516252657978, 30.342243012086083), (-97.976196300350608, 30.34247219922803))

The way I tried to create a set is as following:

# import pyomo.envrion as pe
# create a model m 
m = pe.ConcreteModel()
# network is an object I created by Networkx module
m.node_set = pe.Set(initialize= self.network.nodes()) 
m.arc_set = pe.Set(initialize= self.network.edges())

However, I kept getting an error message on arc_set.

ValueError: The value=(-97.97516252657978, 30.342243012086083,
-97.976196300350608, 30.34247219922803) does not have dimension=2, 
which is needed for set=arc_set

I found it's weird that somehow my arc_set turned into one tuple instead of two. Then I tried to convert my nodes and arcs into string but still got the error. Could somebody show me some hint? Or how do delete this bug? Thanks!

whh1294
  • 53
  • 7

1 Answers1

4

Underneath the hood, Pyomo "flattens" all indexing sets. That is, it removes nested tuples so that each set member is a single tuple of scalar values. This is generally consistent with other algebraic modeling languages, and helps to make sure that we can consistently (and correctly) retrieve component members regardless of how the user attempted to query them.

In your case, Pyomo will want each member of the the arc set as a single 4-member tuple. There is a utility in PyUtilib that you can use to flatten your tuples when constructing the set:

from pyutilib.misc import flatten
m.arc_set = pe.Set(initialize=(tuple(flatten(x)) for x in self.network.edges())

You can also perform some error checking, in this case to make sure that all edges start and end at known nodes:

from pyutilib.misc import flatten
m.node_set = pe.Set( initialize=self.network.nodes() ) 
m.arc_set = pe.Set(
    within=m.node_set*m.node_set,
    initialize=(tuple(flatten(x)) for x in self.network.edges() )

This is particularly important for models like this where you are using floating point numbers as indices, and subtle round-off errors can produce indices that are nearly the same but not mathematically equal.

There has been some discussion among the developers to support both structured and flattened indices, but we have not quite reached consensus on how to best support it in a backwards compatible manner.

jsiirola
  • 2,259
  • 2
  • 9
  • 12