class ForestFire(Model): ''' Simple Forest Fire model. ''' def __init__(self, height, width, density): ''' Create a new forest fire model. Args: height, width: The size of the grid to model density: What fraction of grid cells have a tree in them. ''' # Initialize model parameters self.height = height self.width = width self.density = density # Set up model objects self.schedule = RandomActivation(self) self.grid = Grid(height, width, torus=False) self.datacollector = DataCollector( {"Fine": lambda m: self.count_type(m, "Fine"), "On Fire": lambda m: self.count_type(m, "On Fire"), "Burned Out": lambda m: self.count_type(m, "Burned Out")}) # Place a tree in each cell with Prob = density for (contents, x, y) in self.grid.coord_iter(): if random.random() < self.density: # Create a tree new_tree = TreeCell((x, y)) # Set all trees in the first column on fire. if x == 0: new_tree.condition = "On Fire" self.grid._place_agent((x, y), new_tree) self.schedule.add(new_tree) self.running = True def step(self): ''' Advance the model by one step. ''' self.schedule.step() self.datacollector.collect(self) # Halt if no more fire if self.count_type(self, "On Fire") == 0: self.running = False @staticmethod def count_type(model, tree_condition): ''' Helper method to count trees in a given condition in a given model. ''' count = 0 for tree in model.schedule.agents: if tree.condition == tree_condition: count += 1 return count
class Money_Model(Model): def __init__(self, N, width=50, height=50, torus=True): self.num_agents = N self.schedule = RandomActivation(self) self.grid = MultiGrid(height, width, torus) self.create_agents() self.dc = DataCollector({"Gini": lambda m: m.compute_gini()}, {"Wealth": lambda a: a.wealth}) self.running = True def create_agents(self): for i in range(self.num_agents): a = Money_Agent(i) self.schedule.add(a) x = random.randrange(self.grid.width) y = random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) def step(self): self.dc.collect(self) self.schedule.step() def run_model(self, steps): for i in range(steps): self.step() def compute_gini(self): agent_wealths = [agent.wealth for agent in self.schedule.agents] x = sorted(agent_wealths) N = self.num_agents B = sum( xi * (N-i) for i,xi in enumerate(x) ) / (N*sum(x)) return (1 + (1/N) - 2*B)
class MoneyModel(Model): """A simple model of an economy where agents exchange currency at random. All the agents begin with one unit of currency, and each time step can give a unit of currency to another agent. Note how, over time, this produces a highly skewed distribution of wealth. """ def __init__(self, N, width, height): self.num_agents = N self.running = True self.grid = MultiGrid(height, width, True) self.schedule = RandomActivation(self) self.datacollector = DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": lambda a: a.wealth} ) # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) self.schedule.add(a) # Add the agent to a random grid cell x = random.randrange(self.grid.width) y = random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) def step(self): self.datacollector.collect(self) self.schedule.step() def run_model(self, n): for i in range(n): self.step()
class BoltzmannWealthModelNetwork(Model): """A model with some number of agents.""" def __init__(self, num_agents=7, num_nodes=10): self.num_agents = num_agents self.num_nodes = num_nodes if num_nodes >= self.num_agents else self.num_agents self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0.5) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.datacollector = DataCollector( model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": lambda _: _.wealth} ) list_of_random_nodes = self.random.sample(self.G.nodes(), self.num_agents) # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) self.schedule.add(a) # Add the agent to a random node self.grid.place_agent(a, list_of_random_nodes[i]) self.running = True self.datacollector.collect(self) def step(self): self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
class MoneyModel(Model): """A model with some number of agents.""" def __init__(self, N, width, height): self.num_agents = N self.running = True self.grid = MultiGrid(height, width, True) self.schedule = RandomActivation(self) self.datacollector = DataCollector(model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": lambda a: a.wealth}) # Create agents for i in range(self.num_agents): a = MoneyAgent(i) self.schedule.add(a) # Add the agent to a random grid cell x = random.randrange(self.grid.width) y = random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) def step(self): self.datacollector.collect(self) self.schedule.step() def run_model(self, n): for i in range(n): self.step()
class SchellingModel(Model): ''' Model class for the Schelling segregation model. ''' def __init__(self, height, width, density, minority_pc, homophily): ''' ''' self.height = height self.width = width self.density = density self.minority_pc = minority_pc self.homophily = homophily self.schedule = RandomActivation(self) self.grid = Grid(height, width, torus=True) self.happy = 0 self.datacollector = DataCollector( {"happy": lambda m: m.happy}, # Model-level count of happy agents # For testing purposes, agent's individual x and y {"x": lambda a: a.x, "y": lambda a: a.y}) self.running = True # Set up agents for x in range(self.width): for y in range(self.height): if random.random() < self.density: if random.random() < self.minority_pc: agent_type = 1 else: agent_type = 0 agent = SchellingAgent((x,y), x, y, agent_type) self.grid[y][x] = agent self.schedule.add(agent) def get_empty(self): ''' Get a list of coordinate tuples of currently-empty cells. ''' empty_cells = [] for x in range(self.width): for y in range(self.height): if self.grid[y][x] is None: empty_cells.append((x, y)) return empty_cells def step(self): ''' Run one step of the model. If All agents are happy, halt the model. ''' self.happy = 0 # Reset counter of happy agents self.schedule.step() self.datacollector.collect(self) if self.happy == self.schedule.get_agent_count(): self.running = False
class WorldModel(Model): def __init__(self, N, width, height): self.grid = SingleGrid(height, width, True) self.schedule = RandomActivation(self) self.num_agents = N self.running = True for i in range(self.num_agents): ethnicity = random.choice(Ethnicities) a = PersonAgent(unique_id=i, model=self, ethnicity=int(ethnicity) ) self.schedule.add(a) # Add the agent to a random grid cell self.grid.position_agent(a) self.datacollector = DataCollector( agent_reporters={ "Nationalism": lambda a: a.nationalism, "X": lambda a: a.pos[0], "Y": lambda a: a.pos[1] } ) def step(self): self.datacollector.collect(self) self.schedule.step()
class Charts(Model): # grid height grid_h = 20 # grid width grid_w = 20 """init parameters "init_people", "rich_threshold", and "reserve_percent" are all UserSettableParameters""" def __init__(self, height=grid_h, width=grid_w, init_people=2, rich_threshold=10, reserve_percent=50,): self.height = height self.width = width self.init_people = init_people self.schedule = RandomActivation(self) self.grid = MultiGrid(self.width, self.height, torus=True) # rich_threshold is the amount of savings a person needs to be considered "rich" self.rich_threshold = rich_threshold self.reserve_percent = reserve_percent # see datacollector functions above self.datacollector = DataCollector(model_reporters={ "Rich": get_num_rich_agents, "Poor": get_num_poor_agents, "Middle Class": get_num_mid_agents, "Savings": get_total_savings, "Wallets": get_total_wallets, "Money": get_total_money, "Loans": get_total_loans}, agent_reporters={ "Wealth": lambda x: x.wealth}) # create a single bank for the model self.bank = Bank(1, self, self.reserve_percent) # create people for the model according to number of people set by user for i in range(self.init_people): # set x, y coords randomly within the grid x = self.random.randrange(self.width) y = self.random.randrange(self.height) p = Person(i, (x, y), self, True, self.bank, self.rich_threshold) # place the Person object on the grid at coordinates (x, y) self.grid.place_agent(p, (x, y)) # add the Person object to the model schedule self.schedule.add(p) self.running = True self.datacollector.collect(self) def step(self): # tell all the agents in the model to run their step function self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self): for i in range(self.run_time): self.step()
class Foraging(Model): number_of_bean = 0 number_of_corn = 0 number_of_soy = 0 def __init__(self, width=50, height=50, torus=True, num_bug=50, seed=42, strategy=None): super().__init__(seed=seed) self.number_of_bug = num_bug if not(strategy in ["stick", "switch"]): raise TypeError("'strategy' must be one of {stick, switch}") self.strategy = strategy self.grid = SingleGrid(width, height, torus) self.schedule = RandomActivation(self) data = {"Bean": lambda m: m.number_of_bean, "Corn": lambda m: m.number_of_corn, "Soy": lambda m: m.number_of_soy, "Bug": lambda m: m.number_of_bug, } self.datacollector = DataCollector(data) # create foods self._populate(Bean) self._populate(Corn) self._populate(Soy) # create bugs for i in range(self.number_of_bug): pos = self.grid.find_empty() bug = Bug(i, self) bug.strategy = self.strategy self.grid.place_agent(bug, pos) self.schedule.add(bug) def step(self): self.schedule.step() self.datacollector.collect(self) if not(self.grid.exists_empty_cells()): self.running = False def _populate(self, food_type): prefix = "number_of_{}" counter = 0 while counter < food_type.density * (self.grid.width * self.grid.height): pos = self.grid.find_empty() food = food_type(counter, self) self.grid.place_agent(food, pos) self.schedule.add(food) food_name = food_type.__name__.lower() attr_name = prefix.format(food_name) val = getattr(self, attr_name) val += 1 setattr(self, attr_name, val) counter += 1
class Schelling(Model): ''' Model class for the Schelling segregation model. ''' def __init__(self, height=20, width=20, density=0.8, minority_pc=0.2, homophily=3): ''' ''' self.height = height self.width = width self.density = density self.minority_pc = minority_pc self.homophily = homophily self.schedule = RandomActivation(self) self.grid = SingleGrid(height, width, torus=True) self.happy = 0 self.datacollector = DataCollector( {"happy": "happy"}, # Model-level count of happy agents # For testing purposes, agent's individual x and y {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]}) # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) for cell in self.grid.coord_iter(): x = cell[1] y = cell[2] if self.random.random() < self.density: if self.random.random() < self.minority_pc: agent_type = 1 else: agent_type = 0 agent = SchellingAgent((x, y), self, agent_type) self.grid.position_agent(agent, (x, y)) self.schedule.add(agent) self.running = True self.datacollector.collect(self) def step(self): ''' Run one step of the model. If All agents are happy, halt the model. ''' self.happy = 0 # Reset counter of happy agents self.schedule.step() # collect data self.datacollector.collect(self) if self.happy == self.schedule.get_agent_count(): self.running = False
class SchellingModel(Model): """ Model class for the Schelling segregation model. """ def __init__(self, height, width, density, minority_pc, homophily): """ """ self.height = height self.width = width self.density = density self.minority_pc = minority_pc self.homophily = homophily self.schedule = RandomActivation(self) self.grid = SingleGrid(height, width, torus=True) self.happy = 0 self.total_agents = 0 self.datacollector = DataCollector( {"unhappy": lambda m: m.total_agents - m.happy}, # For testing purposes, agent's individual x and y {"x": lambda a: a.pos[X], "y": lambda a: a.pos[Y]}, ) self.running = True # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) for cell, x, y in self.grid.coord_iter(): if random.random() < self.density: if random.random() < self.minority_pc: agent_type = 1 else: agent_type = 0 agent = SchellingAgent(self.total_agents, agent_type) self.grid.position_agent(agent, x, y) self.schedule.add(agent) self.total_agents += 1 def step(self): """ Run one step of the model. If All agents are happy, halt the model. """ self.happy = 0 # Reset counter of happy agents self.schedule.step() self.datacollector.collect(self) if self.happy == self.total_agents: self.running = False
class PD_Model(Model): ''' Model class for iterated, spatial prisoner's dilemma model. ''' schedule_types = {"Sequential": BaseScheduler, "Random": RandomActivation, "Simultaneous": SimultaneousActivation} # This dictionary holds the payoff for this agent, # keyed on: (my_move, other_move) payoff = {("C", "C"): 1, ("C", "D"): 0, ("D", "C"): 1.6, ("D", "D"): 0} def __init__(self, height, width, schedule_type, payoffs=None): ''' Create a new Spatial Prisoners' Dilemma Model. Args: height, width: Grid size. There will be one agent per grid cell. schedule_type: Can be "Sequential", "Random", or "Simultaneous". Determines the agent activation regime. payoffs: (optional) Dictionary of (move, neighbor_move) payoffs. ''' self.running = True self.grid = SingleGrid(height, width, torus=True) self.schedule_type = schedule_type self.schedule = self.schedule_types[self.schedule_type](self) # Create agents for x in range(width): for y in range(height): agent = PD_Agent((x, y), self) self.grid.place_agent(agent, (x, y)) self.schedule.add(agent) self.datacollector = DataCollector({ "Cooperating_Agents": lambda m: len([a for a in m.schedule.agents if a.move == "C"]) }) def step(self): self.datacollector.collect(self) self.schedule.step() def run(self, n): ''' Run the model for a certain number of steps. ''' for _ in range(n): self.step()
class InspectionModel(Model): ''' Simple Restaurant Inspection model. ''' def __init__(self, height, width, density): ''' Create a new restaurant inspection model. Args: height, width: The size of the grid to model density: What fraction of grid cells have a restaurant in them. ''' # Initialize model parameters self.height = height self.width = width self.density = density # Set up model objects self.schedule = RandomActivation(self) self.grid = Grid(height, width, torus=False) self.datacollector = DataCollector( {"Good": lambda m: self.count_type(m, "Good"), "Bad": lambda m: self.count_type(m, "Bad")}) # Place a restaurant in each cell with Prob = density for (contents, x, y) in self.grid.coord_iter(): if random.random() < self.density: # Create a restaurant new_restaurant = RestaurantCell((x, y)) self.grid._place_agent((x, y), new_restaurant) self.schedule.add(new_restaurant) self.running = True def step(self): ''' Advance the model by one step. ''' self.schedule.step() self.datacollector.collect(self) @staticmethod def count_type(model, restaurant_hygiene): ''' Helper method to count restaurants in a given condition in a given model. ''' count = 0 for restaurant in model.schedule.agents: if restaurant.hygiene == restaurant_hygiene and restaurant.rating != 'Closed': count += 1 return count
class VirusOnNetwork(Model): """A virus model with some number of agents""" def __init__(self, num_nodes=10, avg_node_degree=3, initial_outbreak_size=1, virus_spread_chance=0.4, virus_check_frequency=0.4, recovery_chance=0.3, gain_resistance_chance=0.5): self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.initial_outbreak_size = initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes self.virus_spread_chance = virus_spread_chance self.virus_check_frequency = virus_check_frequency self.recovery_chance = recovery_chance self.gain_resistance_chance = gain_resistance_chance self.datacollector = DataCollector({"Infected": number_infected, "Susceptible": number_susceptible, "Resistant": number_resistant}) # Create agents for i, node in enumerate(self.G.nodes()): a = VirusAgent(i, self, State.SUSCEPTIBLE, self.virus_spread_chance, self.virus_check_frequency, self.recovery_chance, self.gain_resistance_chance) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes): a.state = State.INFECTED self.running = True self.datacollector.collect(self) def resistant_susceptible_ratio(self): try: return number_state(self, State.RESISTANT) / number_state(self, State.SUSCEPTIBLE) except ZeroDivisionError: return math.inf def step(self): self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
class SchellingModel(Model): ''' Model class for the Schelling segregation model. ''' def __init__(self, height, width, density, type_pcs=[.2, .2, .2, .2, .2]): ''' ''' self.height = height self.width = width self.density = density self.type_pcs = type_pcs self.schedule = RandomActivation(self) self.grid = SingleGrid(height, width, torus=False) self.happy = 0 self.datacollector = DataCollector( {"happy": lambda m: m.happy}, # Model-level count of happy agents # For testing purposes, agent's individual x and y {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]}) self.running = True # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) total_agents = self.height * self.width * self.density agents_by_type = [total_agents*val for val in self.type_pcs] for loc, types in enumerate(agents_by_type): for i in range(int(types)): pos = self.grid.find_empty() agent = SchellingAgent(pos, self, loc) self.grid.position_agent(agent, pos) self.schedule.add(agent) def step(self): ''' Run one step of the model. If All agents are happy, halt the model. ''' self.happy = 0 # Reset counter of happy agents self.schedule.step() self.datacollector.collect(self) if self.happy == self.schedule.get_agent_count(): self.running = False
class SIR_Network_Model(Model): def __init__(self, g=None, outbreak_size=3, si_trans=0.025, ir_trans=0.05): self.schedule = SimultaneousActivation(self) if g is None: g = nx.random_graphs.watts_strogatz_graph(100, 4, 0.05) self.si_trans = si_trans self.ir_trans = ir_trans nodes = g.nodes() agent_nodes = list(map(lambda x: SIR_Agent(x), nodes)) n_map = dict(zip(nodes, agent_nodes)) agent_edges = list(map(lambda e: (n_map[e[0]], n_map[e[1]]) , g.edges())) for agent in agent_nodes: self.schedule.add(agent) # set the initial outbreak for node in sample(list(agent_nodes), outbreak_size): node.state = State.infected self.network = NetworkSpace(agent_nodes, agent_edges) self.dc = DataCollector({"susceptible": lambda m: self.count_state(m, State.susceptible), "infected": lambda m: self.count_state(m, State.infected), "resistant": lambda m: self.count_state(m, State.resistant)}, {"state": lambda a: a.state.value} ) self.dc.collect(self) #initial state self.running = True def step(self): self.schedule.step() self.dc.collect(self) # disease dead? if self.count_state(self, State.infected) == 0: self.running = False @staticmethod def count_state(model,state): count = 0 for agent in model.schedule.agents: if agent.state == state: count +=1 return count
class MockModel(Model): ''' Minimalistic model for testing purposes. ''' schedule = BaseScheduler(None) def __init__(self): self.schedule = BaseScheduler(self) for i in range(10): a = MockAgent(i, i) self.schedule.add(a) self.datacollector = DataCollector( {"total_agents": lambda m: m.schedule.get_agent_count()}, {"value": lambda a: a.val}, {"Final_Values": ["agent_id", "final_value"]}) def step(self): self.schedule.step() self.datacollector.collect(self)
class MoneyModel(Model): """A model with some number of agents.""" def __init__(self, N): self.running = True self.num_agents = N self.schedule = RandomActivation(self) self.create_agents() agent_reporters = {"Wealth": lambda a: a.wealth} model_reporters = {"Gini": compute_gini} self.dc = DataCollector(model_reporters=model_reporters, agent_reporters=agent_reporters) def create_agents(self): """Method to create all the agents.""" for i in range(self.num_agents): a = MoneyAgent(i) self.schedule.add(a) def step(self): self.schedule.step() self.dc.collect(self)
class LifeTimeModel(Model): '''Simple model for running models with a finite life''' def __init__(self, agent_lifetime = 1, n_agents = 10): super().__init__() self.agent_lifetime = agent_lifetime self.n_agents = n_agents ## keep track of the the remaining life of an agent and ## how many ticks it has seen self.datacollector = DataCollector( agent_reporters = {"remaining_life" : lambda a: a.remaining_life, "steps" : lambda a: a.steps}) self.current_ID = 0 self.schedule = RandomActivation(self) for _ in range(n_agents): self.schedule.add(FiniteLifeAgent(self.next_id(), self.agent_lifetime, self)) def step(self): '''Add agents back to n_agents in each step''' self.datacollector.collect(self) self.schedule.step() if len(self.schedule.agents) < self.n_agents: for _ in range(self.n_agents - len(self.schedule.agents)): self.schedule.add(FiniteLifeAgent(self.next_id(), self.agent_lifetime, self)) def run_model(self, step_count = 100): for _ in range(step_count): self.step()
class Sugarscape2ConstantGrowback(Model): ''' Sugarscape 2 Constant Growback ''' verbose = True # Print-monitoring def __init__(self, height=50, width=50, initial_population=100): ''' Create a new Constant Growback model with the given parameters. Args: initial_population: Number of population to start with ''' # Set parameters self.height = height self.width = width self.initial_population = initial_population self.schedule = RandomActivationByBreed(self) self.grid = MultiGrid(self.height, self.width, torus=False) self.datacollector = DataCollector({"SsAgent": lambda m: m.schedule.get_breed_count(SsAgent), }) # Create sugar import numpy as np sugar_distribution = np.genfromtxt("sugarscape/sugar-map.txt") for _, x, y in self.grid.coord_iter(): max_sugar = sugar_distribution[x, y] sugar = Sugar((x, y), self, max_sugar) self.grid.place_agent(sugar, (x, y)) self.schedule.add(sugar) # Create agent: for i in range(self.initial_population): x = random.randrange(self.width) y = random.randrange(self.height) sugar = random.randrange(6, 25) metabolism = random.randrange(2, 4) vision = random.randrange(1, 6) ssa = SsAgent((x, y), self, False, sugar, metabolism, vision) self.grid.place_agent(ssa, (x, y)) self.schedule.add(ssa) self.running = True def step(self): self.schedule.step() self.datacollector.collect(self) if self.verbose: print([self.schedule.time, self.schedule.get_breed_count(SsAgent)]) def run_model(self, step_count=200): if self.verbose: print('Initial number Sugarscape Agent: ', self.schedule.get_breed_count(SsAgent)) for i in range(step_count): self.step() if self.verbose: print('') print('Final number Sugarscape Agent: ', self.schedule.get_breed_count(SsAgent))
class BankReserves(Model): """ This model is a Mesa implementation of the Bank Reserves model from NetLogo. It is a highly abstracted, simplified model of an economy, with only one type of agent and a single bank representing all banks in an economy. People (represented by circles) move randomly within the grid. If two or more people are on the same grid location, there is a 50% chance that they will trade with each other. If they trade, there is an equal chance of giving the other agent $5 or $2. A positive trade balance will be deposited in the bank as savings. If trading results in a negative balance, the agent will try to withdraw from its savings to cover the balance. If it does not have enough savings to cover the negative balance, it will take out a loan from the bank to cover the difference. The bank is required to keep a certain percentage of deposits as reserves and the bank's ability to loan at any given time is a function of the amount of deposits, its reserves, and its current total outstanding loan amount. """ # grid height grid_h = 20 # grid width grid_w = 20 """init parameters "init_people", "rich_threshold", and "reserve_percent" are all UserSettableParameters""" def __init__(self, height=grid_h, width=grid_w, init_people=2, rich_threshold=10, reserve_percent=50,): self.height = height self.width = width self.init_people = init_people self.schedule = RandomActivation(self) self.grid = MultiGrid(self.width, self.height, torus=True) # rich_threshold is the amount of savings a person needs to be considered "rich" self.rich_threshold = rich_threshold self.reserve_percent = reserve_percent # see datacollector functions above self.datacollector = DataCollector(model_reporters={ "Rich": get_num_rich_agents, "Poor": get_num_poor_agents, "Middle Class": get_num_mid_agents, "Savings": get_total_savings, "Wallets": get_total_wallets, "Money": get_total_money, "Loans": get_total_loans}, agent_reporters={ "Wealth": lambda x: x.wealth}) # create a single bank for the model self.bank = Bank(1, self, self.reserve_percent) # create people for the model according to number of people set by user for i in range(self.init_people): # set x, y coords randomly within the grid x = self.random.randrange(self.width) y = self.random.randrange(self.height) p = Person(i, (x, y), self, True, self.bank, self.rich_threshold) # place the Person object on the grid at coordinates (x, y) self.grid.place_agent(p, (x, y)) # add the Person object to the model schedule self.schedule.add(p) self.running = True self.datacollector.collect(self) def step(self): # tell all the agents in the model to run their step function self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self): for i in range(self.run_time): self.step()
class DDAModel(Model): """A simple DDA model""" _width = _WIDTH # width and height of the world. These shouldn't be changed _height = _HEIGHT def __init__(self, N, iterations, bleedout_rate=np.random.normal(0.5, scale=0.1), mp=False): """ Create a new instance of the DDA model. Parameters: N - the number of agents iterations - the number of iterations to run the model for blr - the bleedout rate (the probability that agents leave at the midpoint) (default normal distribution with mean=0.5 and sd=0.1) mp - whether to use multiprocess (agents call step() method at same time) (doesn't work!) (default False) """ self.num_agents = N self._bleedout_rate = bleedout_rate self.iterations = iterations self.mp = mp # Locations of important parts of the environment. These shouldn't be changed self.graveyard = (0, 0) # x,y locations of the graveyard self.loc_a = (1, 0) # Location a (on left side of street) self.loc_b = (23, 0) # Location b (on the right side) self.loc_mid = (12, 0) # The midpoint # 'Cameras' that store the number of agents who pass them over the course of an hour. The historical counts # are saved by mesa using the DataCollector self._camera_a = 0 # Camera A self._camera_b = 0 # Camera B self._camera_m = 0 # The midpoint # Set up the scheduler. Note that this isn't actually used (see below re. agent's stepping) self.schedule = RandomActivation(self) # Random order for calling agent's step methods # For multiprocess step method self.pool = Pool() # Create the environment self.grid = MultiGrid(DDAModel._width, DDAModel._height, False) # Define a variable that can be used to indicate whether the model has finished self.running = True # Create a distribution that tells us the number of agents to be added to the world at each self._agent_dist = DDAModel._make_agent_distribution(N) # Create all the agents for i in range(self.num_agents): a = DDAAgent(i, self) self.schedule.add(a) # Add the agents to the schedule # All agents start as 'retired' in the graveyard a.state = AgentStates.RETIRED self.grid.place_agent(a, self.graveyard) # All agents start in the graveyard print("Created {} agents".format(len(self.schedule.agents))) # Define a collector for model data self.datacollector = DataCollector( model_reporters={"Bleedout rate": lambda m: m.bleedout_rate, "Number of active agents": lambda m: len(m.active_agents()), "Camera A counts": lambda m: m.camera_a, "Camera B counts": lambda m: m.camera_b, "Camera M counts": lambda m: m.camera_m }, agent_reporters={"Location (x)": lambda agent: agent.pos[0], "State": lambda agent: agent.state } ) def step(self): """Advance the model by one step.""" print("Iteration {}".format(self.schedule.steps)) self.datacollector.collect(self) # Collect data about the model # See if the model has finished running. if self.schedule.steps >= self.iterations: self.running = False return # Things to do every hour. # - 1 - reset the camera counters # - 2 - activate some agents num_to_activate = -1 s = self.schedule.steps # Number of steps (for convenience) if s % 60 == 0: # On the hour # Reset the cameras self._reset_cameras() # Calculate the number of agents to activate num_to_activate = int(self._agent_dist[int((s / 60) % 24)]) print("\tActivating {} agents on hour {}".format(num_to_activate, s % 60)) else: num_to_activate = 0 assert num_to_activate >= 0, \ "The number of agents to activate should be greater or equal to 0, not {}".format(num_to_activate) if num_to_activate > 0: # Choose some agents that are currently retired to activate. retired_agents = [a for a in self.schedule.agents if a.state == AgentStates.RETIRED] assert len(retired_agents) >= num_to_activate, \ "Too few agents to activate (have {}, need {})".format(len(retired_agents), num_to_activate) to_activate = np.random.choice(retired_agents, size=num_to_activate, replace=False) print("\t\tActivating agents: {}".format(to_activate)) for a in to_activate: a.activate() # XXXX HERE - see line 477 om wprlomgca,eras/py # Call all agents' 'step' method. if not self.mp: # Not using multiprocess. Do it the mesa way: self.schedule.step() else: # Better to do it a different way to take advantage of multicore processors and to ignore agents who are not # active (no need for them to step at all) # NOTE: Doesn't work! The problem is that the DDAAgent needs the DDAModel class, which means # that this class needs to be pickled and copied to the child processes. The first problem (which can be # fixed by creating functions rather than using lambda, although this is messy) is that DDAModel uses # lambda functions, that can't be pickled. Second and more difficult problem is that the Pool object itself # cannot be shared. Possible solution here: # https://stackoverflow.com/questions/25382455/python-notimplementederror-pool-objects-cannot-be-passed-between-processes # but for the meantime I'm not going to try to fix this. active_agents = self.active_agents() # Get all of the active agents random.shuffle(active_agents) if active_agents is None: print("\tNo agents are active") # Nothing to do else: p = Pool() p.map(DDAAgent._step_agent, active_agents) # Calls step() for all agents # As not using the proper schedule method, need to update time manually. self.schedule.steps += 1 self.schedule.time += 1 def increment_camera_a(self): """Used by agents to tell the model that they have just passed the camera at location A. It would be neater to have the cameras detect the agents, but I think that this would be quite expensive.""" self._camera_a += 1 # Increment the count of the current hour (most recent) def increment_camera_b(self): """Used by agents to tell the model that they have just passed the camera at location B. It would be neater to have the cameras detect the agents, but I think that this would be quite expensive.""" self._camera_b += 1 # Increment the count of the current hour (most recent) def increment_camera_m(self): """Used by agents to tell the model that they have just passed the camera at the midpoint. This is only for information really, in this scenario there is no camera at the midpoint""" self._camera_m += 1 # Increment the count of the current hour (most recent) @property def camera_a(self) -> int: """Getter for the count of the camera at location A""" return self._camera_a @property def camera_b(self) -> int: """Getter for the count of the camera at location B""" return self._camera_b @property def camera_m(self) -> int: """Getter for the count of the camera at the midpoint""" return self._camera_m def _reset_cameras(self): """Reset the cameras to zero. Done on the hour""" self._camera_a = 0 self._camera_b = 0 self._camera_m = 0 @staticmethod def _step_agent(a): """Call the given agent's step method. Only required because Pool.map doesn't take lambda functions.""" a.step() # bleedout rate is defined as a property: http://www.python-course.eu/python3_properties.php @property def bleedout_rate(self): """Get the current bleedout rate""" return self._bleedout_rate @bleedout_rate.setter def bleedout_rate(self, blr: float) -> None: """Set the bleedout rate. It must be between 0 and 1 (inclusive). Failure to do that raises a ValueError.""" if blr < 0 or blr > 1: raise ValueError("The bleedout rate must be between 0 and 1, not '{}'".format(blr)) self._bleedout_rate = blr def active_agents(self) -> List[DDAAgent]: """Return a list of the active agents (i.e. those who are not retired)""" return [a for a in self.schedule.agents if a.state != AgentStates.RETIRED] @classmethod def _make_agent_distribution(cls, N): """Create a distribution that tells us the number of agents to be created at each hour""" a = np.arange(0, 24, 1) # Create an array with one item for each hour rv1 = norm(loc=12., scale=6.0) # A continuous, normal random variable with a peak at 12 dist = rv1.pdf(a) # Draw from the random variable pdf, taking values from a return [round(item * N, ndigits=0) for item in dist] # Return a rounded list (the number of agents at each hour)
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 TotC(Model): """ Main model for the tragedy of the commons """ def __init__(self, initial_herdsmen=5, initial_sheep_per_herdsmen=0, initial_edges=5, l_coop=0, l_fairself=0, l_fairother=0, l_negrecip=0, l_posrecip=0, l_conf=0): super().__init__() self.width = GRID_SIZE self.height = GRID_SIZE self.grass = [] self.herdsmen = [] self.initial_herdsmen = initial_herdsmen self.initial_sheep_per_herdsmen = initial_sheep_per_herdsmen self.sheep_survival_rate = [] self.l_coop = l_coop self.l_fairself = l_fairself self.l_fairother = l_fairother self.l_negrecip = l_negrecip self.l_posrecip = l_posrecip self.l_conf = l_conf self.G = nx.gnm_random_graph(initial_herdsmen, initial_edges) Sheep.sheepdeaths = 0 Herdsman.i = 0 Herdsman.x = np.zeros(initial_herdsmen, dtype=np.int8) self.schedule_Grass = RandomActivation(self) self.schedule_Herdsman = StagedActivation( self, stage_list=["advance", "step"], shuffle=True) self.schedule_Sheep = RandomActivation(self) self.schedule = RandomActivation(self) self.grid = MultiGrid(self.width, self.height, torus=True) # "Grass" is the number of sheep that the grass can sustain self.datacollector = DataCollector({ "Grass": lambda m: self.get_expected_grass_growth() / .5, "Sheep": lambda m: self.get_sheep_count(), "Sheep deaths": lambda m: Sheep.sheepdeaths }) self.init_population() # required for the datacollector to work self.running = True self.datacollector.collect(self) def add_herdsman(self): """ At a herdsman at a random position on the grid. """ x = random.randrange(self.width) y = random.randrange(self.height) herdsman = self.new_agent(Herdsman, (x, y)) self.herdsmen.append(herdsman) def init_grass(self): """ Initialise a patch of grass at every square on the grid. """ for agent, x, y in self.grid.coord_iter(): self.grass.append(self.new_agent(Grass, (x, y))) def init_herdsman(self): """ Spawn `initial_herdsmen' herdsmen on the field. """ for i in range(getattr(self, "initial_herdsmen")): self.add_herdsman() def init_node_attr(self): """ Assign the unique herdsman ID as attribute to graph nodes for the social network. """ N = self.get_herdsman_count() for i in range(getattr(self, "initial_herdsmen")): for j in range(getattr(self, "initial_herdsmen")): if i is not j: if nx.has_path(self.G, source=i, target=j) == True: if nx.shortest_path_length(self.G, source=i, target=j) == 1: if sum(nx.common_neighbors(self.G, i, j)) > 5: self.herdsmen[i].friendship_weights.append(1) else: self.herdsmen[i].friendship_weights.append( .75 + .05 * sum(nx.common_neighbors(self.G, i, j))) elif nx.shortest_path_length(self.G, source=i, target=j) == 1: if sum(nx.common_neighbors(self.G, i, j)) > 10: self.herdsmen[i].friendship_weights.append(1) else: self.herdsmen[i].friendship_weights.append( .5 + .05 * sum(nx.common_neighbors(self.G, i, j))) else: self.herdsmen[i].friendship_weights.append( 1 / nx.shortest_path_length( self.G, source=i, target=j)) else: self.herdsmen[i].friendship_weights.append(1 / N) def init_population(self): """ Initialise grass, herdsmen, sheep, and the social network """ self.init_grass() self.init_herdsman() self.init_node_attr() def get_expected_grass_growth(self): """ Get an estimate of the expected grass growth for the next timestep. If grass is fully grown it will return 0.0123 (the average grass growth over its lifetime. """ return sum([ grass.next_density() if grass.density < 0.99 else 0.0123 for grass in self.grass ]) def get_grass_count(self): """ Get a sum of all grass densities. """ return sum([grass.density for grass in self.grass]) def get_herdsman_count(self): return self.schedule_Herdsman.get_agent_count() def get_sheep_count(self): return self.schedule_Sheep.get_agent_count() def new_agent(self, agent_type, pos): """ Create new agent, and add it to the scheduler. """ agent = agent_type(self.next_id(), self, pos) self.grid.place_agent(agent, pos) getattr(self, f"schedule_{agent_type.__name__}").add(agent) return agent def remove_agent(self, agent): """ Remove agent from the grid and the scheduler. """ self.grid.remove_agent(agent) getattr(self, f"schedule_{type(agent).__name__}").remove(agent) def run_model(self, step_count=200): """ Runs the model for a specific amount of steps. """ for i in range(step_count): self.step() def step(self): """ Calls the step method for grass and sheep. """ self.schedule_Grass.step() a = self.get_sheep_count() self.schedule_Sheep.step() b = self.get_sheep_count() c = b / a if a > 0 else 0 self.sheep_survival_rate.append(c) self.schedule_Herdsman.step() self.schedule.step() # save statistics self.datacollector.collect(self)
class VirusOnNetwork(Model): """A virus model with some number of agents""" def __init__( self, num_nodes=10, avg_node_degree=3, initial_outbreak_size=1, virus_spread_chance=0.4, virus_check_frequency=0.4, recovery_chance=0.3, gain_resistance_chance=0.5, ): self.G =nx.empty_graph(0) lis = [] for x in range(5): lis.append(range(random.randint(2,15))) correlative_num = 0 central = 0 for i,x in enumerate(lis): for y in lis[i]: if y > 0: self.G.add_node(correlative_num) self.G.add_edge(correlative_num, central) correlative_num+=1 else: self.G.add_node(correlative_num) correlative_num+=1 central = correlative_num print("Impresion del grapho") print(self.G.nodes) print(self.G.edges) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.initial_outbreak_size = ( initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes ) self.virus_spread_chance = virus_spread_chance self.virus_check_frequency = virus_check_frequency self.recovery_chance = recovery_chance self.gain_resistance_chance = gain_resistance_chance self.datacollector = DataCollector( { "Infected": number_infected, "Susceptible": number_susceptible, "Resistant": number_resistant, } ) # Create agents for i, node in enumerate(self.G.nodes()): a = VirusAgent( i, self, State.SUSCEPTIBLE, self.virus_spread_chance, self.virus_check_frequency, self.recovery_chance, self.gain_resistance_chance, ) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes): a.state = State.INFECTED self.running = True self.datacollector.collect(self) def resistant_susceptible_ratio(self): try: return number_state(self, State.RESISTANT) / number_state( self, State.SUSCEPTIBLE ) except ZeroDivisionError: return math.inf def step(self): self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
class DumbEcoModel(Model): #inicialização verbose = True economy = None initial_assets = None initial_liabilities = None initial_cash = None initial_inventory = None labor_market = None goods_market = None def __init__(self): self.num_firms = 3 # self.num_firms2 = n2 self.num_households = 30 self.running = True self.grid = MultiGrid(1, 5, True) self.result = 0 #posso criar meu scheduler se for o caso self.schedule = RandomActivation(self) self.datacollector = DataCollector( model_reporters={ "GoodsQt": goods_qt, "GoodsVl": goods_vl, "LaborQt": labor_qt, "LaborVl": labor_vl }) # ) # self.datacollector = DataCollector( # model_reporters={"Labor": computeN1,"Goods": getResult} # ) self.initial_assets = dict() self.initial_liabilities = dict() self.initial_cash = 100.0 self.initial_inventory = dict() self.economy = Economy("DumbEconomy", self) self.schedule.add(self.economy) self.grid.place_agent(self.economy, (0, 0)) self.labor_market = Market("LaborMarket", self) self.schedule.add(self.labor_market) self.grid.place_agent(self.labor_market, (0, 1)) self.goods_market = Market("GoodsMarket", self) self.schedule.add(self.goods_market) self.grid.place_agent(self.goods_market, (0, 2)) #### Define goods and services of the economy self.initial_assets = dict() self.initial_liabilities = dict() self.initial_inventory = dict() # initialize households for i in range(self.num_households): self.initial_cash = 10.0 # Intialize Household assets quantity_of_labor = 1000.0 price_of_labor = .5 value_of_labor = quantity_of_labor * price_of_labor quantity_of_food = 0.0 price_of_food = .1 value_of_food = quantity_of_food * price_of_food available_labor = GoodOrService("labor", 1, quantity_of_labor, price_of_labor, value_of_labor) food = GoodOrService("corn", 1, quantity_of_food, price_of_food, value_of_food) self.initial_assets = { available_labor.name_of_gs: available_labor, food.name_of_gs: food } # Intialize Household liabilities quantity_of_food_demmand = random() * 130.0 price_of_food_demmand = 0.1 value_of_food_demmand = quantity_of_food_demmand * price_of_food_demmand food_demmand = GoodOrService("food_demmand", 1, quantity_of_food_demmand, price_of_food_demmand, value_of_food_demmand) self.initial_liabilities = {food_demmand.name_of_gs: food_demmand} # Initialize Household inventory self.initial_inventory_hh = { food.name_of_gs: food, available_labor.name_of_gs: available_labor } fm = DumbHousehold(i, self, self.economy, self.initial_assets, self.initial_liabilities, self.initial_cash, self.initial_inventory_hh) self.schedule.add(fm) self.grid.place_agent(fm, (0, 3)) fm.enter_labor_market(self.labor_market) fm.enter_goods_market(self.goods_market) # initialize firms for i in range(self.num_firms): # initialize firm assets self.initial_cash = 1000.0 quantity_of_food = 10.0 price_of_food = .1 value_of_food = quantity_of_food * price_of_food produced_food = GoodOrService("corn", 1, quantity_of_food, price_of_food, value_of_food) self.initial_assets = {produced_food.name_of_gs: produced_food} # initialize firm liabilities quantity_of_labor = 0.0 price_of_labor = 0.5 value_of_labor = quantity_of_labor * price_of_labor demmanded_labor = GoodOrService("labor", 1, quantity_of_labor, price_of_labor, value_of_labor) self.initial_liabilities = { demmanded_labor.name_of_gs: demmanded_labor } # initialize firm inventory self.initial_inventory_firm = { produced_food.name_of_gs: produced_food } f1 = DumbFirm(i, self, self.economy, self.initial_assets, self.initial_liabilities, self.initial_cash, self.initial_inventory_firm) self.schedule.add(f1) self.grid.place_agent(f1, (0, 4)) f1.enter_labor_market(self.labor_market) f1.enter_goods_market(self.goods_market) def step(self): print([self.schedule.time, " my_step"]) self.schedule.step() # restart market variables self.datacollector.collect(self) print([ "Labor Market:", self.labor_market.unique_id, "Quantity: ", self.labor_market.total_sells_qt, " Value: ", self.labor_market.total_sells_vl ]) print([ "Goods Market:", self.goods_market.unique_id, "Quantity: ", self.goods_market.total_sells_qt, " Value: ", self.goods_market.total_sells_vl ]) self.goods_market.total_sells_qt = 0.0 self.labor_market.total_sells_qt = 0.0 self.goods_market.total_sells_vl = 0.0 self.labor_market.total_sells_vl = 0.0 print([self.schedule.time, "passei"]) def run_model(self, n): for i in range(n): self.step()
class Population(Model): """Population Adapted from https://www.medrxiv.org/content/10.1101/2020.03.18.20037994v1.full.pdf Model Parameters: spread_chance: probability of infection based on contact gamma: mean incubation period alpha: probability of become asymptomatic vs symptomatic gamma_AR: infectious period for asymptomatic people gamma_YR: infectious period for symptomatic people delta: death rate due to disease The social distancing func takes time passed -> new interaction multiplier """ def __init__(self, graph, model_parameters, social_distancing_func=lambda x: 1): # Model initialization self.population_size = model_parameters['population size'] self.initial_outbreak_size = model_parameters['initial outbreak size'] self.graph = graph self.grid = NetworkGrid(self.graph) self.schedule = SimultaneousActivation(self) self.social_distancing_func = social_distancing_func self.datacollector = DataCollector({ # "Exposed": count_exposed, "Susceptible": count_susceptible, "Recovered": count_recovered, # "Asymptomatic": count_asymptomatic, # "Symptomatic": count_symptomatic, "Diseased": count_diseased, "Removed": count_removed }) self.model_parameters = model_parameters for i, node in enumerate(self.graph.nodes()): a = Person(i, self, State.SUSCEPTIBLE, model_parameters, social_distancing_func) self.schedule.add(a) self.grid.place_agent(a, i) if i % 100 == 0: logger.info("Finished with agent " + str(i)) infected_nodes = self.random.sample(self.graph.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes): a.status = State.EXPOSED self.datacollector.collect(self) print("Model initialized...\n") def step(self): self.datacollector.collect(self) self.schedule.step() def run(self, n): for i in range(n): logger.info("Steps Completed: " + str(i)) self.step() def run_until_stable(self): max_steps = 1e3 steps = 0 window_size = 50 while steps < max_steps: self.step() logger.info("Steps Completed:" + str(steps)) if steps > window_size: data = self.get_data() last_value = int(data.tail(1)['Diseased']) if last_value == 0: break window_average = np.mean( data.tail(window_size) ['Diseased']) # Window for determining stopping rule if abs(last_value - window_average) / window_average < 0.005: break steps += 1 def cross_influence(self, influence_coefficients, ratios): for inf_coeff, ratio in zip(influence_coefficients, ratios): susceptibles = list( filter(lambda x: x.status == State.SUSCEPTIBLE, self.grid.get_all_cell_contents())) to_flip = self.random.sample( susceptibles, int(inf_coeff * ratio * len(susceptibles))) for agent in to_flip: agent.status = State.EXPOSED def clear_social_distancing_func(self): """ Clears the social distancing function of the model and all its agents for pickling :return: None """ self.social_distancing_func = None for agent in self.grid.get_all_cell_contents(): agent.social_distancing_func = None def reinstate_social_distancing_func(self, social_distancing_func=lambda x: 1): """ Re-adds the social distancing func to the model and all its agents :param social_distancing_func: social distancing func to be re-added :return: None """ self.social_distancing_func = social_distancing_func for agent in self.grid.get_all_cell_contents(): agent.social_distancing_func = social_distancing_func def save_model(self, filename): """ Save the model to a pickle and dill file :param filename: filename (without extension) to save to :return: None """ with open(filename + ".dil", 'wb') as f: dill.dump(self.social_distancing_func, f) self.clear_social_distancing_func() with open(filename + ".pkl", 'wb') as f: pickle.dump(self, f) def get_data(self): return self.datacollector.get_model_vars_dataframe() '''
class SIRModel(Model): '''Description of the model''' def __init__(self, width, height, infectivity=2.0, infection_duration = 10, immunity_duration = 15, mutation_probability=0, mutation_strength=10, visualise_each_x_timesteps=-1): # Set the model parameters self.infectivity = infectivity # Infection strength per infected individual self.infection_duration = infection_duration # Duration of infection self.immunity_duration = immunity_duration # Duration of infection #mutations self.mutation_probability = mutation_probability self.mutation_strength = mutation_strength #self.mean_inf_duration_list = [] percentage_starting_infected = 0.01 percentage_starting_recovered = 0.01 self.visualise_each_x_timesteps = visualise_each_x_timesteps self.grid = SingleGrid(width, height, torus=False) self.schedule = SimultaneousActivation(self) for (contents, x, y) in self.grid.coord_iter(): # Place randomly generated individuals rand = random.random() cell = Cell((x,y), self) # place random infected cells with a chance if rand < percentage_starting_infected: cell.initialise_as_infected() # place random infected cells with a chance elif rand < percentage_starting_infected + percentage_starting_recovered: cell.initialise_as_recovered() self.grid.place_agent(cell, (x,y)) self.schedule.add(cell) # Add data collector, to plot the number of individuals of different types self.datacollector_cells = DataCollector(model_reporters={ "Infected": fracI, "Susceptible": fracS, "Recovered": fracR, }) # Add data collector, to plot the mean infection duration self.datacollector_meaninfectionduration = DataCollector(model_reporters={"Mean_inf_duration": compute_mean_infduration}) # collects grids per amount of time steps self.grids_saved = [] self.running = True def get_grid_cells(self): return np.array([cell.state for cell in self.schedule.agents]) def step(self): # collect grids for running without browser if self.visualise_each_x_timesteps != -1: if self.schedule.time % self.visualise_each_x_timesteps == 0: print(f'timestep: {self.schedule.time}, saved grid.') self.grids_saved.append(self.get_grid_cells()) self.datacollector_cells.collect(self) self.datacollector_meaninfectionduration.collect(self) self.schedule.step()
class ShoalModel(Model): """ Shoal model class. Handles agent creation, placement and scheduling. Parameters are interactive, using the user-settable parameters defined above. Parameters: n_fish: Initial number of "Fish" agents. width, height: Size of the space. speed: how fast the boids should move. vision: how far around should each Boid look for its neighbors separation: what's the minimum distance each Boid will attempt to keep from any other cohere, separate, match: factors for the relative importance of the three drives. """ def __init__(self, n_fish=100, width=50, height=50, speed=10, vision=10, separation=2, cohere=0.25, separate=0.025, match=0.3): assert speed < width and speed < height, "speed can't be greater than model area dimensions" self.n_fish = n_fish self.vision = vision self.speed = speed self.separation = separation self.schedule = RandomActivation(self) self.space = ContinuousSpace(width, height, torus=True) self.factors = dict(cohere=cohere, separate=separate, match=match) # self.make_obstructions() # Todo: un-comment this line to include obstructions self.make_fish() self.running = True def make_fish(self): """ Create N "Fish" agents. A random position and starting velocity is assigned for each fish. Call data collectors for position and heading of each fish at each data step. """ for i in range(self.n_fish): x = random.randrange(2, (self.space.x_max - 2)) y = random.randrange(2, (self.space.y_max - 2)) pos = np.array((x, y)) velocity = np.random.random( 2) * 2 - 1 # [-1.0 .. 1.0, -1.0 .. 1.0] fish = Fish(i, self, pos, self.speed, velocity, self.vision, self.separation, **self.factors) self.space.place_agent(fish, pos) self.schedule.add(fish) self.datacollector = DataCollector( # model_reporters={"test": test}) model_reporters={ "positions": positions, "heading": heading }) def make_obstructions(self): """ Create N "Obstruct" agents, with set positions & no movement. Borders are defined as coordinate points between the maximum and minimum extent of the width/height of the obstruction. These ranges are drawn from the model space limits, with a slight buffer. In this case, the obstructions form a line to represent an environmental variable such as a thermo- or halocline. """ max_lim = self.space.x_max - 1 min_lim = self.space.x_min + 1 line = range(min_lim, max_lim) borders = np.asarray([(n, 25) for n in line]) x_points = np.ndarray.tolist(borders[:, 0]) y_points = np.ndarray.tolist(borders[:, 1]) points = list(zip(x_points, y_points)) for i in points: # create obstruction agent for all points along the borders pos = np.array(i) obstruct = Obstruct(i, self, pos) self.space.place_agent(obstruct, pos) self.schedule.add(obstruct) def step(self): self.datacollector.collect(self) self.schedule.step()
class BoltzmannWealthModelNetwork(Model): """A model with some number of agents.""" def __init__(self, b=35.98, a=0.6933, beta=0.95, delta=0.08, theta=0.8, N=100): #N- number of agents self.N = N self.b = b self.a = a self.agents = [] self.fit_alpha = 0 self.fit_loc = 0 self.fit_beta = 0 self.t = 0 self.beta = 0.95 self.delta = 0.08 self.theta = 0.8 self.time = 1 #for sensitivity analysis self.G = nx.barabasi_albert_graph(n=N, m=1) nx.set_edge_attributes( self.G, 1, 'weight') #setting all initial edges with a weight of 1 self.nodes = np.linspace(0, N - 1, N, dtype='int') #to keep track of the N nodes self.schedule = RandomActivation(self) self.datacollector = DataCollector(model_reporters={ 'beta': 'b', 'a': 'a', 'TotalTrap': 'Count', 'TotalSwitch': 'total', 'Threshold': 't' }, agent_reporters={ "k": 'k', 'lamda': 'lamda', 'abilitity': 'alpha', 'technology': 'tec' }) for i, node in enumerate(self.G.nodes()): agent = MoneyAgent(i, self) self.schedule.add(agent) self.running = True self.datacollector.collect(self) def Global_Attachment(self): #print("Global Attachment no: {}".format(self.count)) node1 = random.choice(self.nodes) node2 = random.choice(self.nodes) while (self.G.has_edge(node1, node2) == True): node2 = random.choice(self.nodes) node1 = random.choice(self.nodes) #adding the edge node1-node2 for agent in self.agents: if (agent.unique_id == node1): node1_a = agent if (agent.unique_id == node2): node2_a = agent self.G.add_edge(node1, node2, weight=Edge_Weight(node1_a, node2_a, self.b, self.a)) def step(self): #print(self.time) self.schedule.step() # collect data self.Global_Attachment() #for sensitivity analysis self.datacollector.collect(self) agent_df = self.datacollector.get_agent_vars_dataframe() agent_df.reset_index(level=['Step', 'AgentID'], inplace=True) k = agent_df.k.to_numpy() self.t = np.percentile(k, q=10) #print("Threshold = ", self.t) count = 0 #trap = [] agents = [] for agent in self.nodes: df = agent_df.loc[(agent_df["AgentID"] == agent) & (agent_df['k'] < self.t)].reset_index( drop=True) if (not df.empty): agents.append(agent) j = int(df.loc[0].Step) count = 0 while (j < len(df) - 1): if (int(df.loc[j + 1].Step) - int(df.loc[j].Step) == 1): count += 1 j += 1 #print("i = ", i) #trap.append(count) self.Count = count #self.fit_alpha, self.fit_loc, self.fit_beta=stats.gamma.fit(trap) self.time += 1 # #counting number of switches switch = {'Agent': [], 'Total': []} for agent in self.nodes: df = agent_df.loc[agent_df.AgentID == agent].reset_index(drop=True) tech = df.technology.to_numpy() count = 0 for i in range(len(tech) - 1): if ((tech[i] == 'L' and tech[i + 1] == 'H') or (tech[i] == 'H' and tech[i + 1] == 'L')): count += 1 if (count): switch['Agent'].append(agent) switch['Total'].append(count) switch = pd.DataFrame(switch) no_switch = switch.Total.unique() no_switch.sort() total = {no_switch[i]: [] for i in range(len(no_switch))} #print(total) for i in no_switch: total[i] = len(switch.loc[switch.Total == i]) #print(total) self.total = total def run_model(self, n): for i in tqdm(range(n)): self.time = i + 1 self.step()
class LocustModel(Model): """A model with some number of agents.""" def __init__(self, N, width, height, delta_t, R_plus, v, Lambda, alpha, eta, gamma, beta, theta, delta): self.num_agents = N self.width = width self.height = height self.grid = MultiGrid(width, height, True) self.schedule = SimultaneousActivation(self) self.delta_t = 1 self.R_plus = 200.0 self.resources = [self.R_plus] * width self.v = .04 self.Lambda = .00001 self.a = alpha self.b = eta self.r_fm = gamma self.c = beta self.d = theta self.r_mf = delta self.delta_x = v * delta_t # print("Resources" + str(self.resources)) # Create agents for i in range(self.num_agents): a = LocustAgent(i, self) self.schedule.add(a) #print ("Hi, Iss am agent " + str(a.unique_id) +" at grid : (" + str(0) + "," + str(a.unique_id) +')') # Add the agent to a random grid cell self.grid.place_agent(a, (0, (a.unique_id % self.height))) self.datacollector = DataCollector( model_reporters={"Resources": compute_gini}) def get_locust_At(self, index): size = 0 for i in range(self.height): for agent in self.grid.get_cell_list_contents([(index, i)]): if (agent.isFeeding == 0): size += 1 #print("think this is the size:" + str(size)) return size def get_width(self): return self.width def get_height(self): return self.height #Probability function of going from isFeeding -> ~isFeeding def Kmf(self, r): y = self.b - (self.b - self.a) * np.exp(-self.r_fm * r) return y #Probability function of going from ~isFeeding -> isFeeding def Kfm(self, r): y = (self.d - (self.d - self.c) * np.exp(-self.r_mf * r)) return y # Exp function of S to reduce R by. R(t+1) = R*R_exp(S) def updateResources(self, S): y = np.exp(-self.Lambda * S * self.delta_t / self.delta_x) return y def step(self): self.datacollector.collect(self) '''Advance the model by one step.''' self.schedule.step() ##Update Resource for i in range(self.width): locustcount = 0 locustcount = int(self.get_locust_At(i)) self.resources[i] *= self.updateResources(locustcount) resource_history.append(self.resources) # print("Resources" + str(self.resources)) def get_resource(self, index): return self.resources[index]
class SugarscapeModel(Model): def __init__(self, height=50, width=50, init_agents=500, max_metabolism=3, max_vision=10, max_init_sugar=5, min_age=30, max_age=60, init_poll=3, ex_ratio=2, ex_mod=1, poll_growth_rule=True, inheritance_rule=True): self.height = height self.width = width self.init_agents = init_agents self.init_poll = init_poll self.max_metabolism = max_metabolism self.max_vision = max_vision self.max_init_sugar = max_init_sugar self.min_age = min_age self.max_age = max_age self.ex_ratio = ex_ratio self.ex_mod = ex_mod self.replacement_rule = True self.pollution_rule = False self.diffusion_rule = False self.push_rule = False self.poll_growth_rule = poll_growth_rule self.expend_rule = True self.inheritance_rule = inheritance_rule self.map = self.import_map() self.grid = MultiGrid(height, width, torus=True) self.schedule = RandomActivationByType(self) self.datacollector = DataCollector({'Pollution': (lambda m: m.total_pollution), 'Wealth': (lambda m: m.total_wealth/m.init_agents), 'Agents': (lambda m: len(m.schedule.agents_by_type[ScapeAgent]))}, {'Wealth': self.collect_wealth, 'Metabolism': self.collect_metabolism, 'Vision': self.collect_vision}) self.total_wealth = 0 self.total_pollution = 0 self.populate_sugar() self.populate_agents() def step(self): ''' Step method run by the visualization module''' self.schedule.step([ScapeAgent, SugarPatch]) self.datacollector.collect(self) # if self.schedule.time == 20: # self.pollution_rule = True if self.schedule.time == 30: self.push_rule = True self.total_wealth = 0 self.total_pollution = 0 for agent in self.schedule.agents_by_type[ScapeAgent]: self.total_wealth += agent.wealth for patch in self.schedule.agents_by_type[SugarPatch]: self.total_pollution += patch.pollution def import_map(self): ''' Imports a text file into an array to be used when generating and placing the sugar Agents into the grid ''' f = open('Maps/sugar_map.txt', 'r') map_list = [] for line in f: num_list = line.split(' ') for num in num_list: map_list.append(int(num[0])) return map_list def new_agent(self, uid, inheritance): ''' Place a new agent on the sugarscape in order to replace a death''' free = False while not free: location = random.choice([cell for cell in self.grid.coord_iter()]) if len(location[0]) == 1: free = True pos = (location[1], location[2]) patch = self.grid.get_cell_list_contents([pos])[0] if self.inheritance_rule: if inheritance == 'rand': wealth = random.randint(1, self.max_init_sugar) else: wealth = inheritance else: wealth = random.randint(1, self.max_init_sugar) agent = ScapeAgent(uid, pos, wealth, random.randint(1,self.max_metabolism), random.randint(1,self.max_vision), random.randint(self.min_age, self.max_age), patch, self.ex_ratio, self.ex_mod) self.grid.place_agent(agent, agent.pos) self.schedule.add(agent) def populate_agents(self): ''' Place ScapeAgent's in random unoccupied locations on the grid with randomomized sets of parameters ''' cells = [(cell[1], cell[2]) for cell in self.grid.coord_iter()] for i in range(self.init_agents): uid = 'a' + str(i) location = random.choice(cells) cells.remove(location) patch = self.grid.get_cell_list_contents([location])[0] agent = ScapeAgent(uid, location, random.randint(1,self.max_init_sugar), random.randint(1,self.max_metabolism), random.randint(1,self.max_vision), random.randint(self.min_age, self.max_age), patch, self.ex_ratio, self.ex_mod) self.grid.place_agent(agent, location) self.schedule.add(agent) def populate_sugar(self): ''' Place SugarPatch's on every cell with maximum sugar content according to the imported 'sugar_map.txt' file ''' map_i = 0 for cell in self.grid.coord_iter(): x = cell[1] y = cell[2] uid = 's'+str(y)+str(x) # patch = SugarPatch(uid, (x,y), 3) patch = SugarPatch(uid, (x,y), self.map[map_i], self.init_poll) self.grid.place_agent(patch, (x,y)) self.schedule.add(patch) map_i += 1 def collect_wealth(self, agent): '''Method for datacollector''' if isinstance(agent, ScapeAgent): return agent.wealth def collect_metabolism(self, agent): '''Method for datacollector''' if isinstance(agent, ScapeAgent): return agent.metabolism def collect_vision(self, agent): '''Method for datacollector''' if isinstance(agent, ScapeAgent): return agent.vision def calc_gini(self, wealths): '''Returns gini coefficient''' sort_wealths = sorted(wealths) num_agents = len(sort_wealths) gini,count = 0,0 for wealth in sort_wealths: gini += wealth * (num_agents - count) count += 1 gini /= (num_agents*sum(sort_wealths)) return num_agents**(-1) - 2*gini + 1
class CovidModel(Model): """ initialize the model with N agents, of which M agents are infected initially Parameter J and K for numbers of agents who wears face masks; add all the agents in the agent_list for function position_agent """ def __init__(self, N: int, M: int, J: int, K: int, L: int, width, height, hospital_activated: bool, auto_self_isolation: bool, manual_self_isolation_lvl: int): super().__init__() self.agent_number = N self.initial_infected = M self.healthy_with_mask = J self.carrier_with_mask = K self.hospital_capacity = L self.hospital_occupation = 0 self.hospital_activated = hospital_activated self.auto_self_isolation = auto_self_isolation self.manual_self_isolation_lvl = manual_self_isolation_lvl self.grid = SingleGrid(width, height, True) self.schedule = RandomActivation(self) self.schedule_end_steps = 0 self.schedule_end_flag = False self.highest_morbidity_rate = 0 self.running = True self.agent_list = [] self.symptomatic_list = [] self.symptomatic_rate = 0 self.quarantine_toggle = 0 self.quarantine_list = [] self.quarantined_agent_length_1 = 0 self.quarantined_agent_length_2 = 0 for i in range(self.carrier_with_mask): infected_with_mask = CovidAgent(i, self, True, True) self.schedule.add(infected_with_mask) self.agent_list.append(infected_with_mask) for i in range(self.carrier_with_mask, self.initial_infected): infected_without_mask = CovidAgent(i, self, True, False) self.schedule.add(infected_without_mask) self.agent_list.append(infected_without_mask) for i in range(self.initial_infected, (self.agent_number - self.healthy_with_mask)): healthy_without_mask = CovidAgent(i, self, False, False) self.schedule.add(healthy_without_mask) self.agent_list.append(healthy_without_mask) for i in range((self.agent_number - self.healthy_with_mask), self.agent_number): healthy_with_mask = CovidAgent(i, self, False, True) self.schedule.add(healthy_with_mask) self.agent_list.append(healthy_with_mask) for agent in self.agent_list: # for SingleGrid use position_agent to place them the first time self.grid.position_agent(agent) self.data_collector = DataCollector( model_reporters={ "Fatalities": compute_fatalities, "Immune": compute_immune, "Healthy": compute_healthy_agent, "Infected": compute_infection, "Hospital Occupation": compute_hospital_treated, "Fatality Rate": compute_fatality_rate, "Morbidity Rate": compute_morbidity_rate }) def step(self): self.data_collector.collect(self) self.schedule.step() self.get_end_time() self.get_highest_morbidity_rate() # A function to check if all agents are not infected def check_all_agents(self): for agent in self.agent_list: if agent.is_infected: return False return True def get_end_time(self): # if all the agents get rid of infection and the model has not register the steps if self.check_all_agents() and not self.schedule_end_flag: self.schedule_end_steps = self.schedule.steps # set the flag to true to only record the steps once self.schedule_end_flag = True def auto_quarantine_get_symptomatic_rate(self): # if the agent has symptom, append it to the list self.symptomatic_list = [] for agent in self.agent_list: if agent.has_symptom: self.symptomatic_list.append(agent) # then calculate the symptomatic_rate self.symptomatic_rate = len(self.symptomatic_list) / len( self.agent_list) def auto_quarantine_update_quarantine_list(self): # get number of agents who need to be self-isolated for both quarantine level self.quarantined_agent_length_1 = round( len(self.agent_list) * float(quarantine_rate["1"])) self.quarantined_agent_length_2 = round( len(self.agent_list) * float(quarantine_rate["2"])) # level 1 if float(quarantine_threshold['level_1_threshold'] ) <= self.symptomatic_rate < float( quarantine_threshold['level_2_threshold'] ) and self.quarantine_toggle != 1: self.auto_quarantine_reset_quarantine_list() # pick random agents (number set by lvl 1) to stay still self.quarantine_list = self.random.sample( self.agent_list, self.quarantined_agent_length_1) # set the toggle to 1 self.quarantine_toggle = 1 # level 2 elif float(quarantine_threshold['level_2_threshold'] ) <= self.symptomatic_rate and self.quarantine_toggle != 2: self.auto_quarantine_reset_quarantine_list() # pick random agents (number set by lvl 2) to stay still self.quarantine_list = self.random.sample( self.agent_list, self.quarantined_agent_length_2) # set the toggle to 2 self.quarantine_toggle = 2 elif float(quarantine_threshold['level_1_threshold'] ) > self.symptomatic_rate and self.quarantine_toggle != 0: self.auto_quarantine_reset_quarantine_list() # at lvl 0 no one needs to self-isolate self.quarantine_list = [] self.quarantine_toggle = 0 # all the agents in the list to stay still for agent in self.quarantine_list: agent.self_isolation_toggle = True def auto_quarantine_reset_quarantine_list(self): for agent in self.agent_list: agent.self_isolation_toggle = False def manual_quarantine(self): if not self.quarantine_toggle: self.quarantine_toggle = 1 quarantined_agent_length = round( len(self.agent_list) * float(quarantine_rate[str(self.manual_self_isolation_lvl)])) self.quarantine_list = self.random.sample( self.agent_list, quarantined_agent_length) for agent in self.quarantine_list: agent.self_isolation_toggle = True def get_highest_morbidity_rate(self): morbidity_list = [] for agent in self.agent_list: if agent.is_infected: morbidity_list.append(agent) H = len(morbidity_list) morbidity_rate = H / self.agent_number if morbidity_rate > self.highest_morbidity_rate: self.highest_morbidity_rate = morbidity_rate
class CentralizedArchitecture(Model): """ Simulating traditional cloud server web architectures including client-server relationship """ def __init__(self, num_clouds, gateways_per_cloud, sensors_per_gateway, height=100, width=100): """ Create a new model with centralized database architectures. """ super().__init__() self.height = height self.width = width self.schedule = RandomActivationByBreed(self) self.grid = Grid(height, width, torus=False) self.datacollector = DataCollector( {"Temperature": lambda m: self.retrieve_state_updates(m) }) # self.agents_by_breed = defaultdict(dict) for cloud in range(0, num_clouds): current_cloud_id = self.next_id() new_cloud = Cloud(current_cloud_id, self) # self.grid._place_agent() # where do the cloud symbols go? self.schedule.add(new_cloud) for gateway in range(0, gateways_per_cloud): current_gateway_id = self.next_id() new_gateway = Gateway(current_gateway_id, None, 0, current_cloud_id, self) # self.grid._place_agent((0, gateway ), new_gateway) self.schedule.add(new_gateway) for sensor in range(0, sensors_per_gateway): current_sensor_id = self.next_id() pos = (random.randint(0,width -1), random.randint(0,height -1)) new_sensor = Sensor( current_sensor_id, pos, 10, current_gateway_id, self) # self.grid._place_agent(pos, new_sensor) self.schedule.add(new_sensor) def step(self): print("STEP:", self.schedule.steps) self.schedule.step() self.datacollector.collect(self) # if self.duration < @staticmethod def retrieve_state_updates(model): state_updates = {} # # print(model.schedule.agents) # for breed in model.schedule.agents_by_breed: # print(breed) # state_updates[breed] = {} # for agent in model.schedule.agents_by_breed[breed]: # state_updates[breed][agent] = model.schedule.agents_by_breed[breed][agent].state_updates # # recordings[sensor.pos] = sensor.recordings return state_updates
class InfectionModel(Model): """A model for infection spread.""" def __init__(self, human_count, random_spawn, save_plots, floor_plan_file="floorplan_1.txt", N=10, width=10, height=10, ptrans=0.5, progression_period=3, progression_sd=2, death_rate=0.0193, recovery_days=21, recovery_sd=7): # Load floorplan # floorplan = np.genfromtxt(path.join("infection_spread/floorplans/", # floor_plan_file)) with open( os.path.join("infection_spread/floorplans/", floor_plan_file), "rt", ) as f: floorplan = np.matrix( [line.strip().split() for line in f.readlines()]) # Rotate the floorplan so it's interpreted as seen in the text file floorplan = np.rot90(floorplan, 3) # Check what dimension our floorplan is width, height = np.shape(floorplan) # Init params self.num_agents = N self.initial_outbreak_size = 1 self.recovery_days = recovery_days self.recovery_sd = recovery_sd self.ptrans = ptrans self.death_rate = death_rate self.running = True self.dead_agents = [] self.width = width self.height = height self.human_count = human_count # Set up model objects self.schedule = RandomActivation(self) self.grid = MultiGrid(height, width, torus=False) # Used to easily see if a location is an Exit or Door, # since this needs to be done a lot self.exit_list = [] self.door_list = [] # If random spawn is false, spawn_list will contain the list of # possible spawn points according to the floorplan self.random_spawn = random_spawn self.spawn_list = [] # Load floorplan objects for (x, y), value in np.ndenumerate(floorplan): value = str(value) floor_object = None if value == "W": floor_object = Wall((x, y), self) elif value == "E": floor_object = Exit((x, y), self) self.exit_list.append((x, y)) self.door_list.append((x, y)) # Add exits to doors as well elif value == "D": floor_object = Door((x, y), self) self.door_list.append((x, y)) elif value == "S": self.spawn_list.append((x, y)) if floor_object: self.grid.place_agent(floor_object, (x, y)) self.schedule.add(floor_object) # Create a graph of traversable routes, used by agents for pathing self.graph = nx.Graph() for agents, x, y in self.grid.coord_iter(): pos = (x, y) # If the location is empty, or a door if not agents or any(isinstance(agent, Door) for agent in agents): neighbors = self.grid.get_neighborhood(pos, moore=True, include_center=True, radius=1) for neighbor in neighbors: # If there is contents at this location and they are # not Doors or FireExits, skip them if not self.grid.is_cell_empty(neighbor) \ and neighbor not in self.door_list: continue self.graph.add_edge(pos, neighbor) """ # Start placing human agents # for i in range(0, self.human_count): # if self.random_spawn: # Place human agents randomly # pos = self.grid.find_empty() # else: # Place human agents at specified spawn locations # pos = random.choice(self.spawn_list) #if pos: # Create a random human # health = random.randint(self.MIN_HEALTH * 100, self.MAX_HEALTH * 100 ) / 100 # speed = random.randint(self.MIN_SPEED, self.MAX_SPEED) """ for i in range(self.num_agents): a = Human(unique_id=i, model=self) self.schedule.add(a) # Add the agent to a random grid cell x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) # make some agents infected at start infected = np.random.choice([0, 1], p=[0.98, 0.02]) if infected == 1: a.state = State.INFECTED a.recovery_time = self.get_recovery_time() self.datacollector = DataCollector( # model_reporters={"Gini": compute_gini}, agent_reporters={"State": "state"}) def get_recovery_time(self): return int( self.random.normalvariate(self.recovery_days, self.recovery_sd)) def step(self): """ Advance the model by one step. """ self.schedule.step() self.datacollector.collect(self)
class VirusOnNetwork(Model): """A virus model with some number of agents""" def __init__( self, num_nodes=10, avg_node_degree=3, initial_outbreak_size=1, virus_spread_chance=0.4, virus_check_frequency=0.4, recovery_chance=0.3, gain_resistance_chance=0.5, ): self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.initial_outbreak_size = (initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes) self.virus_spread_chance = virus_spread_chance self.virus_check_frequency = virus_check_frequency self.recovery_chance = recovery_chance self.gain_resistance_chance = gain_resistance_chance self.datacollector = DataCollector({ "Infected": number_infected, "Susceptible": number_susceptible, "Resistant": number_resistant, }) # Create agents for i, node in enumerate(self.G.nodes()): a = VirusAgent( i, self, State.SUSCEPTIBLE, self.virus_spread_chance, self.virus_check_frequency, self.recovery_chance, self.gain_resistance_chance, ) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes): a.state = State.INFECTED self.running = True self.datacollector.collect(self) def resistant_susceptible_ratio(self): try: return number_state(self, State.RESISTANT) / number_state( self, State.SUSCEPTIBLE) except ZeroDivisionError: return math.inf def step(self): self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
class RegimeModel(Model): # A model of a regime def __init__(self, num_nodes, productivity, demand, shape, network_param, resource_inequality, capacity_inequality, uncertainty, shock): # Initialize graph self.num_nodes = num_nodes self.G = create_graph(shape, num_nodes, network_param) self.G = max(nx.connected_component_subgraphs(self.G), key=lambda g: len(g.nodes())) # Initialize other attributes self.grid = NetworkGrid(self.G) self.schedule = StagedActivation(self, stage_list=[ 'add_link', 'cut_link', 'settle_env_transfer', 'settle_link_transfer' ], shuffle=True, shuffle_between_stages=True) self.productivity = productivity self.demand = demand self.resource_inequality = resource_inequality self.capacity_inequality = capacity_inequality self.uncertainty = uncertainty self.datacollector = DataCollector({ "Gini Coeff. of Capacity": gini_capacity, "Gini Coeff. of Resources": gini_resources, "Satisfied": num_satisfied, "Dissatisfied": num_dissatisfied }) self.shock = shock # Initialize agents on graph (cap capacity at 25 to avoid overflow errors) for i, node in enumerate(self.G.nodes()): a = RegimeAgent(i, self, self.demand, math.exp(paretovariate(1 / resource_inequality)), min(25, paretovariate(1 / capacity_inequality)), True) self.schedule.add(a) self.grid.place_agent(a, node) for e in self.G.edges(): capacity_transfer = expovariate(1 / self.uncertainty) agents = ([ self.G.nodes[e[0]]['agent'][0], self.G.nodes[e[1]]['agent'][0] ]) resource_transfer = calculate_transfer( self.productivity, agents[0].capacity + capacity_transfer, agents[1].capacity + capacity_transfer, capacity_transfer) # Give the agent with the higher capacity 2*transfer capacity to # simulate a prior interaction where the transfer was made, when # both the nodes had their current capacity + transfer. max_capacity_agent = max(agents, key=attrgetter('capacity')) min_capacity_agent = min(agents, key=attrgetter('capacity')) max_capacity_agent.capacity += 2 * capacity_transfer min_capacity_agent.exp_resources += resource_transfer # Initialize edge attributes for transfer rates, memory of # resource transfers, and which agent is the patron. self.G.edges[e]['capacity_t'] = capacity_transfer self.G.edges[e]['resource_t'] = resource_transfer self.G.edges[e]['exp_resources'] = [ resource_transfer, resource_transfer ] self.G.edges[e]['patron'] = max_capacity_agent self.running = True self.datacollector.collect(self) def step(self): self.schedule.step() self.datacollector.collect(self) def run_model(self, n): for i in range(n): if i == n // 2: # Halfway through simulation, add shock. self.productivity *= self.shock self.step()
class MASArchitecture(Model): def __init__(self, width, height, distributed, model_reporters_dict=None, agent_reporters_dict=None, newOrderProbability=10, quantity=1, splitSize=1, verbose=True, searchSize=1, ordersPerWeek=1, batchRun=False): sys.stdout = sys.__stdout__ print('New Order Probability {} - Quantity {} - Orders Per week {}'. format(newOrderProbability, quantity, ordersPerWeek)) self.verbose = verbose if (verbose): sys.stdout = sys.__stdout__ else: sys.stdout = open(os.devnull, 'w') self.grid = MultiGrid(width, height, True) self.schedule = RandomActivation(self) self.running = True self.minutes = 0 self.hours = 0 self.days = 0 self.weeks = 0 self.batchRun = batchRun # This is the number of external orders the marketplace recieved per week self.ordersPerWeek = ordersPerWeek # This is the probability at any given step that a factory will produce a new order self.newOrderProbability = newOrderProbability self.splitSize = splitSize federationCentre = SOEF(1, self, (49, 49), searchSize) self.schedule.add(federationCentre) self.grid.place_agent(federationCentre, federationCentre.coordinates) numberOfCheap = round(quantity * 0.5) numberOfASAP = round(quantity * 0.5) # This fella is only used to produce orders, has no capabilities of it's own self.dummyFactoryId = self.schedule.get_agent_count() + 1 self.dummyFactoryAgent = FactoryAgent(self.dummyFactoryId, self, (2, 2), distributed, newOrderProbability, splitSize=splitSize) self.schedule.add(self.dummyFactoryAgent) self.grid.place_agent(self.dummyFactoryAgent, self.dummyFactoryAgent.coordinates) while numberOfCheap != 0: numberOfCheap -= 1 for factory in factoriesAndCapabilities: factoryNumber = self.schedule.get_agent_count() + 1 newFactoryAgent = FactoryAgent(factoryNumber, self, factory[0], distributed, newOrderProbability, splitSize=splitSize) self.schedule.add(newFactoryAgent) self.grid.place_agent(newFactoryAgent, newFactoryAgent.coordinates) incrementedYCoordinate = 2 for capability in factory[1]: coordinates = (factory[0][0] + 3, factory[0][1] - incrementedYCoordinate) newMachine = MachineAgent(self.schedule.get_agent_count() + 1, self, capability, coordinates, factoryNumber, asapOrCheap='cheap') self.schedule.add(newMachine) self.grid.place_agent(newMachine, newMachine.coordinates) incrementedYCoordinate += 2 while numberOfASAP != 0: numberOfASAP -= 1 for factory in factoriesAndCapabilities: coordinates = (factory[0][0] + 15, factory[0][1] + 15) factoryNumber = self.schedule.get_agent_count() + 1 newFactoryAgent = FactoryAgent(factoryNumber, self, coordinates, distributed, newOrderProbability, splitSize=splitSize) self.schedule.add(newFactoryAgent) self.grid.place_agent(newFactoryAgent, newFactoryAgent.coordinates) incrementedYCoordinate = 2 for capability in factory[1]: newCoordinates = (coordinates[0] + 3, coordinates[1] - incrementedYCoordinate) newMachine = MachineAgent(self.schedule.get_agent_count() + 1, self, capability, newCoordinates, factoryNumber, asapOrCheap='asap') self.schedule.add(newMachine) self.grid.place_agent(newMachine, newMachine.coordinates) incrementedYCoordinate += 2 if (model_reporters_dict is None): self.datacollector = DataCollector() else: self.datacollector = DataCollector( model_reporters=model_reporters_dict, agent_reporters=agent_reporters_dict) def step(self): if (not self.batchRun): sys.stdout = sys.__stdout__ print("Steps {} Weeks {} Days {} Hours {}".format( self.schedule.steps, self.weeks, self.days, self.hours)) if (not self.verbose): sys.stdout = open(os.devnull, 'w') # Generate new orders if (self.ordersPerWeek <= 40): number = random.randrange(40 // self.ordersPerWeek) if (number == 0): capabilities = ['3D_SLS'] orderAgent = OrderAgent(self.schedule.get_agent_count() + 1, self, capabilities, self.dummyFactoryId, splitSize=self.splitSize) self.schedule.add(orderAgent) self.grid.place_agent( orderAgent, self.dummyFactoryAgent.newOrderCoordinates) newMessage = Message(self.dummyFactoryId, 'findResources') orderAgent.receivedMessages.append(newMessage) else: count = self.ordersPerWeek // 40 while count != 0: count -= 1 capabilities = ['3D_SLS'] orderAgent = OrderAgent(self.schedule.get_agent_count() + 1, self, capabilities, self.dummyFactoryId, splitSize=self.splitSize) self.schedule.add(orderAgent) self.grid.place_agent( orderAgent, self.dummyFactoryAgent.newOrderCoordinates) newMessage = Message(self.dummyFactoryId, 'findResources') orderAgent.receivedMessages.append(newMessage) '''Advance the model by one step.''' # Way easier to convert to hours... if (self.ordersPerWeek <= 40): number = random.randrange(40 // self.ordersPerWeek) if (number == 0): capabilities = ['3D_SLS'] orderAgent = OrderAgent(self.schedule.get_agent_count() + 1, self, capabilities, self.dummyFactoryId, splitSize=self.splitSize) self.schedule.add(orderAgent) self.grid.place_agent( orderAgent, self.dummyFactoryAgent.newOrderCoordinates) newMessage = Message(self.dummyFactoryId, 'findResources') orderAgent.receivedMessages.append(newMessage) else: count = self.ordersPerWeek // 40 while count != 0: count -= 1 capabilities = ['3D_SLS'] orderAgent = OrderAgent(self.schedule.get_agent_count() + 1, self, capabilities, self.dummyFactoryId, splitSize=self.splitSize) self.schedule.add(orderAgent) self.grid.place_agent( orderAgent, self.dummyFactoryAgent.newOrderCoordinates) newMessage = Message(self.dummyFactoryId, 'findResources') orderAgent.receivedMessages.append(newMessage) self.schedule.step() # Collect data self.datacollector.collect(self) self.hours += 1 if (self.hours == 8): self.hours = 0 self.days += 1 if (self.days == 5): self.days = 0 self.weeks += 1
class NegotiationModel(object): ''' ''' step_stages = ["initialize", "send_threats", "resolve_threats", "resolve_attacks", "finalize", ("resolve_attacks", "Model")] def __init__(self, agents): ''' Instantiate a new model. ''' self.agents = agents self.schedule = StagedActivation(self, self.step_stages) for agent in self.agents: self.schedule.add(agent) self.agent_names = {agent.name: agent for agent in agents} self.running = True self.stage_delta = 1 / len(self.step_stages) self.log = EventLog() self.datacollector = DataCollector( {"Median": get_median, #lambda m: m.find_median(), "Mean": get_mean}, #lambda m: m.find_mean()}, {"Position": get_position}) #lambda a: a.position}) def step(self): ''' One step of the model, through all step_stages. ''' self.datacollector.collect(self) self.schedule.step() if all(a.position == self.agents[0].position for a in self.agents): self.running = False def run_model(self, steps): ''' Run model for `steps` steps, or until it converges. ''' while self.schedule.steps < steps and self.running: self.step() def resolve_attacks(self): done_attacks = [] new_attacks = self.log.get_events(timestamp=self.schedule.steps, action="Attack") for attack in new_attacks: if (attack.target, attack.source) in done_attacks: continue source = self.agent_names[attack.source] target = self.agent_names[attack.target] if self.resolve_conflict(source, target): winner, loser = source, target else: winner, loser = target, source winner.win_conflict(loser) loser.lose_conflict(winner) done_attacks.append((attack.source, attack.target)) def resolve_conflict(self, side_1, side_2): ''' Resolve an active conflict between sides 1 and 2. Args: side_1, side_2: Agent objects Returns: True if side_1 wins, False if side_2 wins. ''' if side_1.position == side_2.position: print(side_1) print(side_2) raise Exception("This attack shouldn't be happening.") side_1_power = 0 side_2_power = 0 for agent in self.agents: v = agent.allocate_support(side_1, side_2) if v < 0: side_2_power += abs(v) else: side_1_power += v try: p = side_1_power / (side_1_power + side_2_power) except Exception as e: print(side_1) print(side_2) raise e if random.random() < p: return True else: return False def add_event(self, source, target, action): ''' Add an event to the event log. ''' if type(source) is not str: source = source.name if type(target) is not str: target = target.name self.log.add_event(source, target, self.schedule.steps, action) def find_median(self): ''' Returns the estimated median voter position. Uses the original BDM model calculation. ''' pairwise_contests = {} for i, j in permutations(self.agents, 2): votes = 0 for agent in self.agents: delta = (abs(agent.position - j.position) - abs(agent.position - i.position)) votes += agent.capability * agent.salience * delta pairwise_contests[(i.position, j.position)] = votes return max(pairwise_contests, key=lambda x: pairwise_contests[x])[0] def find_mean(self): ''' Returns the estimated mean position, weighted by capability*salience. ''' weighted_sum = 0 weights = 0 for agent in self.agents: weight = agent.capability * agent.salience weighted_sum += weight * agent.position weights+= weight return weighted_sum / weights
class SchellingModel(Model): '''Model class for Schelling segregation model''' def __init__(self, height=20, width=20, density=.8, group_ratio=.66, minority_ratio=.5, homophily=3): self.height = height self.width = width self.density = density self.group_ratio = group_ratio self.minority_ratio = minority_ratio self.homophily = homophily self.happy = 0 self.segregated = 0 self.schedule = RandomActivation(self) self.grid = SingleGrid(height, width, torus=False) self.place_agents() self.datacollector = DataCollector( {'happy': (lambda m: m.happy), 'segregated': (lambda m: m.segregated)}) self.running = True def step(self): '''Run one step of model''' self.schedule.step() self.calculate_stats() self.datacollector.collect(self) if self.happy == self.schedule.get_agent_count(): self.running = False def place_agents(self): for cell in self.grid.coord_iter(): x, y = cell[1:3] if random.random() < self.density: if random.random() < self.group_ratio: if random.random() < self.minority_ratio: group = 0 else: group = 1 else: group = 2 agent = SchellingAgent((x,y), group) self.grid.position_agent(agent, (x,y)) self.schedule.add(agent) for agent in self.schedule.agents: count = 0 for neighbour in self.grid.iter_neighbors(agent.pos, moore=False): if neighbour.group == agent.group: count += 1 agent.similar = count def calculate_stats(self): happy_count = 0 avg_seg = 0 for agent in self.schedule.agents: avg_seg += agent.similar if agent.similar >= self.homophily: happy_count += 1 self.happy = happy_count self.segregated = avg_seg/self.schedule.get_agent_count()
class Travel_Model(Model): def __init__(self, networks=None, season_length=91, n_agents=100, max_steps=1000): self.n_steps = 0 self.max_steps = max_steps self.season_length = season_length self.schedule = BaseScheduler(self) if networks is None: networks = self._demo_networks() self.n_seasons = len(networks) # space nodes = networks[0].nodes() edges = [] for network in networks: edges.append(network.edges(data=True)) self.network = MultilayerNetworkSpace(nodes,edges) # agents for n in range(n_agents): start, dest = sample(nodes,2) start_time = randrange(self.n_seasons * self.season_length ) agent = Travel_Agent(start,dest,start_time,n) self.schedule.add(agent) # data collection self.dc = DataCollector( { "enroute": lambda m: self.count_en_route(m) }, { "position": lambda a: a.pos, "travel_time": lambda a: a.travel_time } ) self.dc.collect(self) self.running = True def step(self): self.schedule.step() self.dc.collect(self) self.n_steps +=1 if self.count_en_route(self) == 0 or self.n_steps >= self.max_steps: self.running = False def get_season(self, time): return (time // self.season_length) % self.n_seasons def _demo_networks(self): networks = [] for i in range(3): g = nx.random_graphs.watts_strogatz_graph(100, 2, 0.05) dists = np.random.randint(1,30, g.number_of_edges()) dists = dict(zip(g.edges(),dists)) nx.set_edge_attributes(g, 'distance', dists) networks.append(g) return networks @staticmethod def count_en_route(model): count = len(model.schedule.agents) for agent in model.schedule.agents: if agent.pos == agent.dest: count -=1 return count
class SugarscapeCg(Model): """ Sugarscape 2 Constant Growback """ LIVING_ANTS = "Living Ants" DEAD_ANTS = "Dead Ants" PERCENT_DEAD_LOW_VISION = "% Dead Low Vision" PERCENT_DEAD_HIGH_VISION = "% Dead High Vision" verbose = False # Print-monitoring def __init__(self, height=50, width=50, initial_population=100, recreate=0, share_knowledge=False, solidarity=False): """ Create a new Constant Growback model with the given parameters. Args: initial_population: Number of population to start with """ # Set parameters self.height = height self.width = width self.initial_population = initial_population self.recreate = recreate self.schedule = RandomActivationByBreed(self) self.shared_knowledge = SharedKnowledge(width=width, height=height) if share_knowledge else None self.solidarity = self.shared_knowledge and solidarity self.grid = MultiGrid(self.height, self.width, torus=False) self.datacollector = DataCollector({ "initial_population": lambda m: m.initial_population, "shared_knowledge": lambda m: m.shared_knowledge is not None, "recreate": lambda m: m.recreate, "solidarity": lambda m: m.solidarity, self.LIVING_ANTS: lambda m: m.schedule.get_breed_count(Ant), self.DEAD_ANTS: lambda m: m.schedule.num_dead, self.PERCENT_DEAD_LOW_VISION: lambda m: m.schedule.percent_dead(filter=self.PERCENT_DEAD_LOW_VISION), self.PERCENT_DEAD_HIGH_VISION: lambda m: m.schedule.percent_dead(filter=self.PERCENT_DEAD_HIGH_VISION), "min_sugar": lambda m: m.schedule.min_sugar(), "max_sugar": lambda m: m.schedule.max_sugar(), "avg_sugar": lambda m: m.schedule.avg_sugar(), "stdev_sugar": lambda m: m.schedule.stdev_sugar(), }) # Create sugar sugar_distribution = np.genfromtxt("sim/sugar-map.txt") for _, x, y in self.grid.coord_iter(): max_sugar = sugar_distribution[x, y] sugar = Sugar((x, y), self, max_sugar) self.grid.place_agent(sugar, (x, y)) self.schedule.add(sugar) # Create ants: for i in range(self.initial_population): self._create_ant() self.running = True self.datacollector.collect(self) def _create_ant(self): sugar = self.random.randrange(6, 25) metabolism = self.random.randrange(2, 4) vision = self.random.randrange(1, 5) while self.is_occupied(pos:=( self.random.randrange(self.width), self.random.randrange(self.height) )):
class OpinionModel(Model): ''' A model with some number of agents Keyword arguments: N -- Number of agents neighborhoods -- An NxX matrix. neighborhoods[a] is agent a\'s list of neighbors. The neighborhoods is not actually a matrix. Each inner list may be of different length. intial_opinions -- This is a list size N of opinions. Each inner list must have the same length, the opinions are then labeled Opinion0, Opinion1, ..., OpinionZ. potentials -- Potentials is a 1-D list of functions Each function describes how agents are influenced by innodes. coupling -- AxA matrix, where A = |opinions|. Describes how opinions affect each other. ''' def __init__(self, N, neighborhoods, weights, initial_opinions, potentials, coupling, schedule="simultaneous"): self.ALPHA = .001 self.num_agents = N self.neighborhoods = neighborhoods self.initial_opinions = initial_opinions self.potentials = potentials self.coupling = coupling if weights is None: self.weights = [None for i in range(self.num_agents)] else: self.weights = weights #Set schedule if schedule == 'simultaneous': self.schedule = StagedActivation( self, stage_list=["simultaneousStep", "simultaneousAdvance"], shuffle=False) elif schedule == 'pairwise': self.schedule = StagedActivation( self, stage_list=["reset", "pairwiseStep", "noise"], shuffle=True) #stage_list is where you add "coupling" and "noise". #Create agents for i in range(self.num_agents): a_params = OpinionAgentParameters(i, self, self.neighborhoods[i], self.weights[i], self.potentials[i], initial_opinions[i]) if schedule == 'simultaneous': a = OpinionAgent(a_params) elif schedule == 'pairwise': a = OpinionAgent(a_params) self.schedule.add(a) ag_reps = dict() for i in range(len(self.schedule.agents[0].opinions)): ag_reps["Opinion" + str(i)] = self.makeLam(i) self.datacollector = DataCollector(agent_reporters=ag_reps) def step(self): ''' Advance the model one step ''' self.datacollector.collect(self) self.schedule.step() def run(self, steps): for i in range(steps): self.step() def total_change(self): sum = 0 for j in range(len(self.schedule.agents[0].opinions)): for i in range(self.num_agents): sum += abs(self.schedule.agents[i].opinions[0] - self.schedule.agents[i].nextOpinion[0]) return sum def makeLam(self, i): ''' Don't use outside of the context of the OpinionModel's init method. This function is needed in order for the DataCollector to work properly, intializing the lambdas inside __init__ inside a for loop causes the last value of the iterator to be used for all of the functions, causing unwanted behavior. So that's why this function exists. ''' return lambda a: a.opinions[i]
class covid_Model(Model): """ Class representing Model. The Model is responsible for the logical structure of the ABM. Here, the model is set up both visually to the grid and logical to the algorithm The most important attributes are listed below. The rest is commented in the class below. --------- N : int Number of agents in grid in current timestep height : int height of grid width : int width of grid setUpType : List List of classroom types/construction (2,3,4/H,R,G) grid : grid Visual grid schedule : module Logical grid datacollector : module collects data that we want to analyze time_step : int Indicates current time step minute_count : int Counting minutes hour_count : int Counting hours day_count : int Counting days TAs : List List of TAs at the current time_step classroom_2 : List List of seat-positions with corrosponding direction in the H set up classroom_3 : List List of seat-positions with corrosponding direction in the R set up classroom_4 : List List of seat-positions with corrosponding direction in the G set up """ def __init__(self, N, height, width, setUpType): self.n_agents = N self.height = height self.grid = MultiGrid(width, height, torus=False) #torus wraps edges self.schedule = SimultaneousActivation(self) self.setUpType = setUpType self.datacollector = DataCollector( model_reporters={ "infected": lambda m: get_infected_count(self), "Agent_count": lambda m: all_agents_count(self), "recovered": lambda m: get_recovered_count(self), "Home": lambda m: get_home_sick_count(self), "Reproduction": lambda m: get_list_of_reproduction(self) }) #Lists to hold agent-objects with with different states self.TAs = [] self.agents_at_home = [] self.recovered_agents = [] self.infected_agents = [] self.canteen_agents_at_work = [] self.canteen_backups_to_go_home = [] #Counting minutes and days self.minute_count = 1 self.hour_count = 1 self.day_count = 1 # self.door = () self.entre = [(15, 0), (16, 0), (17, 0), (25, 34), (25, 33), (25, 32)] #Define time schedule + when breaks are self.class_times = [105, 120, 225, 300, 405, 420, 525] self.breaks_for_all = [i for i in range(105, 120)] + [ i for i in range(225, 300) ] + [i for i in range(405, 420)] self.breaks_for_ft = [i for i in range(105, 300)] self.breaks_for_sf = [i for i in range(225, 420)] self.other_courses, self.range46, self.range13 = [], [], [] create_courses(self) #Define classrooms + directions self.classroom_2 = [((0, 0), dir['E']), ((1, 0), dir['N']), ((2, 0), dir['N']), ((3, 0), dir['N']), ((4, 0), dir['N']), ((5, 0), dir['N']), ((6, 0), dir['N']), ((7, 0), dir['N']), ((0, 1), dir['E']), ((0, 2), dir['E']), ((0, 3), dir['E']), ((0, 4), dir['E']), ((0, 5), dir['E']), ((0, 6), dir['E']), ((0, 7), dir['E']), ((0, 8), dir['E']), ((0, 9), dir['E']), ((1, 9), dir['S']), ((2, 9), dir['S']), ((3, 9), dir['S']), ((4, 9), dir['S']), ((5, 9), dir['S']), ((6, 9), dir['S']), ((7, 9), dir['S'])] self.classroom_3 = [((1, 6), dir['E']), ((1, 7), dir['E']), ((1, 8), dir['E']), ((1, 9), dir['E']), ((2, 0), dir['E']), ((2, 1), dir['E']), ((2, 2), dir['E']), ((2, 3), dir['E']), ((3, 6), dir['E']), ((3, 7), dir['E']), ((3, 8), dir['E']), ((3, 9), dir['E']), ((4, 0), dir['E']), ((4, 1), dir['E']), ((4, 2), dir['E']), ((4, 3), dir['E']), ((5, 6), dir['E']), ((5, 7), dir['E']), ((5, 8), dir['E']), ((5, 9), dir['E']), ((6, 0), dir['E']), ((6, 1), dir['E']), ((6, 2), dir['E']), ((6, 3), dir['E'])] self.classroom_4 = [((1, 1), dir['N']), ((1, 2), dir['S']), ((2, 1), dir['N']), ((2, 2), dir['S']), ((1, 4), dir['N']), ((1, 5), dir['S']), ((2, 4), dir['N']), ((2, 5), dir['S']), ((1, 7), dir['N']), ((1, 8), dir['S']), ((2, 7), dir['N']), ((2, 8), dir['S']), ((4, 1), dir['N']), ((4, 2), dir['S']), ((5, 1), dir['N']), ((5, 2), dir['S']), ((4, 4), dir['N']), ((4, 5), dir['S']), ((5, 4), dir['N']), ((5, 5), dir['S']), ((4, 7), dir['N']), ((4, 8), dir['S']), ((5, 7), dir['N']), ((5, 8), dir['S'])] #Set up seats, canteen tables, queuing area self.seats, self.seat, self.toilet = [], (), () self.canteen_table_1 = [((22, y), dir['E']) for y in range(25, 33)] + [ ((18, y), dir['E']) for y in range(25, 33) ] + [((14, y), dir['E']) for y in range(25, 33)] self.canteen_table_2 = [((23, y), dir['W']) for y in range(25, 33)] + [ ((19, y), dir['W']) for y in range(25, 33) ] + [((15, y), dir['W']) for y in range(25, 33)] self.canteen_tables = self.canteen_table_1 + self.canteen_table_2 self.canteen_counter = 0 self.enter_canteen_area12 = [(x, y) for y in range(0, 4) for x in range(17, 26)] self.enter_canteen_area10 = [(x, y) for y in range(0, 4) for x in range(21, 26)] self.canteen_queue_area = [(22, 3), (24, 3)] + [ (23, y) for y in range(3, 21) ] + [(25, y) for y in range(5, 19)] self.classroom_area = [(x, y) for y in range(0, height + 1) for x in range(0, 9)] self.toilet_queue_area = [(x, y) for x in range(8, 15) for y in [height - 1, height - 2]] set_up_initial_attributes(self) #Make seats (if family group restriction is on, dont shuffle) self.seat = make_classroom_seating(setUpType, self) for list in self.seat: if family_groups == False: #Shuffle self.seats.append(random.sample(list, k=len(list))) elif family_groups == True: #Dont shuffle self.seats.append(list) self.copy_of_seats = self.seats self.datacollector.collect(self) self.running = True if 0 < percentages_of_vaccinated < 1: vaccinate_agents(self) def step(self): '''' Invoked every time step and is multi-purposed. Collect data in the data-collector and invokes all agents' step() function Tracks and manage time and time count, including weekends, day off and off school parameters at students. Initiate several different functions; chosing agents to go toilet, set students with questions in class, make classroom seating, set canteen employees, :param self: class-object :return: None ''' #Only let people go to toilet, if toilet is open (closed when everyone go home in breaks) if go_home_in_breaks == False: choose_agents_to_go_to_toilet(self) #Go home if you dont have any more courses today set_off_school(self) #Day off if self.day_count % 7 == 2 or self.day_count % 7 == 3 or self.day_count % 7 == 4 or self.day_count % 7 == 5: set_day_off(self) #Update classroom seatings if go_home_in_breaks == False: if self.minute_count in [1, 119, 299, 419]: self.seats = make_classroom_seating(self.setUpType, self) #Handle employees set_canteen_employees(self) #Answer questions students_with_questions(self) #Tell the students that are attending class next, that they need to go to class. Timing this depends on #wether or not everyone go home in breaks or not set_students_go_to_class_next(self) #Step every agent and collect data self.schedule.step() self.datacollector.collect(self) #Time count, manage weekends and toilet cleaning self.minute_count += 1 if self.minute_count % 60 == 0: self.hour_count += 1 if self.minute_count % 526 == 0: self.day_count += 1 if self.day_count in [6, 13, 20, 27, 34, 41, 48, 55, 62]: self.day_count += 2 weekend(self) self.minute_count = 1 self.hour_count = 1 try: self.toilet.has_been_infected = False #Clean toilet everyday except: return
class World(Model): def __init__(self, N, coop, e_prob, width=100, height=100): self.num_agents = N self.grid = MultiGrid(width, height, False) self.schedule = RandomActivation(self) self.running = True self.population_center_x = np.random.randint( POP_MARGIN, self.grid.width - POP_MARGIN) self.population_center_y = np.random.randint( POP_MARGIN, self.grid.height - POP_MARGIN) self.num_energy_resources = RESERVE_SIZE self.num_explorers = 0 self.num_exploiters = 0 self.datacollector = DataCollector(model_reporters={ "Num_Explorer": "num_explorers", "Num_Exploiter": "num_exploiters" }) self.bases = self.init_base() self.ages = [] self.memoryLens = [ ] # This will store average memory length at death for agent self.expected_ages = [] self.member_tracker = [] self.energy_tracker = [] self.decay_rates = self.init_decay_rates() # add social agents to the world for i in range(1, self.num_agents + 1): if np.random.uniform() <= EXPLORER_RATIO: # create a new explorer a = Explorer("explorer_%d" % (i), self, coop) self.num_explorers += 1 else: # create a new exploiter a = Exploiter("exploiter_%d" % (i), self, coop, e_prob) self.num_exploiters += 1 # keep society members confined at beginning x = np.random.randint(self.population_center_x - POP_SPREAD, self.population_center_x + POP_SPREAD) y = np.random.randint(self.population_center_y - POP_SPREAD, self.population_center_y + POP_SPREAD) self.grid.place_agent(a, (x, y)) # add agent to scheduler self.schedule.add(a) self.expected_ages.append(a.energy / a.living_cost) # add energy reserves to the world for i in range(self.num_energy_resources): a = EnergyResource("energy_reserve_%d" % (i), self, self.decay_rates[i]) # decide location of energy reserve x = np.random.randint(0, self.grid.width) y = np.random.randint(0, self.grid.height) self.grid.place_agent(a, (x, y)) self.schedule.add(a) def init_base(self): pos = (self.population_center_x, self.population_center_y) return self.grid.get_neighborhood(pos, True, radius=POP_SPREAD) def init_decay_rates(self): decay_rates = list( np.random.uniform(-0.1, 0.02, self.num_energy_resources)) np.random.shuffle(decay_rates) return decay_rates def step(self): self.datacollector.collect(self) self.schedule.step() self.member_tracker.append((self.num_explorers, self.num_exploiters)) # keep track of total energy in world if self.schedule.time % 50 == 0: energies = [ e.reserve for e in self.schedule.agents if isinstance(e, EnergyResource) ] if len(energies) > 0: mean_energy = np.mean(energies) else: mean_energy = 0 self.energy_tracker.append(mean_energy) # change location of energy resources every 500 steps randomly # and change decay_rate to opposite every 100 steps if self.schedule.time % 50 == 0: for e in self.schedule.agents: if isinstance(e, EnergyResource) and np.random.uniform() < 0.1: # change location e.decay_rate *= -1 if self.schedule.time % 500 == 0: self.grid.remove_agent(e) x = np.random.randint(0, self.grid.width) y = np.random.randint(0, self.grid.height) self.grid.place_agent(e, (x, y))
class WolfSheepPredation(Model): ''' Wolf-Sheep Predation Model ''' height = 20 width = 20 initial_sheep = 100 initial_wolves = 50 sheep_reproduce = 0.04 wolf_reproduce = 0.05 wolf_gain_from_food = 20 grass = False grass_regrowth_time = 30 sheep_gain_from_food = 4 verbose = False # Print-monitoring description = 'A model for simulating wolf and sheep (predator-prey) ecosystem modelling.' def __init__(self, height=20, width=20, initial_sheep=100, initial_wolves=50, sheep_reproduce=0.04, wolf_reproduce=0.05, wolf_gain_from_food=20, grass=False, grass_regrowth_time=30, sheep_gain_from_food=4): ''' Create a new Wolf-Sheep model with the given parameters. Args: initial_sheep: Number of sheep to start with initial_wolves: Number of wolves to start with sheep_reproduce: Probability of each sheep reproducing each step wolf_reproduce: Probability of each wolf reproducing each step wolf_gain_from_food: Energy a wolf gains from eating a sheep grass: Whether to have the sheep eat grass for energy grass_regrowth_time: How long it takes for a grass patch to regrow once it is eaten sheep_gain_from_food: Energy sheep gain from grass, if enabled. ''' # Set parameters self.height = height self.width = width self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves self.sheep_reproduce = sheep_reproduce self.wolf_reproduce = wolf_reproduce self.wolf_gain_from_food = wolf_gain_from_food self.grass = grass self.grass_regrowth_time = grass_regrowth_time self.sheep_gain_from_food = sheep_gain_from_food self.schedule = RandomActivationByBreed(self) self.grid = MultiGrid(self.height, self.width, torus=True) self.datacollector = DataCollector({ "Wolves": lambda m: m.schedule.get_breed_count(Wolf), "Sheep": lambda m: m.schedule.get_breed_count(Sheep) }) # Create sheep: for i in range(self.initial_sheep): x = random.randrange(self.width) y = random.randrange(self.height) energy = random.randrange(2 * self.sheep_gain_from_food) sheep = Sheep((x, y), self, True, energy) self.grid.place_agent(sheep, (x, y)) self.schedule.add(sheep) # Create wolves for i in range(self.initial_wolves): x = random.randrange(self.width) y = random.randrange(self.height) energy = random.randrange(2 * self.wolf_gain_from_food) wolf = Wolf((x, y), self, True, energy) self.grid.place_agent(wolf, (x, y)) self.schedule.add(wolf) # Create grass patches if self.grass: for agent, x, y in self.grid.coord_iter(): fully_grown = random.choice([True, False]) if fully_grown: countdown = self.grass_regrowth_time else: countdown = random.randrange(self.grass_regrowth_time) patch = GrassPatch((x, y), self, fully_grown, countdown) self.grid.place_agent(patch, (x, y)) self.schedule.add(patch) self.running = True self.datacollector.collect(self) def step(self): self.schedule.step() # collect data self.datacollector.collect(self) if self.verbose: print([ self.schedule.time, self.schedule.get_breed_count(Wolf), self.schedule.get_breed_count(Sheep) ]) def run_model(self, step_count=200): if self.verbose: print('Initial number wolves: ', self.schedule.get_breed_count(Wolf)) print('Initial number sheep: ', self.schedule.get_breed_count(Sheep)) for i in range(step_count): self.step() if self.verbose: print('') print('Final number wolves: ', self.schedule.get_breed_count(Wolf)) print('Final number sheep: ', self.schedule.get_breed_count(Sheep))
class KSUModel(Model): """A model simulating KSU student""" def __init__(self, n_students, n_active: int, width: int, height: int): self.running = True self.schedule = SimultaneousActivation(self) self.grid = SingleGrid(width, height, torus=False) self.n_students: int = n_students self._semester_gen = self._gen_semester_code() self.semester = next(self._semester_gen) self.ALL_GENDERS = gen_gender(self.n_students) # Majors self.F1SEQ1_MAJORS = gen_f1seq1_majors(self.n_students) self.major_switcher = MajorSwitch() # Adding Student to KSU Environment for i in range(self.n_students): # Percentage of student agent that will be active and the rest inactive per_active = n_active / 100 if np.random.binomial(1, per_active): student = Student(i, self, self.ALL_GENDERS[i]) student.majors.append(self.F1SEQ1_MAJORS[i]) else: student = Student(i, self, self.ALL_GENDERS[i], False) student.majors.append("N/A") self.schedule.add(student) self.grid.position_agent(student) self.datacollector = DataCollector( agent_reporters={ "GPA": "gpa", "ATTEMPTED_HRS": "attempted_hrs", "EARNED_HRS": "earned_hrs", "Major": "curr_major" }) def step(self): self.datacollector.collect(self) self.schedule.step() try: self.update_semester() self.update_credit_hrs() self.update_gpa() except StopIteration: agent_gpa = self.datacollector.get_agent_vars_dataframe() agent_gpa.to_csv("gpa.csv", index=False) self.running = False def update_semester(self) -> None: self.semester = next(self._semester_gen) def update_credit_hrs(self): active_students: List[Student] = [ student for student in self.schedule.agents if student.is_active ] n_active_students = len(active_students) earned_hrs = [ round(earned_hr) for earned_hr in gen_credit_hrs(self.semester, n_active_students) ] attempted_hrs = [ round(attempted_hr) for attempted_hr in gen_credit_hrs( self.semester, n_active_students, False) ] for i, student in enumerate(active_students): curr_major = tlz.last(student.majors) new_earned_hrs = student.earned_hrs new_attempted_hrs = student.attempted_hrs # Check if earned & attempted credit hours exists for current semester if earned_hrs: new_earned_hrs = 0 if curr_major == "E" else earned_hrs[i] new_attempted_hrs = 0 if curr_major == "E" else attempted_hrs[i] student.earnedhrs_history.append(new_earned_hrs) student.attemptedhrs_history.append(new_attempted_hrs) def update_gpa(self): active_students: List[Student] = [ student for student in self.schedule.agents if student.is_active ] n_active_students = len(active_students) gpa_distr = [ round(earned_hr) for earned_hr in gen_credit_hrs(self.semester, n_active_students) ] for i, student in enumerate(active_students): curr_major = tlz.last(student.majors) new_gpa = student.gpa # Check if gpa exists for current semester if gpa_distr: new_gpa = 0 if curr_major == "E" else gpa_distr[i] student.gpa_history.append(new_gpa) @staticmethod def _gen_semester_code(): semester_pos = [x for x in range(1, 7)] semester_season = product(semester_pos, ("F", "F", "S", "S")) seq = cycle([1, 2]) for semester in semester_season: pos, season = semester yield f"{season}{pos}SEQ{next(seq)}"
class InfoSpread(Model): """A virus model with some number of agents""" def __init__( self, num_nodes=10, avg_node_degree=3, rewire_prob=.1, initial_outbreak_size=1, threshold=2, inf_prob_model=1, ): self.num_nodes = num_nodes self.G = nx.watts_strogatz_graph( n=self.num_nodes, k=avg_node_degree, p=rewire_prob) #G generate graph structure self.grid = NetworkGrid( self.G ) #grid is the Masa native defintion of space: a coorindate with specified topology on which agents sits and interact self.schedule = SimultaneousActivation(self) self.initial_outbreak_size = (initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes) self.inf_prob_model = inf_prob_model self.datacollector = DataCollector({ "Infected": number_infected, "Susceptible": number_susceptible, }) # Create agents for i, node in enumerate(self.G.nodes()): a = User(i, self, "susceptible", threshold, inf_prob_model) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes): a.state = "infected" neighbors_nodes = self.grid.get_neighbors(a.pos) for n in self.grid.get_cell_list_contents(neighbors_nodes): n.state = 'infected' self.running = True self.datacollector.collect(self) def proportion_infected(self): try: return number_state(self, "infected") / self.num_nodes except ZeroDivisionError: return math.inf def step(self): self.schedule.step( ) #this model updates with symoutanous schedule, meaning, # collect data self.datacollector.collect(self) def run_model(self, n): ''' could experiment terminating model here too''' for i in range(n): self.step()
class Society(Model): """ A very simple of a society where taxes are collected and redistributed. """ def __init__(self, num_agents, num_evaders, collecting_rates, redistribution_rates, invest_rate, catch, fine_rate): assert len(collecting_rates) == len( redistribution_rates ), "different number of collecting and redistributing segments." self.num_agents = num_agents # number of agents self.common_fund = 0. # collected taxes for each transition self.num_segments = len(collecting_rates) # number of segments self.collecting_rates = collecting_rates # collecting rates by group self.redistribution_rates = redistribution_rates # redistribution rates by group self.invest_rate = invest_rate # interest return to the investment of the common fund assert num_evaders <= self.num_agents, "more evaders than agents" self.num_evaders = num_evaders # number of evader agents self.catch = catch # probability of catching an evader self.fine_rate = fine_rate # fine to be imposed if an evader in caught self.schedule = RandomActivation(self) self.running = True # create agents for i in range(self.num_agents): a = Individual(self, i) self.schedule.add(a) # assign some of the agents as evaders randomly for _ in range(self.num_evaders): random_agent = choice(self.schedule.agents) while random_agent.is_evader: random_agent = choice(self.schedule.agents) random_agent.is_evader = True # assign agents to their wealth group self.assign_agents_to_segments() # data collector on wealth of agents and Gini index of society self.data_collector = DataCollector( model_reporters={"Gini_wealth": compute_gini_wealth}, agent_reporters=dict(Wealth="wealth", Segment="segment", Position="position", Evader="is_evader")) # collect initial data self.data_collector.collect(self) def assign_agents_to_segments(self): """ Assign the agents in a model to their wealth segment and overall position. """ # assign agents to their wealth segment sorted_agents = sorted(self.schedule.agents, key=lambda a: a.wealth) # assign agents to their position in ranking for i in range(len(sorted_agents)): setattr(sorted_agents[i], 'position', self.num_agents - i - 1) # assign agents to their segment cut_index = int(self.num_agents / self.num_segments) for n in range(self.num_segments): for ag in sorted_agents[n * cut_index:(n + 1) * cut_index]: setattr(ag, 'segment', n) try: [ setattr(ag, 'segment', n) for ag in sorted_agents[(n + 1) * cut_index:] ] except: pass def step(self): """ Taxes and (if any) fines are collected into the common fund, and redistributed with interest. """ self.common_fund = 0. # collect taxes from all agents self.schedule.step() # redistribute common fund for ind in self.schedule.agents: ind.wealth += self.common_fund * ( 1 + self.invest_rate) * self.redistribution_rates[ ind.segment] * self.num_segments / self.num_agents # recompute segments self.assign_agents_to_segments() # collect data self.data_collector.collect(self)
class DCGame(Model): def __init__(self, adjMat, visibles, adversaries): self.adjMat = adjMat #matrix that keeps track of all players and their neighbors self.schedule = SimultaneousActivation( self ) # An activation in which players' states are effectively updated simultaneously as opposed to sequentially self.numAgents = len(adjMat) self.terminate = False # if all non-adversarial players have reached consensus, terminal state is achieved self.time = 0 # logging information self.log = Log() # total number of color changes in a game self.colorChanges = 0 # convert adjMat to adjList def getAdjList(adjMat): adjList = {key: [] for key in range(self.numAgents)} for node in range(self.numAgents): #adjList[node] = [idx for idx, value in enumerate(adjMat[node]) if value == True] adjList[node] = [ idx for idx, value in enumerate(adjMat[node]) if value == 'True' ] return adjList self.adjList = getAdjList(self.adjMat) ############# designate visible, adversarial, and consensus nodes ############# self.visibleColorNodes = visibles self.adversarialNodes = adversaries self.consensusNodes = [ n for n in range(self.numAgents) if n not in self.adversarialNodes ] # adversarial nodes and regular nodes should not overlap assert set(self.adversarialNodes) & set(self.consensusNodes) == set() # visible nodes should belong to regular nodes assert set(self.visibleColorNodes) & set(self.consensusNodes) == set( self.visibleColorNodes) # logging simulation configuration self.log.add("#visible nodes: " + str(self.visibleColorNodes)) self.log.add("#adversarial nodes: " + str(self.adversarialNodes)) self.log.add("#consensus nodes: " + str(self.consensusNodes) + '\n') ############# initialize all agents ############# for i in range(self.numAgents): # if i is a visible node isVisibleNode = i in self.visibleColorNodes # if i is an adversarial isAdversarial = i in self.adversarialNodes # make sure adversarial nodes are not intersected with visible nodes assert isVisibleNode & isAdversarial == False neighbors = self.adjList[i] # visible color nodes in i's neighbors vNode = list(set(neighbors) & set(self.visibleColorNodes)) a = GameAgent(i, isVisibleNode, isAdversarial, neighbors, vNode, self) self.schedule.add(a) # instantiate all nodes' neighbors and visibleColorNodes for agent in self.schedule.agents: agent.instantiateNeighbors(self) agent.instantiateVisibleColorNodes(self) self.datacollector = DataCollector( model_reporters={ "red": getRed, "green": getGreen }, agent_reporters={"agent_color": lambda a: a.color}) # simulate the whole model for one step def step(self): # # # if either red or green reaches consensus, terminates! # # in terminal state we do not collect data if not self.terminate: self.datacollector.collect(self) self.schedule.step() return self.terminate def simulate(self, simulationTimes): for i in range(simulationTimes): self.updateTime(i) # update model's time terminate = self.step() if terminate: break #added by Yifan hasWhitePlayers = False if not terminate: # if consensus was not reached in the simulation for agent in self.schedule.agents: if not agent.isAdversarial and not agent.isVisibleNode and agent.color == "white": #at least one consensus player remained white hasWhitePlayers = True # output log file to disk self.log.outputLog('result/simResult.txt') simulatedResult = self.datacollector.get_model_vars_dataframe() return (simulatedResult, hasWhitePlayers) # update model's clock def updateTime(self, t): self.time = t def setTerminal(self): assert self.terminate == False self.terminate = True def addRecord(self, msg): self.log.add(msg) # for degub purpose only def outputAdjMat(self, path): with open(path, 'w') as fid: for line in self.adjMat: # convert list of boolean values to string values tline = ["1" if item else "0" for item in line] fid.write(' '.join(tline) + '\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.range = 5 self.neighborhood_deltas = self.initialize_neighborhood_deltas() 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 = Grid(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): while True: x = self.random.randrange(1, self.grid.width - 1) y = self.random.randrange(1, self.grid.height - 1) pos = (x, y) if not pos in taken_pos: break new_human = Pedestrian(i, self, pos, self.exponent, seed=i) self.schedule.add(new_human) self.grid.place_agent(new_human, pos) taken_pos.append(pos) self.data_collector = DataCollector() self.running = True self.data_collector.collect(self) def initialize_neighborhood_deltas(self): neighborhood_deltas = [] for x in range(-self.range, self.range + 1): for y in range(-self.range, self.range + 1): if x**2 + y**2 <= self.range**2: neighborhood_deltas.append((x, y)) return neighborhood_deltas 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 get_neighbor_ids(self, position, id): x = position[0] y = position[1] neighbors = [] for deltas in self.neighborhood_deltas: dx = deltas[0] dy = deltas[1] nx, ny = x + dx, y + dy if (not (0 <= nx < self.width) or not (0 <= ny < self.height)): continue content = self.grid[nx][ny] if isinstance(content, Pedestrian) and content.unique_id != id: #neighbors += [content.unique_id] neighbors += [content.unique_id] return neighbors 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 ''' #print('ADJACENCY UPDATE') agents = self.schedule.agents for i, agent in enumerate(agents): neighbor_ids = self.get_neighbor_ids(agent.pos, agent.unique_id) for neighbor_id in neighbor_ids: if neighbor_id > agent.unique_id: self.adjacency_matrix[agent.unique_id, neighbor_id] += 1 def step(self): self.schedule.step() self.update_adjecency_matrix() #self.current_step_contacts=[] #self.data_collector.collect(self) def run(self, steps): for i in range(steps): 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 LanguageModel(Model): """ A model that simulates Language Emergence through Self-Organization. For more information check the original paper: https://digital.csic.es/bitstream/10261/127969/1/Spatial%20Vocabulary.pdf """ def __init__(self, n, literate, r, alpha, beta, new_word_rate, antecipated_prob, success_window, width, height): self.num_agents = n self.literate = literate self.change_rate = r self.alpha = alpha self.beta = beta self.new_word_rate = new_word_rate self.success_window = success_window self.antecipated_prob = antecipated_prob # Last arg, if True makes grid toroidal self.grid = MultiGrid(width, height, False) # At each step, agents move in random order self.schedule = RandomActivation(self) self.running = True self.vocabulary = {} self.success_array = [] self.total_dialogs = 0 # Initialize a vocabulary. For each meaning it will collect the words used by the agents if literate > 0: for e in range(self.num_agents): self.vocabulary[e] = {STD_WORDS[e]: list(range(literate))} else: for e in range(self.num_agents): self.vocabulary[e] = {} # Create agents for i in range(self.num_agents): if i < self.literate: a = LanguageAgent(i, self, True) else: a = LanguageAgent(i, self, False) self.schedule.add(a) # Add the agent to a random grid cell x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) self.datacollector = DataCollector( model_reporters={ "Average Success (of the last window of dialogs)": compute_graph}, agent_reporters={"Meanings": "meanings"} ) def step(self): # TODO: Refactor the conditional out of this step method. self.datacollector.collect(self) self.total_dialogs = sum( [a.number_of_dialogs for a in self.schedule.agents]) # show global vocabulary every 50 iterations if self.schedule.time % 50 == 0: print("Dialog ", self.total_dialogs) self.showVocabulary() self.schedule.step() def showVocabulary(self): print("----------------") for e in self.vocabulary: text = str(e) + ": " for word in self.vocabulary[e]: text += str(word) + ":" + str(self.vocabulary[e][word]) + " " print(text) print("----------------") def addSuccess(self, success): if len(self.success_array) + 1 >= self.success_window: del self.success_array[0] self.success_array.append(success)
class WolfSheep(Model): ''' Wolf-Sheep Predation Model ''' height = 20 width = 20 initial_sheep = 100 initial_wolves = 50 sheep_reproduce = 0.04 wolf_reproduce = 0.05 wolf_gain_from_food = 20 grass = False grass_regrowth_time = 30 sheep_gain_from_food = 4 verbose = False # Print-monitoring description = 'A model for simulating wolf and sheep (predator-prey) ecosystem modelling.' def __init__(self, height=20, width=20, initial_sheep=100, initial_wolves=50, sheep_reproduce=0.04, wolf_reproduce=0.05, wolf_gain_from_food=20, grass=False, grass_regrowth_time=30, sheep_gain_from_food=4): ''' Create a new Wolf-Sheep model with the given parameters. Args: initial_sheep: Number of sheep to start with initial_wolves: Number of wolves to start with sheep_reproduce: Probability of each sheep reproducing each step wolf_reproduce: Probability of each wolf reproducing each step wolf_gain_from_food: Energy a wolf gains from eating a sheep grass: Whether to have the sheep eat grass for energy grass_regrowth_time: How long it takes for a grass patch to regrow once it is eaten sheep_gain_from_food: Energy sheep gain from grass, if enabled. ''' super().__init__() # Set parameters self.height = height self.width = width self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves self.sheep_reproduce = sheep_reproduce self.wolf_reproduce = wolf_reproduce self.wolf_gain_from_food = wolf_gain_from_food self.grass = grass self.grass_regrowth_time = grass_regrowth_time self.sheep_gain_from_food = sheep_gain_from_food self.schedule = RandomActivationByBreed(self) self.grid = MultiGrid(self.height, self.width, torus=True) self.datacollector = DataCollector( {"Wolves": lambda m: m.schedule.get_breed_count(Wolf), "Sheep": lambda m: m.schedule.get_breed_count(Sheep)}) # Create sheep: for i in range(self.initial_sheep): x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.sheep_gain_from_food) sheep = Sheep(self.next_id(), (x, y), self, True, energy) self.grid.place_agent(sheep, (x, y)) self.schedule.add(sheep) # Create wolves for i in range(self.initial_wolves): x = self.random.randrange(self.width) y = self.random.randrange(self.height) energy = self.random.randrange(2 * self.wolf_gain_from_food) wolf = Wolf(self.next_id(), (x, y), self, True, energy) self.grid.place_agent(wolf, (x, y)) self.schedule.add(wolf) # Create grass patches if self.grass: for agent, x, y in self.grid.coord_iter(): fully_grown = self.random.choice([True, False]) if fully_grown: countdown = self.grass_regrowth_time else: countdown = self.random.randrange(self.grass_regrowth_time) patch = GrassPatch(self.next_id(), (x, y), self, fully_grown, countdown) self.grid.place_agent(patch, (x, y)) self.schedule.add(patch) self.running = True self.datacollector.collect(self) def step(self): self.schedule.step() # collect data self.datacollector.collect(self) if self.verbose: print([self.schedule.time, self.schedule.get_breed_count(Wolf), self.schedule.get_breed_count(Sheep)]) def run_model(self, step_count=200): if self.verbose: print('Initial number wolves: ', self.schedule.get_breed_count(Wolf)) print('Initial number sheep: ', self.schedule.get_breed_count(Sheep)) for i in range(step_count): self.step() if self.verbose: print('') print('Final number wolves: ', self.schedule.get_breed_count(Wolf)) print('Final number sheep: ', self.schedule.get_breed_count(Sheep))
class Schelling(Model): ''' Model class for the Schelling segregation model. ''' def __init__(self, height=20, width=20, density=0.8, minority_pc=0.2, homophily=3, num_steps=1000, server=True): ''' ''' self.server = server self.num_steps = num_steps self.height = height self.width = width self.density = density self.minority_pc = minority_pc self.homophily = homophily self.schedule = RandomActivation(self) self.grid = SingleGrid(height, width, torus=True) self.happy = 0 self.datacollector = DataCollector( {"happy": "happy"}, # Model-level count of happy agents # For testing purposes, agent's individual x and y { "x": lambda a: a.pos[0], "y": lambda a: a.pos[1] }) # Set up agents # We use a grid iterator that returns # the coordinates of a cell as well as # its contents. (coord_iter) for cell in self.grid.coord_iter(): x = cell[1] y = cell[2] if self.random.random() < self.density: if self.random.random() < self.minority_pc: agent_type = 1 else: agent_type = 0 agent = SchellingAgent((x, y), self, agent_type) self.grid.position_agent(agent, (x, y)) self.schedule.add(agent) self.running = True self.datacollector.collect(self) def step(self): ''' Run one step of the model. If All agents are happy, halt the model. ''' self.happy = 0 # Reset counter of happy agents self.schedule.step() # collect data self.datacollector.collect(self) if self.happy == self.schedule.get_agent_count(): self.running = False def run_model(self, n=None): if n: self.num_steps = n if self.server == False: for _ in range(self.num_steps): self.step() return self else: from .server import server server.launch()
class VariableSchelModel(Model): def __init__(self, density, width, height, satisfaction_ratio=[0.5, 0.5], group_count=2, group_pct=[0.5]): """ Schelling's Model with a variable number of 'groups' :param density: density of the total number of agents, expressed as a percentage of size of model :param width: width of the model :param height: height of the model :param group_count: number of 'groups' :param satisfaction_ratio: satisfaction ratios for the different groups, takes an array of length equivalent to the number of 'groups' :param group_pct: group population size as a percentage of total number of agents, takes an array of length one less than the number of groups. Last group percentage is calculated automatically """ if isinstance(density, str): print('string') if group_count < 2: raise ValueError('Group Count cannot be less than 2!') if group_count - 1 != len(group_pct): raise ValueError( 'Group Percentages and Group Count mismatch! ' 'Group percentages should be 1 less than the number of groups') if len(satisfaction_ratio) != group_count: raise ValueError( 'Satisfaction ratio should be a list with length equivalent to the group count' ) self.num_agents = int(density * (width * height)) self.running = True self.grid = SingleGrid(width, height, True) self.schedule = RandomActivation(self) self.satisfaction_ratio = satisfaction_ratio self.reached_equilibrium = False self.agents = [] self.ratio_sum = 0 self.lowest_ratio = 1 self.mean_ratio = 0 id_offset = 0 for group in range(group_count): if group == group_count - 1: # Final count (Percentage isn't explicit, have to calculate,i.e., group_pct length doesnt match ratio) pct = 1 - sum(group_pct) for i in range(id_offset, id_offset + int(pct * self.num_agents)): a = SchelAgent(i, self, group) self.agents.append(a) id_offset += 1 self.schedule.add(a) self.grid.place_agent(a, self.grid.find_empty()) else: for i in range( id_offset, id_offset + int(group_pct[group] * self.num_agents)): a = SchelAgent(i, self, group) self.agents.append(a) id_offset += 1 self.schedule.add(a) self.grid.place_agent(a, self.grid.find_empty()) self.data_collector = DataCollector(model_reporters={ "mean ratio value": lambda model: model.mean_ratio, "lowest ratio value": lambda model: model.lowest_ratio }, agent_reporters={ "coordinates": lambda agent: agent.pos, "group": lambda agent: agent.group, "satisfaction": lambda agent: agent.satisfied, "agent ratio value": lambda agent: agent.ratio }) self.data_collector.collect(self) def step(self): self.ratio_sum = 0 self.lowest_ratio = 1.0 self.reached_equilibrium = True self.schedule.step() self.mean_ratio = self.ratio_sum / self.num_agents self.data_collector.collect(self) # Debugging for server charts # print(self.mean_ratio) # print(self.lowest_ratio) # Stops model if model reached equilibrium self.running = False if self.reached_equilibrium is True else True
class Modelo(Model): #Algunas constantes SUCEPTIBLE = 0 EXPUESTO = 1 INFECTADO = 2 RECUPERADO = 3 salud_to_str = { 0: 'Suceptible', 1: 'Expuesto', 2: 'Infectado', 3: 'Recuperado' } def __init__(self, N, city_object, agent_object): super().__init__() self.num_ind = N self.city_object = city_object self.agent_object = agent_object self.schedule = RandomActivation(self) self.crearciudad() self.grid = self.ciudad.nodes['aurrera']['espacio'] self.datacollector = DataCollector( model_reporters={ 'Suceptibles': self.conteo_func(self.SUCEPTIBLE), 'Expuestos': self.conteo_func(self.EXPUESTO), 'Infectados': self.conteo_func(self.INFECTADO), 'Recuperados': self.conteo_func(self.RECUPERADO) }) self.conteo_instantaneo = [N, 0, 0, 0] def crearciudad(self): self.ciudad = self.city_object(self, self.agent_object) for ind in self.ciudad.generarindividuos(): self.schedule.add(ind) #Se planta un infectado en la simulación self.schedule.agents[0].salud = self.INFECTADO #Se crean las casas distribuyendo los individuos self.ciudad.crear_hogares() #Se agrega una tienda a la ciudad y se conecta con todas las casas self.ciudad.crear_nodo('aurrera', tipo='tienda', tamano=25) self.ciudad.conectar_a_casas('aurrera') def step(self): self.conteo() self.datacollector.collect(self) self.schedule.step() def conteo(self): #Una función para contar los casos actuales en la ciudad self.conteo_instantaneo = [0, 0, 0, 0] for a in self.schedule.agents: self.conteo_instantaneo[a.salud] += 1 return self.conteo_instantaneo def conteo_func(self, tipo): def contar(modelo): return modelo.conteo_instantaneo[tipo] return contar
class CivilViolenceModel(Model): """ Model 1 from "Modeling civil violence: An agent-based computational approach," by Joshua Epstein. http://www.pnas.org/content/99/suppl_3/7243.full Attributes: height: grid height width: grid width citizen_density: approximate % of cells occupied by citizens. cop_density: approximate % of calles occupied by cops. citizen_vision: number of cells in each direction (N, S, E and W) that citizen can inspect cop_vision: number of cells in each direction (N, S, E and W) that cop can inspect legitimacy: (L) citizens' perception of regime legitimacy, equal across all citizens max_jail_term: (J_max) active_threshold: if (grievance - (risk_aversion * arrest_probability)) > threshold, citizen rebels arrest_prob_constant: set to ensure agents make plausible arrest probability estimates movement: binary, whether agents try to move at step end max_iters: model may not have a natural stopping point, so we set a max. """ def __init__(self, height, width, citizen_density, cop_density, citizen_vision, cop_vision, legitimacy, max_jail_term, active_threshold=.1, arrest_prob_constant=2.3, movement=True, max_iters=1000): super(CivilViolenceModel, self).__init__() self.height = height self.width = width self.citizen_density = citizen_density self.cop_density = cop_density self.citizen_vision = citizen_vision self.cop_vision = cop_vision self.legitimacy = legitimacy self.max_jail_term = max_jail_term self.active_threshold = active_threshold self.arrest_prob_constant = arrest_prob_constant self.movement = movement self.running = True self.max_iters = max_iters self.iteration = 0 self.schedule = RandomActivation(self) self.grid = Grid(height, width, torus=True) model_reporters = { "Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"), "Active": lambda m: self.count_type_citizens(m, "Active"), "Jailed": lambda m: self.count_jailed(m)} agent_reporters = { "x": lambda a: a.pos[0], "y": lambda a: a.pos[1], 'breed': lambda a: a.breed, "jail_sentence": lambda a: getattr(a, 'jail_sentence', None), "condition": lambda a: getattr(a, "condition", None), "arrest_probability": lambda a: getattr(a, "arrest_probability", None) } self.dc = DataCollector(model_reporters=model_reporters, agent_reporters=agent_reporters) unique_id = 0 if self.cop_density + self.citizen_density > 1: raise ValueError( 'Cop density + citizen density must be less than 1') for (contents, x, y) in self.grid.coord_iter(): if random.random() < self.cop_density: cop = Cop(unique_id, (x, y), vision=self.cop_vision, model=self) unique_id += 1 self.grid[y][x] = cop self.schedule.add(cop) elif random.random() < ( self.cop_density + self.citizen_density): citizen = Citizen(unique_id, (x, y), hardship=random.random(), regime_legitimacy=self.legitimacy, risk_aversion=random.random(), threshold=self.active_threshold, vision=self.citizen_vision, model=self) unique_id += 1 self.grid[y][x] = citizen self.schedule.add(citizen) def step(self): """ Advance the model by one step and collect data. """ self.schedule.step() self.dc.collect(self) self.iteration += 1 if self.iteration > self.max_iters: self.running = False @staticmethod def count_type_citizens(model, condition, exclude_jailed=True): """ Helper method to count agents by Quiescent/Active. """ count = 0 for agent in model.schedule.agents: if agent.breed == 'cop': continue if exclude_jailed and agent.jail_sentence: continue if agent.condition == condition: count += 1 return count @staticmethod def count_jailed(model): """ Helper method to count jailed agents. """ count = 0 for agent in model.schedule.agents: if agent.breed == 'citizen' and agent.jail_sentence: count += 1 return count
class PabsModel(Model): """Model your your pandemic""" def __init__(self, num_agents=10, width=10, height=10, initial_outbreak_size=0.1, virus_spread_chance=0.4, virus_check_frequency=0.4, recovery_chance=0.3, gain_resistance_chance=0.5, min_infection_duration=5, death_chance=0.5, resistance_duration=-1, movers=0.1): self.agents = set() self.num_agents = num_agents self.width = width self.height = height self.grid = MultiGrid(height, width, True) self.schedule = RandomActivation(self) self.initial_outbreak_size = initial_outbreak_size if initial_outbreak_size <= 1.0 else 1.0 self.virus_spread_chance = virus_spread_chance self.virus_check_frequency = virus_check_frequency self.recovery_chance = recovery_chance self.gain_resistance_chance = gain_resistance_chance self.resistance_duration = resistance_duration self.movers = movers self.death_chance = death_chance self.datacollector = DataCollector({"Infected": number_infected, "Susceptible": number_susceptible, "Resistant": number_resistant, "Dead": number_dead, "Total cases": number_total }) # Create agents for i in range(self.num_agents): a = self.create_new_agent(i, min_infection_duration) mrn = self.random.random() if mrn < movers: a.movable = True self.schedule.add(a) self.agents.add(a) # Add the agent to the node x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) # # Infect some nodes # infected_nodes = self.random.sample(self., self.initial_outbreak_size) # for a in self.grid.get_cell_list_contents(infected_nodes): # a.state = State.INFECTED self.running = True self.datacollector.collect(self) def create_new_agent(self, i, min_infection_duration): rn = self.random.random() if rn <= self.initial_outbreak_size: a = PabsAgent(i, self, State.INFECTED, self.virus_spread_chance, self.virus_check_frequency, self.recovery_chance, self.gain_resistance_chance, min_infection_duration, self.death_chance, movable=False) else: a = PabsAgent(i, self, State.SUSCEPTIBLE, self.virus_spread_chance, self.virus_check_frequency, self.recovery_chance, self.gain_resistance_chance, min_infection_duration, self.death_chance, movable=False) return a def resistant_susceptible_ratio(self): try: return number_state(self, State.RESISTANT) / number_state(self, State.SUSCEPTIBLE) except ZeroDivisionError: return math.inf def step(self): self.schedule.step() # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()