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")

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.

• 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")

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:

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?

• 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.

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?

• 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.

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.
• 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.

Yes Jaromil I understood your point.

But doing so, There is some issue.

See now my constraint is looking like:

.

(6,0)(6,1)(6,2)........so on.....till last element: (150,95)

Yes Ofcourse:

• 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 - beforefor 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.

As you can see My variables looks like:

Reources(res_cat,time)

Resources(resources,time)

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?

• Gurobi Staff

Can we write steel.lp before optimising it?

Yes

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 !

• 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.

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.


• 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] + ... = 1Taskexecution[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$$.

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

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)

• 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 heat2tasksfor 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?

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] = 0Resources[7,0] =0 Resources[8,0]= 0Resources[9,0] = 0Resources[10,0] = 0Resources[11,0] = 0Resources[12,0]= 0Resources[13,0] =0 Resources[14,0] =0 Resources[15,0] = Resources[16,0]=0 Resources[17,0] =0 Resources[18,0] = 0Resources[19,0] = 0Resources[20,0]= 0Resources[21,0] =0 Resources[22,0] =0 Resources[23,0] = 0Resources[24,0]=0 Resources[25,0] = 0Resources[26,0] = 0Resources[27,0] = 0Resources[28,0]= 0Resources[29,0] = 0 Transfertime[1]: Resources[6,1] =0 Resources[7,1] =0 Resources[8,1] = 0........
• Gurobi Staff

Transfertime[0]: Resources[6,0] = 0Resources[7,0] =0 Resources[8,0]= 0Resources[9,0] = 0Resources[10,0] = 0Resources[11,0] = 0Resources[12,0]= 0Resources[13,0] =0 Resources[14,0] =0 Resources[15,0] = Resources[16,0]=0 Resources[17,0] =0 Resources[18,0] = 0Resources[19,0] = 0Resources[20,0]= 0Resources[21,0] =0 Resources[22,0] =0 Resources[23,0] = 0Resources[24,0]=0 Resources[25,0] = 0Resources[26,0] = 0Resources[27,0] = 0Resources[28,0]= 0Resources[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.

I understand what you are trying to say : but:

your loop runs FIRST for t

THEN for res

But,

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

Just for understanding.

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=6stage2units = {'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.

• Gurobi Staff

I understand what you are trying to say : but:

your loop runs FIRST for t

THEN for res

But,

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.

• 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]...
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

Taskexecution[156]:....


"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.

• 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.