Kaggle competition: Santa's Workshop Tour 2019
tfms = get_transforms(do_flip=False, max_rotate=20.0, max_warp=0.0 )<load_pretrained>
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]
data = ImageDataBunch.from_folder(
 path=output_root_path,
train='train',
test='test',
seed=1234,
valid_pct=0.2,
size=28,
ds_tfms=tfms
).normalize()<choose_model_class>
@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
learn = cnn_learner(data, models.resnet152, metrics=accuracy, callback_fns=ShowGraph ).mixup()<train_model>
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 )
learn.fit_one_cycle(10 )<find_best_params>
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
lrf=learn.lr_find()<save_model>
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
learn.save('model')
 learn.load('model' )<train_model>
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
learn.unfreeze()
learn.fit_one_cycle(10, 1e-02 )<predict_on_test>
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
class_score, _ = learn.get_preds(DatasetType.Test)
 class_score = np.argmax(class_score, axis=1 )<define_variables>
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
ImageId = [os.path.splitext(path)[0] for path in os.listdir(output_root_path/'test')]
ImageId = [int(path)for path in ImageId]
ImageId = [ID+1 for ID in ImageId]<save_to_csv>
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 )
pd.DataFrame({
"ImageId": ImageId,
"Label": class_score
} ).to_csv("submission.csv", index=False )<load_from_csv>
%%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;
}
train = pd.read_csv('.. /input/train.csv')
 train.head()<load_from_csv>
!g++ -pthread -lpthread -O3 -std=c++17 -o main main.cpp
test = pd.read_csv(".. /input/test.csv")
 test.head()<count_values>
train['label'].value_counts().sort_index()<prepare_x_and_y>
NUMBER_DAYS = 100
NUMBER_FAMILIES = 5000
data = pd.read_csv('/kaggle/input/santa-workshop-tour-2019/family_data.csv')
 submission = pd.read_csv('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 )
<feature_engineering><EOS> 
MAX_BEST_CHOICE = 5
NUM_SWAP = 2500
NUM_SECONDS = 1800
NUM_THREADS = 4
for _ in range(10):
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] )