Download
from Numberjack import *
from validator import read_instance_day1, MAX_PREFERENCE_VALUE
import csv
def get_model_day1(all_student_preferences, company_cap_limits, num_interviews_each):
num_students = len(all_student_preferences)
num_companies = len(all_student_preferences[0])
model = Model()
# Matrix of FD variables 0..num_companies-1, for each student and each of their interviews,
# what company they are assigned to.
assignments = Matrix(num_students, num_interviews_each, num_companies)
for row in assignments.row:
# Each of the assignments for a student must be different, redundant with ordering below
model += AllDiff(row)
# Break symmetry by enforcing ordering on the row
for i in range(len(row)-1):
model += row[i] < row[i+1]
# Cardinality constraint limiting the number of students assigned to each company
gcc_cap_limits = dict(
(company_id, [0, cap_limit]) for company_id, cap_limit in enumerate(company_cap_limits)
)
model += Gcc(assignments.flat, gcc_cap_limits)
obj1_scaling = (MAX_PREFERENCE_VALUE * num_interviews_each) + 1 # Factor to scale objective 1
obj1_variables, obj1_coefficients = [], []
obj2_components = [] # a list of the preference values for each student
for row, preferences in zip(assignments.row, all_student_preferences):
student_obj2_vars, student_obj2_coeffs = [], []
for company_id, preference_value in enumerate(preferences):
for cell in row:
# Components for the total preference value, scaled up above objective 2
obj1_variables.append(cell == company_id)
obj1_coefficients.append(preference_value * obj1_scaling)
# Components for linear sum of this student's preference value
student_obj2_vars.append(cell == company_id)
student_obj2_coeffs.append(preference_value)
obj2_components.append(Sum(student_obj2_vars, student_obj2_coeffs))
# Variable for the second objective, i.e. the maximum preference value across students
obj2 = Variable(0, obj1_scaling, 'obj2')
for obj2_component in obj2_components:
# At least as big as each student's preference value
model += obj2 >= obj2_component
obj1 = Sum(obj1_variables, obj1_coefficients)
obj = obj1 + obj2
model += Minimise(obj)
return model, assignments, obj, obj1, obj2, obj1_scaling
def output_assignments(assignments, filename):
with open(filename, "wt") as f:
writer = csv.writer(f)
writer.writerow(["StudentNr", "CompanyNr"])
for student_id, student_row in enumerate(assignments.row):
for assigned_variable in student_row:
# +1 for one-based indexing instead of zero-based
writer.writerow([student_id + 1, assigned_variable.get_value() + 1])
def main(param):
all_student_preferences, company_cap_limits = read_instance_day1(param['instance'])
num_interviews_each = param['num_interviews_each']
model, assignments, obj, obj1, obj2, obj1_scaling = get_model_day1(
all_student_preferences, company_cap_limits, num_interviews_each
)
if param['verbosity'] >= 3:
print model
solver = model.load(param['solver'])
solver.setVerbosity(param['verbosity'])
solver.solve()
if solver.is_sat():
print "SAT"
print assignments
print "Obj:", obj.get_value()
obj1_value = obj1.get_value()
print "Obj1:", obj1_value, "(%d * %d)" % (int(obj1_value / obj1_scaling), obj1_scaling)
print "Obj2:", obj2.get_value()
output_assignments(assignments, param['solution'])
elif solver.is_unsat():
print "UNSAT"
else:
print "Unknown"
if __name__ == '__main__':
default = {
'instance': 'testproblems/1/preferences.csv',
'solution': 'test-njsolution.csv',
'num_interviews_each': 3,
'solver': 'CPLEX',
'verbosity': 1,
}
param = input(default)
main(param)