def link_public_private_buildings(building_data): for resd_building in bt.find_buildings_in_type(bt.RESIDENTIAL, building_data): for type in bt.ALL_PUBLIC_BUILDING_TYPES: public_buildings_in_type = bt.find_buildings_in_type( type, building_data) dist_lst = [ (public_in_type, util.calc_distance_two_buildings(resd_building, public_in_type)) for public_in_type in public_buildings_in_type ] dist_lst_sorted = sorted(dist_lst, key=lambda x: x[1]) # closest public, take the first argument of the tuple (building, dist) # closest_public = dist_lst_sorted[0] closest_public = dist_lst_sorted[0][0] dist = dist_lst_sorted[0][1] # link residential to public closest_public.add_user_buildings(resd_building) # link public to residential resd_building.add_used_public_building(closest_public, dist) return building_data
def __evaluate_plan_distance(self): # if already calculated: if self.__plan_distance_score != -1: return self.__plan_distance_score # first time, should calculate it self.__plan_distance_score = 1 # loop over only public buildings!! for b_type in bt.ALL_PUBLIC_BUILDING_TYPES: buildings_in_type = bt.find_buildings_in_type(b_type, self.updated_building_data_all) updated_building_data_resd = bt.find_buildings_in_type(bt.RESIDENTIAL, self.updated_building_data_all) sum_avg_dist_lst_prob = evaluate_buildings_distances_for_type(updated_building_data_resd, buildings_in_type) # finally, duplicate the probability (importance) of the PUBLIC buildings with its extra height i = 0 evaluated_for_type = 0.0 sum_all_public_same_type = 0.0 for public_building in buildings_in_type: sum_area_public_building = public_building.get_extra_height() * public_building.get_area() sum_all_public_same_type += sum_area_public_building evaluated_for_type += sum_avg_dist_lst_prob[i] * (sum_area_public_building) # take sum_avg_dist_lst_prob[i] * sum_area_public_building as output maybe # so it will be good both for evalation and both calc_public_floors... if evaluated_for_type > 0: self.__plan_distance_score *= evaluated_for_type/max(sum_all_public_same_type, 1) return self.__plan_distance_score
def __evaluate_plan_linked_distance(self): if self.__plan_distance_score != -1: return self.__plan_distance_score # first time, should calculate it self.__plan_distance_score = 1 # loop over only public buildings!! for b_type in bt.ALL_PUBLIC_BUILDING_TYPES: buildings_in_type = bt.find_buildings_in_type(b_type, self.updated_building_data_all) updated_building_data_resd = bt.find_buildings_in_type(bt.RESIDENTIAL, self.updated_building_data_all) updated_building_data_public_type = bt.find_buildings_in_type(b_type, self.updated_building_data_all) for p_building in updated_building_data_public_type: for building_resd_used in p_building.get_users_buildings(): pass
def __calculate_public_plan_prob_importance(self): # in current situation (before building more floors), we don't know the cost, because it depends on the num # of floors, and the needs are mandatory for us. so importance is based on distance only. public_plan_prob_vec_per_type_dist = dict() # same function used for evaluation (distance parameter), just for planning the public here. for public_type in bt.all_public_building_types(): # it doesnt' matter if it is self.__init_buildings_data or updated_building_data_resd, # because the public buildings are not updated anyway.. public_buildings_sametype = bt.find_buildings_in_type(public_type, self.__init_buildings_data) updated_building_data_resd = bt.find_buildings_in_type(bt.RESIDENTIAL, self.updated_building_data_all) public_plan_prob_vec_per_type_dist[public_type] = evaluate_buildings_distances_for_type\ (updated_building_data_resd, public_buildings_sametype) return public_plan_prob_vec_per_type_dist
def evaluate_personal_satisfaction(plan): residential_buildings = bt.find_buildings_in_type(bt.RESIDENTIAL, plan) people = [] people.append(p.Person(CHILD, False)) people.append(p.Person(SINGLE, False)) people.append(p.Person(SINGLE, True)) people.append(p.Person(PARENT, False)) people.append(p.Person(ELDERLY, True)) personal_satisfaction_raw = [] max_sat = 0 for person in people: curr_person_type_score = [] for j in range(50): person.set_residence(residential_buildings[np.random.randint( len(residential_buildings))]) # curr_sat = round(person.set_satisfaction(),4) curr_sat = person.set_satisfaction() curr_person_type_score.append(curr_sat) curr_person_type_sat = np.mean(curr_person_type_score) personal_satisfaction_raw.append(curr_person_type_sat) if (curr_person_type_sat > max_sat): max_sat = curr_person_type_sat personal_satisfaction = [] for i in range(len(people)): curr_sat = round(personal_satisfaction_raw[i] / max_sat, 4) personal_satisfaction.append( (people[i].get_type(), str(people[i].get_religious()), people[i].get_residence(), curr_sat)) return personal_satisfaction
def __calculate_public_plan(self): for public_type in bt.ALL_PUBLIC_BUILDING_TYPES: units_needed_for_type = self.__all_needs[public_type] # ex: 3 units area_per_unit_for_type = needs.one_unit_in_meter_square(public_type) # ex: 100 m^2 per unit area_needed_for_type = units_needed_for_type * area_per_unit_for_type # ex: 300 m^2 overall public_in_type = bt.find_buildings_in_type(public_type, self.updated_building_data_all) left_area = area_needed_for_type idx = 0 search_more = True while left_area > 0 and search_more: building_score_type = [(public_building.calc_building_score(self.__all_needs), public_building) for public_building in public_in_type] building_score_type_sorted = sorted(building_score_type, key=lambda x: x[0]) # take the building with the lowest score while idx < len(building_score_type_sorted): first_to_build = building_score_type_sorted[idx][1] building_area = first_to_build.get_area() building_height = first_to_build.get_overall_height() max_height = first_to_build.get_max_height() if (building_height > max_height): idx += 1 if idx == len(building_score_type_sorted): search_more = False elif left_area > 0.5 * building_area: # if worth to add a floor first_to_build.add_extra_height(1) left_area -= building_area break else: idx += 1 if idx == len(building_score_type_sorted): search_more = False
def __evaluate_plan_needs(self): # already calculated: if self.__plan_needs_score != -1: return self.__plan_needs_score # first time, should calculate it self.__plan_needs_score = 1 # for all buildings, including residential and public!! for b_type in bt.ALL_BUILDING_TYPES: unit_needs_for_type = self.__all_needs[b_type] buildings_in_type = bt.find_buildings_in_type(b_type, self.updated_building_data_all) extra_units_for_type = 0 for building in buildings_in_type: extra_units_for_type += building.get_area() * building.get_extra_height() extra_units_for_type = math.ceil(extra_units_for_type/needs.one_unit_in_meter_square(b_type)) ratio = 1 if extra_units_for_type != 0 and unit_needs_for_type != 0: if extra_units_for_type >= unit_needs_for_type: ratio = unit_needs_for_type / extra_units_for_type else: ratio = extra_units_for_type / unit_needs_for_type self.__plan_needs_score *= ratio return self.__plan_needs_score
def get_only_floor_lst(self): only_floors = [] self.updated_building_data = self.evaluate_plan_obj.get_updated_building_data_all() for b_type in bt.ALL_BUILDING_TYPES: for building in bt.find_buildings_in_type(b_type, self.updated_building_data): only_floors.append(building.get_extra_height()) return only_floors
def min_conflict_solution(buildings_data, all_needs, housing_units_to_add): # main idea of min-conflict algorithm: in each iteration, check the conflicts, and add a floor to the # building with least conflict. (I think that the right way to do it is to check violations of the # optimal values (for example - 20 kids in a classroom, where each additional child is a conflict) num_added_units = 0 added_floors_resd = [0] * len( bt.find_buildings_in_type(bt.RESIDENTIAL, buildings_data)) new_state = state.State(buildings_data, added_floors_resd, all_needs) residential_buildings = building_types.find_buildings_in_type( building_types.RESIDENTIAL, buildings_data) # the algorithm runs until we have enough housing units while num_added_units < housing_units_to_add: # a for loop, iterating over all the residential buildings and counts their conflicts for i in range(10): if num_added_units < housing_units_to_add: conflicts = [] # for building in residential_buildings: for i in range(len(residential_buildings)): conflicts.append( (calculate_conflicts(residential_buildings[i], new_state), i)) sorted_conflicts = sorted(conflicts, key=lambda building: building[0] ) #TODO check if it works properly for i in range(5): if num_added_units < housing_units_to_add: building_to_increase = sorted_conflicts[i][1] added_floors_resd[building_to_increase] += (10 - i) num_added_units += util.add_floors( (10 - i), new_state, residential_buildings[building_to_increase] ) # returns the number of units added new_state.update_floors(added_floors_resd) # score return (new_state.get_score(), new_state.get_updated_building_data())
def get_floor_state(self): if not self.additional_floors_all: self.updated_building_data = self.evaluate_plan_obj.get_updated_building_data_all() for b_type in bt.ALL_BUILDING_TYPES: if b_type != bt.RESIDENTIAL: additional_floors_for_type = [building.get_extra_height() for building in bt.find_buildings_in_type(b_type, self.updated_building_data)] self.additional_floors_all.append((b_type, additional_floors_for_type)) else: self.additional_floors_all.append((bt.RESIDENTIAL, self.additional_floors_resd)) return self.additional_floors_all
def reproduce(population, buildings_data, add_housing_unit, all_needs, mutatio_prob): residential_buildings = bt.find_buildings_in_type(bt.RESIDENTIAL, buildings_data) elite = get_top_individuals(population) new_pop = elite while (len(new_pop) < len(population) * 4): random_val = random.random() if (random_val < mutatio_prob): new_individual = generate_random_state(buildings_data, add_housing_unit, all_needs) else: new_individual = merge_elite(get_pair(elite), add_housing_unit, all_needs, residential_buildings) new_pop.append(new_individual) new_pop = get_top_individuals(new_pop) return new_pop
def generate_random_state(buildings_data, add_housing_units, all_needs_dict): residential_buildings = bt.find_buildings_in_type(bt.RESIDENTIAL, buildings_data) added_floors_resd = [0] * len(residential_buildings) units_added = 0 while units_added < add_housing_units: building_to_rise = random.randint(0, len(residential_buildings) - 1) added_floors_resd[building_to_rise] += 1 units_added += util.get_units_added_to_one_building( residential_buildings[building_to_rise], 1) new_state = state.State(buildings_data, added_floors_resd, all_needs_dict) return new_state
def __init__(self, init_buildings_data, plan_floors_resd_state, all_needs): self.__init_buildings_data = init_buildings_data self.__init_buildings_data_resd = bt.find_buildings_in_type(bt.RESIDENTIAL, init_buildings_data) self.__buildings_data_public = bt.find_buildings_public(init_buildings_data) self.__plan_floors_resd_state = plan_floors_resd_state self.__all_needs = all_needs # this function is updating the the init_building_data with the new state of additional floors # of ONLY residential buildings. the result will be stores in __updated_building_data_resd self.updated_building_data_all = \ bt.update_building_resd_with_floors_plan(self.__init_buildings_data, plan_floors_resd_state) self.__plan_needs_score = -1 self.__plan_distance_score = -1 self.__plan_cost_score = -1 # CALCULATE PUBLIC PLAN, and update retult in __init_building_data # like __init_building_data, updated with extra floors of the given RESIDENTIAL (state), and the calculated PUBLIC self.__calculate_public_plan()
def __evaluate_plan_cost(self): # already calculated: if self.__plan_cost_score != -1: return self.__plan_cost_score self.__plan_cost_score = 1 for b_type in bt.ALL_BUILDING_TYPES : for building in bt.find_buildings_in_type(b_type, self.updated_building_data_all): original_height = building.get_init_height() extra_height = building.get_extra_height() extra_extra = max(extra_height - math.ceil(FLOORS_PRCTG * original_height), 0) cost = pow(COST_MORE_PRSTG, extra_extra) if extra_height >= FLOORS_NUM: cost = min(COST_MORE_NUM_FLOORS, 1-cost) self.__plan_cost_score *= cost # max(evaluated_for_type / needs_for_type, 1) return self.__plan_cost_score
def calc_needs(buildings_data, add_housing_units): add_population = math.ceil(add_housing_units * AVG_FAMILY_SIZE) original_population = math.ceil( get_residential_sum_area(buildings_data) / METERS_PER_UNIT * AVG_FAMILY_SIZE) type_importance_dict = {} avg_imp = 1 / (len(bt.ALL_BUILDING_TYPES) - 1 ) # -1 for not considering the residential for building in bt.ALL_BUILDING_TYPES: type_importance_dict[str(building)] = avg_imp # number of kids in a certain age (for ex. 102 kids in the age of 10 yo) # grade_size = age_percentage18 * population_increase / PERCENTAGE grade_size = AGE_GROUP18_PRCTG * add_population / PERCENTAGE # add units of public services kindergarden_needs = (grade_size * KINDERGARDEN_NUM_GRADES) / CLASS_SIZE primary_needs = (grade_size * PRIMARY_NUM_GRADES) / CLASS_SIZE highschool_needs = (grade_size * HS_NUM_GRADES) / CLASS_SIZE synagogue_needs = math.ceil( ((add_population * RELIGION_PRCTG / PERCENTAGE) * 0.49) * 1.1) / one_unit_in_meter_square(bt.SYNAGOUGE) mikve_needs = math.ceil( ((add_population * RELIGION_PRCTG / PERCENTAGE) / 22.5) * 0.35) / one_unit_in_meter_square(bt.MIKVE) # 0.35 instead of 0.007 # POLICE previous_police = 0 for building in bt.find_buildings_in_type(bt.POLICE, buildings_data): previous_police += building.get_area() if add_population + original_population < 7000: police_needs = max(100 - previous_police, 0) elif add_population + original_population < 15000: police_needs = max(500 - previous_police, 0) elif add_population + original_population < 40000: police_needs = max(1500 - previous_police, 0) elif add_population + original_population < 100000: police_needs = max(3600 - previous_police, 0) else: police_needs = max(4400 - previous_police, 0) police_needs /= math.ceil(one_unit_in_meter_square(bt.POLICE)) # COMMUNITY CENTER previous_community = 0 for building in bt.find_buildings_in_type(bt.COMMUNITY_CNTR, buildings_data): previous_community += building.get_area() if add_population + original_population < 300: community_center_needs = max(250 - previous_community, 0) elif add_population + original_population < 600: community_center_needs = max(400 - previous_community, 0) else: community_center_needs = max(750 - previous_community, 0) community_center_needs /= one_unit_in_meter_square(bt.COMMUNITY_CNTR) # ELDERLY CENTER elderly_center_needs = 0 # CLINIC previous_health_clinic = 0 for building in bt.find_buildings_in_type(bt.CLINIC, buildings_data): previous_health_clinic += building.get_area() if add_population + original_population < 300: health_clinic_needs = max(300 - previous_health_clinic, 0) elif add_population + original_population < 600: health_clinic_needs = max(500 - previous_health_clinic, 0) else: health_clinic_needs = max(1000 - previous_health_clinic, 0) health_clinic_needs /= one_unit_in_meter_square(bt.CLINIC) # HOSPITAL hospital_needs = 0 # SPORT sport_needs = 0 all_needs_dict = dict() # All needs are in area (square meters) for b_type in bt.ALL_BUILDING_TYPES: if b_type == bt.KINDERGARDEN: all_needs_dict[b_type] = kindergarden_needs elif b_type == bt.PRIMARY_SCHOOL: all_needs_dict[b_type] = primary_needs elif b_type == bt.HIGH_SCHOOL: all_needs_dict[b_type] = highschool_needs elif b_type == bt.SYNAGOUGE: all_needs_dict[b_type] = synagogue_needs elif b_type == bt.MIKVE: all_needs_dict[b_type] = mikve_needs elif b_type == bt.POLICE: all_needs_dict[b_type] = police_needs elif b_type == bt.COMMUNITY_CNTR: all_needs_dict[b_type] = community_center_needs elif b_type == bt.ELDERLY_CNTR: all_needs_dict[b_type] = elderly_center_needs elif b_type == bt.CLINIC: all_needs_dict[b_type] = health_clinic_needs elif b_type == bt.HOSPITAL: all_needs_dict[b_type] = hospital_needs elif b_type == bt.SPORT: all_needs_dict[b_type] = sport_needs elif b_type == bt.RESIDENTIAL: all_needs_dict[b_type] = add_housing_units return all_needs_dict
def get_residential_sum_area(building_data): resd_buildings = bt.find_buildings_in_type(bt.RESIDENTIAL, building_data) all_areas = [resd_building.get_area() for resd_building in resd_buildings] return sum(all_areas)