Skip to main content

Remove variables with 0 coefficient in the LP file

Answered

Comments

6 comments

  • Daniel Espinoza

    Hi Martin,

    AddVars has the ability to work with partial sets of tuples; for example:

    vars = m.addVars(5,10,10,name='X')

    will create 500 variables, but if you know that only some of them are relevant, you can create a tuplelist

    OD=tuplelist({(0,1,1),(1,1,2),....,(4,9,0)})

    vars = m.addVars(OD, name='X')

    will create as many as #OD variables, and you can still use the full selection API, i.e., 

    expr = vars.sum(1,'*',4)

    This means you will have a lighter-weight memory footprint for your projects.

    Hope this helps

    0
  • Martin Bagaram
    Gurobi-versary
    First Question
    First Comment

    Thank you. I was asking the question because I wanted to avoid defining the tuplelist explicitly. It would be good if Gurobi could remove those variables when writing the lp file. After all those variables have no effect on the solution so why keep it. It is just a suggestion.

     

    Thank you 

    0
  • Luis Fortes
    Gurobi-versary
    First Comment
    First Question

    Hello

    The zeros in the coefficients is also a big problem for me too. Have the friends of Gurobi's development already created the solution suggested by Mr. Martin Bagaram? If so, what parameter can I change so that zero coefficients are automatically disregarded from the lp file?

    Best regards 

    0
  • Sebastian Meza
    Gurobi-versary
    First Comment
    First Question

    Hi Eli, 

    any example or code snippet that you could provide, please, by the case when the model also has general constraints added to the model?

    I'm having the situation here referenced. https://support.gurobi.com/hc/en-us/community/posts/20549609141137-Indicator-constraints-and-objective-function?page=1#community_comment_20551748675473

     

    Thank you in advance!

    Sebastian

    0
  • Eli Towle
    Gurobi Staff Gurobi Staff

    Below is a code snippet that should remove from the model variables that:

    • Do not appear in any linear constraints,
    • Do not appear in any general constraints, and
    • Have a linear objective coefficient of \( 0 \).

    The code does not account for quadratic model components, piecewise-linear objective terms, and so on.

    m.update()

    gcfuncs = {
        GRB.GENCONSTR_MAX: m.getGenConstrMax,
        GRB.GENCONSTR_MIN: m.getGenConstrMin,
        GRB.GENCONSTR_ABS: m.getGenConstrAbs,
        GRB.GENCONSTR_AND: m.getGenConstrAnd,
        GRB.GENCONSTR_OR: m.getGenConstrOr,
        GRB.GENCONSTR_NORM: m.getGenConstrNorm,
        GRB.GENCONSTR_INDICATOR: m.getGenConstrIndicator,
        GRB.GENCONSTR_PWL: m.getGenConstrPWL,
        GRB.GENCONSTR_POLY: m.getGenConstrPoly,
        GRB.GENCONSTR_EXP: m.getGenConstrExp,
        GRB.GENCONSTR_EXPA: m.getGenConstrExpA,
        GRB.GENCONSTR_LOG: m.getGenConstrLog,
        GRB.GENCONSTR_LOGA: m.getGenConstrLogA,
        GRB.GENCONSTR_LOGISTIC: m.getGenConstrLogistic,
        GRB.GENCONSTR_POW: m.getGenConstrPow,
        GRB.GENCONSTR_SIN: m.getGenConstrSin,
        GRB.GENCONSTR_COS: m.getGenConstrCos,
        GRB.GENCONSTR_TAN: m.getGenConstrTan,
    }

    # Indices of variables participating in general constraints
    gcvars = set()

    for gc in m.getGenConstrs():
      retvals = gcfuncs[gc.GenConstrType](gc)

        # Vars are found in return values of type Var, LinExpr, and List[Var]
        for rv in retvals:
            if isinstance(rv, gp.Var):
                gcvars.add(rv.index)
            elif isinstance(rv, gp.LinExpr):
                for i in range(rv.size()):
                    gcvars.add(rv.getVar(i).index)
            elif isinstance(rv, list) and len(rv) > 0 and isinstance(rv[0], gp.Var):
                for v in rv:
                    gcvars.add(v.index)

    to_remove = [
        v
        for v in m.getVars()
      if not m.getCol(v).size() and not v.Obj and v.index not in gcvars
    ]
    m.remove(to_remove)
    print(f"Removed {len(to_remove)} unused variables")

    The code first builds a set of indices of all variables participating in general constraints. It iterates through the general constraints via Model.getGenConstrs(), then calls the appropriate Model.getGenConstrXXX() method (e.g., Model.getGenConstrIndicator()) based on the general constraint's GenConstrType attribute. The number and types of values returned by these methods vary between general constraints. Thus, the code looks for return types that are variable objects (Var) or could contain variable objects (a LinExpr or a list of Var objects).

    After this set of indices is built, variables that do not participate in any general constraints or linear constraints and have an objective coefficient of \( 0 \) are removed.

    That said, I would be surprised if this had any impact on the solver's performance. Gurobi should remove unused variables from the model during presolve.

    0
  • Eli Towle
    Gurobi Staff Gurobi Staff

    Gurobi does not modify the model as defined by the user. Variables that are added to the model but are not used in the constraints/objective appear in the objective function of the LP file with a \( 0 \) coefficient. This way, Gurobi knows that these variables were added to the model and can keep the model in the same state that it was defined. Gurobi removes unused variables during presolve when solving the problem, though this does not affect the original model formulation.

    Automatically removing unused variables from the model definition could lead to confusing behavior. For example, a user could:

    1. Add a variable (say, \( x \)) to a model without adding it to any constraints or the objective function
    2. Write an LP file of the model
    3. Read the LP model file and attempt to retrieve \( x \) to include in a new constraint

    If \( x \) were automatically removed from the LP file, the user would not be able to later retrieve and use \( x \) in step 3, which is not intuitive.

    Have you tried adding logic to prevent such variables from ever being added to the model? As Daniel mentioned, this would result in a model with a smaller memory footprint.

    It's also possible to remove unused variables yourself. Below is a Python code snippet that iterates over all variables and removes those that do not appear in any linear constraints and have a zero linear objective coefficient. The code snippet uses Model.getVars(), Model.getCol(), Column.size(), the Obj variable attribute, and Model.remove().

    to_remove = [v for v in m.getVars() if not m.getCol(v).size() and not v.Obj]
    m.remove(to_remove)
    print(f'Removed {len(to_remove)} unused variables')

    Note that the above code assumes your model is comprised of linear constraints and a linear objective, meaning it has no quadratic components, general constraints, piecewise-linear objective terms, etc. However, the code can be extended to cover those cases, too.

    -1

Please sign in to leave a comment.