How to arrange the same task to start at the same time?
AnsweredI'm studying optimization for maintenance planning.
The work of this I want to make tasks that are executed in each order start at the same time. I'm thinking of adding a timing constrain to each order, but I haven't figured it out yet. How can it be done?

Hi Harichai,
In order to answer your question we will need to understand how you have modelled the problem. What are your variables, what are your constraints, what is the objective function?
Please also describe the data you have posted  what do the rows mean, what do the columns mean, how do we interpret the values in the cells?
 Riley
0 
Hello Riley,
Thank you for answering my question.
These are the VARIABLES that I use, where manpower_list is a list of workers' names, order_list is the order numbers, and calendar_list is the day the work will be performed.Next is the part of the CONSTRAINTS, most of which are timerelated, are related to the amount of work.
And finally, the Objective Function is to minimize free time in order to maximize the amount of work that can be done in one day.
In the previous display that I posted, it shows the values from the gp_manpower_worktime variable, but what I actually want is to display it as a Gantt chart, with Yaxis being the names of the workers, Xaxis being the dates and time, and in the cells, the order numbers. And orders with the same number will be performed at the same time.0 
Hi Harichai,
Would constraints of the form
gp_order_status.sum("*", order) == 1 for order in order_list
work? Then an order could only be done on a specific date?
As an aside, looking at the code you posted:Constraints on line 105 should be modelled as
gp_manpower_status[date, manpower, order] <= gp_order_status[date, order]
(for each date, manpower, order combination) which models the ifthen relationship correctly and is a much stronger set of constraints.
 Riley
0 
Hello Riley,
model.addConstrs(gp_order_status.sum("*", order) == 1 for order in order_list)
model.addConstrs(gp_manpower_status[date, manpower, order] <= gp_order_status[date, order] for date in calendar_list for manpower in manpower_list for order in order_list)After implementing your code try it out. It was found that the results are as follows.
But the result I want is that you will see that there are total 38 orders if A and B are included.
Which can be seen that in many orders there is a division of labor for employees to jointly do.
Therefore, I want the shared work to be done at the same time.
0 
Hi Harichai,
If B19 appears in both "three" and "four", doesn't this mean that
gp_order_status[three, B19] == 1
gp_order_status[four, B19] == 1or have I incorrectly assumed what these variables are supposed to mean?
 Riley
0 
What is needed is not to make each order no division of labor.
But I just want the command to work with division work start in the same column. If you look at it in terms of time, you have to start doing it together.
The order can be divided or not divided depending on the suitability.
0 
Hi Harichai,
But I just want the command to work with division work start in the same column. If you look at it in terms of time, you have to start doing it together.
I understand this, but I don't understand the meaning of your variables and how you are using their solution values to construct your table.
 Riley
0 
This is my table code.
time_list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
rows = manpower_list.copy()
columns = calendar_list.copy()
sub_columns = pd.MultiIndex.from_product([(columns), (time_list)])
worktime_obj = {}
manpower_plan_by_order_ref = pd.DataFrame(columns=sub_columns, index=rows, data="")
for year, mine, order in gp_manpower_worktime.keys():
try:
worktime_obj[order].loc[mine, year] = np.round(gp_manpower_worktime[year, mine, order].x, 1)
if worktime_obj[order].loc[mine, year] > 0:
for t in time_list:
if manpower_plan_by_order_ref.loc[mine, (year, t)] == "":
manpower_plan_by_order_ref.loc[mine, (year, t)] = f"{order}={np.round(gp_manpower_worktime[year, mine, order].x, 1)}"
break
except:
worktime_obj[order] = pd.DataFrame(columns=columns, index=rows, data=0.0)
worktime_obj[order].loc[mine, year] = np.round(gp_manpower_worktime[year, mine, order].x, 1)
if worktime_obj[order].loc[mine, year] > 0:
for t in time_list:
if manpower_plan_by_order_ref.loc[mine, (year, t)] == "":
manpower_plan_by_order_ref.loc[mine, (year, t)] = f"{order}={np.round(gp_manpower_worktime[year, mine, order].x, 1)}"
break
pd.set_option('display.max_columns', 50)
manpower_plan_by_order_ref0 
If I understand correctly, timelist does not feature in your model  it is only used in construction of the table?
In which case this problem is to do with displaying of data, and nothing to do with gurobi? I'm happy to help either way, just making sure I understand the situation.
 Riley
0 
Yes you understand correctly time list is not included in the model.
The desired result will be a Gantt chart that will be the work schedule.
I'm still not sure about defining conditions that are executed in the same order and executed at the same time. Can this be fixed in the model or in the Gantt chart creation comma.
0 
I'm wondering if this is possible for this chart.
Referring to the first chart there is a B14 in cell (Supachai, five) and (Phonlawat, six). So you have to shift everything in Supachai row from B14 to the right to line B14 up. But that row is already full and can't be shifted.
Unless the orders don't have to be in numerical order, i.e. can B2 appear before B1 in the same row? If that is the case, it might be possible, but you will have another optimization problem to solve to arrange them, or you will need to reformulate your problem to include assigning of orders to times, and not just dates.
 Riley
0 
In fact, the work in each order will have different importance in repairing. This is given in priority score, but if the orders are optimized to be executed on the same day, the order can be swapped, i.e. B36 is the first instruction of the day and A01 is the last instruction of the day.
But you will have another optimization problem to solve to arrange them, or you will need to reformulate your problem to include assigning of orders to times, and not just dates.
If it needs to be adjusted like you mentioned. What fixes do I need? Can you advise me?
0 
If I was to formulate this problem I think I would index most variables by a timeslot for every day, i.e.
gp_manpower_status = model.addVars(timeslot_list, manpower_list, order_list, ...)
timeslot_list[0] will correspond to 20211101 one,
timeslot_list[1] will correspond to 20211101 two,
etcYou will have 12x as many gp_manpower_status variables as you did before.
I'd index gp_manpower_worktime in the same way. This should make the modeling easier and probably lead to a tighter formulation. It certainly helps with being able to enforce that everybody who is working on order is working at the same time.
 Riley
0 
Hello Riley,
I’m not sure I understand, This is the constraint after adding timeslot_list.
# CONSTRAINTS
# Number of manpowers should be less than or equal to the capacity
model.addConstrs(gp_manpower_status.sum(date, "*", "*", order) <= capacity_manpowers[(order)]
for date in calendar_list for order in order_list)
# Order Status should be 1 if any manpower working on that order
model.addConstrs(gp_order_status.sum("*", order) == 1 for order in order_list)
model.addConstrs(gp_manpower_status[(date, slot, manpower, order)] <= gp_order_status[(date, order)] for date in calendar_list
for slot in timeslot_list for manpower in manpower_list for order in order_list)
# Work Time Balance for any order is calculated by cycle time / summing up all manpowers status working on that order
model.addConstrs(gp_balance_worktime[(order)] * gp_manpower_status.sum("*", "*", "*", order) == order_requirements[(order)]
for order in order_list)
# Work Time for each manpower should be equal to the balance work time but considered with manpower status
model.addConstrs(gp_manpower_worktime[(date, slot, manpower, order)] == gp_manpower_status[(date, slot, manpower, order)]
* gp_balance_worktime[(order)] for date in calendar_list for slot in timeslot_list for manpower in manpower_list for order in order_list)
# Total work time of all orders for each of manpower in each day should not exceed the regular work time
model.addConstrs(gp_manpower_worktime.sum(date, "*", manpower, "*") <= regular_work_mins for date in calendar_list
for manpower in manpower_list)
# Free Time for each manpower should be equal to the regular work time minus the work time
model.addConstrs(gp_manpower_freetime[(date, manpower)] == regular_work_mins  gp_manpower_worktime.sum(date, "*", manpower, "*")
for date in calendar_list for manpower in manpower_list)
# Total work time = all required work time
model.addConstr(gp.quicksum(gp_manpower_worktime[(date, slot, manpower, order)] for date in calendar_list for slot in timeslot_list
for manpower in manpower_list for order in order_list) == (gp.quicksum(daily_requirements[(date, order)]
for date in calendar_list for order in order_list)))
# The same order should be done in the same day
model.addConstrs(gp_manpower_worktime.sum(date, "*", "*", order) == order_requirements[(order)] * gp_order_status[(date, order)]
for date in calendar_list for order in order_list)
# Average working hours per day
model.addConstrs(gp_manpower_worktime.sum(date, "*", manpower, "*")
<= gp_manpower_worktime.sum(date, "*", "*", "*")
/ max_manpower_possible + different_work_mins for date in calendar_list for manpower in manpower_list)
model.addConstrs(gp_manpower_worktime.sum(date, "*", manpower, "*")
>= gp_manpower_worktime.sum(date, "*", "*", "*")
/ max_manpower_possible  different_work_mins for date in calendar_list for manpower in manpower_list)
# Set the value of gap worktime (positive for early, negative for late)
for l in range(len(calendar_list)):
model.addConstrs(gp_gap_worktime[(calendar_list[l], order)] == gp.quicksum(gp_manpower_worktime[(date, slot, manpower, order)]
for date in calendar_list[: l + 1] for slot in timeslot_list for manpower in manpower_list)
 (gp.quicksum(daily_requirements[(date, order)] for date in calendar_list[: l + 1])) for order in order_list)
# Set the value of ABS(gap worktime)
model.addConstrs(abs_gp_gap_worktime[(date, order)] == gp.abs_(gp_gap_worktime[(date, order)])
for date in calendar_list for order in order_list)
# Set the value of early worktime
model.addConstrs(early_worktime[(date, order)] == (gp_gap_worktime[(date, order)] + abs_gp_gap_worktime[(date, order)]) / 2
for date in calendar_list for order in order_list)
# Set the value of early benefits
model.addConstrs(early_benefit[(date, order)] == early_worktime[(date, order)] * priority_scores[order]
for date in calendar_list for order in order_list)
# Set the value of late worktime
model.addConstrs(late_worktime[(date, order)] == (abs_gp_gap_worktime[(date, order)]  gp_gap_worktime[(date, order)]) / 2
for date in calendar_list for order in order_list)
# Set the value of late costs
model.addConstrs(late_costs[(date, order)] == late_worktime[(date, order)] * priority_scores[order]
for date in calendar_list for order in order_list)The result I got from gp_manpower_worktime now is that some orders have not been executed, some purchase orders did not meet the deadline, and some purchase orders did not match the slots.
How do I set up a constraint so that everyone working on the same order at the same slot?
0 
Hi Harichai,
I meant for the timeslot list to include dates as well, not be separate list from dates. If you look closely at what I wrote then I think you will realize this.
 Riley
0 
How should I solve this constraint?
for l in range(len(calendar_list)):
since daily_requirements is a dict imported from excel with calendar_list as keys. i.e.
model.addConstrs(gp_gap_worktime[(calendar_list[l], order)] == gp.quicksum(gp_manpower_worktime[(date, manpower, order)]
for date in calendar_list[: l + 1] for manpower in manpower_list)
 (gp.quicksum(daily_requirements[(date, order)] for date in calendar_list[: l + 1])) for order in order_list){('20211101', 'A01'): 0, ('20211101', 'A02'): 100, ('20211101', 'B01'): 60, ('20211101', 'B02'): 0 ....after changing to timeslot_listfor l in range(len(timeslot_list)):
model.addConstrs(gp_gap_worktime[(timeslot_list[l], order)] == gp.quicksum(gp_manpower_worktime[(date, manpower, order)]
for date in timeslot_list[: l + 1] for manpower in manpower_list)
 (gp.quicksum(daily_requirements[(date, order)] for date in timeslot_list[: l + 1])) for order in order_list)An error will occur.0 
Hi Harichai,
If I understand your model correctly then I think you will want to sum the variables who timeslots occur on the same date.
 Riley
0 
Hello Riley,
Constraint in this section. It will look for advance jobs or late jobs.
The result is that if the result is positive, it indicates that the work is completed before the deadline.
But if it's negative, the work will be completed later than scheduled.This is the equation I came up with:
0
Please sign in to leave a comment.
Comments
18 comments