class TestMultiGrid(unittest.TestCase): ''' Testing a toroidal MultiGrid ''' torus = True def setUp(self): ''' Create a test non-toroidal grid and populate it with Mock Agents ''' width = 3 height = 5 self.grid = MultiGrid(width, height, self.torus) self.agents = [] counter = 0 for x in range(width): for y in range(height): for i in range(TEST_MULTIGRID[x][y]): counter += 1 # Create and place the mock agent a = MockAgent(counter, None) self.agents.append(a) self.grid.place_agent(a, (x, y)) def test_agent_positions(self): ''' Ensure that the agents are all placed properly on the MultiGrid. ''' for agent in self.agents: x, y = agent.pos assert agent in self.grid[x][y] def test_neighbors(self): ''' Test the toroidal MultiGrid neighborhood methods. ''' neighborhood = self.grid.get_neighborhood((1, 1), moore=True) assert len(neighborhood) == 8 neighborhood = self.grid.get_neighborhood((1, 4), moore=True) assert len(neighborhood) == 8 neighborhood = self.grid.get_neighborhood((0, 0), moore=False) assert len(neighborhood) == 4 neighbors = self.grid.get_neighbors((1, 4), moore=False) assert len(neighbors) == 0 neighbors = self.grid.get_neighbors((1, 4), moore=True) assert len(neighbors) == 5 neighbors = self.grid.get_neighbors((1, 1), moore=False, include_center=True) assert len(neighbors) == 7 neighbors = self.grid.get_neighbors((1, 3), moore=False, radius=2) assert len(neighbors) == 11
class DiseaseModel(Model): def __init__(self, home_store): self.num_agents = 1000 self.grid = MultiGrid(200, 200, True) self.schedule = RandomActivation(self) self.running = True for i in range(self.num_agents): a = Agent(i, self) self.schedule.add(a) while True: #x = round(int(np.random.normal(self.grid.width/2, 10, 1))) #y = round(int(np.random.normal(self.grid.height/2, 10, 1))) x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) if len( self.grid.get_neighbors( (x, y), moore=True, include_center=True, radius=10)) <= 7: self.grid.place_agent(a, (x, y)) home_store[i, :] = x, y break if i < 1: a.infected = 1 self.datacollector = DataCollector( model_reporters={"Tot informed": compute_informed}, agent_reporters={"Infected": "infected"}) def step(self): self.datacollector.collect(self) self.schedule.step()
def step(self): temp_gain = 0 # fishing or waiting behavior if self.model.total_yearly_caught < self.model.yearly_quotum: self.fisherman_move() # Looking range is set to 0 as fisherman can only locate schools directly below them. surrounding = MultiGrid.get_neighbors(self.model.grid, self.pos, True, True,0) # Looks for fish to catch for agent in surrounding: if type(agent) == Fish: if agent.size <= self.model.catch_rate: self.size += agent.size self.model.total_yearly_caught += agent.size agent.size = 0 elif self.model.max_load - self.size < self.model.catch_rate: agent.size -= self.model.max_load - self.size self.size = self.model.max_load self.model.total_yearly_caught += self.model.max_load - self.size else: agent.size -= self.model.catch_rate self.size += self.model.catch_rate self.model.total_yearly_caught += self.model.catch_rate break # Sells fish if load is full if self.size == self.model.max_load: self.size = 0 temp_gain += self.model.full_catch_reward # paying the weekly cost of living temp_gain -= self.model.initial_wallet / self.model.initial_wallet_survival # removing if going bankrupt if self.wallet <= 0: self.model.remove_agent(self) # update the rolling gains del self.rolling_gains[0] self.rolling_gains.append(temp_gain) # update the wallet self.wallet += temp_gain # update the overall cumulative gain self.model.cumulative_gain += temp_gain
def step(self): ''' Randomly move Fish school and spawn new fish every year ''' curr_time = self.model.schedule_Fish.time self.move() # New fish spawn every 48 weeks (12, 4 week months) (added + 1 just so they don't reproduce immediately) if (curr_time + 1) % 48 == 0: self.size *= self.model.fish_reproduction_number*random.uniform(0.95,1.05) # Looking for Food surrounding = MultiGrid.get_neighbors(self.model.grid, self.pos, True, True,0) for agent in surrounding: if type(agent) == Food and agent.food == True: agent.food = False self.wallet += self.model.energy_gain break elif type(agent) == Fisherman: pass else: if self.model.food_bool == True: percentage = 1 - (self.model.split_size - self.size)/self.model.split_size self.energy_loss = 1/(1+0.00005**(percentage-0.5)) + 1 self.wallet -= self.energy_loss break # school size shrinks to half its size when energy is depleted if self.wallet <= 0 and self.model.food_bool == True: self.size /= 2 self.wallet += self.model.energy_gain # Schools above N tonnes will split in half if self.size > self.model.split_size and random.uniform(0,1) > 0.75: self.model.new_agent(Fish, self.pos, self.size*0.5, self.wallet*0.5, True, 0, False, self.model.energy_loss) self.size *= 0.5 if self.model.food_bool == True: self.wallet *= 0.5 # Schools under a threshold are removed if self.size < (self.model.initial_school_size / 20): self.model.remove_agent(self)
class ContactModel(Model): #@jit(nopython=True) def __init__(self, N, height, width, exponent, steps): self.number_of_agents = N self.height = height self.width = width self.exponent = exponent #self.x_locs = np.zeros((N, steps)) #self.y_locs = np.zeros((N)) self.direction_range=3 self.directions = Directions(self.direction_range) self.current_step_contacts=[] self.adjacency_matrix = np.zeros((N, N)) self.grid = MultiGrid(self.width, self.height, torus=False) self.schedule = BaseScheduler(self) self.current_step = 0 # Add N pedestrians to model (schedule, grid) for i in range(self.number_of_agents): x = self.random.randrange(1, self.grid.width-1) y = self.random.randrange(1, self.grid.height-1) pos = (x, y) new_human = Pedestrian(i, self, pos, self.exponent, self.directions) self.schedule.add(new_human) self.grid.place_agent(new_human, pos) self.data_collector=DataCollector() self.running=True self.data_collector.collect(self) ''' #@jit(nopython=True) def contact_update(self, contact_ids): contact_ids =sorted(contact_ids) if contact_ids not in self.current_step_contacts: self.current_step_contacts.append(contact_ids) ''' #@jit(nopython=True) def update_adjecency_matrix(self): agents = self.schedule.agents for i, agent in enumerate(agents): neighbors = self.grid.get_neighbors(agent.pos, moore=True, radius= 5) for neighbor in neighbors: if neighbor.unique_id > agent.unique_id: self.adjacency_matrix[agent.unique_id, neighbor.unique_id]+=1 ''' neighbors_in_contact = self.model.grid.get_neighbors(self.pos, moore=True, radius = 5) if len(neighbors_in_contact) > 0: for neighbor in neighbors_in_contact: if neighbor.unique_id != self.unique_id: self.model.contact_update((self.unique_id, neighbor.unique_id)) #TODO: order agent steps, order updates, double or not for id_tuple in self.current_step_contacts: self.adjacency_matrix[id_tuple[0], id_tuple[1]]+=1 ''' #@jit(nopython=True) def step(self): self.schedule.step() #self.update_adjecency_matrix() #self.current_step_contacts=[] self.data_collector.collect(self) #@jit(nopython=True) def run(self, N): for i in range(N): self.current_step+=1 self.step() if i%100 == 0: print(i)
class Environment(Model): """ A model which contains a number of ant colonies. """ def __init__(self, width, height, n_colonies, n_ants, n_obstacles, decay=0.2, sigma=0.1, moore=False, birth=True, death=True, pheromone_strength=10): """ :param width: int, width of the system :param height: int, height of the system :param n_colonies: int, number of colonies :param n_ants: int, number of ants per colony :param decay: float, the rate in which the pheromone decays :param sigma: float, sigma of the Gaussian convolution :param moore: boolean, True/False whether Moore/vonNeumann is used """ super().__init__() # Agent variables self.birth = birth self.death = death self.pheromone_level = 1 self.pheromone_strength = pheromone_strength self.nr_on_track = 0 # Environment variables self.n_ants = n_ants self.width = width self.height = height self.grid = MultiGrid(width, height, False) self.moore = moore self.sigma = sigma self.decay = decay # Environment attributes self.schedule = RandomActivation(self) self.colonies = [ Colony(self, i, (width // 2, height // 2), n_ants, birth=self.birth, death=self.death) for i in range(n_colonies) ] self.pheromones = np.zeros((width, height), dtype=np.float) self.pheromone_updates = [] self.found_pheromone = False self.food = FoodGrid(self) self.food.add_food() self.obstacles = [] for _ in range(n_obstacles): self.obstacles.append(Obstacle(self)) # Metric + data collection self.min_distance = distance.cityblock(self.colonies[0].pos, self.food.get_food_pos()[0]) self.datacollector = DataCollector(model_reporters={ "Minimum path length": metrics.min_path_length, "Mean minimum path length": metrics.mean_min_path_length }, agent_reporters={ "Agent minimum path length": lambda x: min(x.path_lengths), "Encounters": Ant.count_encounters }) # Animation attributes self.pheromone_im = None self.ax = None def step(self): """ Do a single time-step using freeze-dry states, colonies are updated each time-step in random orders, and ants are updated per colony in random order. """ self.food.step() self.datacollector.collect(self) # Update all colonies for col in random.sample(self.colonies, len(self.colonies)): col.step() self.schedule.step() self.update_pheromones() if not self.check_exit(): return "ended" else: return "running" def check_exit(self): # print(self.pheromones, self.found_pheromone) for i in self.pheromones: for j in i: if j > 1 and len(np.where(self.food.grid > 0)[0]) == 0: self.found_pheromone = True return True if self.found_pheromone is False: return True return False def move_agent(self, ant, pos): """ Move an agent across the map. :param ant: class Ant :param pos: tuple (x, y) """ if self.moore: assert np.sum(np.subtract(pos, ant.pos) ** 2) in [1, 2], \ "the ant can't move from its original position {} to the new position {}, because the distance " \ "is too large".format(ant.pos, pos) else: assert np.sum(np.subtract(pos, ant.pos) ** 2) == 1, \ "the ant can't move from its original position {} to the new position {}, because the distance " \ "is too large, loc_food {}".format(ant.pos, pos, self.food.get_food_pos()) self.grid.move_agent(ant, pos) def calc_ratio(self): """ Calculate number of ants on a track with pheromones from a specific threshold. Return ratio of ants on the track / ants off the track. """ nr_on_track = 0 for i in range(self.width): for j in range(self.height): neighborhood = self.grid.get_neighbors((i, j), moore=True, radius=0, include_center=True) for agent in neighborhood: if type(agent) == Ant: if self.pheromones[i][j] > self.pheromone_strength: nr_on_track += 1 break return [nr_on_track, self.n_ants - nr_on_track] def get_random_position(self): return (np.random.randint(0, self.width), np.random.randint(0, self.height)) def position_taken(self, pos): if pos in self.food.get_food_pos(): return True for colony in self.colonies: if colony.on_colony(pos): return True for obstacle in self.obstacles: if obstacle.on_obstacle(pos): return True return False def add_food(self): """ Add food somewhere on the map, which is not occupied by a colony yet """ self.food.add_food() def place_pheromones(self, pos): """ Add pheromone somewhere on the map :param pos: tuple (x, y) """ self.pheromone_updates.append((pos, self.pheromone_level)) def get_neighbor_pheromones(self, pos, id): """ Get the passable neighboring positions and their respective pheromone levels for the pheromone id :param pos: :param id: :return: """ indices = self.grid.get_neighborhood(pos, self.moore) indices = [ x for x in indices if not any([isinstance(x, Obstacle) for x in self.grid[x[0]][x[1]]]) ] pheromones = [self.pheromones[x, y] for x, y in indices] return indices, pheromones def update_pheromones(self): """ Place the pheromones at the end of a timestep on the grid. This is necessary for freeze-dry time-steps """ for (pos, level) in self.pheromone_updates: # self.pheromones[pos] += level self.pheromones[pos] += self.pheromone_strength self.pheromone_updates = [] # gaussian convolution using self.sigma self.pheromones = gaussian_filter(self.pheromones, self.sigma) * self.decay def animate(self, ax): """ :param ax: :return: """ self.ax = ax self.animate_pheromones() self.animate_colonies() self.animate_ants() self.animate_food() self.animate_obstacles() def animate_pheromones(self): """ Update the visualization part of the Pheromones. :param ax: """ pheromones = np.rot90( self.pheromones.astype(np.float64).reshape(self.width, self.height)) if not self.pheromone_im: self.pheromone_im = self.ax.imshow(pheromones, vmin=0, vmax=5, interpolation='None', cmap="Greens") else: self.pheromone_im.set_array(pheromones) def animate_colonies(self): """ Update the visualization part of the Colonies. :return: """ for colony in self.colonies: colony.update_vis() def animate_food(self): """ Update the visualization part of the FoodGrid. :return: """ self.food.update_vis() def animate_ants(self): """ Update the visualization part of the Ants. """ for ant in self.schedule.agents: ant.update_vis() def animate_obstacles(self): """ Update the visualization part of the Obstacles. :return: """ for obstacle in self.obstacles: obstacle.update_vis() def grid_to_array(self, pos): """ Convert the position/indices on self.grid to imshow array. :param pos: tuple (int: x, int: y) :return: tuple (float: x, float: y) """ return pos[0] - 0.5, self.height - pos[1] - 1.5 def pheromone_threshold(self, threshold): """ Returns an array of the positions in the grid in which the pheromone levels are above the given threshold""" pher_above_thres = np.where(self.pheromones >= threshold) return list(zip(pher_above_thres[0], pher_above_thres[1])) def find_path(self, pher_above_thres): """ Returns the shortest paths from all the colonies to all the food sources. A path can only use the positions in the given array. Therefore, this function checks whether there is a possible path for a certain pheremone level. Essentially a breadth first search""" space_searched = False all_paths = [] food_sources = self.food.get_food_pos() # Search the paths for a colony to all food sources for colony in self.colonies: colony_paths = [] pos_list = {colony.pos} # Prooning possible_paths = [[colony.pos]] # Continue expanding search area until all food sources found # or until the entire space is searched while food_sources != [] and not space_searched: space_searched = True temp = [] for path in possible_paths: for neighbor in self.grid.get_neighborhood( include_center=False, radius=1, pos=path[-1], moore=self.moore): if neighbor in food_sources: food_path = copy(path) food_path.append(neighbor) colony_paths.append(food_path) food_sources.remove(neighbor) # Add epanded paths to the possible paths if neighbor in pher_above_thres and neighbor not in pos_list: space_searched = False temp_path = copy(path) temp_path.append(neighbor) temp.append(temp_path) pos_list.add(neighbor) possible_paths.remove(path) possible_paths += temp all_paths.append(colony_paths) return all_paths
class Themepark(Model): def __init__(self, N_attr, N_cust, width, height, strategy, theme, max_time, weight, adaptive): self.max_time = max_time self.N_attr = N_attr self.penalty_per = PENALTY_PERCENTAGE self.weight = weight self.adaptive = adaptive self.strategies = STRATEGIES self.x_list, self.y_list, self.positions = xlist, ylist, positions self.x_list, self.y_list, self.positions = get_attraction_coordinates( WIDTH, HEIGHT, self.N_attr, theme) self.happinesses = [] self.path_coordinates = get_coordinates(WIDTH, HEIGHT, NUM_OBSTACLES, self.N_attr, theme) self.N_cust = N_cust # num of customer agents self.total_steps = 0 self.cust_ids = N_cust self.strategy = strategy self.grid = MultiGrid(width, height, torus=False) self.schedule = BaseScheduler(self) self.schedule_Attraction = BaseScheduler(self) self.schedule_Customer = BaseScheduler(self) self.totalTOTAL = 0 #TODO: DEZE NAAM VERANDEREN self.attractions = self.make_attractions() self.attraction_history = self.make_attr_hist() self.park_score = [] self.data_dict = {} self.hist_random_strat = [] self.hist_close_strat = [] self.all_rides_list = [] self.strategy_composition = self.make_strategy_composition() self.customers = self.add_customers(self.N_cust) self.monitor = Monitor(self.max_time, self.N_attr, self.positions) self.running = True # TODO: ALS HET GOED IS KUNNEN AL DEZE INITS WEG, MAAR DIT MOETEN WE WEL NOG EVEN DUBBEL CHECKEN # --> dit is gecheckt op het runnen van main_cluster_random_noise # self.theme = theme # self.starting_positions = [[int((WIDTH/2)-1), 0], [int(WIDTH/2), 0], [int((WIDTH/2)+1), 0]] # self.width = width # self.height = height # self.data = [] # self.data_customers = [] # self.memory = 5 # self.customer_score = [] # self.only_random = False # self.total_waited_time = 0 # Initialize dictionary of attractions for attraction in self.get_attractions(): self.data_dict[attraction.unique_id] = ({ "id": attraction.unique_id, "length": attraction.attraction_duration, "waiting_list": [] }) # TODO ADD COMMENT (SNAP NIET WAT DE DATACOLLECTOR IS) if len(self.strategies) == 6: self.datacollector = DataCollector({ "Random": lambda m: self.strategy_counter(self.strategies[0]), "0.00": lambda m: self.strategy_counter(self.strategies[1]), "0.25": lambda m: self.strategy_counter(self.strategies[2]), "0.50": lambda m: self.strategy_counter(self.strategies[3]), "0.75": lambda m: self.strategy_counter(self.strategies[4]), "1.00": lambda m: self.strategy_counter(self.strategies[5]), }) else: self.datacollector = DataCollector({ "0.00": lambda m: self.strategy_counter(self.strategies[0]), "0.25": lambda m: self.strategy_counter(self.strategies[1]), "0.50": lambda m: self.strategy_counter(self.strategies[2]), "0.75": lambda m: self.strategy_counter(self.strategies[3]), "1.00": lambda m: self.strategy_counter(self.strategies[4]), }) self.datacollector2 = DataCollector( {"score": lambda m: self.make_score()}) def make_score(self): """ Get the efficiency score """ ideal = {} cust_in_row = 0 for i in range(len(self.get_attractions())): ideal[i] = self.N_cust / self.N_attr cust_in_row += self.get_attractions()[i].N_current_cust tot_difference = 0 for i in range(len(self.get_attractions())): difference = abs(cust_in_row / self.N_attr - self.get_attractions()[i].N_current_cust) tot_difference += difference fraction_not_right = (tot_difference / self.N_cust) return abs(1 - (fraction_not_right)) * cust_in_row / self.N_cust def make_attr_hist(self): """ Initialize a dictionary in which the history of the attractions can be added in. """ attraction_history = {} for attraction in self.get_attractions(): attraction_history[attraction] = [0] * (self.max_time + 1) return attraction_history def strategy_counter(self, strategy): """ Count how many customers of different strategies are at the attractions """ counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors(attraction_pos, moore=True, radius=0, include_center=True) counter = 0 for agent in self.customers: if agent.weight == strategy: counter += 1 return counter def make_strategy_composition(self): """ TODO: ANNEMIJN KAN JIJ HIER COMMENTS BIJ DOEN? + RANDOM_TEST_4 WEGHALEN """ if self.strategy == "Random_test_4": self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0] dict = { self.strategies[0]: 1 / 6, self.strategies[1]: 0.20, self.strategies[2]: 0.20, self.strategies[3]: 0.20, self.strategies[4]: 0.20, self.strategies[5]: 0.20 } composition_list = [] for i in range(len(self.strategies)): if i == 0: dict[self.strategies[i]] = FRACTION_RANDOM continue else: composition_list.append(random.randint(0, 100)) sum_comp = sum(composition_list) sum_comp = sum_comp - sum_comp * FRACTION_RANDOM for i in range(len(self.strategies)): if i == 0: continue else: dict[self.strategies[i]] = composition_list[i - 1] / sum_comp else: dict = { self.strategies[0]: 0.20, self.strategies[1]: 0.20, self.strategies[2]: 0.20, self.strategies[3]: 0.20, self.strategies[4]: 0.20 } composition_list = [] for i in range(len(self.strategies)): composition_list.append(random.randint(0, 100)) sum_comp = sum(composition_list) sum_comp = sum_comp for i in range(len(self.strategies)): dict[self.strategies[i]] = composition_list[i - 1] / sum_comp return dict def make_attractions(self): """ Initialize attractions on fixed position. """ attractions = {} for i in range(self.N_attr): pos = (self.x_list[i], self.y_list[i]) if self.grid.is_cell_empty(pos): name = str(i) a = Attraction(i, self, pos, name, self.N_cust, self.weight) attractions[i] = a self.schedule_Attraction.add(a) self.grid.place_agent(a, pos) return attractions def get_attractions(self): """ Get a list with all attractions. """ agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) attractions = [] for agent in agents: if type(agent) == Attraction: attractions.append(agent) return attractions def calculate_people(self): """ Calculate how many customers are in which attraction. """ counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors(attraction_pos, moore=True, radius=0, include_center=True) counter = 0 for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent attraction.N_current_cust = counter counter_total[attraction.unique_id] = counter return list(counter_total.values()) def add_customers(self, N_cust, added=False): """ Initialize customers on random positions. """ weights_list = [] if self.adaptive is True: for j in self.strategy_composition.keys(): for i in range(round(N_cust * self.strategy_composition[j])): weights_list.append(j) if len(weights_list) < self.N_cust: rand = random.choice(self.strategies) weights_list.append(rand) elif len(weights_list) > self.N_cust: rand = random.choice(weights_list) weights_list.remove(rand) else: # if the strategy is not random add weights to weights_list if self.strategy is not "Random": for i in range(round(N_cust)): weights_list.append(self.weight) cust_list = [] for i in range(N_cust): pos_temp = [ random.randint(0, WIDTH - 1), random.randint(0, HEIGHT - 1) ] rand_x, rand_y = pos_temp[0], pos_temp[1] pos = (rand_x, rand_y) if added is True: i = self.cust_ids if self.strategy == "Random_test_4": if weights_list[i] == "Random_test_4": strategy = "Random_test_4" else: strategy = "Closest_by" else: strategy = self.strategy # Set weight if weights_list == []: weight = None else: weight = weights_list[i] # Initialize customer and add customer to the model a = Customer(i, self, pos, self.x_list, self.y_list, self.positions, strategy, weight, self.adaptive) self.schedule_Customer.add(a) self.grid.place_agent(a, pos) cust_list.append(a) return cust_list def calc_waiting_time(self): """ Calculate the waitingtime per attraction """ counter_total = {} attractions = self.get_attractions() for attraction in attractions: counter_total[ attraction.unique_id] = attraction.current_waitingtime return counter_total def calculate_people_sorted(self): """ Calculate how many customers are in which attraction. Returns a SORTED LIST. For example: indexes = [3, 2, 5, 1, 4] indicates that attraction3 has the least people waiting. """ counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors(attraction_pos, moore=True, radius=0, include_center=True) counter = 0 for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent attraction.N_current_cust = counter self.attraction_history[attraction][self.totalTOTAL] = counter counter_total[attraction.unique_id] = counter return counter_total def make_route(self): """ Draw coordinates of a possible path. """ for i in range(len(self.path_coordinates)): pos = self.path_coordinates[i] if pos not in self.positions: # Create path agent path = Route(i, self, pos) self.schedule.add(path) self.grid.place_agent(path, pos) # TODO: THEMEPARK SCORE GEBRUIKEN WE NIET MEER, DIT ALLEMAAL VERWIJDEREN OVERAL? def get_themepark_score(self): """ Get score of a themepark based on: - A total of all waitingtimes for every customer - The total number of rides taken """ attractions = self.get_attractions() total_wait, total_rides = 0, 0 for attraction in attractions: total_wait += attraction.current_waitingtime if attraction.current_a is not None: total_rides += 1 if total_rides == 0: return total_rides return (total_wait / total_rides) def get_strategy_history(self): """ Update history with how many customers chose which strategy. """ customers = self.get_customers() randomstrat, closebystrat = 0, 0 for customer in customers: if customer.strategy == "Random" or customer.strategy == "Random_test_4": randomstrat += 1 elif customer.strategy == "Closest_by": closebystrat += 1 self.hist_random_strat.append(randomstrat) self.hist_close_strat.append(closebystrat) def get_customers(self): """ Returns a list of all the customers in the themepark. """ agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) customers = [] # Add customers to list for agent in agents: if type(agent) == Customer: customers.append(agent) return customers def get_data_customers(self): """ Return dictionary with data of customers. """ data = {} agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) for agent in agents: if type(agent) is Customer: data[agent.unique_id] = { "totalwaited": agent.total_ever_waited, "visited_attractions": agent.nmbr_attractions, "strategy": agent.strategy, "swapped_strat": agent.strategy_swap_hist } return data # TODO: NET ALS THEMEPARK SCORE GEBRUIKEN WE DIT NEIT MEER TOCH???? def calc_hapiness(self): """ Calculate mean hapiness of all customers, based on: - How many rides were taken - Number of times in the same attraction - Total waiting time """ customers = self.get_customers() scores = [] for customer in customers: history = customer.history values = list(history.values()) total_rides = sum(values) if total_rides != 0: scores.append(total_rides / self.N_attr - self.totalTOTAL / customer.total_ever_waited) else: return None scores = np.interp(scores, (min(scores), max(scores)), (1, 10)) return np.mean(scores) def get_history_list(self): """ Create a list with the history of the customers. """ customers = self.get_customers() histories = {} for customer in customers: history = customer.history values = list(history.values()) histories[customer.unique_id] = values return histories def final(self): """ End run and return data. """ # Create list with at every time step the amount of attractions that are active attractions = self.get_attractions() self.all_rides_list = [0] * len(attractions[0].in_attraction_list) for attraction in attractions: for i in range(len(attraction.in_attraction_list)): self.all_rides_list[i] += attraction.in_attraction_list[i] # Change the all_rides_list into fractions for i in range(len(self.all_rides_list)): self.all_rides_list[i] /= self.N_attr # Initialize history list and get agents hist_list = [] agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) # Save history of all customers cust_data = self.get_data_customers() for agent in agents: if type(agent) is Customer: sum_attr = sum(agent.history.values()) if sum_attr > 0: hist_list.append(agent.strategy_swap_hist) else: hist_list.append(agent.strategy_swap_hist) histories = self.get_history_list() # Save data try: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("../data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("../data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("../data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("../data/park_score.p", "wb")) pickle.dump(self.happinesses, open("../data/hapiness.p", "wb")) pickle.dump(histories, open("../data/cust_history.p", 'wb')) except: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("data/park_score.p", "wb")) pickle.dump(self.happinesses, open("data/hapiness.p", "wb")) pickle.dump(histories, open("data/cust_history.p", 'wb')) try: pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb")) except: pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb")) print() print("RUN HAS ENDED") print() def save_data(self): """ Save data of all attractions and customers. """ # Get info waitinglines = self.calc_waiting_time() for i in range(len(self.attractions)): self.data_dict[i]["waiting_list"].append(waitinglines.get(i)) self.park_score.append(sum(waitinglines.values())) self.happinesses.append(self.calc_hapiness()) def step(self): """ Advance the model by one step. """ # Take a step if max steps is not reached if self.totalTOTAL < self.max_time: self.totalTOTAL += 1 self.schedule.step() self.datacollector.collect(self) self.datacollector2.collect(self) self.schedule_Attraction.step() self.schedule_Customer.step() self.total_steps += 1 self.save_data() self.get_strategy_history() # Stop simulation else: for key in self.attraction_history.keys(): y = self.attraction_history[key] x = list(range(0, self.max_time)) self.final()
class EgyptSim(Model): """ Simulation Model for wealth distribution represented by grain in ancient Egypt """ # Variable declarations for non python programmer sanity # Map variables height = 30 width = 30 # Simulation Variables timeSpan = 500 currentTime = 0 startingSettlements = 14 startingHouseholds = 7 startingHouseholdSize = 5 startingGrain = 3000 minAmbition = 0.1 minCompetency = 0.5 generationalVariation = 0.9 knowledgeRadius = 20 distanceCost = 10 fallowLimit = 4 popGrowthRate = 0.1 fission = False fissionChance = 0.7 rental = True rentalRate = 0.5 totalPopulation = startingSettlements * startingHouseholds * startingHouseholdSize totalGrain = startingGrain * startingHouseholds startingPopulation = totalPopulation projectedHistoricalPopulation = totalPopulation maxHouseholdGrain = startingGrain # Step variables mu = 0 sigma = 0 alpha = 0 beta = 0 # Visualisation description = "A model simulating wealth growth and distribution in Ancient Egypt.\n\nThe model allows one to see how variables such as the flooding of the Nile, human character traits and random chance effect the acquisition and distribution of wealth." # List of identifiers and colors for settlements SETDICT = {"s1": "#FF0000", "s2": "#FF4500", "s3": "#BC8F8F", "s4": "#00FF00", "s5": "#00FFFF", "s6": "#0000FF", "s7": "#FF00FF", "s8": "#FF1493", "s9": "#708090", "s10": "#DC143C", "s11": "#FF8C00", "s12": "#FF69B4", "s13": "#800000", "s14": "#7CFC00", "s15": "#008B8B", "s16": "#483D8B", "s17": "#4B0082", "s18": "#FF69B4", "s19": "#000000", "s20": "#8B4513"} def __init__(self, height: int = 30, width: int = 30, timeSpan: int = 500, startingSettlements: int = 14, startingHouseholds: int = 7, startingHouseholdSize: int = 5, startingGrain: int = 3000, minAmbition: float = 0.1, minCompetency: float = 0.5, generationalVariation: float = 0.9, knowledgeRadius: int = 20, distanceCost: int = 10, fallowLimit: int = 4, popGrowthRate: float = 0.1, fission: bool = False, fissionChance: float = 0.7, rental: bool = True, rentalRate: float = 0.5): """ Create a new EgyptSim model Args: height: The height of the simulation grid width: The width of the simulation grid timeSpan: The number of years over which the model is to run startingSettlements: The starting number of Settlements startingHouseholds: The starting number of Households per Settlement startingHouseholdSize: The starting number of workers in a Household startingGrain: The starting amount of grain for each Household minAmbition: The minimum ambition value for a Household minCompetency: The minimum competency value for a Household generationalVariation: The difference between generations of a Household knowledgeRadius: How far outside ther Settlement a Household can "see" distanceCost: The cost to move grain per cell away from a settlemnt fallowLimit: The number of years a field can lay fallow before it is harvested popGrowthRate: The rate at which the population grows fission: If Household fission (Moving between settlements) is allowed fissionChance: The chance fission occuring rental: If land rental is allowed rentalRate: The rate at which households will rent land """ super().__init__() # Set Parameters # Map size self.height = height self.width = width # If the number of starting settlements is greater than the maximum reasonable number of households considering territory and farming area # Considers that each household needs at least two field to survive at a minimum number of members, a Settlment needs 9 (territory) + 2 * households # Tiles to survive if startingSettlements > ((width - 1) * height) // (9 + (startingHouseholds * 2)): if startingSettlements > 20: self.startingSettlements = 20 else: self.startingSettlements = ((height - 1) * width) // (9 + (startingHouseholds * 2)) print("Too many starting settlements to support the settlements and household, truncating to: ", self.startingSettlements) else: self.startingSettlements = startingSettlements # Simulation Variables self.timeSpan = timeSpan self.currentTime = 0 self.startingHouseholds = startingHouseholds self.startingHouseholdSize = startingHouseholdSize self.startingGrain = startingGrain self.minAmbition = minAmbition self.minCompetency = minCompetency self.generationalVariation = generationalVariation self.knowledgeRadius = knowledgeRadius self.distanceCost = distanceCost self.fallowLimit = fallowLimit self.popGrowthRate = popGrowthRate self.fission = fission self.fissionChance = fissionChance self.rental = rental self.rentalRate = rentalRate self.totalGrain = startingGrain * startingHouseholds * startingSettlements self.totalPopulation = startingSettlements * startingHouseholds * startingHouseholdSize self.startingPopulation = self.totalPopulation self.projectedHistoricalPopulation = self.startingPopulation self.maxHouseholdGrain = startingGrain # Scheduler and Grid self.schedule = EgyptSchedule(self) self.grid = MultiGrid(height = self.height, width = self.width, torus=False) # Define specific tables for data collection purposes setlist = [] for i in range(self.startingSettlements): setlist.append("s" + str(i + 1) + "_Population") tables = {"Settlement Population": setlist} # Data collection self.datacollector = DataCollector(model_reporters = {"Households": lambda m: m.schedule.get_breed_count(Household), "Settlements": lambda m: m.schedule.get_breed_count(Settlement), "Total Grain": lambda m: m.totalGrain, "Total Population": lambda m: m.totalPopulation, "Projected Hisorical Poulation (0.1% Growth)": lambda m: m.projectedHistoricalPopulation, "Gini-Index": gini, "Maximum Settlement Population": maxSetPop, "Minimum Settlement Population": minSetPop, "Mean Settlement Poulation" : meanSetPop, "Maximum Household Wealth": maxHWealth, "Minimum Household Wealth": minHWealth, "Mean Household Wealth" : meanHWealth, "Number of households with < 33% of wealthiest grain holding": lowerThirdGrainHoldings, "Number of households with 33 - 66% of wealthiest grain holding": middleThirdGrainHoldings, "Number of households with > 66% of wealthiest grain holding": upperThirdGrainHoldings }, tables = tables) self.setup() self.running = True self.collectTableData() self.datacollector.collect(self) def collectTableData(self): setPops = {} for s in self.schedule.get_breed(Settlement): setPops[s.unique_id + "_Population"] = s.population self.datacollector.add_table_row("Settlement Population", setPops, True) def setupMapBase(self): """ Create the grid as field and river """ for agent, x, y in self.grid.coord_iter(): # If on left edge, make a river if x == 0: uid = "r" + str(x) + "|" + str(y) river = River(uid, self, (x, y)) self.grid.place_agent(river, (x, y)) # Otherwise make a field else: uid = "f" + str(x) + "|" + str(y) field = Field(uid, self, (x, y), 0.0) self.grid.place_agent(field, (x, y)) self.schedule.add(field) def setupSettlementsHouseholds(self): """ Add settlements and households to the simulation """ h = 1 for i in range(self.startingSettlements): # Loop untill a suitable location is found while True: x = self.random.randrange(1, self.width) y = self.random.randrange(self.height) flag = False cell = self.grid.get_cell_list_contents((x, y)) # Check that tile is available for agent in cell: if agent.settlementTerritory: break else: flag = True break if flag: break # Add settlement to the grid population = self.startingHouseholds * self.startingHouseholdSize uid = "s" + str(i + 1) # Use a custom id for the datacollector settlement = Settlement(uid, self, (x, y), population, self.startingHouseholds, uid, self.SETDICT[uid]) self.grid.place_agent(settlement, (x, y)) # Set the surrounding fields as territory local = self.grid.get_neighbors((x, y), moore=True, include_center=True, radius=1) for a in local: a.settlementTerritory = True # Add households for the settlement to the scheduler for j in range(self.startingHouseholds): huid = "h" + str(h) # Use a custom id for the datacollector ambition = np.random.uniform(self.minAmbition, 1) competency = np.random.uniform(self.minCompetency, 1) genCount = self.random.randrange(5) + 10 household = Household(huid, self, settlement, (x, y), self.startingGrain, self.startingHouseholdSize, ambition, competency, genCount) # ! Dont add household to grid, is redundant self.schedule.add(household) h += 1 # Add settlement to the scheduler self.schedule.add(settlement) def setup(self): """ Setup model parameters """ self.setupMapBase() self.setupSettlementsHouseholds() def step(self): self.currentTime += 1 self.maxHouseholdGrain = 0 self.setupFlood() self.schedule.step() self.projectedHistoricalPopulation = round(self.startingPopulation * ((1.001) ** self.currentTime)) self.datacollector.collect(self) # Add settlement data to table self.collectTableData() # Cease running once time limit is reached or everyone is dead if self.currentTime >= self.timeSpan or self.totalPopulation == 0: self.running = False def setupFlood(self): """ Sets up common variables used for the flood method in Fields """ self.mu = random.randint(0, 10) + 5 self.sigma = random.randint(0, 5) + 5 self.alpha = (2 * self.sigma ** 2) self.beta = 1 / (self.sigma * math.sqrt(2 * math.pi))
class World(Model): def __init__(self, width, height): super().__init__() self.height = height self.width = width self.n_ants = int(125) self.diffusion_rate = 50 self.evaporation_rate = 10 # Para dar identificador único a los agentes self.unique_id = 0 # Creación del planificador y del grid self.schedule = RandomActivation(self) self.grid = MultiGrid(self.width, self.height, torus=False) # Inicialización de patches self.setup_patches() # Inicialización de hormigas self.setup_ants() # Ejecución en navegador despues de crear self.running = True # Inicialización de hormigas def setup_ants(self): for i in range(self.n_ants): # Colocar hormigas aleatorias # x = random.randint(0, self.width - 1) # y = random.randint(0, self.height - 1) # pos = (x, y) # Colocar hormigas en el centro pos = (int(self.width / 2) - 1, int(self.height / 2) - 1) ant = Ant(self.unique_id, self, pos) self.unique_id += 1 self.grid.place_agent(ant, pos) self.schedule.add(ant) # Inicialización de patches def setup_patches(self): """Inicialización del mundo""" # Colocar 1 patch en cada punto del grid for agent, x, y in self.grid.coord_iter(): patch = Patch(self.unique_id, self, (x, y)) self.unique_id += 1 self.grid.place_agent(patch, (x, y)) # Quizás no es necesario añadir al schedule self.schedule.add(patch) # Colocar Nido self.setup_nest() # Colocar Comida self.setup_food() # Colorear patches self.setup_color() def setup_nest(self): """Creación del nido""" # Se pone nido en el centro y 5 cuadros alrededor center = (int(self.width / 2) - 1, int(self.height / 2) - 1) for agent in self.grid.get_neighbors(center, True, True, 5): if isinstance(agent, Patch): agent.is_nest = True # Distancia al hormiguero for (agentSet, x, y) in self.grid.coord_iter(): for agent in agentSet: if isinstance(agent, Patch): agent.nest_scent = 200 - self.distance(center, (x, y)) @staticmethod def distance(pos1, pos2): return math.hypot(pos1[0] - pos2[0], pos1[1] - pos2[1]) def setup_food(self): """Creación de la comida""" center = (int(self.width / 2) - 1, int(self.height / 2) - 1) center_food1 = (int(center[0] + 0.6 * center[0]), int(center[1])) center_food2 = (int(center[0] - 0.6 * center[0]), int(center[1] - 0.6 * center[1])) center_food3 = (int(center[0] - 0.8 * center[0]), int(center[1] + 0.8 * center[1])) for agent in self.grid.get_neighbors(center_food1, True, True, 5): if isinstance(agent, Patch): agent.food_source_number = 1 agent.food = random.randint(1, 2) for agent in self.grid.get_neighbors(center_food2, True, True, 5): if isinstance(agent, Patch): agent.food_source_number = 2 agent.food = random.randint(1, 2) for agent in self.grid.get_neighbors(center_food3, True, True, 5): if isinstance(agent, Patch): agent.food_source_number = 3 agent.food = random.randint(1, 2) def setup_color(self): for (agentSet, x, y) in self.grid.coord_iter(): for agent in agentSet: if isinstance(agent, Patch): agent.set_color() def step(self): self.schedule.step()
class EmasModel(Model): description = ( "A model for simulating using EMAS model" ) def __init__( self, height=HEIGHT, width=WIDTH, columns=2, rows=2, death_level=0, migration_level=10, init_energy=100, moore=True, reproduction_level=7, parent_part_to_child=30, base_child_energy=5, energy_redistribution_radius=4, islands=[] ): super().__init__() self.height: int = height self.width: int = width self.columns: int = columns self.rows: int = rows self.death_level: float = death_level self.migration_level: float = migration_level self.moore: bool = moore self.reproduction_level: float = reproduction_level self.parent_part_to_child: float = parent_part_to_child self.base_child_energy: float = base_child_energy self.init_energy: float = init_energy self.energy_redistribution_radius: int = energy_redistribution_radius self.islands: List[Tuple[Tuple[int, int], Tuple[int, int]]] = islands self.grid = MultiGrid(self.height, self.width, torus=True) borders_x_indexes = sorted([int(self.width * part / columns) for part in range(1, self.columns)]) columns_points: Set[Tuple[int, int]] = {(x, y) for x in borders_x_indexes for y in range(self.height)} borders_y_indexes = sorted([int(self.height * part / rows) for part in range(1, self.rows)]) rows_points: Set[Tuple[int, int]] = {(x, y) for x in range(self.width) for y in borders_y_indexes} for border_cords in columns_points | rows_points: border = IslandBorderAgent(self.next_id(), border_cords, self) self.grid.place_agent(border, border_cords) islands_x_corners = [-1] + borders_x_indexes + [self.width] islands_y_corners = [-1] + borders_y_indexes + [self.height] # left lower and right upper corner self.islands = [ ((x, y), (islands_x_corners[x_u + 1], islands_y_corners[y_u + 1])) for x_u, x in enumerate(islands_x_corners) if x != self.width for y_u, y in enumerate(islands_y_corners) if y != self.height ] def get_neighborhood(self, pos: Coordinate, include_center=False, radius=1, moore=True): next_moves = self.grid.get_neighborhood(pos, moore, include_center, radius) island = self.get_island(pos) return self._filter_coors_in_island(island, next_moves) def redistribute_energy(self, pos: Coordinate, energy: float, include_center=False, radius=10): neighbours = self.grid.get_neighbors(pos, self.moore, include_center, radius) island = self.get_island(pos) close_neighbours = list(filter(lambda n: self._is_in_island(island, n.pos), neighbours)) emas_neighbours = list(filter(lambda a: isinstance(a, EmasAgent), close_neighbours)) if emas_neighbours: energy_delta = energy / len(emas_neighbours) for neighbour in emas_neighbours: neighbour.energy += energy_delta def get_island(self, pos: Coordinate): return list(filter(lambda coors: coors[0][0] < pos[0] < coors[1][0] and coors[0][1] < pos[1] < coors[1][1], self.islands)).pop() def _filter_coors_in_island(self, island: Tuple[Tuple[int, int], Tuple[int, int]], positions: List[Tuple[int, int]]): return list(filter(lambda move: island[0][0] < move[0] < island[1][0] and island[0][1] < move[1] < island[1][1], positions)) def _is_in_island(self, island, pos): return island[0][0] < pos[0] < island[1][0] and island[0][1] < pos[1] < island[1][1] def _filter_for_emas_agents(self, agents): return filter(lambda a: isinstance(a, EmasAgent), agents) def get_closest_neighbour_on_island(self, pos: Coordinate): island = self.get_island(pos) taxi_radius = abs(island[0][0] - island[1][0]) + abs(island[0][1] - island[1][1]) # moore=False -> find neighbourhood using taxi metric neighbours = self.grid.get_neighbors(pos, False, include_center=False, radius=taxi_radius) island_neighbours = [ agent for agent in neighbours if self._is_in_island(island, agent.pos) and isinstance(agent, EmasAgent) ] closest_neighbour = None closest_distance = taxi_radius for agent in island_neighbours: if self.taxi_distance(pos, agent.pos) < closest_distance: closest_distance = self.taxi_distance(pos, agent.pos) closest_neighbour = agent return closest_neighbour def taxi_distance(self, pos_a: Coordinate, pos_b: Coordinate): return abs(pos_a[0] - pos_b[0]) + abs(pos_a[1] - pos_b[1])
class AntModel(Model): def __init__(self, num_ln, num_fj, num_mk_col, num_ft_col, width, height): """ :param num_ln: Number of L. Niger agents :param num_fj: Number of F. Japonica agents :param num_mk_col: Number of M. Kuricola colonies :param num_ft_col: Number of F. Tropicalis colonies :param width: Width of the model grid :param height: Height of the model grid """ super().__init__() self.num_ln = num_ln self.num_fj = num_fj self.num_mk_col = num_mk_col self.num_ft_col = num_ft_col self.grid = MultiGrid(width, height, True) self.schedule = RandomActivation(self) self.running = True for h in range(self.num_fj): ant = FJaponica(uuid4(), self) self.schedule.add(ant) self.grid.place_agent(ant, self.grid.find_empty()) for j in range(self.num_mk_col): colony = MKuricolaColony(uuid4(), self) self.schedule.add(colony) self.grid.place_agent(colony, self.grid.find_empty()) for k in range(self.num_ft_col): colony = FTropicalisColony(uuid4(), self) self.schedule.add(colony) self.grid.place_agent(colony, self.grid.find_empty()) for i in range(self.num_ln): ant = LNiger(uuid4(), self) self.schedule.add(ant) self.grid.place_agent(ant, self.grid.find_empty()) ant._init_post_place() self.data_collector = DataCollector( model_reporters={}, agent_reporters={"states": ant_state_collector}) self.weights_dict = json.load(open("newout.json", "r")) def drop_pheromone(self, location): """ Drops a LNPheromone object at the given location if one does not already exist. If one does already exist, 1 is added to the existing object's 'tracks' field. :param location: An (x, y) tuple detailing the location to drop the pheromone. :return: None """ if not self.is_pheromone_in_cell(location): self.grid.place_agent(LNPheromone(uuid4(), self), location) else: self.get_pheromone_in_cell(location).tracks += 1 def is_pheromone_in_cell(self, location): """ Determines if a pheromone already exists in a given cell. :param location: The location to check. :return: boolean """ return True in [ type(x) == LNPheromone for x in self.grid.get_cell_list_contents(location) ] def is_ant_in_cell(self, location): """ Determines whether an ant exists in a given cell. :param location: The location to check. :return: boolean """ return True in [ isinstance(x, Ant) for x in self.grid.get_cell_list_contents(location) ] def is_colony_in_cell(self, location): """ Determines whether an aphid colony exists in a given cell. :param location: The location to check. :return: boolean """ return True in [ type(x) == MKuricolaColony or type(x) == FTropicalisColony for x in self.grid.get_cell_list_contents(location) ] def get_pheromone_in_cell(self, location): """ Returns a LNPheromone object from a cell. ASsumes the cell has already been proven to have a pheromone object in it. :param location: The cell location to check. :return: The LNPheromone object within the cell. """ in_cell_pheromone = None for i in self.grid.get_cell_list_contents(location): if type(i) == LNPheromone: in_cell_pheromone = i return in_cell_pheromone def get_closest_agent_of_type(self, agent, agent_type): """ Gets the closest agent (besides self) of type agent_type. Returns -1 if it cannot find one. :param agent: The agent to find the closest agent_type to. :param agent_type: The type of the agent we are looking for. :return: """ for radius in range(1, self.grid.width): for neighbor in self.grid.get_neighbors(pos=agent.pos, moore=True, include_center=False, radius=radius): if isinstance(neighbor, agent_type): return neighbor return -1 def get_closest_colony(self, agent): """ Gets the closest colony to an agent. If an agent is of type colony, it returns itself. :param agent: The agent to find the closest colony to. :return: The closest colony or -1 if not found. """ return self.get_closest_agent_of_type(agent, Colony) @staticmethod def distance_between_cells(location_a, location_b): """ Calculates the distance between two cells on the grid. :param location_a: First cell location. :param location_b: Second cell location. :return: """ return np.sqrt((location_a[0] - location_b[0])**2 + (location_a[1] - location_a[1])**2) def get_nearest_cell_to_goal(self, goal_cell, possible_cells): """ Returns the cell from a list of possible cells which is closest to the end location. :param goal_cell: The goal cell of the agent :param possible_cells: Candidate cells. :return: The location of the closest cell to the goal cell. """ closest_neighbor_index = -1 closest_neighbor_distance = np.inf for i in range(0, len(possible_cells)): dist = self.distance_between_cells(possible_cells[i], goal_cell) if dist < closest_neighbor_distance: closest_neighbor_index = i closest_neighbor_distance = dist return possible_cells[closest_neighbor_index] def get_number_of_agents_in_radius(self, location, radius, agent_type): """ Returns the number of agents of type agent_type within a radius (not including center) of location. :param location: Location to search around. :param radius: Radius to search. :param agent_type: Type of agent to search for. :return: int """ total_agents = 0 for neighbor in self.grid.get_neighbors(pos=location, moore=True, include_center=False, radius=radius): if isinstance(neighbor, agent_type): total_agents += 1 return total_agents def get_all_of_agent_type(self, agent_type): """ Returns all instances of agents of type agent_type in the Grid. :param agent_type: The type of agent to find. :return: A list of agent objects. """ return [ x for x in self.grid.get_neighbors(pos=(0, 0), moore=True, include_center=True, radius=self.grid.width) if isinstance(x, agent_type) ] def step(self): """ A method called every step that occurs :return: None """ self.data_collector.collect(self) self.schedule.step()
class Themepark(Model): def __init__(self, N_attr, N_cust, width, height, strategy, theme, max_time, weight, adaptive): self.theme = theme self.max_time = max_time self.N_attr = N_attr self.penalty_per = PENALTY_PERCENTAGE self.weight = weight self.adaptive = adaptive self.strategies = STRATEGIES self.x_list, self.y_list, self.positions = xlist, ylist, positions self.x_list, self.y_list, self.positions = get_attraction_coordinates(WIDTH, HEIGHT, self.N_attr, theme) self.happinesses = [] self.starting_positions = [[int((WIDTH/2)-1), 0], [int(WIDTH/2), 0], [int((WIDTH/2)+1), 0]] self.path_coordinates = get_coordinates(WIDTH, HEIGHT, NUM_OBSTACLES, self.N_attr, theme) self.N_attr = N_attr # num of attraction agents self.N_cust = N_cust # num of customer agents self.width = width self.height = height self.total_steps = 0 self.cust_ids = N_cust self.strategy = strategy self.grid = MultiGrid(width, height, torus=False) self.schedule = BaseScheduler(self) self.schedule_Attraction = BaseScheduler(self) self.schedule_Customer = BaseScheduler(self) self.totalTOTAL = 0 self.attractions = self.make_attractions() self.attraction_history = self.make_attr_hist() self.running = True self.data = [] self.data_customers = [] self.park_score = [] self.data_dict = {} self.hist_random_strat = [] self.hist_close_strat = [] self.all_rides_list = [] self.strategy_composition = self.make_strategy_composition() self.memory = 5 self.customer_score = [] self.customers = self.add_customers(self.N_cust) self.only_random = False for attraction in self.get_attractions(): self.data_dict[attraction.unique_id] = ({ "id": attraction.unique_id, "length": attraction.attraction_duration, "waiting_list": []}) if len(self.strategies) == 6: self.datacollector = DataCollector( {"Random": lambda m: self.strategy_counter(self.strategies[0]), "0.00": lambda m: self.strategy_counter(self.strategies[1]), "0.25": lambda m: self.strategy_counter(self.strategies[2]), "0.50": lambda m: self.strategy_counter(self.strategies[3]), "0.75": lambda m: self.strategy_counter(self.strategies[4]), "1.00": lambda m: self.strategy_counter(self.strategies[5]), }) else: self.datacollector = DataCollector( {"0.00": lambda m: self.strategy_counter(self.strategies[0]), "0.25": lambda m: self.strategy_counter(self.strategies[1]), "0.50": lambda m: self.strategy_counter(self.strategies[2]), "0.75": lambda m: self.strategy_counter(self.strategies[3]), "1.00": lambda m: self.strategy_counter(self.strategies[4]), }) self.datacollector2 = DataCollector( {"score": lambda m: self.make_score()}) self.total_waited_time = 0 self.monitor = Monitor(self.max_time, self.N_attr, self.positions) def make_score(self): ideal = {} cust_in_row = 0 for i in range(len(self.get_attractions())): ideal[i] = self.N_cust/self.N_attr cust_in_row += self.get_attractions()[i].N_current_cust tot_difference = 0 for i in range(len(self.get_attractions())): difference = abs(cust_in_row/self.N_attr - self.get_attractions()[i].N_current_cust) tot_difference += difference fraction_not_right = (tot_difference/self.N_cust) return abs(1-(fraction_not_right)) * cust_in_row/self.N_cust def make_attr_hist(self): attraction_history = {} for attraction in self.get_attractions(): attraction_history[attraction] = [0] * (self.max_time + 1) return attraction_history def strategy_counter(self, strategy): counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors( attraction_pos, moore=True, radius=0, include_center=True ) counter = 0 for agent in self.customers: if agent.weight == strategy: counter += 1 return counter def make_strategy_composition(self): if self.strategy == "Random_test_4": self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0] dict = {self.strategies[0]: 1/6, self.strategies[1]:0.20, self.strategies[2]:0.20, self.strategies[3]:0.20, self.strategies[4]:0.20, self.strategies[5]: 0.20} composition_list = [] for i in range(len(self.strategies)): if i == 0: dict[self.strategies[i]] = FRACTION_RANDOM continue else: composition_list.append(random.randint(0,100)) sum_comp = sum(composition_list) sum_comp = sum_comp - sum_comp * FRACTION_RANDOM for i in range(len(self.strategies)): if i == 0: continue else: dict[self.strategies[i]] = composition_list[i-1] /sum_comp else: dict = {self.strategies[0]: 0.20, self.strategies[1]:0.20, self.strategies[2]:0.20, self.strategies[3]:0.20, self.strategies[4]:0.20} composition_list = [] for i in range(len(self.strategies)): composition_list.append(random.randint(0,100)) sum_comp = sum(composition_list) sum_comp = sum_comp for i in range(len(self.strategies)): dict[self.strategies[i]] = composition_list[i-1] /sum_comp return dict def make_attractions(self): """ Initialize attractions on fixed position.""" attractions = {} for i in range(self.N_attr): pos = (self.x_list[i], self.y_list[i]) if self.grid.is_cell_empty(pos): name = str(i) a = Attraction(i, self, pos, name, self.N_cust, self.weight) attractions[i] = a self.schedule_Attraction.add(a) self.grid.place_agent(a, pos) return attractions def get_attractions(self): """ Get a list with all attractions """ agents = self.grid.get_neighbors( mid_point, moore=True, radius=RADIUS, include_center=True) attractions = [] for agent in agents: if type(agent) == Attraction: attractions.append(agent) return attractions def add_customers(self, N_cust, added=False): """ Initialize customers on random positions.""" weights_list = [] if self.adaptive is True: for j in self.strategy_composition.keys(): for i in range(round(N_cust*self.strategy_composition[j])): weights_list.append(j) if len(weights_list) < self.N_cust: rand = random.choice(self.strategies) weights_list.append(rand) elif len(weights_list) > self.N_cust: rand = random.choice(weights_list) weights_list.remove(rand) else: if self.strategy is not "Random": # do what normally is done for i in range(round(N_cust)): weights_list.append(self.weight) cust_list = [] # weight_counter = 0 # pick_weight = 0 for i in range(N_cust): # pos_temp = random.choice(self.starting_positions) pos_temp = [random.randint(0,WIDTH-1), random.randint(0,HEIGHT-1)] rand_x, rand_y = pos_temp[0], pos_temp[1] pos = (rand_x, rand_y) if added is True: i = self.cust_ids if self.strategy == "Random_test_4": if weights_list[i] == "Random_test_4": strategy = "Random_test_4" else: strategy = "Closest_by" else: strategy = self.strategy # Deze if is omdat bij alleen random self.weight none is! if weights_list == []: weight = None else: weight = weights_list[i] a = Customer(i, self, pos, self.x_list, self.y_list, self.positions, strategy, weight, self.adaptive) self.schedule_Customer.add(a) self.grid.place_agent(a, pos) cust_list.append(a) return cust_list def calculate_people(self): """Calculate how many customers are in which attraction.""" counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors( attraction_pos, moore=True, radius=0, include_center=True ) counter = 0 for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent attraction.N_current_cust = counter counter_total[attraction.unique_id] = counter return list(counter_total.values()) def calc_waiting_time(self): counter_total = {} attractions = self.get_attractions() for attraction in attractions: counter_total[attraction.unique_id] = attraction.current_waitingtime return counter_total def calculate_people_sorted(self): """ Calculate how many customers are in which attraction. Returns a SORTED LIST. For example: indexes = [3, 2, 5, 1, 4] indicates that attraction3 has the least people waiting. """ counter_total = {} for attraction_pos in self.positions: agents = self.grid.get_neighbors( attraction_pos, moore=True, radius=0, include_center=True ) counter = 0 for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent attraction.N_current_cust = counter self.attraction_history[attraction][self.totalTOTAL] = counter counter_total[attraction.unique_id] = counter return counter_total def make_route(self): """Draw coordinates of a possible path.""" for i in range(len(self.path_coordinates)): pos = self.path_coordinates[i] if pos not in self.positions: # Create path agent path = Route(i, self, pos) self.schedule.add(path) self.grid.place_agent(path, pos) def get_themepark_score(self): """ Get score of a themepark based on: - A total of all waitingtimes for every customer - TODO """ attractions = self.get_attractions() total_wait, total_rides = 0, 0 for attraction in attractions: total_wait += attraction.current_waitingtime if attraction.current_a is not None: total_rides += 1 if total_rides == 0: return total_rides return (total_wait / total_rides) def get_strategy_history(self): """ Update history with how many customers chose which strategy """ customers = self.get_customers() randomstrat, closebystrat = 0, 0 for customer in customers: if customer.strategy == "Random" or customer.strategy == "Random_test_4": randomstrat += 1 elif customer.strategy == "Closest_by": closebystrat += 1 self.hist_random_strat.append(randomstrat) self.hist_close_strat.append(closebystrat) def get_customers(self): agents = self.grid.get_neighbors( mid_point, moore=True, radius=RADIUS, include_center=True) customers = [] # Count customer agents for agent in agents: if type(agent) == Customer: customers.append(agent) return customers def get_data_customers(self): """ Return dictionary with data of customers """ data = {} agents = self.grid.get_neighbors( mid_point, moore=True, radius=RADIUS, include_center=True) for agent in agents: if type(agent) is Customer: data[agent.unique_id] = { "totalwaited": agent.total_ever_waited, "visited_attractions": agent.nmbr_attractions, "strategy": agent.strategy, "swapped_strat": agent.strategy_swap_hist } return data def calc_hapiness(self): """ Calculate mean hapiness of all customers, based on: - How many rides were taken - Number of times in the same attraction - Total waiting time """ customers = self.get_customers() scores = [] for customer in customers: history = customer.history values = list(history.values()) total_rides = sum(values) if total_rides != 0: scores.append(total_rides / self.N_attr - self.totalTOTAL / customer.total_ever_waited) else: return None scores = np.interp(scores, (min(scores), max(scores)), (1, 10)) return np.mean(scores) def get_history_list(self): customers = self.get_customers() histories = {} for customer in customers: history = customer.history values = list(history.values()) histories[customer.unique_id] = values return histories def final(self): """ Return data """ hist_list = [] agents = self.grid.get_neighbors( mid_point, moore=True, radius=RADIUS, include_center=True) attractions = self.get_attractions() self.all_rides_list = [0] * len(attractions[0].in_attraction_list) for attraction in attractions: for i in range(len(attraction.in_attraction_list)): self.all_rides_list[i] += attraction.in_attraction_list[i] for i in range(len(self.all_rides_list)): self.all_rides_list[i] /= self.N_attr print("ALL RIDES LIST", self.all_rides_list) cust_data = self.get_data_customers() for agent in agents: if type(agent) is Customer: sum_attr = sum(agent.history.values()) if sum_attr > 0: hist_list.append(agent.strategy_swap_hist) else: hist_list.append(agent.strategy_swap_hist) # print("swap:",agent.strategy_swap_hist , "sum:",sum_attr) # plt.hist(hist_list) # plt.show() histories = self.get_history_list() # save data try: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("../data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("../data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("../data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("../data/park_score.p", "wb")) pickle.dump(self.happinesses, open("../data/hapiness.p", "wb")) pickle.dump(histories, open("../data/cust_history.p", 'wb')) except: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("data/park_score.p", "wb")) pickle.dump(self.happinesses, open("data/hapiness.p", "wb")) pickle.dump(histories, open("data/cust_history.p", 'wb')) try: pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb")) except: pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb")) print() print("RUN HAS ENDED") print() def save_data(self): """Save data of all attractions and customers.""" # Get info waitinglines = self.calc_waiting_time() for i in range(len(self.attractions)): self.data_dict[i]["waiting_list"].append(waitinglines.get(i)) self.park_score.append(sum(waitinglines.values())) self.happinesses.append(self.calc_hapiness()) def step(self): """Advance the model by one step.""" if self.totalTOTAL < self.max_time: self.totalTOTAL += 1 self.schedule.step() self.datacollector.collect(self) self.datacollector2.collect(self) self.schedule_Attraction.step() self.schedule_Customer.step() # update memory of attractions # attractions = self.get_attractions() # for attraction in attractions: # attraction.update_memory() self.total_steps += 1 self.save_data() self.get_strategy_history() else: for key in self.attraction_history.keys(): y = self.attraction_history[key] x = list(range(0, self.max_time)) self.final()
class Themepark(Model): def __init__(self, N_attr, N_cust, width, height, strategy, theme, max_time, weight, adaptive): """ Args: N_attr (int): the amount of attractions in the theme park N_cust (int): the amout of customers in the theme park width (int): the width of the theme park grid height (int): the height of the theme park grid strategy (str): the strategy of this run (random agents, adaptive agents or adaptive agents with noise) theme (str): the setup of the park (circle, random or clustered) max_time (int): the number of time steps the park will do in one run weight (float): if the customer agents are non-adaptive, this is the strategy they are using adaptive (bool): whether customer agents are able to switch strategies """ self.theme = theme self.max_time = max_time self.N_attr = N_attr self.penalty_per = PENALTY_PERCENTAGE self.weight = weight self.adaptive = adaptive self.strategies = STRATEGIES self.happinesses = [] self.N_attr = N_attr self.N_cust = N_cust self.width = width self.height = height self.total_steps = 0 self.cust_ids = N_cust self.strategy = strategy self.grid = MultiGrid(width, height, torus=False) self.schedule = BaseScheduler(self) self.schedule_Attraction = BaseScheduler(self) self.schedule_Customer = BaseScheduler(self) # Cluster or circle coordinates if self.theme == "cluster": self.x_list, self.y_list, self.positions = xlist, ylist, positions elif self.theme == "circle": self.x_list, self.y_list, self.positions = get_attraction_coordinates( width, height, N_attr, "circle") # keeps up the current time self.totalTOTAL = 0 # add attractions to park self.attractions = self.make_attractions() self.attraction_history = self.make_attr_hist() self.running = True self.data_dict = {} # keep up a history of the strategies self.hist_random_strat = [] self.hist_close_strat = [] # keep up a history of the occupation of the rides self.all_rides_list = [] # distribution of strategies throughout population self.strategy_composition = self.make_strategy_composition() # add customers to park self.customers = self.add_customers(self.N_cust) # keep up park score throughout time self.park_score = [] for attraction in self.get_attractions(): self.data_dict[attraction.unique_id] = ({ "id": attraction.unique_id, "length": attraction.attraction_duration, "waiting_list": [] }) # datacollector for the visualisation of the data if self.strategy == "Random_test_4": self.datacollector = DataCollector({ "Random": lambda m: self.strategy_counter(self.strategies[0]), "0.00": lambda m: self.strategy_counter(self.strategies[1]), "0.25": lambda m: self.strategy_counter(self.strategies[2]), "0.50": lambda m: self.strategy_counter(self.strategies[3]), "0.75": lambda m: self.strategy_counter(self.strategies[4]), "1.00": lambda m: self.strategy_counter(self.strategies[5]), }) elif self.strategy == "Random": self.datacollector = DataCollector( {"Random": lambda m: self.N_cust}) else: self.datacollector = DataCollector({ "0.00": lambda m: self.strategy_counter(self.strategies[0]), "0.25": lambda m: self.strategy_counter(self.strategies[1]), "0.50": lambda m: self.strategy_counter(self.strategies[2]), "0.75": lambda m: self.strategy_counter(self.strategies[3]), "1.00": lambda m: self.strategy_counter(self.strategies[4]), }) self.datacollector2 = DataCollector( {"score": lambda m: self.make_score()}) def make_score(self): """ Calculates and returns an efficiency score for the enire theme park. """ # calculates the ideal distribution of customers over the attractions per attraction ideal = {} cust_in_row = 0 for i in range(len(self.get_attractions())): ideal[i] = self.N_cust / self.N_attr cust_in_row += self.get_attractions()[i].N_current_cust # calculates the difference between the ideal and the real situation tot_difference = 0 for i in range(len(self.get_attractions())): difference = abs(cust_in_row / self.N_attr - self.get_attractions()[i].N_current_cust) tot_difference += difference # the fraction of customers that is not optimally distributed fraction_not_right = (tot_difference / self.N_cust) # add a penalty by multiplying by the fraction of people currently in a line score = abs(1 - (fraction_not_right)) * cust_in_row / self.N_cust return score def make_attr_hist(self): """ Make history of attractions that are visited for each customer. """ attraction_history = {} for attraction in self.get_attractions(): attraction_history[attraction] = [0] * (self.max_time + 1) return attraction_history def strategy_counter(self, strategy): """ Input is a strategy, for example "0.25" or "Random", output is the number of strategies are adopted at a current time step. """ for attraction_pos in self.positions: counter = 0 for agent in self.customers: if agent.weight == strategy: counter += 1 return counter def make_strategy_composition(self): """ Make a composition of all strategies over the entire theme park. Returns a dictionary with strategies as keys and the fraction of people using this strategy as value. """ # if this is a run with adaptive agents and noise if self.strategy == "Random_test_4": self.strategies = ["Random_test_4", 0.0, 0.25, 0.50, 0.75, 1.0] # make dictionary with fractions (values can be ignored!) dict = { self.strategies[0]: 0, self.strategies[1]: 0.20, self.strategies[2]: 0.20, self.strategies[3]: 0.20, self.strategies[4]: 0.20, self.strategies[5]: 0.20 } composition_list = [] for i in range(len(self.strategies)): if i == 0: dict[self.strategies[i]] = FRACTION_RANDOM continue else: # choose a random number composition_list.append(random.randint(0, 100)) sum_comp = sum(composition_list) sum_comp = sum_comp - sum_comp * FRACTION_RANDOM for i in range(len(self.strategies)): if i == 0: continue else: # determine the fraction of customer agents with this strategy dict[self.strategies[i]] = composition_list[i - 1] / sum_comp # runs without noise else: # make dictionary with fractions (values can be ignored!) dict = { self.strategies[0]: 0.20, self.strategies[1]: 0.20, self.strategies[2]: 0.20, self.strategies[3]: 0.20, self.strategies[4]: 0.20 } composition_list = [] for i in range(len(self.strategies)): # choose a random number composition_list.append(random.randint(0, 100)) sum_comp = sum(composition_list) for i in range(len(self.strategies)): # determine the fraction of agents with this strategy dict[self.strategies[i]] = composition_list[i - 1] / sum_comp return dict def make_attractions(self): """ Initialize attractions on fixed positions, defined in the global variables x_list and y_list. Returns a dictionary of attractions """ attractions = {} for i in range(self.N_attr): pos = (self.x_list[i], self.y_list[i]) # place attraction if grid cell is empty if self.grid.is_cell_empty(pos): name = str(i) a = Attraction(i, self, pos, name) attractions[i] = a self.schedule_Attraction.add(a) self.grid.place_agent(a, pos) return attractions def get_attractions(self): """ Return a list with all attractions. """ agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) attractions = [] for agent in agents: if type(agent) == Attraction: attractions.append(agent) return attractions def add_customers(self, N_cust, added=False): """ Initialize customers on random positions. Returns a list of all customers """ # a list of weights of which the indices correspond to the id of the agent # to who this weight is given weights_list = [] # Adaptive strategy if self.adaptive is True: # Decide weights for every customer for j in self.strategy_composition.keys(): for i in range(round(N_cust * self.strategy_composition[j])): weights_list.append(j) # Add weights for the random strategy if len(weights_list) < self.N_cust: rand = random.choice(self.strategies) weights_list.append(rand) elif len(weights_list) > self.N_cust: rand = random.choice(weights_list) weights_list.remove(rand) else: if self.strategy is not "Random": for i in range(round(N_cust)): print(self.weight) weights_list.append(self.weight) cust_list = [] for i in range(N_cust): # Get random location within grid height and width pos_temp = [ random.randint(0, WIDTH - 1), random.randint(0, HEIGHT - 1) ] rand_x, rand_y = pos_temp[0], pos_temp[1] pos = (rand_x, rand_y) if added is True: i = self.cust_ids if self.strategy == "Random_test_4": if weights_list[i] == "Random_test_4": strategy = "Random_test_4" else: strategy = "Closest_by" else: strategy = self.strategy if weights_list == []: weight = None else: weight = weights_list[i] # make customer a = Customer(i, self, pos, strategy, weight, self.adaptive) self.schedule_Customer.add(a) self.grid.place_agent(a, pos) cust_list.append(a) return cust_list def calculate_people(self): """ Calculate how many customers are in which attraction. Returns a list of which the indices correspond to the ids of the atttraction """ counter_total = {} # loop through attractions for attraction_pos in self.positions: agents = self.grid.get_neighbors(attraction_pos, moore=True, radius=0, include_center=True) counter = 0 # find customers in this attraction for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent # update the amount of customers in this attraction attraction.N_current_cust = counter counter_total[attraction.unique_id] = counter return list(counter_total.values()) def calc_waiting_time(self): """ Return a dictionary of watingtimes for every attraction """ counter_total = {} attractions = self.get_attractions() for attraction in attractions: counter_total[ attraction.unique_id] = attraction.current_waitingtime return counter_total def calculate_people_sorted(self): """ Calculate how many customers are in which attraction. Returns a dictionary with attraction-ids as keys and customers in line as values. """ counter_total = {} # loop through attractions for attraction_pos in self.positions: agents = self.grid.get_neighbors(attraction_pos, moore=True, radius=0, include_center=True) counter = 0 # find customers in this attraction for agent in agents: if type(agent) is Customer: counter += 1 else: attraction = agent # update the amount of customers in this attraction attraction.N_current_cust = counter self.attraction_history[attraction][self.totalTOTAL] = counter counter_total[attraction.unique_id] = counter return counter_total def get_strategy_history(self): """ Update history with how many customers chose which strategy """ customers = self.get_customers() randomstrat, closebystrat = 0, 0 for customer in customers: if customer.strategy == "Random" or customer.strategy == "Random_test_4": randomstrat += 1 elif customer.strategy == "Closest_by": closebystrat += 1 self.hist_random_strat.append(randomstrat) self.hist_close_strat.append(closebystrat) def get_customers(self): """Return a list of all customers in the theme park.""" agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) customers = [] # Count customer agents for agent in agents: if type(agent) == Customer: customers.append(agent) return customers def get_data_customers(self): """ Return dictionary with data of customers """ data = {} agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) for agent in agents: if type(agent) is Customer: data[agent.unique_id] = { "totalwaited": agent.total_ever_waited, "visited_attractions": agent.nmbr_attractions, "strategy": agent.strategy, "swapped_strat": agent.strategy_swap_hist } return data def calc_hapiness(self): """ Calculate mean hapiness of all customers, based on: - How many rides were taken - Number of times in the same attraction - Total waiting time Returns the mean happiness of all customers """ customers = self.get_customers() scores = [] for customer in customers: history = customer.history values = list(history.values()) total_rides = sum(values) if total_rides != 0: scores.append(total_rides / self.N_attr - self.totalTOTAL / customer.total_ever_waited) else: return None scores = np.interp(scores, (min(scores), max(scores)), (1, 10)) return np.mean(scores) def get_history_list(self): """ Return a dictionary of the history of visited attraction for each customer. """ customers = self.get_customers() histories = {} for customer in customers: history = customer.history values = list(history.values()) histories[customer.unique_id] = values return histories def final(self): """ Return and save data at the end of the run.""" hist_list = [] agents = self.grid.get_neighbors(mid_point, moore=True, radius=RADIUS, include_center=True) attractions = self.get_attractions() # Make a list for the fraction of occupied rides at all time steps self.all_rides_list = [0] * len(attractions[0].in_attraction_list) for attraction in attractions: for i in range(len(attraction.in_attraction_list)): self.all_rides_list[i] += attraction.in_attraction_list[i] for i in range(len(self.all_rides_list)): self.all_rides_list[i] /= self.N_attr cust_data = self.get_data_customers() for agent in agents: if type(agent) is Customer: sum_attr = sum(agent.history.values()) if sum_attr > 0: hist_list.append(agent.strategy_swap_hist) else: hist_list.append(agent.strategy_swap_hist) histories = self.get_history_list() # save data try: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("../data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("../data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("../data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("../data/park_score.p", "wb")) pickle.dump(self.happinesses, open("../data/hapiness.p", "wb")) pickle.dump(histories, open("../data/cust_history.p", 'wb')) pickle.dump(self.all_rides_list, open("../data/all_rides.p", "wb")) except: pickle.dump(self.datacollector.get_model_vars_dataframe(), open("data/strategy_history.p", 'wb')) pickle.dump(self.datacollector2.get_model_vars_dataframe(), open("data/eff_score_history.p", 'wb')) pickle.dump(cust_data, open("data/customers.p", 'wb')) pickle.dump(self.park_score[-1], open("data/park_score.p", "wb")) pickle.dump(self.happinesses, open("data/hapiness.p", "wb")) pickle.dump(histories, open("data/cust_history.p", 'wb')) pickle.dump(self.all_rides_list, open("data/all_rides.p", "wb")) print() print("RUN HAS ENDED") print() def save_data(self): """Save data of all attractions and customers.""" # Get info waitinglines = self.calc_waiting_time() for i in range(len(self.attractions)): self.data_dict[i]["waiting_list"].append(waitinglines.get(i)) self.park_score.append(sum(waitinglines.values())) self.happinesses.append(self.calc_hapiness()) def step(self): """Advance the model by one step.""" if self.totalTOTAL < self.max_time: self.totalTOTAL += 1 self.schedule.step() # Collect data for every step self.datacollector.collect(self) self.datacollector2.collect(self) # Step for both attraction and customer self.schedule_Attraction.step() self.schedule_Customer.step() self.total_steps += 1 # Save data and update strategy history self.save_data() self.get_strategy_history() else: for key in self.attraction_history.keys(): y = self.attraction_history[key] x = list(range(0, self.max_time)) self.final()
class Trade(Model): verbose = False # Print-monitoring os.chdir(os.path.dirname(__file__)) fpath = os.getcwd() + '/parameters.csv' reader = csv.reader(open(fpath, 'r')) d = dict() for key, value in reader: d[key] = float(value) height = int(d['height']) width = int(d['width']) ini_buyers = int(d['ini_buyers']) ini_sellers = int(d['ini_sellers']) def __init__(self, height=height, width=width, ini_buyers=ini_buyers, ini_sellers=ini_sellers): '''Parameters''' reader = csv.reader(open(self.fpath, 'r')) d = dict() for key, value in reader: d[key] = float(value) self.height = int(d['height']) self.width = int(d['width']) self.ini_buyers = int(d['ini_buyers']) self.ini_sellers = int(d['ini_sellers']) self.ini_cash = d['ini_cash'] self.num_w = int(d['num_w']) self.trust_w = d['trust_w'] self.costs = d['costs'] * ini_buyers self.mktresearch = d['mktresearch'] self.priceRange = d['priceRange'] self.csa = d['csa'] self.csa_length = int(d['csa_length']) self.network = d['network'] self.lb = d['lb'] # Lower bound self.ub = d['ub'] # Upper bound (in effect, unbounded) self.up = d['up'] # Up rate self.down = d['down'] # Down rate ''' Entry mode 0: No entry 1: Full market research 2: Whenever Avg cash balance > entryThreshhold with a random position 3: Whenever Max cash balance > entryThreshhold enter nearby that position ''' self.entry = int(d['entry']) self.entryFrequency = int(d['entryFrequency']) self.entryThreshhold = d['entryThreshhold'] * self.ini_cash self.entryRadius = int(d['entryRadius']) # Area within high earner that a new seller will plop down '''Debugging''' self.sellerDebug = d['sellerDebug'] self.buyerDebug = d['buyerDebug'] self.networkDebug = d['networkDebug'] self.utilweightDebug = d['utilweightDebug'] self.entryDebug = d['entryDebug'] self.schedule = RandomActivationByType(self) self.grid = MultiGrid(self.height, self.width, torus=True) self.datacollector = DataCollector( {"Sellers": lambda m: m.schedule.get_type_count(Seller), "Buyers": lambda m: m.schedule.get_type_count(Buyer)}) '''Initialization''' self.cnt = 0 # Period counter self.buyers = {} # Dictionary of buyer instances self.sellers = {} # Dictionary of seller instances self.sid_alive = [] self.pi = [0] * (height * width) # Profitability prices = {} for i in range(ini_sellers): prices[i] = self.priceRange * np.random.rand() + 1 min_price = min(prices.values()) for i in range(self.num_w): prices[i] = min_price * 0.9 self.prices = prices e = {} # Embeddedness for i in range(ini_sellers): e[i] = 0.8*np.random.rand() + 0.2 # 0.2 - 1.0 for i in range(self.num_w): e[i] = 0 self.e = e '''Create buyers''' for i in range(self.ini_buyers): # It seems coincidence in the same cell is allowed x = np.random.randint(self.width) y = np.random.randint(self.height) α = d['alpha'] trust = {} β = d['beta']*np.random.rand() for j in range(ini_sellers): trust[j] = np.random.rand() for j in range(self.num_w): trust[j] = self.trust_w γ = d['gamma'] ''' Network ties ties[j]=0 means 'no connection with bid=j buyer' ties[own bid] = 0 or 1 means nothing. ''' ties = dict(zip(range(ini_buyers),[0]*ini_buyers)) buyer = Buyer(i, self.grid, (x, y), True, α, trust, β, γ, ties) self.buyers[i] = buyer # Dictionary key is an integer self.grid.place_agent(buyer, (x, y)) self.schedule.add(buyer) '''Create sellers''' for i in range(self.ini_sellers): x = np.random.randint(self.width) y = np.random.randint(self.height) cash = self.ini_cash costs = self.costs price = self.prices[i] w = False if i < self.num_w: w = True e = self.e[i] seller = Seller(i, self.grid, (x, y), True, cash, costs, price, w, e) self.sellers[i] = seller self.grid.place_agent(seller, (x, y)) self.schedule.add(seller) self.running = True def step(self): '''Initialization''' self.cnt += 1 self.sid_alive = [] # Excluding Wal-Mart for sid, seller in self.sellers.items(): if seller.csa == False: '''Adjacent sales''' seller.sales = 0 '''Customer list''' seller.customers[self.cnt] = [] else: seller.customers[self.cnt] = seller.customers[self.cnt - 1] '''A list of living sellers (excluding Wal-Mart)''' if (seller.alive and seller.w == False): self.sid_alive.append(sid) # For entry if self.entry == 1: # Initialize the profitability vector self.pi = [0] * (self.height * self.width) elif self.entry == 2: # Calculate the average cash balance (scalar) total_cash = 0 cnt_seller = 0 total_cash = sum([self.sellers[sid].cash for sid in self.sid_alive]) self.avg_cash = total_cash / len(self.sid_alive) elif self.entry == 3: # Calculate the max cash balance (scalar) temp_sids = self.sid_alive cash_bals = [self.sellers[sid].cash for sid in temp_sids] new_sellers = True # Loops over maximal sellers until it finds one with no new firms nearby while(new_sellers): max_cash = max(cash_bals) if(max_cash < self.entryThreshhold): break max_cash_ind = cash_bals.index(max_cash) max_sid = temp_sids[max_cash_ind] max_x = self.sellers[max_sid].pos[0] max_y = self.sellers[max_sid].pos[1] if(self.entryDebug): print("Max Cash, sid:", max_sid, ", Cell:(" + str(max_x) + ", " + str(max_y) + ")") print("-Neighbor Ages:", end=" ") new_sellers = False # Check the age of all firms nearby the max cash balance firm # (wants to avoid new firms) for neighbor in self.grid.get_neighbors((max_x, max_y),True,True,self.entryRadius): if(isinstance(neighbor, Seller) and self.entryDebug): print(str(neighbor.age), end=" ") if(isinstance(neighbor, Seller) and neighbor.age < 52): new_sellers = True if(new_sellers): if(self.entryDebug): print("\n-New Firm Exists Near sid: ", max_sid, ", Cell:(" + str(max_x) + ", " + str(max_y) + ")") del temp_sids[max_cash_ind] del cash_bals[max_cash_ind] ''' Entry Entry=1 Determine the most profitable position and whether to enter Threshold: the fixed costs Entry=2 Enter whenever Avg cash balance > entryThreshhold Entry=3 Checks that no new firms are near the max balance seller Enters within entryRadius units of the seller with max cash balance ''' entry_on = False if (self.entry == 1 and self.mktresearch): opt = max(self.pi) opt_pos = self.pi.index(opt) if opt >= self.costs: x = opt_pos // self.width y = opt_pos % self.width entry_on = True elif (self.entry == 2 and self.avg_cash > self.entryThreshhold): x = np.random.randint(self.width) y = np.random.randint(self.height) entry_on = True elif (self.entry == 3 and max_cash > self.entryThreshhold and not new_sellers): x = max_x + np.random.randint(-self.entryRadius,self.entryRadius) y = max_y + np.random.randint(-self.entryRadius,self.entryRadius) x = x % self.width y = y % self.height entry_on = True if entry_on: cash = self.ini_cash costs = self.costs w = False price = np.mean([self.sellers[sid].price for sid in self.sid_alive]) # e = np.random.choice([self.sellers[sid].e for sid in self.sid_alive]) e = np.random.rand() sid = max([seller.sid for seller in self.sellers.values()]) + 1 self.sid_alive.append(sid) seller = Seller(sid, self.grid, (x, y), True, cash, costs, price, w, e) self.sellers[sid] = seller self.sellers[sid].customers[self.cnt] = [] for buyer in self.buyers.values(): buyer.trust[sid] = self.lb self.grid.place_agent(seller, (x, y)) self.schedule.add(seller) self.prices[sid] = price if (self.entry >= 1 and self.entryDebug): entry_NewFirm(sid, x, y) self.mktresearch = False '''Move''' self.schedule.step() self.datacollector.collect(self) if self.verbose: print([self.schedule.time, self.schedule.get_type_count(Seller), self.schedule.get_type_count(Buyer)]) '''Network''' if self.network: network.formation(self.cnt, self.buyers, self.sellers) def run_model(self, step_count): for _ in range(step_count): self.step() ''' Debugging ''' '''Display trust levels''' if self.buyerDebug: debug.buyers(self.buyers) '''Network''' if self.networkDebug: debug.network(self.buyers) '''Display seller information''' if self.sellerDebug: debug.sellers(self.cnt, self.num_w, self.sellers, self.buyers) '''End of the run''' print("\n************\nPut a summary here.\n************")
class ContactModel(Model): def __init__(self, N, height, width, exponent, steps, seed): self.number_of_agents = N self.height = height self.width = width self.exponent = exponent self.x_locs = np.zeros((N, steps + 1)) self.y_locs = np.zeros((N, steps + 1)) self.current_step = 0 #NEW self.current_step_contacts = [] self.adjacency_matrix = np.zeros((N, N)) self.grid = MultiGrid(self.width, self.height, torus=False) self.schedule = BaseScheduler(self) #RandomActivation(self) # Add N pedestrians to model (schedule, grid) taken_pos = [] for i in range(self.number_of_agents): x = self.random.randrange(1, self.grid.width - 1) y = self.random.randrange(1, self.grid.height - 1) pos = (x, y) new_human = Pedestrian(i, self, pos, self.exponent, seed=i) self.schedule.add(new_human) self.grid.place_agent(new_human, pos) self.x_locs[i][0] = x self.y_locs[i][0] = y taken_pos.append(pos) self.data_collector = DataCollector() self.running = True self.data_collector.collect(self) def contact_update(self, contact_ids): contact_ids = sorted(contact_ids) if contact_ids not in self.current_step_contacts: self.current_step_contacts.append(contact_ids) def update_adjecency_matrix(self): ''' #TODO: order agent steps, order updates, double or not for id_tuple in self.current_step_contacts: self.adjacency_matrix[id_tuple[0], id_tuple[1]]+=1 ''' agents = self.schedule.agents for i, agent in enumerate(agents): neighbors = self.grid.get_neighbors(agent.pos, moore=True, radius=5) for neighbor in neighbors: if neighbor.unique_id > agent.unique_id: self.adjacency_matrix[agent.unique_id, neighbor.unique_id] += 1 def step(self): self.schedule.step() self.update_adjecency_matrix() #self.current_step_contacts=[] #self.data_collector.collect(self) def run(self, N): for i in range(N): self.step() self.current_step += 1 #NEW for agent in self.schedule.agents: self.x_locs[agent.unique_id][i + 1] = agent.pos[0] self.y_locs[agent.unique_id][i + 1] = agent.pos[1] if i % 100 == 0: print(i)
class miModelo(Model): #definimos la clase miModelo def __init__( self, N_humanos ): #constructor, metemos el argumento Número Humanos iniciales self.running = True #permite la ejecución continua self.schedule = RandomActivation(self) # elige el tipo de schedule self.grid = MultiGrid(GRID_FINAL_X, GRID_FINAL_Y, False) #tipo y tamaño de grid self.posTorniquetesEntrada = [ ] #guarda las posiciones de los torniquites de entrada self.posTorniquetesSalida = [ ] #guarda las posiciones de los torniquites de salida self.posPuertas = [] #guarda las posiciones de las puertas self.puertas = [] #guarda los objetos puerta self.posUInteriores = calcularUInteriores( ) #te calcula todas las posiciones interios del vagón a donde se dirigen los usuarios self.contador = 1 # contador de ticks para abrir y cerrar puertas self.humanosEntraronTorniquetes = 0 #contador para humanos que entran a torniquetes self.humanosSalieronTorniquetes = 0 #contador para humanos que entran a torniquetes self.humanosEntraronVagon = 0 #contador para humanos que entran a torniquetes self.humanosSalieronVagon = 0 #contador para humanos que entran a torniquetes #self.timer = TIMERABIERTO pintarTorniquetes(self) #Dibuja los torniquetes pintarPuertas(self) #Dibuja todas las puertas pintarMuros(self) #Dibuja todos los muros pintarHumanos(self, N_humanos, False) def step(self): print("Start tick") self.schedule.step() pintarNuevosHumanos(self, 1) # N_humanos = self.random.randint(1,12) if self.schedule.get_agent_count() < 2: self.running = False self.contador += 1 humanosAEntrar = [] humanosASalir = [] if self.contador == TIMERABRIR and self.puertas[0].cerrada: humanosAEntrar = self.obtenerHumanosEnRango( GRID_INICIAL_X, GRID_FINAL_X, YMURO_TREN, YMURO_TORNIQUETES) print("HUMANOS A ENTRAR ", len(humanosAEntrar)) time.sleep(5) for puerta in self.puertas: puerta.cerrada = False self.contador = 0 pintarHumanos(self, self.random.randint(MIN_H_LLEGANDO_VAGON, MAX_H_LLEGANDO_VAGON), True) #Humanos aleatorio que aparecen en el vagon elif self.contador == TIMERCERRAR and not self.puertas[0].cerrada: for puerta in self.puertas: puerta.cerrada = True self.contador = 0 elif self.contador == TIMERCERRAR - 1 and not self.puertas[0].cerrada: humanosEntraron = self.obtenerHumanosEnRango( GRID_INICIAL_X, GRID_FINAL_X, GRID_INICIAL_Y + 1, YMURO_TREN) humanosNoEntraron = self.obtenerHumanosEnRango( GRID_INICIAL_X, GRID_FINAL_X, YMURO_TREN, YMURO_TORNIQUETES) print("HUMANOS QUE ENTRARON ", len(humanosEntraron)) print("HUMANOS QUE NO ENTRARON", len(humanosNoEntraron)) time.sleep(5) # if self.contador == self.timer: # for puerta in self.puertas: # puerta.cerrada = not puerta.cerrada # if self.timer == 10: # self.timer = 9 # elif self.timer == 9: # self.timer = 10 # pintarHumanos(self, self.random.randint(30,50), True) # self.contador = 0 print("Los humanos que han entrado por los torniquetes son ", self.humanosEntraronTorniquetes) print("Los humanos que han salido por los torniquetes son ", self.humanosSalieronTorniquetes) print("Los Humanos que han entrado al vagon son ", self.humanosEntraronVagon) print("Los Humanos que han salido del vagon son ", self.humanosSalieronVagon) print("---- End of tick ----") def getTorniquetesEntrada(self): #return [(),(),()] return self.posTorniquetesEntrada def getPuertas(self): #return [(),(),()] return self.posPuertas def getUInteriores(self): return self.posUInteriores def getTorniquetesSalida(self): return self.posTorniquetesSalida def obtenerHumanosEnRango(self, xinicial, xfinal, yinicial, yfinal): totalHumanos = [] for x in range(xinicial, xfinal + 1): for y in range(yinicial, yfinal): pos = (x, y) humanos = self.grid.get_neighbors(pos, moore=True, include_center=True, radius=0) humanos = [ x for x in humanos if type(x) is Humano and x.direccion == True ] if len(humanos) > 0: totalHumanos = totalHumanos + humanos return totalHumanos