pythonlinear-programminggurobimixed-integer-programming

Inferring (printing) algebraic form of constraint(s) and objective function from the gurobipy model


Is there a way by which we can print the underlying algebraic form of constraints or objective function. For instance with AMPL there is a handy functionality display which prints the algebraic form of the constraint or the objective function. I understand that we can always write the model in an LP form and then investigate, but can we print (interactively) the constraint in the python console ?

A concrete example:

import gurobipy as gp
from gurobipy import GRB

products, price = gp.multidict({"table": 80, "chair": 45})
resources, availability = gp.multidict({"mahogany": 400, "labour": 450})

bom = {
    ("mahogany", "chair"): 5,
    ("mahogany", "table"): 20,
    ("labour", "chair"): 10,
    ("labour", "table"): 15,
}

model = gp.Model("furniture")

make = model.addVars(products, name="make", obj=list(price.values()), vtype=GRB.INTEGER)

res = model.addConstrs(
    gp.quicksum(bom[r, p] * make[p] for p in products) <= availability[r]
    for r in resources
)

model.ModelSense = GRB.MAXIMIZE
model.optimize()

Now, I make add another product to the formulation -

add_prod = "bed"
make[add_prod] = model.addVar(
    obj=90, vtype=GRB.INTEGER, column=gp.Column([15, 18], res.values()), name="make[bed]"
)

model.optimize()

If, I want to verify whether the change I have made has happened in an expected manner, I want to print the constraint and objective function. I can write the model in an .lp format but is there a way I can print the constraint, without having to look at the .lp file. Especially, if the model is very large, and I want to see only one constraint, then seeing the entire lp file could be an overkill.


Solution

  • Model.getRow() retrieves the LinExpr object that represents the linear expression for a constraint, and Model.getObjective() retrieves the LinExpr or QuadExpr object for the objective function. If you simply want to print this for debugging, just print these objects directly like this:

    model.update()
    
    print(f"Obj: {model.getObjective()}")
    for r in resources:
        print(f"{res[r].ConstrName}: {model.getRow(res[r])} {res[r].Sense} {res[r].RHS}")
    

    If you need to work with the variables and coefficients, then you can iterate over a LinExpr object like this:

    obj = model.getObjective()
    for i in range(obj.size()):
        print(f"{obj.getCoeff(i)} * {obj.getVar(i).VarName}")
    

    Finally, it's good practice to name constraints by adding a string when calling Model.addConstrs():

    res = model.addConstrs(
        (gp.quicksum(bom[r, p] * make[p] for p in products) <= availability[r]
        for r in resources), name="avail"
    )
    

    Note that I added a call to Model.update() to process the model modifications; without this, you may not see the changes to your model.

    (Disclaimer: I work at Gurobi).