Skip to main content

How can I normalize a variable?

Answered

Comments

10 comments

  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    Hi WI, 

    Thanks for the response. Sure, I get that your decision variables represent the Euclidean distance between pairs of points (let us refer to them as \(x\) for simplicity) on a grid of 1 by 1 with lower and upper bounds 0 and \(\sqrt{2}\). And there are binary decision variables in the objective function. However, I am still not clear why you want to normalize the variables \(x\) in the objective function and how they are connected to the binary decision variables.

    Anyways, assuming \(x_{\tiny \mbox{min}}\) and \(x_{\tiny \mbox{max}}\) are decision variables and are not the lower and upper bounds of the variables \(x\), you need to do the following:

    • Define a new continuous variable \(x_{\tiny \mbox{min}}\) and then use the Gurobi min_() general constraint helper API to define \(x_{\tiny \mbox{min}} = \min_{i} x_i \)
    • Define a new continuous variable \(x_{\tiny \mbox{max}}\) and then use the Gurobi max_() general constraint helper API to define \(x_{\tiny \mbox{max}} = \max_{i} x_i \)
    • Define new continuous variables \(y\) between 0 and 1 and then use the Model.addQConstr() method to model \(y_i (x_{\tiny \mbox{max}} - x_{\tiny \mbox{min}}) = x_i -x_{\tiny \mbox{min}}\) for all \(i\). This will make your problem non-convex and you would need to use NonConvex=2 setting to solve the problem

    Best regards,

    Maliheh

    1
  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    Hi WI, 

    You need a few changes. Please see below.

    import gurobipy as gp
    from gurobipy import GRB
    from math import sqrt

    if __name__ == "__main__":
    model = gp.Model("name")

    Var = model.addVars(len(A), lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="var")
    xmin = model.addVar(lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="xmin")
    xmax = model.addVar(lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="xmax")
    y = model.addVars(len(A), lb=0, ub=1, vtype=GRB.CONTINUOUS, name="y")

    model.addConstr(xmin == gp.min_(Var), name="min") # [*]
    model.addConstr(xmax == gp.max_(Var), name="max") # [*]

    for i in range(len(A)):
    model.addConstr(
    gp.quicksum(compute_value(A[i], B[j]) forjinrange(len(B))) == Var[i],
    name=f"var_{i}",
    )
    model.addQConstr(y[i] * (xmax - xmin) == Var[i] - xmin, name=f"normalize_{i}"
    # [*]: Edited
     
    Best regards,
    Maliheh
    1
  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    Hi WI, 

    Since \(\texttt{Var_min}\) and \(\texttt{Var_max}\) are variables in your model, normalizing the decision variables as \((\texttt{Var[i]} - \texttt{Var_min})/(\texttt{Var_max} - \texttt{Var_min})\) will make the problem non-linear. Do you really need to do this? Have you considered defining your decision variables to be between 0 and 1 right from the start? For example, instead of decision variable to represent the amount of money invested in each asset, you can define them to represent the fraction of the total budget invested in each asset. This way, you do not need to map the decision variables to values between 0 and 1.

    What the decision variables and the objective function represent in your model in English?

    Best regards,

    Maliheh

    0
  • Wl MK
    Conversationalist
    First Question

    Hi Maliheh,
    The decision variables in the objective function are a binary type (0 or 1), and the variable Var is a Euclidean distance between points in a graph that has the x and y-axis bounded between 0 and 1.

    0
  • Wl MK
    Conversationalist
    First Question

    So I should proceed as:

    model = gp.Model('name')
    Var = m.addVars(len(A), vtype=GRB.CONTINUOUS, name='var')
    xmin=m.addVars(1, vtype=GRB.CONTINUOUS, name='xmin')
    xmax=m.addVars(1, vtype=GRB.CONTINUOUS, name='xmax')
    y=m.addVars(len(A),ub=1, vtype=GRB.CONTINUOUS, name='xmin')

    for i in range(len(A)):
    m.addConstr(gp.quicksum(compute_value(A[i],B[j]) for j in range(len(B)) == Var[i], name=f'var_{i}')

    xmin=min(Var)
    xmax=min(Var)

    m.addQConstr(y[i]=x[i]-xmin/(xmax-xmin) for i in Var)

    then I utilize y in the objective function instead of Var, then:

    m.params.NonConvex=2
    m.optimize()

    Is that right?
    And thank you for your help.

    0
  • Wl MK
    Conversationalist
    First Question

    Thank you Maliheh

    When runing the code it raises an error:

    TypeError: unsupported operand type(s) for -: 'GenExprMax' and 'GenExprMin'


    at this line:

    m.addQConstr(y[i]*(xmax - xmin) == h[i] - xmin, name=f"normalize_{i}")


    Any idea how to fix it?

    0
  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    Hi WI, 

    The error means that the subtraction is not supported for the general expressions, i.e., we cannot subtract \(x_{\small\mbox{min}}\) from \(x_{\small\mbox{max}}\). We likely need additional auxiliary variables to model this. 

    Could you please post the snippet that you are running such that I can reproduce the error? If the snippet is too long, you can share the file using Dropbox, Google Drive, or OneDrive.

    Best regards,

    Maliheh

    0
  • Wl MK
    Conversationalist
    First Question

    This code generates the same error:

    A=[0,4,3,2,8]
    B=[7,9,4,5,2]
    model = gp.Model("name")
    Var = model.addVars(5, lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="var")
    xmin = model.addVar(lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="xmin")
    xmax = model.addVar(lb=0, ub=sqrt(2), vtype=GRB.CONTINUOUS, name="xmax")
    y = model.addVars(5, lb=0, ub=1, vtype=GRB.CONTINUOUS, name="y")

    xmin = gp.min_(Var)
    xmax = gp.max_(Var)

    for i in range(5):
     model.addConstr(gp.quicksum(A[i]*B[j] for j in range(5)) == Var[i], name=f"var_{i}")
      model.addQConstr(y[i] * (xmax - xmin) == Var[i] - xmin, name=f"normalize_{i}")

    And thank you for your comprehension

     
    0
  • Maliheh Aramon
    Gurobi Staff Gurobi Staff

    Hi WI, 

    Please see the snippet edited above. Specifically, below needs to change from:

    xmin = gp.min_(Var)
    xmax = gp.max_(Var)

    to:

    model.addConstr(xmin == gp.min_(Var), name="min") 
    model.addConstr(xmax == gp.max_(Var), name="max")

    Your snippet should build the model without the previous error now.

    Best regards,

    Maliheh

    0
  • Wl MK
    Conversationalist
    First Question

    Thank you very much Maliheh.

    0

Please sign in to leave a comment.