Kaggle competition: House Prices - Advanced Regression Techniques
def optimization_preference(submission, MAX_BEST_CHOICE=7, NUM_SWAP=3000, NUM_SECONDS=2000,NUM_THREADS=4):
NUMBER_DAYS = 100
NUMBER_FAMILIES = 5000
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.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)
 for _ in range(20):
solver = pywraplp.Solver('Optimization preference cost', pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
 daily_occupancy = get_daily_occupancy(assigned_days ).astype(float)
 fids = np.random.choice(range(NUMBER_FAMILIES), NUM_SWAP, replace=False)
 PCOSTM, B = {}, {}
for fid in range(NUMBER_FAMILIES):
if fid in fids:
for i in range(MAX_BEST_CHOICE):
PCOSTM[fid, DESIRED[fid][i]-1] = COST_PER_FAMILY[i] + N_PEOPLE[fid] * COST_PER_FAMILY_MEMBER[i]
B[ fid, DESIRED[fid][i]-1] = solver.BoolVar('')
 else:
daily_occupancy[assigned_days[fid]-1] -= N_PEOPLE[fid]
solver.set_time_limit(NUM_SECONDS*NUM_THREADS*1000)
 solver.SetNumThreads(NUM_THREADS)
 for day in range(NUMBER_DAYS):
if daily_occupancy[day]:
solver.Add(solver.Sum([N_PEOPLE[fid] * B[fid, day] for fid in range(NUMBER_FAMILIES)if(fid,day)in B])== daily_occupancy[day])
 for fid in fids:
solver.Add(solver.Sum(B[fid, day] for day in range(NUMBER_DAYS)if(fid, day)in B)== 1)
 solver.Minimize(solver.Sum(PCOSTM[fid, day] * B[fid, day] for fid, day in B))
sol = solver.Solve()
status = ['OPTIMAL', 'FEASIBLE', 'INFEASIBLE', 'UNBOUNDED', 'ABNORMAL', 'MODEL_INVALID', 'NOT_SOLVED']
if status[sol] in ['OPTIMAL', 'FEASIBLE']:
tmp = assigned_days.copy()
for fid, day in B:
if B[fid, day].solution_value() > 0.5:
tmp[fid] = day+1
if cost_function(tmp)[2] < cost_function(assigned_days)[2]:
assigned_days = tmp
submission['assigned_day'] = assigned_days
submission.to_csv('submission.csv', index=False)
 print('Result:', status[sol], cost_function(tmp))
else:
print('Result:', status[sol] )<load_from_csv>
train_df = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/train.csv')
 test_df = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/test.csv' )
def mincostflow(submission, MAX_BEST_CHOICE=4):
NUMBER_DAYS = 100
NUMBER_FAMILIES = 5000
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.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'].astype(int ).values
def get_daily_occupancy(assigned_days):
daily_occupancy = np.zeros(100, np.int32)
 for i, r in enumerate(assigned_days):
daily_occupancy[r-1] += N_PEOPLE[i]
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 > MAX_OCCUPANCY or v < MIN_OCCUPANCY:
penalty += 100000000
accounting_cost = max(0,(daily_occupancy[days[0]]-125.0)/ 400.0 * daily_occupancy[days[0]]**(0.5))
yesterday_count = daily_occupancy[days[0]]
for day in days[1:]:
today_count = daily_occupancy[day]
diff = abs(today_count - yesterday_count)
 accounting_cost += max(0,(today_count-125.0)/ 400.0 * today_count**(0.5 + diff / 50.0))
yesterday_count = today_count
return penalty, accounting_cost, penalty + accounting_cost
_,_,reference = cost_function(submission['assigned_day'])
 for num_members in range(2, 9):
daily_occupancy = get_daily_occupancy(assigned_days)
 fids = np.where(N_PEOPLE == num_members)[0]
PCOSTM = {}
for fid in range(NUMBER_FAMILIES):
if fid in fids:
for i in range(MAX_BEST_CHOICE):
PCOSTM[fid, DESIRED[fid][i]-1] = COST_PER_FAMILY[i] + N_PEOPLE[fid] * COST_PER_FAMILY_MEMBER[i]
else:
daily_occupancy[assigned_days[fid]-1] -= N_PEOPLE[fid]
offset = fids.shape[0]
solver = pywrapgraph.SimpleMinCostFlow()
for day in range(NUMBER_DAYS):
solver.SetNodeSupply(offset+day, int(daily_occupancy[day]//num_members))
for i in range(offset):
fid = fids[i]
solver.SetNodeSupply(i, -1)
 for j in range(MAX_BEST_CHOICE):
day = DESIRED[fid][j]-1
solver.AddArcWithCapacityAndUnitCost(int(offset+day), i, 1, int(PCOSTM[fid, day]))
solver.SolveMaxFlowWithMinCost()
for i in range(solver.NumArcs()):
if solver.Flow(i)> 0:
assigned_days[fids[solver.Head(i)]] = solver.Tail(i)- offset + 1
print(cost_function(assigned_days))
_,_,new_reference = cost_function(submission['assigned_day'])
 if new_reference < reference:
submission.to_csv("submission.csv", index=False )<define_search_space>
data = pd.concat([train_df, test_df], sort=False)
 data = data.reset_index(drop=True)
 data.head()
%%writefile main.cpp
using namespace std;
using namespace std::chrono;
constexpr array<uint8_t, 14> DISTRIBUTION{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5}; // You can setup how many families you need for swaps and what best choice use for each family
// {3, 5} it's mean the first random family will brute force for choices 1-2 and the second random family will brute force for choices 1-5
constexpr int MAX_OCCUPANCY = 300;
constexpr int MIN_OCCUPANCY = 125;
constexpr int BEST_N = 1000;
array<uint8_t, 5000> n_people;
array<array<uint8_t, 10>, 5000> choices;
array<array<uint16_t, 10>, 5000> PCOSTM;
array<array<double, 176>, 176> ACOSTM;
void init_data() {
ifstream in(".. /input/santa-workshop-tour-2019/family_data.csv");
assert(in && "family_data.csv");
string header;
int n,x;
char comma;
getline(in, header);
for(int j = 0; j < choices.size() ; ++j){
in >> x >> comma;
for(int i = 0; i < 10; ++i){
in >> x >> comma;
choices[j][i] = x-1;
}
in >> n;
n_people[j] = n;
}
array<int, 10> pc{0, 50, 50, 100, 200, 200, 300, 300, 400, 500};
array<int, 10> pn{0, 0, 9, 9, 9, 18, 18, 36, 36, 235};
for(int j = 0; j < PCOSTM.size() ; ++j)
 for(int i = 0; i < 10; ++i)
 PCOSTM[j][i] = pc[i] + pn[i] * n_people[j];
for(int i = 0; i < 176; ++i)
 for(int j = 0; j < 176; ++j)
 ACOSTM[i][j] = i * pow(i+125, 0.5 + abs(i-j)/ 50.0)/ 400.0;
}
array<uint8_t, 5000> read_submission(string filename){
ifstream in(filename);
assert(in && "submission.csv");
array<uint8_t, 5000> assigned_day{};
string header;
int id, x;
char comma;
getline(in, header);
for(int j = 0; j < choices.size() ; ++j){
in >> id >> comma >> x;
assigned_day[j] = x-1;
auto it = find(begin(choices[j]), end(choices[j]), assigned_day[j]);
if(it != end(choices[j]))
assigned_day[j] = distance(begin(choices[j]), it);
}
return assigned_day;
}
struct Index {
Index(array<uint8_t, 5000> assigned_days_): assigned_days(assigned_days_){
setup() ;
}
array<uint8_t, 5000> assigned_days;
array<uint16_t, 100> daily_occupancy_{};
int preference_cost_ = 0;
void setup() {
preference_cost_ = 0;
daily_occupancy_.fill(0);
for(int j = 0; j < assigned_days.size() ; ++j){
daily_occupancy_[choices[j][assigned_days[j]]] += n_people[j];
preference_cost_ += PCOSTM[j][assigned_days[j]];
}
}
double calc(const array<uint16_t, 5000>& indices, const array<uint8_t, DISTRIBUTION.size() >& change){
double accounting_penalty = 0.0;
auto daily_occupancy = daily_occupancy_;
int preference_cost = preference_cost_;
for(int i = 0; i < DISTRIBUTION.size() ; ++i){
int j = indices[i];
daily_occupancy[choices[j][assigned_days[j]]] -= n_people[j];
daily_occupancy[choices[j][ change[i]]] += n_people[j];
preference_cost += PCOSTM[j][change[i]] - PCOSTM[j][assigned_days[j]];
}
for(auto occupancy : daily_occupancy)
 if(occupancy < MIN_OCCUPANCY)
 return 1e12*(MIN_OCCUPANCY-occupancy);
else if(occupancy > MAX_OCCUPANCY)
 return 1e12*(occupancy - MAX_OCCUPANCY);
for(int day = 0; day < 99; ++day)
 accounting_penalty += ACOSTM[daily_occupancy[day]-125][daily_occupancy[day+1]-125];
accounting_penalty += ACOSTM[daily_occupancy[99]-125][daily_occupancy[99]-125];
return preference_cost + accounting_penalty;
}
void reindex(const array<uint16_t, DISTRIBUTION.size() >& indices, const array<uint8_t, DISTRIBUTION.size() >& change){
for(int i = 0; i < DISTRIBUTION.size() ; ++i){
assigned_days[indices[i]] = change[i];
}
setup() ;
}
};
double calc(const array<uint8_t, 5000>& assigned_days, bool print=false){
int preference_cost = 0;
double accounting_penalty = 0.0;
array<uint16_t, 100> daily_occupancy{};
for(int j = 0; j < assigned_days.size() ; ++j){
preference_cost += PCOSTM[j][assigned_days[j]];
daily_occupancy[choices[j][assigned_days[j]]] += n_people[j];
}
for(auto occupancy : daily_occupancy)
 if(occupancy < MIN_OCCUPANCY)
 return 1e12*(MIN_OCCUPANCY-occupancy);
else if(occupancy > MAX_OCCUPANCY)
 return 1e12*(occupancy - MAX_OCCUPANCY);
for(int day = 0; day < 99; ++day)
 accounting_penalty += ACOSTM[daily_occupancy[day]-125][daily_occupancy[day+1]-125];
accounting_penalty += ACOSTM[daily_occupancy[99]-125][daily_occupancy[99]-125];
if(print){
cout << preference_cost << " " << accounting_penalty << " " << preference_cost+accounting_penalty << endl;
}
return preference_cost + accounting_penalty;
}
void save_sub(const array<uint8_t, 5000>& assigned_day){
ofstream out("submission.csv");
out << "family_id,assigned_day" << endl;
for(int i = 0; i < assigned_day.size() ; ++i)
 out << i << "," << choices[i][assigned_day[i]]+1 << endl;
}
const vector<array<uint8_t, DISTRIBUTION.size() >> changes = []() {
vector<array<uint8_t, DISTRIBUTION.size() >> arr;
array<uint8_t, DISTRIBUTION.size() > tmp{};
for(int i = 0; true; ++i){
arr.push_back(tmp);
tmp[0] += 1;
for(int j = 0; j < DISTRIBUTION.size() ; ++j)
 if(tmp[j] >= DISTRIBUTION[j]){
if(j >= DISTRIBUTION.size() -1)
 return arr;
tmp[j] = 0;
++tmp[j+1];
}
}
return arr;
}() ;
template<class ExitFunction>
void stochastic_product_search(Index index, ExitFunction fn){ // 15'360'000it/s 65ns/it 0.065µs/it
double best_local_score = calc(index.assigned_days);
thread_local std::mt19937 gen(std::random_device{}());
uniform_int_distribution<> dis(0, 4999);
array<uint16_t, 5000> indices;
iota(begin(indices), end(indices), 0);
array<uint16_t, DISTRIBUTION.size() > best_indices{};
array<uint8_t, DISTRIBUTION.size() > best_change{};
for(; fn() ;){
bool found_better = false;
for(int k = 0; k < BEST_N; ++k){
for(int i = 0; i < DISTRIBUTION.size() ; ++i)//random swap
swap(indices[i], indices[dis(gen)]);
for(const auto& change : changes){
auto score = index.calc(indices, change);
if(score < best_local_score){
found_better = true;
best_local_score = score;
best_change = change;
copy_n(begin(indices), DISTRIBUTION.size() , begin(best_indices)) ;
}
}
}
if(found_better){ // reindex from N best if found better
index.reindex(best_indices, best_change);
// save_sub(index.assigned_days);
calc(index.assigned_days, true);
}
}
save_sub(index.assigned_days);
}
int main() {
init_data() ;
auto assigned_day = read_submission("./submission.csv");
Index index(assigned_day);
calc(index.assigned_days, true);
// auto forever = []() { return true; };
// auto count_exit = [start = 0]() mutable { return(++start <= 1000); };
auto time_exit = [start = high_resolution_clock::now() ]() {
return duration_cast<minutes>(high_resolution_clock::now() -start ).count() < 15; //5h55
};
stochastic_product_search(index, time_exit);
return 0;
}<set_options>
nans=pd.isnull(data ).sum()
nans[nans>0]
!g++ -pthread -lpthread -O3 -std=c++17 -o main main.cpp<load_from_csv>
data['MSZoning'] = data['MSZoning'].fillna(data['MSZoning'].mode() [0])
 data['Utilities'] = data['Utilities'].fillna(data['Utilities'].mode() [0])
 data['Exterior1st'] = data['Exterior1st'].fillna(data['Exterior1st'].mode() [0])
 data['Exterior2nd'] = data['Exterior2nd'].fillna(data['Exterior2nd'].mode() [0])
 data["BsmtFinSF1"] = data["BsmtFinSF1"].fillna(0)
 data["BsmtFinSF2"] = data["BsmtFinSF2"].fillna(0)
 data["BsmtUnfSF"] = data["BsmtUnfSF"].fillna(0)
 data["TotalBsmtSF"] = data["TotalBsmtSF"].fillna(0)
 data["BsmtFullBath"] = data["BsmtFullBath"].fillna(0)
 data["BsmtHalfBath"] = data["BsmtHalfBath"].fillna(0)
 data["BsmtQual"] = data["BsmtQual"].fillna("None")
 data["BsmtCond"] = data["BsmtCond"].fillna("None")
 data["BsmtExposure"] = data["BsmtExposure"].fillna("None")
 data["BsmtFinType1"] = data["BsmtFinType1"].fillna("None")
 data["BsmtFinType2"] = data["BsmtFinType2"].fillna("None")
 data['KitchenQual'] = data['KitchenQual'].fillna(data['KitchenQual'].mode() [0])
 data["Functional"] = data["Functional"].fillna("Typ")
 data["FireplaceQu"] = data["FireplaceQu"].fillna("None")
 data["GarageType"] = data["GarageType"].fillna("None")
 data["GarageYrBlt"] = data["GarageYrBlt"].fillna(0)
 data["GarageFinish"] = data["GarageFinish"].fillna("None")
 data["GarageCars"] = data["GarageCars"].fillna(0)
 data["GarageArea"] = data["GarageArea"].fillna(0)
 data["GarageQual"] = data["GarageQual"].fillna("None")
 data["GarageCond"] = data["GarageCond"].fillna("None")
 data["PoolQC"] = data["PoolQC"].fillna("None")
 data["Fence"] = data["Fence"].fillna("None")
 data["MiscFeature"] = data["MiscFeature"].fillna("None")
 data['SaleType'] = data['SaleType'].fillna(data['SaleType'].mode() [0])
 data['LotFrontage'].interpolate(method='linear',inplace=True)
 data["Electrical"] = data.groupby("YearBuilt")['Electrical'].transform(lambda x: x.fillna(x.mode() [0]))
data["Alley"] = data["Alley"].fillna("None")
 data["MasVnrType"] = data["MasVnrType"].fillna("None")
 data["MasVnrArea"] = data["MasVnrArea"].fillna(0 )
trials = 1
for r in range(trials):
pytorch_optimim(MAX_CHOICE=6)
 !./main
submission = pd.read_csv("./submission.csv")
 optimization_preference(submission, MAX_BEST_CHOICE=6, NUM_SWAP=3000, NUM_SECONDS=2000, NUM_THREADS=4)
 submission = pd.read_csv("./submission.csv")
 mincostflow(submission, MAX_BEST_CHOICE=6)
 !./main<save_to_csv>
nans=pd.isnull(data ).sum()
nans[nans>0]
save = False
if save:
def create_download_link(title = "Download CSV file", filename = "data.csv"):
html = '<a href={filename}>{title}</a>'
html = html.format(title=title,filename=filename)
 return HTML(html)
 create_download_link(filename='submission.csv' )<load_from_csv>
warnings.filterwarnings(action='ignore' )
SEED = 1
random.seed(SEED)
 np.random.seed(SEED)
 N_DAYS = 100
N_FAMILIES = 5000
N_CHOICES = 10
MAX_POP = 300
MIN_POP = 125
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv')
 choice_cols = [f'choice_{i}' for i in range(N_CHOICES)]
CHOICES = data[choice_cols].values - 1
COST_PER_FAMILY = [0, 50, 50, 100, 200, 200, 300, 300, 400, 500, 500]
COST_PER_MEMBER = [0, 0, 9, 9, 9, 18, 18, 36, 36, 235, 434]
F_COUNTS = data['n_people'].astype(int ).values
C_COSTS = np.zeros(( N_FAMILIES, N_DAYS), dtype=np.int32)
 for f in range(N_FAMILIES):
for d in range(N_DAYS):
if d in CHOICES[f, :]:
c = list(CHOICES[f, :] ).index(d)
 else:
c = N_CHOICES
C_COSTS[f, d] = COST_PER_FAMILY[c] + F_COUNTS[f] * COST_PER_MEMBER[c]<feature_engineering>
_list = []
for col in data.columns:
if type(data[col][0])== type('str'):
_list.append(col)
 le = preprocessing.LabelEncoder()
for li in _list:
le.fit(list(set(data[li])))
 data[li] = le.transform(data[li] )
@njit(fastmath=True)
 def get_daily_occupancy(schedule):
daily_occupancy = np.zeros(N_DAYS, np.int32)
 for f, d in enumerate(schedule):
daily_occupancy[d] += F_COUNTS[f]
return daily_occupancy
@njit(fastmath=True)
 def cost_function(schedule):
choice_cost = 0
for f, d in enumerate(schedule):
choice_cost += C_COSTS[f, d]
daily_occupancy = get_daily_occupancy(schedule)
 accounting_cost = 0
for d0 in range(N_DAYS):
pop0 = daily_occupancy[d0]
d1 = min(d0+1, N_DAYS-1)
 pop1 = daily_occupancy[d1]
accounting_cost += max(0,(pop0-125.0)/ 400.0 * pop0**(0.5 + abs(pop0 - pop1)/ 50.0))
violations =(np.count_nonzero(daily_occupancy < MIN_POP)+
np.count_nonzero(daily_occupancy > MAX_POP))
penalty = int(violations * 10e8)
 return choice_cost, accounting_cost, penalty
def fix_schedule(schedule):
daily_occupancy = get_daily_occupancy(schedule)
 f_list = np.flip(np.argsort(F_COUNTS))
while(daily_occupancy.min() < MIN_POP)or \ 
(daily_occupancy.max() > MAX_POP):
for c in range(N_CHOICES):
for f in f_list:
n = F_COUNTS[f]
d_old = schedule[f]
d_new = CHOICES[f, c]
if(daily_occupancy[d_old] > MAX_POP)and \ 
(( daily_occupancy[d_new] + n)<= MAX_POP):
schedule[f] = d_new
daily_occupancy[d_new] += n
daily_occupancy[d_old] -= n
for c in range(N_CHOICES):
for f in f_list:
n = F_COUNTS[f]
d_old = schedule[f]
d_new = CHOICES[f, c]
if(daily_occupancy[d_new] < MIN_POP)and \ 
(( daily_occupancy[d_old] - n)>= MIN_POP):
schedule[f] = d_new
daily_occupancy[d_new] += n
daily_occupancy[d_old] -= n
return schedule<prepare_x_and_y>
train, test = data[:len(train_df)], data[len(train_df):]
X = train.drop(columns=['SalePrice', 'Id'])
 y = train['SalePrice']
test = test.drop(columns=['SalePrice', 'Id'] )
model = Solver('SantaLinear', Solver.GLOP_LINEAR_PROGRAMMING)
 set_f = range(N_FAMILIES)
 set_d = range(N_DAYS)
 x = {(f, d): model.BoolVar(f'x[{f},{d}]')for f in set_f for d in CHOICES[f, :]}
y = {(d): model.IntVar(0, MAX_POP-MIN_POP, f'y[{d}]')for d in set_d}
for f in set_f:
model.Add(model.Sum(x[f, d] for d in set_d if(f, d)in x.keys())== 1)
 pops = [model.Sum(x[f, d] * F_COUNTS[f] for f in set_f if(f, d)in x.keys())for d in set_d]
for d0 in set_d:
pop0 = pops[d0]
model.Add(pop0 >= MIN_POP)
 model.Add(pop0 <= MAX_POP)
 d1 = min(d0+1, N_DAYS-1)
 pop1 = pops[d1]
model.Add(pop0 - pop1 <= y[d])
 model.Add(pop1 - pop0 <= y[d])
 model.Add(y[d] <= 30)
 DELTA_WEIGHT = 500
objective = model.Sum(x[f, d] * C_COSTS[f, d] for f, d in x.keys())
 objective += model.Sum(y[d] for d in set_d)* DELTA_WEIGHT
model.Minimize(objective)
 model.SetTimeLimit(5 * 60 * 1000)
 status = model.Solve()
if status == Solver.OPTIMAL:
print('Found Optimal Solution')
 else:
print(f'Solver Error.Status = {status}')
 schedule = np.full(N_FAMILIES, -1, dtype=np.int8)
 x_vals = np.zeros(( N_FAMILIES, N_DAYS))
for f, d in x.keys() :
x_vals[f, d] = x[f, d].solution_value()
for f, vals in enumerate(x_vals):
d = np.argmax(vals)
 schedule[f] = d
score = cost_function(schedule)
 print(sum(score), '|', score)
 schedule = fix_schedule(schedule)
 score = cost_function(schedule)
 print(sum(score), '|', score )<compute_train_metric>
kfold = KFold(n_splits=10, random_state = 77, shuffle = True )
def choice_search(schedule):
best_score = cost_function(schedule)
 f_list = np.flip(np.argsort(F_COUNTS))
for f in f_list:
d_old = schedule[f]
for d_new in CHOICES[f, :]:
schedule[f] = d_new
score = cost_function(schedule)
 if(sum(score)< sum(best_score)) or \ 
(sum(score)== sum(best_score)and np.random.random() < 0.5):
best_score = score
d_old = d_new
else:
schedule[f] = d_old
return schedule<define_search_space>
params = {
'task' : 'train',
'objective' : 'regression',
'subsample' : 0.8,
'max_depth' : 7
}
param_grid = {
'learning_rate': [0.1],
'feature_fraction' : [0.5, 0.8],
'num_leaves':[31, 63, 127]
}
lgbm_model = lgbm.LGBMRegressor(**params, verbose=-1)
 lgbm_grid = GridSearchCV(lgbm_model,
param_grid,
cv=kfold,
scoring='neg_mean_squared_error',
return_train_score=True)
 lgbm_grid.fit(X, y)
 r2_score(lgbm_grid.predict(X), y )
def min_cost_flow(schedule):
MIN_FAMILY = F_COUNTS.min()
MAX_FAMILY = F_COUNTS.max()
solver = SimpleMinCostFlow()
occupancy = np.zeros(( N_DAYS, MAX_FAMILY+1), dtype=np.int32)
 for f, n in enumerate(F_COUNTS):
f_node = int(f)
 f_demand = -1
solver.SetNodeSupply(f_node, f_demand)
 d = schedule[f]
occupancy[d, n] += 1
for d in range(N_DAYS):
for n in range(MIN_FAMILY, MAX_FAMILY):
occ_node = int(N_FAMILIES +(n-2)* N_DAYS + d)
 occ_supply = int(occupancy[d, n])
 solver.SetNodeSupply(occ_node, occ_supply)
 for f, n in enumerate(F_COUNTS):
f_node = int(f)
 for c in range(N_CHOICES):
d = CHOICES[f, c]
c_cost = int(C_COSTS[f, d])
 occ_node = int(N_FAMILIES +(n-2)* N_DAYS + d)
 solver.AddArcWithCapacityAndUnitCost(occ_node, f_node, 1, c_cost)
 status = solver.SolveMaxFlowWithMinCost()
if status == SimpleMinCostFlow.OPTIMAL:
for arc in range(solver.NumArcs()):
if solver.Flow(arc)> 0:
head = solver.Head(arc)
 if head in range(N_FAMILIES):
f = head
n = F_COUNTS[f]
occ_node = solver.Tail(arc)
 d = occ_node - N_FAMILIES -(n-2)* N_DAYS
schedule[f] = d
else:
print(f'Solver Error.Status = {status}')
 return schedule<statistical_test>
lgbm_grid.best_estimator_
def swap_search(schedule):
best_score = cost_function(schedule)
 f_list = np.random.permutation(N_FAMILIES)
 for f0 in f_list:
d0 = schedule[f0]
c0 = list(CHOICES[f0, :] ).index(d0)
 swapped = False
for d1 in CHOICES[f0, 0:c0]:
f1_set = np.where(schedule == d1)[0]
for f1 in f1_set:
if d0 in CHOICES[f1, :]:
schedule[f0] = d1
schedule[f1] = d0
score = cost_function(schedule)
 if(sum(score)< sum(best_score)) or \ 
(sum(score)== sum(best_score)and np.random.random() < 0.5):
best_score = score
swapped = True
break
else:
schedule[f0] = d0
schedule[f1] = d1
if swapped: break
return schedule<find_best_params>
param_grid = {
'learning_rate': [0.1],
'subsample' : [0.5, 0.8, 1.0],
'max_depth':[4, 5, 8]
}
xgb_model = xgb.XGBRegressor()
xgb_grid = GridSearchCV(xgb_model,
param_grid,
cv=kfold,
scoring='neg_mean_squared_error')
 xgb_grid.fit(X, y)
 r2_score(xgb_grid.predict(X), y )
def random_climb(schedule, repeats=100000):
best_score = cost_function(schedule)
 for _ in range(repeats):
f = np.random.randint(N_FAMILIES)
 c = np.random.randint(N_CHOICES)
 d_old = schedule[f]
d_new = CHOICES[f, c]
schedule[f] = d_new
score = cost_function(schedule)
 if(sum(score)< sum(best_score)) or \ 
(sum(score)== sum(best_score)and np.random.random() < 0.5):
best_score = score
else:
schedule[f] = d_old
return schedule<find_best_params>
xgb_grid.best_estimator_
best_score = cost_function(schedule)
 no_improvement = 0
while no_improvement < 5:
improved = False
while True:
schedule = random_climb(schedule)
 score = cost_function(schedule)
 print('Random :', sum(score), '|', score)
 if sum(score)< sum(best_score):
best_score = score
improved = True
else:
break
while True:
schedule = swap_search(schedule)
 score = cost_function(schedule)
 print('Swaps :', sum(score), '|', score)
 if sum(score)< sum(best_score):
best_score = score
improved = True
else:
break
while True:
schedule = choice_search(schedule)
 score = cost_function(schedule)
 print('Choice :', sum(score), '|', score)
 if sum(score)< sum(best_score):
best_score = score
improved = True
else:
break
if not improved:
schedule = min_cost_flow(schedule)
 score = cost_function(schedule)
 print('MinCost :', sum(score), '|', score)
 if sum(score)< sum(best_score):
best_score = score
improved = True
no_improvement = 0 if improved else no_improvement + 1<save_to_csv>
lasso = make_pipeline(RobustScaler() , Lasso(alpha =0.0001))
lasso.fit(X, y)
 r2_score(lasso.predict(X), y )
submission = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/sample_submission.csv')
 submission['assigned_day'] = schedule + 1
submission.to_csv('submission.csv', index=False )<define_search_space>
ENet = make_pipeline(RobustScaler() , ElasticNet(alpha=0.0001, l1_ratio=.9))
ENet.fit(X, y)
 r2_score(ENet.predict(X), y )
%%writefile main.cpp
using namespace std;
using namespace std::chrono;
constexpr array<uint8_t, 21> DISTRIBUTION{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5};
// {2, 5} it's mean the first random family will brute force for choices 1-2 and the second random family will brute force for choices 1-5
constexpr int MAX_OCCUPANCY = 300;
constexpr int MIN_OCCUPANCY = 125;
constexpr int BEST_N = 10;
array<uint8_t, 5000> n_people;
array<array<uint8_t, 10>, 5000> choices;
array<array<uint16_t, 10>, 5000> PCOSTM;
array<array<double, 176>, 176> ACOSTM;
static std::atomic<bool> flag(false);
static array<uint8_t, 5000> global_assigned_days = {};
auto START_TIME = high_resolution_clock::now() ;
int END_TIME = 515; //475
int N_JOBS = 4;
struct Index {
Index(array<uint8_t, 5000> assigned_days_): assigned_days(assigned_days_){
setup() ;
}
array<uint8_t, 5000> assigned_days;
array<uint16_t, 100> daily_occupancy_{};
int preference_cost_ = 0;
void setup() {
preference_cost_ = 0;
daily_occupancy_.fill(0);
for(int j = 0; j < assigned_days.size() ; ++j){
daily_occupancy_[choices[j][assigned_days[j]]] += n_people[j];
preference_cost_ += PCOSTM[j][assigned_days[j]];
}
}
double calc(const array<uint16_t, 5000>& indices, const array<uint8_t, DISTRIBUTION.size() >& change){
double accounting_penalty = 0.0;
auto daily_occupancy = daily_occupancy_;
int preference_cost = preference_cost_;
for(int i = 0; i < DISTRIBUTION.size() ; ++i){
int j = indices[i];
daily_occupancy[choices[j][assigned_days[j]]] -= n_people[j];
daily_occupancy[choices[j][ change[i]]] += n_people[j];
preference_cost += PCOSTM[j][change[i]] - PCOSTM[j][assigned_days[j]];
}
for(auto occupancy : daily_occupancy)
 if(occupancy < MIN_OCCUPANCY)
 return 1e12 *(MIN_OCCUPANCY - occupancy);
else if(occupancy > MAX_OCCUPANCY)
 return 1e12 *(occupancy - MAX_OCCUPANCY);
for(int day = 0; day < 99; ++day)
 accounting_penalty += ACOSTM[daily_occupancy[day] - 125][daily_occupancy[day + 1] - 125];
accounting_penalty += ACOSTM[daily_occupancy[99] - 125][daily_occupancy[99] - 125];
return preference_cost + accounting_penalty;
}
void reindex(const array<uint16_t, DISTRIBUTION.size() >& indices, const array<uint8_t, DISTRIBUTION.size() >& change){
for(int i = 0; i < DISTRIBUTION.size() ; ++i){
assigned_days[indices[i]] = change[i];
}
setup() ;
}
};
void init_data() {
ifstream in("/kaggle/input/santa-workshop-tour-2019/family_data.csv");
assert(in && "family_data.csv");
string header;
int n, x;
char comma;
getline(in, header);
for(int j = 0; j < choices.size() ; ++j){
in >> x >> comma;
for(int i = 0; i < 10; ++i){
in >> x >> comma;
choices[j][i] = x - 1;
}
in >> n;
n_people[j] = n;
}
array<int, 10> pc{0, 50, 50, 100, 200, 200, 300, 300, 400, 500};
array<int, 10> pn{0, 0, 9, 9, 9, 18, 18, 36, 36, 235};
for(int j = 0; j < PCOSTM.size() ; ++j)
 for(int i = 0; i < 10; ++i)
 PCOSTM[j][i] = pc[i] + pn[i] * n_people[j];
for(int i = 0; i < 176; ++i)
 for(int j = 0; j < 176; ++j)
 ACOSTM[i][j] = i * pow(i + 125, 0.5 + abs(i - j)/ 50.0)/ 400.0;
}
array<uint8_t, 5000> read_submission(string filename){
ifstream in(filename);
assert(in && "submission.csv");
array<uint8_t, 5000> assigned_day{};
string header;
int id, x;
char comma;
getline(in, header);
for(int j = 0; j < choices.size() ; ++j){
in >> id >> comma >> x;
assigned_day[j] = x - 1;
auto it = find(begin(choices[j]), end(choices[j]), assigned_day[j]);
if(it != end(choices[j]))
assigned_day[j] = distance(begin(choices[j]), it);
}
return assigned_day;
}
double calc(const array<uint8_t, 5000>& assigned_days, bool print = false){
int preference_cost = 0;
double accounting_penalty = 0.0;
array<uint16_t, 100> daily_occupancy{};
for(int j = 0; j < assigned_days.size() ; ++j){
preference_cost += PCOSTM[j][assigned_days[j]];
daily_occupancy[choices[j][assigned_days[j]]] += n_people[j];
}
for(auto occupancy : daily_occupancy)
 if(occupancy < MIN_OCCUPANCY)
 return 1e12 *(MIN_OCCUPANCY - occupancy);
else if(occupancy > MAX_OCCUPANCY)
 return 1e12 *(occupancy - MAX_OCCUPANCY);
for(int day = 0; day < 99; ++day)
 accounting_penalty += ACOSTM[daily_occupancy[day] - 125][daily_occupancy[day + 1] - 125];
accounting_penalty += ACOSTM[daily_occupancy[99] - 125][daily_occupancy[99] - 125];
if(print){
cout << preference_cost << " " << accounting_penalty << " " << preference_cost + accounting_penalty << endl;
}
return preference_cost + accounting_penalty;
}
bool time_exit_fn() {
return duration_cast<minutes>(high_resolution_clock::now() -START_TIME ).count() < END_TIME;
}
const vector<array<uint8_t, DISTRIBUTION.size() >> changes = []() {
vector<array<uint8_t, DISTRIBUTION.size() >> arr;
array<uint8_t, DISTRIBUTION.size() > tmp{};
for(int i = 0; true; ++i){
arr.push_back(tmp);
tmp[0] += 1;
for(int j = 0; j < DISTRIBUTION.size() ; ++j)
 if(tmp[j] >= DISTRIBUTION[j]){
if(j >= DISTRIBUTION.size() - 1)
 return arr;
tmp[j] = 0;
++tmp[j + 1];
}
}
return arr;
}() ;
void stochastic_product_search(array<uint8_t, 5000> assigned_days, double best_local_score){ // 15'360'000it/s 65ns/it 0.065µs/it
Index index(assigned_days);
thread_local std::mt19937 gen(std::random_device{}());
uniform_int_distribution<> dis(0, 4999);
array<uint16_t, 5000> indices;
iota(begin(indices), end(indices), 0);
array<uint16_t, DISTRIBUTION.size() > best_indices{};
array<uint8_t, DISTRIBUTION.size() > best_change{};
for(;time_exit_fn() ;){
bool found_better = false;
for(int k = 0; k < BEST_N; ++k){
for(int i = 0; i < DISTRIBUTION.size() ; ++i)//random swap
swap(indices[i], indices[dis(gen)]);
for(const auto& change : changes){
auto score = index.calc(indices, change);
if(score < best_local_score){
found_better = true;
best_local_score = score;
best_change = change;
copy_n(begin(indices), DISTRIBUTION.size() , begin(best_indices)) ;
}
}
}
if(flag.load() == true){
return;
}
if(found_better && flag.load() == false){ // reindex from N best if found better
flag = true;
index.reindex(best_indices, best_change);
global_assigned_days = index.assigned_days;
return;
}
}
}
array<uint16_t, 5000> sort_indexes(const array<uint8_t, 5000> v){
// initialize original index locations
array<uint16_t, 5000> idx;
iota(idx.begin() , idx.end() , 0);
// sort indexes based on comparing values in v
sort(idx.begin() , idx.end() ,
[v](size_t i1, size_t i2){return v[i1] > v[i2];});
return idx;
}
void seed_finding(array<uint8_t, 5000> assigned_days, double best_local_score, int order){ // 15'360'000it/s 65ns/it 0.065µs/it
thread_local std::mt19937 gen(std::random_device{}());
uniform_real_distribution<> dis(0.0, 1.0);
auto original_score = best_local_score;
auto indices = sort_indexes(n_people); // sort by descending
if(order == 0){ // sort by ascending
reverse(begin(indices), end(indices)) ;
}
for(;time_exit_fn() ;){
auto local_assigned_days = assigned_days;
if(order == 1){ // sort by random
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count() ;
shuffle(indices.begin() , indices.end() , std::default_random_engine(seed)) ;
}
for(int t = 0; t < 700; t++){
for(auto& i : indices){
for(int j = 0; j < 10; j++){
auto di = local_assigned_days[i];
local_assigned_days[i] = j;
auto cur_score = calc(local_assigned_days, false);
double KT = 1;
if(t < 5){
KT = 1.5;
}
else if(t < 10){
KT = 4.5;
}
else {
if(cur_score > best_local_score + 100){
KT = 3;
}
else if(cur_score > best_local_score + 50){
KT = 2.75;
}
else if(cur_score > best_local_score + 20){
KT = 2.5;
}
else if(cur_score > best_local_score + 10){
KT = 2;
}
else if(cur_score > best_local_score){
KT = 1.5;
}
else {
KT = 1;
}
}
if(cur_score <= best_local_score){
best_local_score = cur_score;
}
else {
auto prob = exp(-(cur_score - best_local_score)/ KT);
if(dis(gen)< prob){
best_local_score = cur_score;
}
else {
local_assigned_days[i] = di;
}
}
}
}
if(flag.load() == true){
return;
}
if(best_local_score < original_score && flag.load() == false){
flag = true;
global_assigned_days = local_assigned_days;
return;
}
}
if(best_local_score <= original_score && flag.load() == false){
flag = true;
global_assigned_days = local_assigned_days;
return;
}
}
}
void save_sub(const array<uint8_t, 5000>& assigned_day){
ofstream out("submission.csv");
out << "family_id,assigned_day" << endl;
for(int i = 0; i < assigned_day.size() ; ++i)
 out << i << "," << choices[i][assigned_day[i]] + 1 << endl;
}
int main() {
init_data() ;
auto assigned_days = read_submission("submission.csv");
double best_score = calc(assigned_days, true);
for(;time_exit_fn() ;){
std::thread threads[N_JOBS];
for(int i = 0; i < N_JOBS; i++){
//threads[i] = std::thread(stochastic_product_search, assigned_days, best_score);
if(i < 2){
threads[i] = std::thread(stochastic_product_search, assigned_days, best_score);
}
else {
int order = i % 3;
threads[i] = std::thread(seed_finding, assigned_days, best_score, order);
}
//int order = i % 3;
//threads[i] = std::thread(seed_finding, assigned_days, best_score, order);
}
for(int i = 0; i < N_JOBS; i++){
threads[i].join() ;
}
// global_assigned_days return from threads
best_score = calc(global_assigned_days, true);
save_sub(global_assigned_days);
flag = false;
assigned_days = global_assigned_days;
}
return 0;
}<set_options>
stacking = StackingRegressor(estimators=[ 
('lgbm', lgbm_grid.best_estimator_), 
('xgb', xgb_grid.best_estimator_), 
('lasso', lasso.steps[1][1]), 
('elastic',ENet.steps[1][1])],
final_estimator=lgbm_grid.best_estimator_,
passthrough=True)
 model = stacking.fit(X, y)
 r2_score(model.predict(X), y )
!g++ -pthread -lpthread -O3 -std=c++17 -o main main.cpp<load_from_csv>
submission = pd.DataFrame()
submission['Id'] = test_df['Id']
submission['SalePrice'] = np.round(list(model.predict(test)) , 0)
 submission.head()
<load_from_csv>
submission.to_csv("submission.csv", index=False )
<define_variables><EOS> 
submission.to_csv("submission.csv", index=False )