The final important preliminary we would like to discuss is the tupledict
class. This is a custom sub-class of the Python dict
class that allows you to efficiently work with subsets of Gurobi variable objects. To be more specific, you can use the sum
and prod
methods on a tupledict
object to easily and concisely build linear expressions. The keys for a tupledict
are stored as a tuplelist
, so the same select
syntax can be used to choose subsets of entries. Specifically, by associating a tuple with each Gurobi variable, you can efficiently create expressions that contain a subset of matching variables. For example, using the sum
method on a tupledict
object, you could easily build an expression that captures the sum over all Gurobi variables for which the first field of the corresponding tuple is equal to 3 (using x.sum(3, '*')
).
While you can directly build your own tupledict
, the Gurobi interface provides an addVars
method that adds one Gurobi decision variable to the model for each tuple in the input argument(s) and returns the result as a tupledict
. Let us give a simple example. We'll begin by constructing a list of tuples, and then we'll create a set of Gurobi variables that are indexed using this list:
gurobi> l = list([(1, 2), (1, 3), (2, 3), (2, 4)]) gurobi> d = model.addVars(l, name="d") gurobi> model.update()
The addVars
method will create variables d(1,2)
, d(1,3)
, d(2,3)
, and d(2,4)
. Note that the name
argument is used to name the resulting variables, but it only gives the prefix for the name - the names are subscripted by the tuple keys (so the variables would be named d[1,2]
, d[1,3]
, etc.). The final call to update
synchronizes certain internal data structures; this detail can be safely ignored for now.
You can then use this tupledict
to build linear expressions. For example, you could do:
gurobi> sum(d.select(1, '*'))
The select
method returns a list of Gurobi variables where the first field of the associated tuple is 1. The Python sum
statement then creates a linear expression that captures the sum of these variables. In this case, that expression would be d(1,2) + d(1,3)
. Similarly, sum(d.select('*', 3))
would give d(1,3) + d(2,3)
. As with a tuplelist
, you use a '*'
string to indicate that any value is acceptable in that position in the tuple.
The tupledict
class includes a method that simplifies the above. Rather than sum(d.select('*', 3))
, you can use d.sum('*', 3)
instead.
The tupledict
class also includes a prod
method, for cases where you need to build a linear expression with coefficients that aren't all 1.0
. Coefficients are provided through a dict
argument. They are indexed using the same tuples as the tupledict
. For example, given a dict
named coeff
with two entries: coeff(1,2)
= 5 and coeff(2,3)
= 7, a call to d.prod(coeff)
would give the expression 5 d(1,2) + 7 d(2,3)
. You can also include a filter, so d.prod(coeff, 2, '*')
would give just 7 d(2,3)
.
Note that tupledict
is a sub-class of dict
, so you can use the standard dict
methods to access or modify a tupledict
:
gurobi> print(d[1,3]) <gurobi.Var d[1,3]> gurobi> d[3, 4] = 0.3 gurobi> print(d[3, 4]) 0.3 gurobi> print(d.values()) dict_values([<gurobi.Var d[1,2]>, 0.3, <gurobi.Var d[1,3]>, <gurobi.Var d[2,3]>, <gurobi.Var d[2,4]>])
In our upcoming network flow example, once we've built a tupledict
that contains a variable for each valid commodity-source-destination combination on the network (we'll call it flows
), we can create a linear expression that captures the total flow on all arcs that empty into a specific destination city as follows:
gurobi> inbound = flows.sum('*', '*', 'New York')
We now present an example that illustrates the use of all of the concepts discussed so far.
Comments
0 comments
Please sign in to leave a comment.