Exam scheduling infeasible solutions?
回答済みHi all,
I am trying to solve an examination timetabling problem (ITC 2007 track 1 instances, using only a subset of the original constraints)
I am trying to run below code but for some reason not all exams get assigned a room and a period, even though I have a constraint taking care of that every exam needs to be scheduled for exactly 1 room and 1 period. Could anyone perhaps help me out with this?
# Create the model within the Gurobi environment
model = gp.Model(env=env)
model.setParam('PoolSearchMode', 2) # Find multiple solutions
model.setParam('PoolSolutions', 40) # Maximum number of solutions to store in the pool
# Decision Variables
num_exams = timetable_instance.meta['Exams']
num_periods = timetable_instance.meta['Periods']
num_rooms = timetable_instance.meta['Rooms']
imm = timetable_instance.imm
durations = timetable_instance.durations
# Calculate the maximum number of invigilators
num_max_invigilators = sum(math.ceil(len(timetable_instance.imm[exam]) / 30) for exam in range(num_exams))
x = gp.tuplelist([(exam, room, period) for exam in range(num_exams) for room in range(num_rooms) for period in range(num_periods)])
x = model.addVars(x, vtype=GRB.BINARY, name="x")
model.update()
# Constraints
# Constraint: Each exam is assigned to exactly one room and one period
for exam in range(num_exams):
model.addConstr(gp.quicksum(x[exam, room, period] for room in range(num_rooms) for period in range(num_periods)) == 1,
f'exam_assignment[{exam}]')
model.update()
# Constraint: Room capacities are never to be exceeded
for room in range(num_rooms):
for period in range(num_periods):
# Create a list to hold expressions for each exam in the current room and period
exam_expressions = []
for exam in range(num_exams):
expr = gp.LinExpr()
expr.addTerms(len(timetable_instance.imm[exam]), x[exam, room, period])
exam_expressions.append(expr)
# Sum up the expressions for all exams in the current room and period
total_students_expr = gp.LinExpr()
for expr in exam_expressions:
total_students_expr.add(expr)
# Add a constraint that the total students should not exceed the room's capacity
model.addConstr(total_students_expr <= timetable_instance.rooms[room]['capacity'],
f'room_capacity[{room},{period}]')
model.update()
#Constraint: Each student can only be assigned to one exam in a period
student_exams = {}
for i in range(len(timetable_instance.imm)):
for student_number in timetable_instance.imm[i]:
if student_number not in student_exams:
student_exams[student_number] = []
student_exams[student_number].append(i)
for student, ex in student_exams.items():
num_exams_this_student = len(ex)
if num_exams_this_student>1:
for i in range(num_exams_this_student - 1):
exam1 = ex[i]
for j in range(i + 1, num_exams_this_student):
exam2 = ex[j]
for period in range(num_periods):
model.addConstr(gp.quicksum(x[exam1, room, period] + \
x[exam2, room, period] for room in range(num_rooms))\
<= 1, f'student_{student}_period_{period}_constraint')
model.update()
# Constraint: Period lengths respected
for exam in range(num_exams):
for room in range(num_rooms):
for period in range(num_periods):
if durations[exam] > timetable_instance.periods[period]['duration']:
model.addConstr(x[exam, room, period] == 0, f'duration_constraint[{exam},{room},{period}]')
model.update()
model.setParam('FeasibilityTol', 1e-9)
model.params.TimeLimit = 1800 # setting a time limit of 300 seconds
solutions_dfs = []
# Optimize the model
model.optimize()
if model.Status == GRB.INFEASIBLE:
model.computeIIS()
# Print out the IIS constraints and variables
print('\nThe following constraints and variables are in the IIS:')
for c in model.getConstrs():
if c.IISConstr:
print(f'\t{c.constrname}: {model.getRow(c)} {c.Sense} {c.RHS}')
for v in model.getVars():
if v.IISLB:
print(f'\t{v.varname} ≥ {v.LB}')
if v.IISUB:
print(f'\t{v.varname} ≤ {v.UB}')
else:
num_solutions = model.SolCount
print("Number of solutions found:", num_solutions)
print('runtime is',model.Runtime)
# Getting % usage of virtual_memory ( 3rd field)
print('RAM memory % used:', psutil.virtual_memory()[2])
solution_pool_size = model.getAttr('SolCount')
print(f"Solution pool contains {solution_pool_size} solutions")
# Access every solution from the Gurobi solution pool
solution_dataframes = []
count_0s=0
for solution_index in range(solution_pool_size):
model.params.SolutionNumber = solution_index
# Create a DataFrame to store variable values for this solution
solution_vars_df = pd.DataFrame(columns=['Exam', 'Room', 'Period','Invigilators'])
# Retrieve and store variable values for this solution
for exam in range(num_exams):
for room in range(num_rooms):
for period in range(num_periods):
if exam==0:
if x[exam, room, period].Xn == 1:
count_0s+=1
if x[exam, room, period].Xn == 1:
solution_vars_df = solution_vars_df.append({
'Exam': exam,
'Room': room,
'Period': period,
}, ignore_index=True)
# Store the DataFrame for this solution
solution_dataframes.append(solution_vars_df)
-
Hi Suzan,
As the first step to debugging the behaviour you are observing, please write your model in the LP file format using model.write("mymodel.lp") and check if the exam_assignment constraints are constructed as per your expectation in the model. The lp file can be viewed with any text editor.
Best regards,
Simran0
サインインしてコメントを残してください。
コメント
1件のコメント