Gurobi Cookbook - Column-wise Formation (5/5)
written by Abremod, LLC on 2019-07-01
Using a column-wise formulation
Often, is it more convenient to think of the contraints that a variable participates in instead of the variables that are part of a constraint. Also, in a column-generation procedure, this is required. To support this, Gurobi has a Column object that can be used to when defining a model. Let's create the same model as before.
model = grb.Model() c0 = model.addConstr(0, '>', 10, name='c0') c1 = model.addConstr(0, '<', 4, name='c1') model.update() x = model.addVar(name='x', obj=2, column=grb.Column([1, 1], [c0, c1])) y = model.addVar(name='y', obj=3, column=grb.Column([1, -1], [c0, c1])) model.update() model.optimize()
Optimize a model with 2 rows, 2 columns and 4 nonzeros Coefficient statistics: Matrix range [1e+00, 1e+00] Objective range [2e+00, 3e+00] Bounds range [0e+00, 0e+00] RHS range [4e+00, 1e+01] Presolve time: 0.00s Presolved: 2 rows, 2 columns, 4 nonzeros Iteration Objective Primal Inf. Dual Inf. Time 0 0.0000000e+00 1.000000e+01 0.000000e+00 0s 2 2.3000000e+01 0.000000e+00 0.000000e+00 0s Solved in 2 iterations and 0.01 seconds Optimal objective 2.300000000e+01
The column-wise approach often looks awkward. Linear programming formualtions are almost always documented in a row-wise format and it is natural to think in terms of the variables being added to constraints. Because of that, this method of creating models is often restricted to column-generation proceedures, where they are required and are probably under-utilized in other cases.
Obtaining the dual solution
When computing the solution to a linear problem (with no integer constraints), gurobi will also compute dual prices for the contraints. This has the economic interpretation as the change in the value of the objective as the right hand side of the contraint is increased or decreased, and it is used in Column Generation procedures. The attribute is Pi
constr_names = model.getAttr('ConstrName', model.getConstrs()) constr_values = model.getAttr("Pi", model.getConstrs()) zip(constr_names, constr_values)
[('c0', 2.5), ('c1', -0.5)]
In this example, the dual price of
c0 ($x + y \ge 10$) was 2.5. That means, increasing the right hand side from 10, to say 10.1 and resolving would increase the objective from 2.3 to at least 2.55, and reducing the right hand side from 10 to 9.9 would reduce the objective by at most .25 to at least 2.05. We say at least 2.55 because we are minimizing.