Skip to main content

Using a for loop for a single or specific value in variable list in constraint formulation

Answered

Comments

188 comments

  • Tanmoy Das
    Gurobi-versary
    Investigator
    Collaborator

    It will be easier to think if you post your constraints math equation. Anyway, 

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in x )==1 for res in res_list3),name="Endresources") 

    "for res in res_list3" at the end of the code means for all 

    say we have i = [1,2], k =[ 4,5] then, the above equation becomes D_1, D_2 (excluding D_1,4 and so on)

    If you dont need for all in your equation, you can try something like below to produce D_1,4 ; D_1,5; D_2,4, D_2,5

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t]==1) 
    for t in x for res in res_list3),name="Endresources")

     

    0
  • Tanmoy Das
    Gurobi-versary
    Investigator
    Collaborator

    Comment outside of your question: Its better to be consistent on naming the variables e.g. all INDICES with small_loc two/few letters e.g. time, res and then associated SET with CapLock maybe. e.g. Time, Resources

    for time in Time for res in Resources 

    compared to
    for t in r for res in res_list 

    Also, we already know that Resources is a Python list since it is a SET in the model equation. Typically, we can adapt direct words/notation from the math equation from the manuscript/related documents. easier to track which one is index and which one is set in the model. 

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Note that

    (gp.quicksum(Resources[res,t]==1) 

    will not work because quicksum requires a list of terms.

    How can I use "time" in Constraint instead of "x"

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in time )==1 for res in res_list3),name="Endresources")

    Regarding the naming of constraints, could you please elaborate more what exactly you want to achieve?

    Currently you construct your constraint over the \(\texttt{res_list3}\) so the constraint names are indexed over the \(\texttt{res_list3}\) list. Now you seem to have an additional single value \(95\) which you would like to add to the name? Is this correct? You could just hard code it

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in time )==1 for res in res_list3),name="Endresources_95")

     

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    Hi Jaromil, First of all many thanks for the reply.

    Let me clear out some points.

    Data:

     

    [res_cat2idx, res_num] =  [OrderedDict([('EAF', [1]),              ('AOD', [2]),              ('LF', [3]),              ('CC1', [4]),              ('CC2', [5]),              ('H_A_S1', range(6, 30)),              ('H_A_S2', range(30, 54)),              ('H_B_S2', range(54, 78)),              ('H_A_S3', range(78, 102)),              ('H_B_S3', range(102, 126)),              ('H_A_S4', range(126, 150)),              ('H_B_S4', range(150, 174)),              ('EN', [174])]), 174]
    [tasks, task_num] =
    [OrderedDict([('EAF', range(1, 25)),              ('TR_S1', range(25, 49)),              ('AOD', range(49, 73)),              ('TR_S2', range(73, 97)),              ('LF', range(97, 121)),              ('TR_S3', range(121, 145)),              ('CC1', range(145, 151)),              ('CC2', range(151, 157))]), 156]
    time = list()
    for x in range(0,int(num_t)):
        time.append(x)
    res_list = [res for cat, cat_res in res_cat2idx.items() for res in cat_res]
    task_list=[task for cat, cat_task in tasks.items() for task in cat_task]

     

    Now I am defining my Variables:

    Resources=steel.addVars(res_list,time,vtype=GRB.INTEGER,name="Resources")
    Tasks=steel.addVars(task_list,time,vtype= GRB.BINARY,name="Tasks"

    I have this constraint:

     

    where

    1)R : Resources(Variable)

    2)H should be some values from res_list  :(like)

    res_list3 = [res for cat, cat_res in res_cat2idx.items() if cat == 'H_B_S4' for res in cat_res]

    3)T should be (time[95]) , means the value is also 95(if you see time list)

    So I have done:

    x=[]
    x.append(time[95]) 

    & with this, I have formulated my constraint like:

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in x)==1 for res in res_list3 for t in x),name="Endresources"

    BUT:

    I wanted to ask that:

    The constraint should be formulated using the attributes of Variables right?

    By this, I mean:

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in time)==1 for res in res_list for t in x),name="Endresources"

    BUT, How Can I use specific values from those lists which are passed as a attribute in Variables?

    And Suppose If I dont do so, Will Gurobi use incorrect Variables?

     

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Apologies, but I think I still don't fully understand your issue with the \(\texttt{Endresources}\) constraints.

    BUT, How Can I use specific values from those lists which are passed as a attribute in Variables?

    And Suppose If I dont do so, Will Gurobi use incorrect Variables?

    What do you exactly mean by pass as an attribute in Variables? Do you want to use a variable with a specific index? Do you want to set some variable attributes? Note that variable attributes such as the solution value are filled by Gurobi after optimization has been performed.

    Regarding your \(\texttt{Endresources}\) constraints

    Endresources=steel.addConstrs((gp.quicksum(Resources[res,t] for t in time)==1 for res in res_list for t in x),name="Endresources") 

    You are using variable \(\texttt{t}\) twice which may lead to wrong results, so it is better to one of those by, e.g., \(\texttt{t1}\). The above statement will generate constraints

    \[\begin{align*}
    \sum_t Resources_{res,t} = 1 \quad \forall res
    \end{align*}\]
    The \(\texttt{for t in x}\) does not really do anything because \(\texttt{x}\) holds only 1 item.

    To get more clarity on what exactly is happening and how your constraints look like, please generate a human-readable LP file after you added all constraints via

    steel.write("steel.lp")

    The above command will generate a file called \(\texttt{steel.lp}\) which you can inspect and see what model you constructed.

    If this does not help, please write what constraint you are currently constructing as you will see in the \(\texttt{steel.lp}\) file and state what the constraint should actually look like.

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious
    Steel.lp 

    returns me only the "WARNING"

    It doesn't return me the human readable form.

    I am writing what I am expecting:

    "Do you want to use a variable with a specific index?"

    YES

    where:

    res= (Specific indexes from res_list)

    res_list3 = [res for cat, cat_res in res_cat2idx.items() if cat == 'H_B_S4' for res in cat_res]

    t= (Last index of time)

    time[95]

    What do you exactly mean by pass as an attribute in Variables? 

    As you see I am using res_list 3 in my for loop instead of res_list (which is a attribute of my variable)

    Will this create an ISSUE?

     

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Thanks for the clarification.

    returns me only the "WARNING"

    The file will still be generated and you can look at it in any text editor.

    "Do you want to use a variable with a specific index?"

    YES

    Then you have to loop the quicksum over res instead of time.

    Endresources=steel.addConstr((gp.quicksum(Resources[res,time[95]] for res in res_list3)==1 ),name="Endresources_time[95]") 

    As you see I am using res_list 3 in my for loop instead of res_list (which is a attribute of my variable)

    I think this is causing some confusion. It is not an attribute of the variable but the index of the respective tupledict. Variable attributes are listed in the documentation.

    Will this create an ISSUE?

    No, it should not create an issue.

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    Many thanks Jaromil for the infotmation:

    My  last question to make it very clear from the above explanation:

    The Data is posted in previous comments.

    y=[]
    for res_cat, resources in res_cat2idx.items():
            if 'H_A_' not in res_cat:
                continue
            y.append(resources)
    Transfertime=steel.addConstrs((((Resources[res,t] for res in y) == 0)for res in y for t in time),name ="Transfertime")
    As you can see, it is using range , But I want every element from that range:

    Can you please explain?
    By this I feel I will now understand writing constraints.

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    You have to turn the ranges saved in \(\texttt{y}\) to lists. Otherwise, it is interpreted as a string in this case.

    Transfertime=steel.addConstrs((((Resources[res,t] for res in list(y)) == 0) for t in time),name ="Transfertime")

    Please note that I removed the second \(\texttt{for res in y}\) because otherwise you would loop over \(\texttt{y}\) in each quicksum and when constructing constraints via addConstrs, so you would have the same constraint multiple times but with a different name.

    If you are using the range object in a similar manner across the rest of your code, you should adjust the quicksum and addConstrs calls as above.

    0
  • Abhishek Bajpai
    Gurobi-versary
    First Comment
    First Question

    Yes Jaromil I understood your point. 

    But doing so, There is some issue.

    See now my constraint is looking like:

    .

    Instead it should look like:

    (6,0)

    (6,1)

    (6,2)........so on.....till last element: (150,95)
    0
  • Abhishek Bajpai
    Gurobi-versary
    First Comment
    First Question

    Yes Ofcourse:

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    The issue with range is even bigger because it gets interpreted as a string when constructing constraints. It would be best if you would work with lists instead of the range object. So you should go with

    # intermediate products and final products, A - after, B - before
    for stage in range(1, num_stage+1):
      res_cat2idx['H_A_S%s' % stage] = list(range(r_idx, r_idx + num_heats)) #'H_A_S1'=EAd1 (after stage1) 'H_B_S4'=EAs4(before stage4) #'H_A_S4'=intermediate product after stage 4 i.e final products heats
        r_idx = r_idx + num_heats
        if int(stage) == 1:
            continue
      res_cat2idx['H_B_S%s' % stage] = list(range(r_idx, r_idx + num_heats))
        r_idx =r_idx + num_heats

    There are possibly other place which would need adjustment.

    For Transfertime the following should work

    y={}
    for res_cat, resources in res_cat2idx.items():
            if 'H_A_' not in res_cat:
                continue

            y[res_cat]= list(resources)

    Transfertime=steel.addConstrs(((gp.quicksum(Resources[res,t] for res in y[y1]) == 0) for y1 in y for t in time),name ="Transfertime")

    Please note that Transfertime was missing a quicksum. This is the reason why from time to time you definitely should write a \(\texttt{steel.lp}\) file and have a look at what you actually constructed.

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    As you can see My variables looks like:

    Reources(res_cat,time)

    instead It should look like:

    Resources(resources,time)

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    This is the reason why from time to time you definitely should write a steel.lp file and have a look at what you actually constructed.

    Can we write steel.lp before optimising it?

     

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Can we write steel.lp before optimising it?

    Yes

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    Ok Jaromil but can you please elaborate why the constraints are looking thus?

    Do you want me to provide my queries here in a single post?

    Just to make it very clear !

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Do you want me to provide my queries here in a single post?

    Yes, please elaborate which constraints you mean, how they look now and how they should look like. It would be great if you could provide a code snippet which constructs only the constraints of interest and holds all lists and dictionaries that are needed. The snippet should be self contained.

    Please do not post your whole code.

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious
    Variables:

    res_list = [res for cat, cat_res in res_cat2idx.items() for res in cat_res]
    task_list=[task for cat, cat_task in tasks.items() for task in cat_task]

    [res_cat2idx,tasks ,time- already provided above)

    Resources=steel.addVars(res_list,time,vtype=GRB.INTEGER,name="Resources")
    Tasks=steel.addVars(task_list,time,vtype= GRB.BINARY,name="Tasks")
    Constraint:


    It looks like:


    where: task =
    heat2tasks = [task for task_cat, task_list2 in tasks.items()
                   if 'CC' not in task_cat for task in task_list2]


    It should look like:


    Constraint:


    where res :
    Values
    of H_A_S1,H_A_S2,H_A_S3,H_A_S4 in res_cat2idx.

    It looks like:

    Instead: It should look like:

    (Note: H_A_S1 = List =[6,7,8,9....30])

    Constraint:


    where:

    x={}
    for res_cat, resources in res_cat2idx.items():
        if 'H_B_' not in res_cat:
                continue
        max_wait_time = trans_time_max['TR
    _S%d' % (int(res_cat[-1]) - 1)]
        min_tran_time = trans_time['TR_S%d' % (int(res_cat[-1]) - 1)]
       
        for res in resources:   # each heat a constraint
            x[res] = (math.ceil((max_wait_time-min_tran_time)/rtn_t0))

    Transfertime1= steel.addConstrs((gp.quicksum(Resources[res,t] for t in time )<=x[res]
    for res in x.keys()),name="Transfertime1")

    It looks like:


    Instead it should look like:


    Constraint:


    where:
    res =
    res_list3 = [res for cat, cat_res in res_cat2idx.items() if cat == 'H_B_S4' for res in cat_res]

    It looks like:

    Instead it should look like:

    I hope I made it brief.Kindly Jaromil let me know what are the tiny things I am missing out, As this is the very initial of me doing GUROBI.


     






    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Let's do it step by step and start with \(\texttt{Taskexecution}\). You are currently constructing the constraints

    \[\begin{align*}
    \sum_{t \in time} Tasks_{task,t} = 1 \quad \forall task \in heat2task
    \end{align*}\]

    The constraints are called \(\texttt{Taskexecution[task]}\) with \(\texttt{task in heat2task}\), which look exactly as you wanted \(\sum_t N_{task,t} = 1 \forall task\). You can verify it by writing the model

    steel.write("steel.lp")

    opening the \(\texttt{steel.lp}\) file in any standard text editor and looking at \(\texttt{Taskexecution}\) constraints. You will see

    Taskexecution[1]: Tasks[1,0] + Tasks[1,1] + ... = 1
    Taskexecution[2]: Tasks[2,0] + Tasks[2,1] + ... = 1
    ...

    which look exactly as expected.

    You say you want that the constraints look like \(N_{task,0}, N_{task,1}, \dots \) but this does not correspond to \(\sum_t N_{task,t} = 1 \forall task\).

    Could you please clarify whether you want constraints \(\sum_t N_{task,t} = 1 \forall task\) or some other constraints called \(N_{task,0}, N_{task,1}, \dots \).

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    Okay, Lets do step by step... I will share my ".lp" file also.So it is very clear:

    1)Task Excecution Constraint:

    Taskexecution=steel.addConstrs((gp.quicksum(Tasks[task,t] for t in time)==1 for task in heat2tasks for t in time),name="Taskexecution")

    Output:

    1)

    Last Constraint: [ I hope that is what is wanted but one thing: It should be called Task Execution[task] , instead it is showing: Taskexecution[task,t] ]

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    It does that because you have a redundat \(\texttt{for t in time}\) loop to construct constraints

    Taskexecution=steel.addConstrs(
    (gp.quicksum(Tasks[task,t] for t in time)==1
    for task in heat2tasks
    for t in time )
    ,name="Taskexecution")

    which should rather be

    Taskexecution=steel.addConstrs(
    (gp.quicksum(Tasks[task,t] for t in time)==1
    for task in heat2tasks)
    ,name="Taskexecution")

    Does this work now?

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    Yes Jaromil, It works now. THANKS.

    Lets move to second one.

    2) Transfer time constraint:

    y={}
    for res_cat, resources in res_cat2idx.items():
            if 'H_A_' not in res_cat:
                continue

            y[res_cat]= list(resources)

    Transfertime=steel.addConstrs(((gp.quicksum(Resources[res,t] for res in y[y1]) == 0) for y1 in y for t in time),name ="Transfertime")

    Ouput:[WRONG]

     

    IT SHOULD BE:

    Transfertime[0]: Resources[6,0] = 0
    Resources[7,0] =0
    Resources[8,0]= 0
    Resources[9,0] = 0
    Resources[10,0] = 0
    Resources[11,0] = 0
    Resources[12,0]= 0
    Resources[13,0] =0
    Resources[14,0] =0
    Resources[15,0] =
    Resources[16,0]=0
    Resources[17,0] =0
    Resources[18,0] = 0
    Resources[19,0] = 0
    Resources[20,0]= 0
    Resources[21,0] =0
    Resources[22,0] =0
    Resources[23,0] = 0
    Resources[24,0]=0
    Resources[25,0] = 0
    Resources[26,0] = 0
    Resources[27,0] = 0
    Resources[28,0]= 0
    Resources[29,0] = 0

    Transfertime[1]:
    Resources[6,1] =0
    Resources[7,1] =0
    Resources[8,1] = 0........
    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    Please note that

    Transfertime[0]: Resources[6,0] = 0
    Resources[7,0] =0
    Resources[8,0]= 0
    Resources[9,0] = 0
    Resources[10,0] = 0
    Resources[11,0] = 0
    Resources[12,0]= 0
    Resources[13,0] =0
    Resources[14,0] =0
    Resources[15,0] =
    Resources[16,0]=0
    Resources[17,0] =0
    Resources[18,0] = 0
    Resources[19,0] = 0
    Resources[20,0]= 0
    Resources[21,0] =0
    Resources[22,0] =0
    Resources[23,0] = 0
    Resources[24,0]=0
    Resources[25,0] = 0
    Resources[26,0] = 0
    Resources[27,0] = 0
    Resources[28,0]= 0
    Resources[29,0] = 0

    is not possible, because every equality \(\texttt{Resource[x,y]=0}\) is a constraints and thus, has to have a unique constraint name. The best you could do is

    Transfertime[6,0]: Resources[6,0] = 0
     Transfertime[6,1]: Resources[6,1] = 0
     Transfertime[6,2]: Resources[6,2] = 0
     Transfertime[6,3]: Resources[6,3] = 0
     Transfertime[6,4]: Resources[6,4] = 0
     Transfertime[6,5]: Resources[6,5] = 0
     Transfertime[6,6]: Resources[6,6] = 0
     Transfertime[6,7]: Resources[6,7] = 0
     Transfertime[6,8]: Resources[6,8] = 0
     Transfertime[6,9]: Resources[6,9] = 0
     Transfertime[6,10]: Resources[6,10] = 0
    ...

    This can be done with

    y={}
    for res_cat, resources in res_cat2idx.items():
            if 'H_A_' not in res_cat:
                continue

            y[res_cat]= list(resources)

    for key in y:
        y_list = y[key]
        Transfertime=steel.addConstrs((Resources[res,t]  == 0 for res in y_list for t in time),name ="Transfertime")

    Note that you have to use an additional \(\texttt{for}\)-loop to get the correct list out of your \(\texttt{y}\) dictionary.

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    I understand what you are trying to say : but:

    YOUR OUTPUT SAYS:

    your loop runs FIRST for t

    THEN for res

    But,

    your code says :

    loop is running for res first & t later...

    Just for understanding.

     

    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    I am writing next constraint (which has a issue) for further discussion :

    Constraint:

    Taskexecution1=steel.addConstrs(
    (gp.quicksum(Tasks[task,t] for t in time)==1 
    for task in group2tasks 
    for group in range(1, num_groups + 1))
    ,name="Taskexecution1")

    where:

    group2tasks = dict()
    for group in range(1, num_groups + 1):
        group2tasks[group] = [tasks[caster][group - 1] for caster in stage2units['4'].keys()]

    num_groups=6
    stage2units = {'1': {'EAF': 2}, '2': {'AOD': 2}, '3': {'LF': 2}, '4': {'CC1': 1, 'CC2': 1}}

    Output:[WRONG]

    Taskexecution1[1,1]: Tasks[1,0] + Tasks[1,1] + Tasks[1,2] + Tasks[1,3]
       + Tasks[1,4] + Tasks[1,5] + Tasks[1,6] + Tasks[1,7] + Tasks[1,8]
       + Tasks[1,9] + Tasks[1,10] + Tasks[1,11] + Tasks[1,12] + Tasks[1,13]
       + Tasks[1,14] + Tasks[1,15] + Tasks[1,16] + Tasks[1,17] + Tasks[1,18]
       + Tasks[1,19] + Tasks[1,20] + Tasks[1,21] + Tasks[1,22] + Tasks[1,23]
       + Tasks[1,24] + Tasks[1,25] + Tasks[1,26] + Tasks[1,27] + Tasks[1,28]
       + Tasks[1,29] + Tasks[1,30] + Tasks[1,31] + Tasks[1,32] + Tasks[1,33]
       + Tasks[1,34] + Tasks[1,35] + Tasks[1,36] + Tasks[1,37] + Tasks[1,38]
       + Tasks[1,39] + Tasks[1,40] + Tasks[1,41] + Tasks[1,42] + Tasks[1,43]
       + Tasks[1,44] + Tasks[1,45] + Tasks[1,46] + Tasks[1,47] + Tasks[1,48]
       + Tasks[1,49] + Tasks[1,50] + Tasks[1,51] + Tasks[1,52] + Tasks[1,53]
       + Tasks[1,54] + Tasks[1,55] + Tasks[1,56] + Tasks[1,57] + Tasks[1,58]
       + Tasks[1,59] + Tasks[1,60] + Tasks[1,61] + Tasks[1,62] + Tasks[1,63]
       + Tasks[1,64] + Tasks[1,65] + Tasks[1,66] + Tasks[1,67] + Tasks[1,68]
       + Tasks[1,69] + Tasks[1,70] + Tasks[1,71] + Tasks[1,72] + Tasks[1,73]
       + Tasks[1,74] + Tasks[1,75] + Tasks[1,76] + Tasks[1,77] + Tasks[1,78]
       + Tasks[1,79] + Tasks[1,80] + Tasks[1,81] + Tasks[1,82] + Tasks[1,83]
       + Tasks[1,84] + Tasks[1,85] + Tasks[1,86] + Tasks[1,87] + Tasks[1,88]
       + Tasks[1,89] + Tasks[1,90] + Tasks[1,91] + Tasks[1,92] + Tasks[1,93]
       + Tasks[1,94] + Tasks[1,95] = 1
     Taskexecution1[1,2]: Tasks[1,0] + Tasks[1,1] + Tasks[1,2] + Tasks[1,3]
       + Tasks[1,4] + Tasks[1,5] + Tasks[1,6] +.......

     

    IT SHOULD BE:

    Taskexecution1[145]: Tasks[145,0] + Tasks[145,1] + Tasks[145,2] + Tasks[145,3]
     + Tasks[145,4] + Tasks[145,5] + Tasks[145,6] + Tasks[145,7] + Tasks[145,8]
     + Tasks[145,9] + Tasks[145,10] + Tasks[145,11] + Tasks[145,12] + Tasks[145,13]
     + Tasks[145,14] + Tasks[145,15] + Tasks[145,16] + Tasks[145,17] + Tasks[145,18]
     + Tasks[145,19] + Tasks[145,20] + Tasks[145,21] + Tasks[145,22] + Tasks[145,23]
     + Tasks[145,24] + Tasks[145,25] + Tasks[145,26] + Tasks[145,27] .......+Tasks[145,95]= 1

    Taskexecution1[151]: Tasks[151,0] + Tasks[151,1] + Tasks[151,2] + Tasks[151,3]
     + Tasks[151,4] + Tasks[151,5] + Tasks[151,6] +.....+Tasks[151,95] =1


    Taskexecution[156]:.....

    I think it has been coded also perfectly. I am unsure why it is throwing output like this.

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    I understand what you are trying to say : but:

    YOUR OUTPUT SAYS:

    your loop runs FIRST for t

    THEN for res

    But,

    your code says :

    loop is running for res first & t later...

    Just for understanding.

    There is no first and second loop. There is an outer loop \(\texttt{for res in y_list}\)  and an inner loop \(\texttt{for t in time}\). So the code

    for key in y:
        y_list = y[key]
        Transfertime=steel.addConstrs((Resources[res,t]  == 0 for res in y_list for t in time),name ="Transfertime")

    is equivalent to

    for key in y:
        y_list = y[key]
        for res in y_list:
            for t in time:
                Transfertime=steel.addConstr((Resources[res,t]  == 0 ),name ="Transfertime[%d,%d]"%(res,t))

    and produces

    Transfertime[6,0]: Resources[6,0] = 0
     Transfertime[6,1]: Resources[6,1] = 0
     Transfertime[6,2]: Resources[6,2] = 0
     Transfertime[6,3]: Resources[6,3] = 0
     Transfertime[6,4]: Resources[6,4] = 0
     Transfertime[6,5]: Resources[6,5] = 0
     Transfertime[6,6]: Resources[6,6] = 0
    ...

    If you want to switch the indices, you just have to switch the inner and outer loop

    for key in y:
        y_list = y[key]
        Transfertime=steel.addConstrs((Resources[res,t]  == 0 for t in time for res in y_list ),name ="Transfertime")

    will produce

    Transfertime[0,6]: Resources[6,0] = 0
     Transfertime[0,7]: Resources[7,0] = 0
     Transfertime[0,8]: Resources[8,0] = 0
     Transfertime[0,9]: Resources[9,0] = 0
     Transfertime[0,10]: Resources[10,0] = 0
     Transfertime[0,11]: Resources[11,0] = 0
     Transfertime[0,12]: Resources[12,0] = 0
    ...

    You can always write the LP file and inspect it to verify any change yourself. It is way easier and more convincing to start verifying things on your own.

     

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    You can see that in the following constraint

    Taskexecution1=steel.addConstrs(
    (gp.quicksum(Tasks[task,t] for t in time)==1 
    for task in group2tasks 
    for group in range(1, num_groups + 1))
    ,name
    ="Taskexecution1")

    you dont use \(\texttt{group}\) at all but it will still generate an additional loop and an additional index layer. Simply removing it to get

    Taskexecution1=steel.addConstrs(
        (gp.quicksum(Tasks[task,t] for t in time ) ==1 
       for task in group2tasks),
    name="Taskexecution1")

    which generates

    Taskexecution1[1]: Tasks[1,0] + Tasks[1,1] + Tasks[1,2] + Tasks[1,3]
       + Tasks[1,4] + Tasks[1,5] + Tasks[1,6] + Tasks[1,7] + Tasks[1,8]
       + Tasks[1,9] + Tasks[1,10] + Tasks[1,11] + Tasks[1,12] + Tasks[1,13]
       + Tasks[1,14] + Tasks[1,15] + Tasks[1,16] + Tasks[1,17] + Tasks[1,18]
       + Tasks[1,19] + Tasks[1,20] + Tasks[1,21] + Tasks[1,22] + Tasks[1,23]
       + Tasks[1,24] + Tasks[1,25] + Tasks[1,26] + Tasks[1,27] + Tasks[1,28]
       + Tasks[1,29] + Tasks[1,30] + Tasks[1,31] + Tasks[1,32] + Tasks[1,33]
       + Tasks[1,34] + Tasks[1,35] + Tasks[1,36] + Tasks[1,37] + Tasks[1,38]
       + Tasks[1,39] + Tasks[1,40] + Tasks[1,41] + Tasks[1,42] + Tasks[1,43]
       + Tasks[1,44] + Tasks[1,45] + Tasks[1,46] + Tasks[1,47] + Tasks[1,48]
       + Tasks[1,49] + Tasks[1,50] + Tasks[1,51] + Tasks[1,52] + Tasks[1,53]
       + Tasks[1,54] + Tasks[1,55] + Tasks[1,56] + Tasks[1,57] + Tasks[1,58]
       + Tasks[1,59] + Tasks[1,60] + Tasks[1,61] + Tasks[1,62] + Tasks[1,63]
       + Tasks[1,64] + Tasks[1,65] + Tasks[1,66] + Tasks[1,67] + Tasks[1,68]
       + Tasks[1,69] + Tasks[1,70] + Tasks[1,71] + Tasks[1,72] + Tasks[1,73]
       + Tasks[1,74] + Tasks[1,75] + Tasks[1,76] + Tasks[1,77] + Tasks[1,78]
       + Tasks[1,79] + Tasks[1,80] + Tasks[1,81] + Tasks[1,82] + Tasks[1,83]
       + Tasks[1,84] + Tasks[1,85] + Tasks[1,86] + Tasks[1,87] + Tasks[1,88]
       + Tasks[1,89] + Tasks[1,90] + Tasks[1,91] + Tasks[1,92] + Tasks[1,93]
       + Tasks[1,94] + Tasks[1,95] + Tasks[1,96] = 1
     Taskexecution1[2]: Tasks[2,0] + Tasks[2,1] + Tasks[2,2] + Tasks[2,3]
    ...
    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious
    group2tasks:{
    1: [145, 151],
    2: [146, 152],
    3: [147, 153],
    4: [148, 154],
    5: [149, 155],
    6: [150, 156]
    }
    This is my group2tasks dict.

    Hence by OUTPUT SHOULD BE:

    Taskexecution1[145]: Tasks[145,0] + Tasks[145,1] + Tasks[145,2] + Tasks[145,3]
     + Tasks[145,4] + Tasks[145,5] + Tasks[145,6] + Tasks[145,7] + Tasks[145,8]
     + Tasks[145,9] + Tasks[145,10] + Tasks[145,11] + Tasks[145,12] + Tasks[145,13]
     + Tasks[145,14] + Tasks[145,15] + Tasks[145,16] + Tasks[145,17] + Tasks[145,18]
     + Tasks[145,19] + Tasks[145,20] + Tasks[145,21] + Tasks[145,22] + Tasks[145,23]
     + Tasks[145,24] + Tasks[145,25] + Tasks[145,26] + Tasks[145,27] .......+Tasks[145,95]= 1
    Taskexecution1[151]: Tasks[151,0] + Tasks[151,1] + Tasks[151,2] + Tasks[151,3]
     + Tasks[151,4] + Tasks[151,5] + Tasks[151,6] +.....+Tasks[151,95] =1
    Taskexecution[156]:....
     
    0
  • Margi Shah
    Gurobi-versary
    Thought Leader
    Curious

    "You can always write the LP file and inspect it to verify any change yourself. It is way easier and more convincing to start verifying things on your own."-

    That was a wonderful explanation Jaromil.

    0
  • Jaromił Najman
    Gurobi Staff Gurobi Staff

    When you loop over a dictionary such as \(\texttt{group2tasks}\) via, e.g., \(\texttt{for task in group2tasks}\) then \(\texttt{tasks}\) will attain the key values of the dictionary and not its actual item values. You can add an additional \(\texttt{for}\)-loop to actually go over the dictionary items which are lists.

    for key in group2tasks:
      Taskexecution1=steel.addConstrs(
                     (gp.quicksum(Tasks[task,t] for t in time ) ==1 
                     for task in group2tasks[key]),name="Taskexecution1")

    I would recommend to have a look at some Python tutorials to better understand how basic Python data structures work which would help you avoid such issues. I fully understand that working with Gurobi for the first time can be overwhelming (I have been there myself). This is the reason why understanding the programming language behavior as good as possible is a tremendous help.

    0

Please sign in to leave a comment.