Kaggle competition: Santa's Workshop Tour 2019
<save_to_csv><EOS> 
%%time
NUMBER_DAYS = 100
NUMBER_FAMILIES = 5000
MAX_BEST_CHOICE = 5
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv')
 submission = pd.read_csv('/kaggle/input/c-stochastic-product-search-65ns/submission.csv')
 assigned_days = submission['assigned_day'].values
columns = data.columns[1:11]
DESIRED = data[columns].values
COST_PER_FAMILY = [0,50,50,100,200,200,300,300,400,500]
COST_PER_FAMILY_MEMBER = [0, 0, 9, 9, 9, 18, 18, 36, 36,235]
N_PEOPLE = data['n_people'].values
def get_daily_occupancy(assigned_days):
daily_occupancy = np.zeros(100, int)
 for fid, assigned_day in enumerate(assigned_days):
daily_occupancy[assigned_day-1] += N_PEOPLE[fid]
return daily_occupancy
def cost_function(prediction):
N_DAYS = 100
MAX_OCCUPANCY = 300
MIN_OCCUPANCY = 125
penalty = 0
days = list(range(N_DAYS,0,-1))
tmp = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv', index_col='family_id')
 family_size_dict = tmp[['n_people']].to_dict() ['n_people']
cols = [f'choice_{i}' for i in range(10)]
choice_dict = tmp[cols].to_dict()
daily_occupancy = {k:0 for k in days}
for f, d in enumerate(prediction):
n = family_size_dict[f]
choice_0 = choice_dict['choice_0'][f]
choice_1 = choice_dict['choice_1'][f]
choice_2 = choice_dict['choice_2'][f]
choice_3 = choice_dict['choice_3'][f]
choice_4 = choice_dict['choice_4'][f]
choice_5 = choice_dict['choice_5'][f]
choice_6 = choice_dict['choice_6'][f]
choice_7 = choice_dict['choice_7'][f]
choice_8 = choice_dict['choice_8'][f]
choice_9 = choice_dict['choice_9'][f]
daily_occupancy[d] += n
if d == choice_0:
penalty += 0
elif d == choice_1:
penalty += 50
elif d == choice_2:
penalty += 50 + 9 * n
elif d == choice_3:
penalty += 100 + 9 * n
elif d == choice_4:
penalty += 200 + 9 * n
elif d == choice_5:
penalty += 200 + 18 * n
elif d == choice_6:
penalty += 300 + 18 * n
elif d == choice_7:
penalty += 300 + 36 * n
elif d == choice_8:
penalty += 400 + 36 * n
elif d == choice_9:
penalty += 500 + 36 * n + 199 * n
else:
penalty += 500 + 36 * n + 398 * n
for _, v in daily_occupancy.items() :
if(v < MIN_OCCUPANCY):
penalty += 100000000
accounting_cost =(daily_occupancy[days[0]]-125.0)/ 400.0 * daily_occupancy[days[0]]**(0.5)
 accounting_costs = [max(0, accounting_cost)]
diffs = [0]
yesterday_count = daily_occupancy[days[0]]
for day in days[1:]:
today_count = daily_occupancy[day]
diff = abs(today_count - yesterday_count)
 accounting_costs.append(max(0,(today_count-125.0)/ 400.0 * today_count**(0.5 + diff / 50.0)))
 yesterday_count = today_count
return penalty, sum(accounting_costs), penalty + sum(accounting_costs)
 seed(2)
 for f in range(100):
ad = assigned_days.copy()
days_for_fix = np.array(sample(range(1,101),50))
daily_occupancy = get_daily_occupancy(ad)
 fids = np.where(np.isin(ad, days_for_fix)) [0]
solver = pywraplp.Solver('Setup occupation of days', pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
 PCOSTM, B = {}, {}
for fid in fids:
for i in range(MAX_BEST_CHOICE):
B[fid, DESIRED[fid][i]-1] = solver.BoolVar(f'b{fid, i}')
 PCOSTM[fid, DESIRED[fid][i]-1] = COST_PER_FAMILY[i] + N_PEOPLE[fid] * COST_PER_FAMILY_MEMBER[i]
lower_bounds = np.zeros(100)
 upper_bounds = np.zeros(100)
 delta = 8
for fi in days_for_fix:
lower_bounds[fi-1] = max(daily_occupancy[fi-1]-delta,125)
 upper_bounds[fi-1] = min(daily_occupancy[fi-1]+delta,300)
 D = {}
for j in range(NUMBER_DAYS):
I = solver.IntVar(lower_bounds[j], upper_bounds[j], f'I{j}')
 solver.Add(solver.Sum([N_PEOPLE[i] * B[i, j] for i in range(NUMBER_FAMILIES)if(i,j)in B])== I)
 if upper_bounds[j]>124:
rj = range(int(lower_bounds[j]),int(upper_bounds[j])+1)
 for i in rj:
D[j, i] = solver.BoolVar(f'd{j, i}')
 solver.Add(solver.Sum([D[j, i]*i for i in rj])== I)
 for i in fids:
solver.Add(solver.Sum(B[i, j] for j in range(NUMBER_DAYS)if(i,j)in B)== 1)
 sM =solver.Sum(PCOSTM[i, j] * B[i, j] for i, j in B)
 for i in range(NUMBER_DAYS):
if np.isin(i,days_for_fix-1):
ri = range(int(lower_bounds[i]),int(upper_bounds[i])+1)
 if i<99:
sM += solver.Sum(D[i,j]*(j-125)/400*j**(0.5+abs(j-daily_occupancy[i+1])/50)for j in ri)
 if i>0:
sM += solver.Sum(D[i,j]*(daily_occupancy[i-1]-125)/400*daily_occupancy[i-1]**(0.5+abs(j-daily_occupancy[i-1])/50)for j in ri)
 solver.Minimize(sM)
 sol = solver.Solve()
status = ['OPTIMAL', 'FEASIBLE', 'INFEASIBLE', 'UNBOUNDED', 'ABNORMAL', 'MODEL_INVALID', 'NOT_SOLVED']
if status[sol] == 'OPTIMAL':
for i, j in B:
if B[i, j].solution_value() > 0.5:
ad[i] = j+1
if cost_function(ad)[2]<cost_function(assigned_days)[2]:
submission['assigned_day'] = ad
assigned_days = ad
print(cost_function(ad))
score = cost_function(assigned_days)[2]
submission.to_csv(f'submission_{score}.csv', index=False)