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 = 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 COVID_model(Model): def __init__(self): super().__init__(Model) self.susceptible = 0 self.dead = 0 self.recovered = 0 self.infected = 0 interactions = model_params.parameters['interactions'] self.population = model_params.parameters['population'] self.SIR_instance = SIR.Infection( self, ptrans=model_params.parameters['ptrans'], reinfection_rate=model_params.parameters['reinfection_rate'], I0=model_params.parameters["I0"], severe=model_params.parameters["severe"], progression_period=model_params.parameters["progression_period"], progression_sd=model_params.parameters["progression_sd"], death_rate=model_params.parameters["death_rate"], recovery_days=model_params.parameters["recovery_days"], recovery_sd=model_params.parameters["recovery_sd"]) G = SIR.build_network(interactions, self.population) self.grid = NetworkGrid(G) self.schedule = RandomActivation(self) self.dead_agents = [] self.running = True for node in range(self.population): new_agent = agent.human(node, self) #what was self.next_id() self.grid.place_agent(new_agent, node) self.schedule.add(new_agent) #self.meme = 0 self.datacollector = DataCollector( model_reporters={ "infected": lambda m: c_p.compute(m, 'infected'), "recovered": lambda m: c_p.compute(m, 'recovered'), "susceptible": lambda m: c_p.compute(m, "susceptible"), "dead": lambda m: c_p.compute(m, "dead"), "R0": lambda m: c_p.compute(m, "R0"), "severe_cases": lambda m: c_p.compute(m, "severe") }) self.datacollector.collect(self) def step(self): self.schedule.step() self.datacollector.collect(self) ''' for a in self.schedule.agents: if a.alive == False: self.schedule.remove(a) self.dead_agents.append(a.unique_id) ''' if self.dead == self.schedule.get_agent_count(): self.running = False else: self.running = True
class NormModel(Model): def __init__(self, size): self.num_agents = size self.num_nodes = self.num_agents # self.G = the_network[0] self.G = another_network self.grid = NetworkGrid(self.G) self.schedule = SimultaneousActivation(self) self.running = True for i, node in enumerate(self.G.nodes()): a = NormAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, node) self.datacollector = DataCollector( model_reporters={ "PerHate": percent_haters, "AverageSens": average_sensitivity, }, agent_reporters={"Hate": "behavior"}) def step(self): self.datacollector.collect(self) self.schedule.step() if percent_haters( self ) > 0.8: # When the percentage of haters in the model exceeds 80, self.running = False # the simulation is stopped, data collected, and next one is started.
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 propagation_model(Model): def __init__(self): super().__init__(Model) density = model_params.parameters['density'] nodes = model_params.parameters['network_size'] neg_bias = model_params.parameters['neg_bias'] meme_density = model_params.parameters['meme_density'] self.num_agents = nodes self.meme = 0 G = model_functions.build_network(density, nodes) self.grid = NetworkGrid(G) self.schedule = RandomActivation(self) self.running = True for node in range(nodes): new_agent = agent.tweeter(self.next_id(), node, self, neg_bias, meme_density) self.grid.place_agent(new_agent, node) self.schedule.add(new_agent) #self.meme = 0 self.datacollector = DataCollector(model_reporters={"meme_density": model_functions.compute_meme_density}) self.datacollector.collect(self) def step(self): self.schedule.step() self.datacollector.collect(self) if self.meme == self.schedule.get_agent_count(): self.running = False
class OpinionNetwork(Model): '''An opinion model with N agents on an Erdos-Renyi network.''' def __init__(self, N, avg_node_degree, rule='CMR', zero_prob=0.5): self.num_agents = N self.rule = rule self.schedule = RandomActivation(self) prob = avg_node_degree / self.num_agents self.G = nx.erdos_renyi_graph(n=self.num_agents, p=prob) self.grid = NetworkGrid(self.G) #create the agents for i, node in enumerate(self.G.nodes()): a = OpinionAgent( i, self, initial_opinion=self.random.choices( [0, 1], weights=[zero_prob, 1 - zero_prob], k=1)[0]) #we can seed w/a non-equal probability self.schedule.add(a) self.grid.place_agent(a, node) self.datacollector = DataCollector(model_reporters={ 'opinion_0': opinion_0_ct, 'opinion_1': opinion_1_ct }) self.running = True #we could check to see if we've reached a steady state self.datacollector.collect(self) def step(self): self.schedule.step() #each agent gets a step self.datacollector.collect(self) #collect data
class TestSingleNetworkGrid(unittest.TestCase): GRAPH_SIZE = 10 def setUp(self): ''' Create a test network grid and populate with Mock Agents. ''' G = nx.complete_graph(TestSingleNetworkGrid.GRAPH_SIZE) self.space = NetworkGrid(G) self.agents = [] for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): a = self.agents[i] assert a.pos == pos def test_get_neighbors(self): assert len(self.space.get_neighbors( 0, include_center=True)) == TestSingleNetworkGrid.GRAPH_SIZE assert len(self.space.get_neighbors( 0, include_center=False)) == TestSingleNetworkGrid.GRAPH_SIZE - 1 def test_move_agent(self): initial_pos = 1 agent_number = 1 final_pos = TestSingleNetworkGrid.GRAPH_SIZE - 1 _agent = self.agents[agent_number] assert _agent.pos == initial_pos assert _agent in self.space.G.node[initial_pos]['agent'] assert _agent not in self.space.G.node[final_pos]['agent'] self.space.move_agent(_agent, final_pos) assert _agent.pos == final_pos assert _agent not in self.space.G.node[initial_pos]['agent'] assert _agent in self.space.G.node[final_pos]['agent'] def test_is_cell_empty(self): assert not self.space.is_cell_empty(0) assert self.space.is_cell_empty(TestSingleNetworkGrid.GRAPH_SIZE - 1) def test_get_cell_list_contents(self): assert self.space.get_cell_list_contents([0]) == [self.agents[0]] assert self.space.get_cell_list_contents( list(range(TestSingleNetworkGrid.GRAPH_SIZE))) == [ self.agents[0], self.agents[1], self.agents[2] ] def test_get_all_cell_contents(self): assert self.space.get_all_cell_contents() == [ self.agents[0], self.agents[1], self.agents[2] ]
class WasteNet(Model): """Waste collection network model""" def __init__(self, mode, nb_nodes=10, nb_episodes=1): # Network self.G = generate_graph(nb_nodes) self.grid = NetworkGrid(self.G) # Gym Environment env_config = dict(graph=self.G) self.env = WasteNetEnv(env_config) # RL Agent if mode == WasteNetMode.PPO.name: rl_agent = PPOAgent("WasteNet", WasteNetEnv, env_config, best_config) rl_agent.load("./checkpoints/checkpoint-best") else: rl_agent = None # Scheduler self.schedule = WasteNetActivation(self, mode, rl_agent) # Data Collector self.datacollector = DataCollector( model_reporters={ "Empty": nb_empty, "Medium": nb_medium, "Full": nb_full, "Overflow": nb_overflow, }, agent_reporters={ "Fill level (%)": fill_level, "": lambda a: 100 }, ) # Mesa Agents for i, node in enumerate(self.G.nodes()): if i in (0, self.G.number_of_nodes() - 1): a = BaseAgent(i, self) else: a = DumpsterAgent(i, self, self.env.fill_levels[i - 1]) self.schedule.add(a) self.grid.place_agent(a, node) self.remaining_episodes = nb_episodes self.running = True self.datacollector.collect(self) def step(self): done = self.schedule.step() self.datacollector.collect(self) if done: self.env.reset() self.remaining_episodes -= 1 if self.remaining_episodes == 0: self.running = False
class VirusModel(Model): """A virus model with some number of agents""" def __init__(self, num_nodes, avg_node_degree, initial_outbreak_size, alpha, beta, gamma, k, n): self.num_nodes = num_nodes self.avg_node_degree = avg_node_degree self.G = nx.barabasi_albert_graph(n=self.num_nodes, m=avg_node_degree) # _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.alpha = alpha self.beta = beta self.gamma = gamma self.k = k self.n = n self.datacollector = DataCollector({ "Infected": number_active, "Susceptible": number_susceptible, "Carrier": number_inactive, "Removed": number_removed }) # Create agents for i, node in enumerate(self.G.nodes()): a = VirusAgent(i, self, State.SUSCEPTIBLE, self.alpha, self.beta, self.gamma, self.k, self.n) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes active_nodes = random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(active_nodes): a.state = State.ACTIVE self.running = True self.datacollector.collect(self) def removed_susceptible_ratio(self): try: return number_state(self, State.REMOVED) / 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 VirusModel(Model): """A virus model with some number of agents""" def __init__(self, num_nodes, avg_node_degree, initial_outbreak_size, virus_spread_chance, virus_check_frequency, recovery_chance, gain_resistance_chance): 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 = 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 VirusModel(Model): def __init__(self, num_nodes, avg_node_degree, initial_outbreak_size, alpha, beta): self.num_nodes = num_nodes prob = avg_node_degree / self.num_nodes self.initial_outbreak_size = initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes self.alpha = alpha self.beta = beta self.G = nx.barabasi_albert_graph(n=self.num_nodes, m=3) # self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.datacollector = DataCollector({ "Susceptible": number_susceptible, "Infected": number_infected, "Removed": number_removed }) # Create agents for i, node in enumerate(self.G.nodes()): a = VirusAgent( i, self, State.SUSCEPTIBLE, self.alpha, self.beta, ) self.schedule.add(a) self.grid.place_agent(a, node) # Infect some nodes infected_nodes = 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 removed_susceptible_ratio(self): try: return number_state(self, State.REMOVED) / number_state( self, State.SUSCEPTIBLE) except ZeroDivisionError: return math.inf def step(self): self.schedule.step() self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
class TestSingleNetworkGrid(unittest.TestCase): GRAPH_SIZE = 10 def setUp(self): ''' Create a test network grid and populate with Mock Agents. ''' G = nx.complete_graph(TestSingleNetworkGrid.GRAPH_SIZE) self.space = NetworkGrid(G) self.agents = [] for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): a = MockAgent(i, None) self.agents.append(a) self.space.place_agent(a, pos) def test_agent_positions(self): ''' Ensure that the agents are all placed properly. ''' for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): a = self.agents[i] assert a.pos == pos def test_get_neighbors(self): assert len(self.space.get_neighbors(0, include_center=True)) == TestSingleNetworkGrid.GRAPH_SIZE assert len(self.space.get_neighbors(0, include_center=False)) == TestSingleNetworkGrid.GRAPH_SIZE - 1 def test_move_agent(self): initial_pos = 1 agent_number = 1 final_pos = TestSingleNetworkGrid.GRAPH_SIZE - 1 _agent = self.agents[agent_number] assert _agent.pos == initial_pos assert _agent in self.space.G.node[initial_pos]['agent'] assert _agent not in self.space.G.node[final_pos]['agent'] self.space.move_agent(_agent, final_pos) assert _agent.pos == final_pos assert _agent not in self.space.G.node[initial_pos]['agent'] assert _agent in self.space.G.node[final_pos]['agent'] def test_is_cell_empty(self): assert not self.space.is_cell_empty(0) assert self.space.is_cell_empty(TestSingleNetworkGrid.GRAPH_SIZE - 1) def test_get_cell_list_contents(self): assert self.space.get_cell_list_contents([0]) == [self.agents[0]] assert self.space.get_cell_list_contents(list(range(TestSingleNetworkGrid.GRAPH_SIZE))) == [self.agents[0], self.agents[1], self.agents[2]] def test_get_all_cell_contents(self): assert self.space.get_all_cell_contents() == [self.agents[0], self.agents[1], self.agents[2]]
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 """ def __init__(self, graph, model_parameters): # 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.datacollector = DataCollector({ "Exposed": count_exposed, "Susceptible": count_susceptible, "Removed": count_removed, "Asymptomatic": count_asymptomatic, "Symptomatic": count_symptomatic }) self.model_parameters = model_parameters for i, node in enumerate(self.graph.nodes()): a = Person(i, self, State.SUSCEPTIBLE, model_parameters) 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", str(self.model_parameters)) 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)) #print("Steps completed: ", i) self.step()
class PopuNetwork(Model): """ The population network which initializes: - the number of agents - the number of incarcerated agents at start - the race of the simulated community - the average number of relationships per individual """ def __init__(self, num_nodes=1000, avg_node_degree=3, initial_outbreak_size=10, race="black"): self.num_nodes = num_nodes self.race = race self.months = 0 prob = avg_node_degree / self.num_nodes self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) self.sentence = generate_sentence(race) 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.datacollector = DataCollector({ "Incarcerated": number_incarcerated, "Susceptible": number_susceptible, "Released": number_released }) for i, node in enumerate(self.G.nodes()): a = Person(i, self, State.SUSCEPTIBLE, self.sentence) self.schedule.add(a) self.grid.place_agent(a, node) # Begin with some portion of the population in prison 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.INCARCERATED self.running = True self.datacollector.collect(self) def step(self): self.schedule.step() self.months += 1 # collect data self.datacollector.collect(self) def run_model(self, n): for i in range(n): self.step()
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 NormModel(Model): def __init__(self, size, type_no, subset_no): self.num_agents = size self.num_nodes = self.num_agents self.set = str(type_no) self.subset = subset_no self.I = networks_for_use[self.set][self.subset][0] # self.type = str(self.set) self.avg_deg = networks_for_use[self.set][self.subset][1][ 0] # 1st parameter - average node degree self.big_nodes = networks_for_use[self.set][self.subset][1][ 1] # 2nd parameter - degree of highest-degree node self.connectivity = networks_for_use[self.set][self.subset][1][ 2] # 3rd parameter - mean network connectivity self.clustering = networks_for_use[self.set][self.subset][1][ 3] # Network's clustering coeff. self.grid = NetworkGrid(self.I) self.schedule = SimultaneousActivation(self) self.running = True self.step_counter = 1 for i, node in enumerate(self.I.nodes()): a = NormAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, node) print(f"processing network number {self.subset+1}, type {self.set}") self.datacollector = DataCollector( model_reporters={ "PerHate": percent_haters, "NetworkType": network_type, "AveSensitivity": average_sensitivity, "MeanDeg": net_avg_deg, "MaxDeg": net_max_deg, "NetConnect": net_conn, "NetClust": net_clust, "FinalStep": final_step, }, # agent_reporters={"Hate": "behavior"} ) def step(self): self.datacollector.collect(self) self.schedule.step() self.step_counter += 1 if average_sensitivity(self) < 0.1 or self.step_counter > 250: self.running = False
class DiseaseModel(Model): def __init__(self, N, initInfected, percInfect, percRecover, intervention): self.running = True self.num_agents = N self.initInfected = initInfected self.percInfect = percInfect self.percRecover = percRecover self.intervention = intervention #self.grid = nx.ErdosRenyiGraph(self.num_agents,) self.G = nx.complete_graph(self.num_agents) self.grid = NetworkGrid(self.G) self.schedule = RandomSingleActivation(self) # create and initialise agents for i, node in enumerate(self.G.nodes): a = DiseaseAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, node) # make some agents initially infected for a in filter( lambda a: a.unique_id in initInfected, self.schedule.agents ): #random.sample(self.schedule.agents, self.initInfected): a.infect() # set up data collection self.datacollector = DataCollector(agent_reporters={ "State": "state", "Event": "event", "Cause": "cause" }) def reset_events(self): for a in self.schedule.agents: a.event = Event.NOTHING a.cause = -100 def step(self): self.reset_events() # update model self.schedule.step() # perform intervention if necessary if self.intervention is not None: self.intervention.apply(self) # perform data collection self.datacollector.collect(self)
class Population(Model): """Population""" def __init__(self, population_size, initial_outbreak_size, spread_chance): print("Beginning model setup...\n") self.population_size = population_size print("Creating graph...") self.graph = nx.powerlaw_cluster_graph(population_size, 100, 0.5) #self.graph = nx.complete_graph(population_size) print(len(self.graph.edges)) print("Initializing grid...") self.grid = NetworkGrid(self.graph) self.schedule = SimultaneousActivation(self) self.initial_outbreak_size = initial_outbreak_size self.spread_chance = spread_chance print("Initializing data collector...") self.datacollector = DataCollector({ "Infected:": count_infected, "Susceptible:": count_susceptible, "Removed:": count_removed }) for i, node in enumerate(self.graph.nodes()): a = Person(i, self, State.SUSCEPTIBLE, spread_chance) self.schedule.add(a) self.grid.place_agent(a, i) if i % 100 == 0: print("Finished with agent ", 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.INFECTED 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): print("Steps completed: ", i) self.step()
class COVID_model(Model): def __init__(self): super().__init__(Model) self.susceptible = 0 self.dead = 0 self.recovered = 0 self.infected = 0 interactions = model_params.parameters['interactions'] population = model_params.parameters['population'] self.num_agents = population G = model_functions.build_network(interactions, population) self.grid = NetworkGrid(G) self.schedule = RandomActivation(self) self.running = True for node in range(population): new_agent = agent.human(self.next_id(), node, self) self.grid.place_agent(new_agent, node) self.schedule.add(new_agent) #self.meme = 0 self.datacollector = DataCollector(model_reporters={"infected": model_functions.compute_infected, "recovered": model_functions.compute_recovered, "susceptible": model_functions.compute_susceptible, "dead": model_functions.compute_dead, "R0": model_functions.compute_R0, "severe_cases":model_functions.compute_severe}) self.datacollector.collect(self) def step(self): self.schedule.step() self.datacollector.collect(self) if self.dead == self.schedule.get_agent_count(): self.running = False else: self.running = True
class Network(Model): def __init__(self, N): self.num_agents = N self.G = nx.watts_strogatz_graph(N, 2, 0, seed=None) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) node_list = self.random.sample(self.G.nodes(), self.num_agents) for i in range(self.num_agents): a = agent(i, self) self.grid.place_agent(a, node_list[i]) self.schedule.add(a) def step(self): self.schedule.step() def run_model(self, n): for i in range(n): self.step()
class NormModel(Model): def __init__(self, size, net_type): self.num_agents = size self.num_nodes = self.num_agents self.type = net_type if self.type == 1: self.G, self.avg_degree, self.big_nodes, self.connectivity, self.clustering = netgen_ba( 100, 4) if self.type == 2: self.G, self.avg_degree, self.big_nodes, self.connectivity, self.clustering = netgen_er( 100, .078) if self.type == 3: self.G, self.avg_degree, self.big_nodes, self.connectivity, self.clustering = netgen_rr( 100, 4) self.grid = NetworkGrid(self.G) self.schedule = SimultaneousActivation(self) self.running = True self.step_counter = 1 for i, node in enumerate(self.G.nodes()): a = NormAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, node) self.datacollector = DataCollector( model_reporters={ "PerHate": percent_haters, "AverageSens": average_sensitivity, }, agent_reporters={"Hate": "behavior"}) def step(self): self.datacollector.collect(self) self.schedule.step() self.step_counter += 1 if percent_haters( self ) > 0.35 or self.step_counter > 250: # When the percentage of haters in the model exceeds 80, self.running = False # the simulation is stopped, data collected, and next one is started. # Alternatively: if average_sensitivity(self) < 0.1 or self.step_counter > 250: self.running = False
class NormModel(Model): def __init__(self, size, set_no): self.num_agents = size self.num_nodes = self.num_agents self.set = set_no self.I = networks_for_use[self.set][0] self.net_deg = networks_for_use[self.set][ 1] # 1st parameter - average node degree self.big_nodes = networks_for_use[self.set][ 2] # 2nd parameter - huge networks allowed? self.culling = networks_for_use[self.set][ 3] # 3rd parameter - maximum node degree allowed. only use if # big_nodes = True! self.grid = NetworkGrid(self.I) self.schedule = SimultaneousActivation(self) self.running = True for i, node in enumerate(self.I.nodes()): a = NormAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, node) self.datacollector = DataCollector( model_reporters={ "PerHate": percent_haters, "AveKnowing": percent_hate_knowing, "AveHate": average_hate, "MeanDeg": net_avg_deg, "Culling": net_culling, "MaxDeg": max_deg, }, agent_reporters={"Hate": "behavior"}) def step(self): self.datacollector.collect(self) self.schedule.step() if percent_haters( self ) > 0.8: # When the percentage of haters in the model exceeds 80, self.running = False # the simulation is stopped, data collected, and next one is started.
class MoneyModel(Model): """A model with some number of agents.""" #One agent per node def __init__(self, num_agents): self.num_agents = num_agents # self.G = nx.erdos_renyi_graph(n=num_agents, p=0.5) self.G = nx.barabasi_albert_graph(n=num_agents, m=1) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.datacollector = DataCollector( # model_reporters={"Gini": compute_gini}, agent_reporters={ "Wealth": lambda _: _.wealth, "Degree": lambda _: _.degree }) # Create agents for i in range(self.num_agents): a = MoneyAgent(i, self) self.schedule.add(a) # Assign the agent to a node self.grid.place_agent(a, 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 TrollModNetwork(Model): """A model with some number of trolls, mods, and regular users.""" def __init__(self, num_agents=50, percent_trolls=.10, percent_mods=.20): self.num_agents = num_agents self.num_nodes = num_agents self.num_trolls = int(math.floor(float(num_agents) * percent_trolls)) self.num_mods = int(math.floor(float(num_agents) * percent_mods)) self.num_regular = self.num_agents - (self.num_trolls + self.num_mods) # self.G = nx.barabasi_albert_graph(self.num_nodes, int(round(float(self.num_nodes)*0.90)), seed=11) # self.G = nx.barabasi_albert_graph(self.num_nodes, int(round(float(self.num_nodes)*0.10)), seed=11) self.G = nx.powerlaw_cluster_graph(self.num_nodes, int(round(float(self.num_nodes)*0.1)), p=0.9, seed=11) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.running = True self.datacollector = DataCollector( model_reporters={"Average Delta Trolling": compute_troll_delta}, agent_reporters={"Trolling Delta": lambda _: _.trolling_received_snapshot} ) list_of_random_nodes = self.random.sample(self.G.nodes(), self.num_agents) # Create agents for i in range(self.num_trolls): a = TrollUser(i, self) self.schedule.add(a) # Add the agent to a random node self.grid.place_agent(a, list_of_random_nodes[i]) for i in range(self.num_trolls, self.num_trolls + self.num_mods): a = ModUser(i, self) self.schedule.add(a) # Add the agent to a random node self.grid.place_agent(a, list_of_random_nodes[i]) for i in range(self.num_trolls + self.num_mods, self.num_agents): a = RegularUser(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 InfectModel(Model): """A model with some number of agents.""" def __init__(self, N, limit_time, infected_init, neighbors): self.num_agents = N prob = neighbors / self.num_agents self.G = nx.erdos_renyi_graph(n=self.num_agents, p=prob) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.datacollector = DataCollector( { "Infected": number_infected, "Susceptible": number_susceptible, "Resistant": number_resistant, } ) # Create agents for i, node in enumerate(self.G.nodes()): a = PeopleAgent(i,limit_time, self) 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(), infected_init) for a in self.grid.get_cell_list_contents(infected_nodes): a.status = State.ACTIVE self.datacollector.collect(self) def step(self): '''Advance the model by one step.''' self.schedule.step() #collecting data self.datacollector.collect(self)
class SPQRisiko(Model): """A SPQRisiko model with some number of players""" def __init__(self, n_players, points_limit, strategy, goal): super().__init__() self.players_goals = ["BE", "LA", "PP" ] # Definition of acronyms on `strategies.py` self.current_turn = 0 self.journal = [] # Keep track of main events self.reinforces_by_goal = {} self.tris_by_goal = {} # How many agent players wiil be self.n_players = n_players if n_players <= constants.MAX_PLAYERS else constants.MAX_PLAYERS # How many computer players will be self.n_computers = constants.MAX_PLAYERS - n_players # Creation of player, goals and computer agents goals = [] if goal == "Random": for i, player in enumerate(range(n_players)): goals.append(self.players_goals[i % 3]) else: goals = [goal for i in range(self.n_players)] self.players = [ Player(i, computer=False, strategy=self.get_strategy_setup(strategy, i), goal=goals[i], model=self) for i in range(self.n_players) ] for player in self.players: self.log("{} follows {} goal with a {} strategy".format( player.color, player.goal, player.strategy)) self.computers = [ Player(i, computer=True, strategy="Neutral", goal=self.random.choice(self.players_goals), model=self) for i in range(self.n_players, self.n_players + self.n_computers) ] self.points_limit = points_limit # limit at which one player wins self.deck = self.create_deck() self.random.shuffle(self.deck) self.trashed_cards = [] self.precompute_tris_reinforces_by_goal() # Initialize map self.G, self.territories_dict = self.create_graph_map() self.grid = NetworkGrid(self.G) self.datacollector = DataCollector( model_reporters={ "Winner": get_winner, "Turn": get_winner_turn, "Strategy": get_player_strategy, "Goal": get_player_goal }) # Schedule self.schedule = RandomActivation(self) # Subgraphs self.ground_areas = [] self.sea_areas = [] path = os.path.abspath((os.path.join(__file__, '..', '..'))) # Probabilities that the attacker wins on a ground combact with open(path + '/matrices/atta_wins_combact.pkl', 'rb') as f: self.atta_wins_combact = pickle.load(f) # Probabilities that the attacker wins on a combact by sea with open(path + '/matrices/atta_wins_combact_by_sea.pkl', 'rb') as f: self.atta_wins_combact_by_sea = pickle.load(f) territories = list(range(45)) random.shuffle(territories) """ If there're 4 players, Italia must be owned by the only computer player """ if self.n_players == 4: territories.remove(15) # Remove Italia from the territories t = GroundArea(*itemgetter("id", "name", "type", "coords")( self.territories_dict["territories"][15]), model=self) t.armies = 3 t.owner = self.computers[0] self.grid.place_agent(t, 15) self.ground_areas.append(self.grid.get_cell_list_contents([15])[0]) """ Connect nodes to territories and assign them to players """ for i, node in enumerate(territories): t = GroundArea(*itemgetter("id", "name", "type", "coords")( self.territories_dict["territories"][node]), model=self) if i < 9 * self.n_players: t.armies = 2 t.owner = self.players[i % self.n_players] else: t.armies = 3 t.owner = self.computers[i % self.n_computers] self.grid.place_agent(t, node) self.ground_areas.append( self.grid.get_cell_list_contents([node])[0]) """ Add sea area """ for i, node in enumerate(range(45, 57)): t = SeaArea(*itemgetter("id", "name", "type", "coords")( self.territories_dict["sea_areas"][i]), model=self) t.trireme = [0 for _ in range(self.n_players)] self.grid.place_agent(t, node) self.sea_areas.append(self.grid.get_cell_list_contents([node])[0]) self.ground_areas.sort(key=lambda x: x.unique_id) self.sea_areas.sort(key=lambda x: x.unique_id) self.running = True # self.datacollector.collect(self) def get_strategy_setup(self, strategy, i): strats = ["Aggressive", "Passive", "Neutral"] if strategy == "Random": strategy = strats[i % 3] return strategy @staticmethod def get_movable_armies_by_strategy(strategy, minimum, maximum): return round((maximum - minimum) * strategies.nomads_percentage[strategy] + minimum) @staticmethod def create_graph_map(): # Read map configuration from file with open( os.path.join(os.path.dirname(__file__), "config/territories.json"), "r") as f: territories_dict = json.load(f) graph_map = nx.Graph() for territory in territories_dict["territories"]: graph_map.add_node(territory["id"]) for sea in territories_dict['sea_areas']: graph_map.add_node(sea['id']) for edges in territories_dict["edges"]: graph_map.add_edge(edges[0], edges[1]) return graph_map, territories_dict @staticmethod def create_deck(custom_numbers=None): # Read deck from configuration file deck = [] with open(os.path.join(os.path.dirname(__file__), "config/cards.json"), "r") as f: cards = json.load(f) # custom cards' numbers if custom_numbers: # do something return deck for card in cards: c = { "type": card["type"], "adds_on_tris": card["adds_on_tris"], "image": card["image"] } for _ in range(card["number_in_deck"]): deck.append(c) return deck def draw_a_card(self): # if deck is empty, refill from trashed cards if len(self.deck) == 0: if len(self.trashed_cards) == 0: # We finished cards, players must do some tris to refill deck! return None self.deck.extend(self.trashed_cards) self.trashed_cards = [] # return last card from deck return self.deck.pop() @staticmethod def reinforces_from_tris(cards): # assert len(cards) == 3, "Wrong number of cards given to 'tris' method" if len(cards) != 3: return None cards_in_tris = set([card["type"] for card in cards]) # assert len(cards_in_tris) == 3 or len(cards_in_tris) == 1, \ # Tris must be composed of three different cards or three of the same type if len(cards_in_tris) != 3 and len(cards_in_tris) != 1: return None reinforces = { "legionaries": 8 if len(cards_in_tris) == 1 else 10, "centers": 0, "triremes": 0 } for card in cards: for key, value in card["adds_on_tris"].items(): reinforces[key] += value return reinforces def precompute_tris_reinforces_by_goal(self): # precompute tris and assign points based on strategy all_possible_tris = [ list(t) for t in itertools.combinations(self.deck, 3) ] all_reinforces = [ SPQRisiko.reinforces_from_tris(tris) for tris in all_possible_tris ] # Remove None from list real_tris = [ all_possible_tris[i] for i in range(len(all_reinforces)) if all_reinforces[i] ] all_reinforces = [i for i in all_reinforces if i] named_tris = {} for i, tris in enumerate(real_tris): name = self.get_tris_name(tris) named_tris[name] = all_reinforces[i] self.reinforces_by_goal[name] = {} for goal, value in strategies.strategies.items(): self.reinforces_by_goal[name][ goal] = self.get_reinforcements_score( all_reinforces[i], value["tris"]) # order tris name by score for goal, value in strategies.strategies.items(): self.tris_by_goal[goal] = [] for tris in real_tris: name = self.get_tris_name(tris) if name not in self.tris_by_goal[goal]: self.tris_by_goal[goal].append(name) self.tris_by_goal[goal] = sorted( self.tris_by_goal[goal], key=cmp_to_key(lambda a, b: self.reinforces_by_goal[b][goal] - self.reinforces_by_goal[a][goal])) self.reinforces_by_goal["average"] = {} for goal, value in strategies.strategies.items(): points, count = 0, 0 for tris in real_tris: count += 1 points += self.reinforces_by_goal[self.get_tris_name( tris)][goal] self.reinforces_by_goal["average"][goal] = float(points) / count def count_players_sea_areas(self): sea_areas = [0] * self.n_players for sea in self.sea_areas: m = max(sea.trireme) players_max_trireme = [ player for player, n_trireme in enumerate(sea.trireme) if n_trireme == m ] if len(players_max_trireme) == 1: sea_areas[players_max_trireme[0]] += 1 return sea_areas def count_players_territories_power_places(self): territories = [0] * self.n_players power_places = [0] * self.n_players for territory in self.ground_areas: if not territory.owner.computer: territories[territory.owner.unique_id] += 1 if territory.power_place: power_places[territory.owner.unique_id] += 1 return territories, power_places def get_weakest_power_place(self, player): weakest = None for territory in self.ground_areas: if not territory.owner.computer and territory.owner.unique_id == player.unique_id: if territory.power_place: if not weakest or territory.armies < weakest.armies: weakest = territory return weakest def get_weakest_adversary_power_place(self, player): weakest = None for territory in self.ground_areas: if territory.owner.computer or territory.owner.unique_id != player.unique_id: if territory.power_place: if not weakest or territory.armies < weakest.armies: weakest = territory return weakest def find_nearest(self, territory, player): # It's a BFS visit to get the node whose distance from territory is the lesser than any other for ground_area in self.ground_areas: ground_area.found = 0 for sea_area in self.sea_areas: sea_area.found = 0 territory.found = 1 visited = [territory] distances = [0] * 57 while len(visited) > 0: t = visited.pop(0) if distances[t.unique_id] > 4: break for neighbor in self.grid.get_neighbors(t.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.found == 0: neighbor.found = 1 distances[neighbor.unique_id] = distances[t.unique_id] + 1 visited.append(neighbor) if neighbor.type == "ground" and neighbor.owner.unique_id == player.unique_id: return neighbor return None def get_largest_empire(self, player): # It's another DFS visit in which we account for the membership of a node to a connected component def __dfs_visit__(territory, ground_areas, cc_num): territory.found = 1 ground_areas[territory.unique_id] = cc_num for neighbor in self.grid.get_neighbors(territory.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.type == "ground" and \ neighbor.found == 0 and \ neighbor.owner.unique_id == player.unique_id: __dfs_visit__(neighbor, ground_areas, cc_num) cc_num = 0 ground_areas = [-1] * 45 for territory in self.ground_areas: territory.found = 0 for territory in self.ground_areas: if territory.type == "ground" and territory.found == 0 and territory.owner.unique_id == player.unique_id: __dfs_visit__(territory, ground_areas, cc_num) cc_num += 1 stats = list( collections.Counter([t for t in ground_areas if t != -1]).most_common()) if stats != []: return [ self.ground_areas[idx] for idx, cc in enumerate(ground_areas) if cc == stats[0][0] ] return stats def maximum_empires(self): # It's a DFS visit in which we account for # the length of every connected components def __dfs_visit__(territory, d): territory.found = 1 for neighbor in self.grid.get_neighbors(territory.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.type == "ground" and \ neighbor.found == 0 and \ neighbor.owner.unique_id == territory.owner.unique_id: d = __dfs_visit__(neighbor, d) return d + 1 cc_lengths = [0] * self.n_players for territory in self.ground_areas: territory.found = 0 for territory in self.ground_areas: if not territory.owner.computer and territory.found == 0: distance = __dfs_visit__(territory, 0) if distance > cc_lengths[territory.owner.unique_id]: cc_lengths[territory.owner.unique_id] = distance return cc_lengths # Controlla se `player` ha vinto oppure se c'è un vincitore tra tutti def winner(self, player=None): if player is not None: if player.victory_points >= self.points_limit: return True return False max_points = -1 max_player = None for p in self.players: if p.victory_points > max_points: max_points = p.victory_points max_player = p won = True if max_points > self.points_limit else False return max_player, won def get_territories_by_player(self, player: Player, ground_type="ground"): if ground_type == "ground": return [ t for t in self.ground_areas if t.owner.unique_id == player.unique_id ] elif ground_type == "sea": return [ t for t in self.sea_areas if t.trireme[self.players.index(player)] > 0 or max(t.trireme) == 0 ] def get_sea_area_near_ground_area(self, player): sea_areas = [] for sea_area in self.sea_areas: for neighbor in self.grid.get_neighbors(sea_area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if isinstance(neighbor, GroundArea) and \ neighbor.owner.unique_id == player.unique_id: sea_areas.append(sea_area) return sea_areas def n_power_places(self): n = 0 for area in self.ground_areas: if area.power_place: n += 1 return n def update_atta_wins_combact_matrix(self, attacker_armies, defender_armies, mat_type='combact'): print(attacker_armies, defender_armies, self.atta_wins_combact.shape, self.atta_wins_combact_by_sea.shape) if mat_type == 'combact': if attacker_armies > self.atta_wins_combact.shape[ 0] and defender_armies > self.atta_wins_combact.shape[1]: self.atta_wins_combact = get_probabilities_ground_combact( attacker_armies, defender_armies) elif attacker_armies > self.atta_wins_combact.shape[0]: self.atta_wins_combact = get_probabilities_ground_combact( attacker_armies, self.atta_wins_combact.shape[1]) elif defender_armies > self.atta_wins_combact.shape[1]: self.atta_wins_combact = get_probabilities_ground_combact( self.atta_wins_combact.shape[0], defender_armies) elif mat_type == 'combact_by_sea': if attacker_armies > self.atta_wins_combact_by_sea.shape[ 0] and defender_armies > self.atta_wins_combact_by_sea.shape[ 1]: self.atta_wins_combact_by_sea = get_probabilities_combact_by_sea( attacker_armies, defender_armies) elif attacker_armies > self.atta_wins_combact_by_sea.shape[0]: self.atta_wins_combact_by_sea = get_probabilities_combact_by_sea( attacker_armies, self.atta_wins_combact_by_sea.shape[1]) elif defender_armies > self.atta_wins_combact_by_sea.shape[1]: self.atta_wins_combact_by_sea = get_probabilities_combact_by_sea( self.atta_wins_combact_by_sea.shape[0], defender_armies) def step(self): self.current_turn += 1 for player in self.players: if not player.eliminated: can_draw = False territories, power_places = self.count_players_territories_power_places( ) player_territories = self.get_territories_by_player( player, "ground") sea_areas = self.count_players_sea_areas() empires = self.maximum_empires() # 1) Aggiornamento del punteggio player.update_victory_points(empires, territories, sea_areas, power_places) # 1.1) Controllo vittoria if self.winner(player): self.running = False print(player) print(get_winner(self)) print(get_winner_turn(self)) self.datacollector.collect(self) self.log("{} has won!".format(player.color)) return True # 2) Fase dei rinforzi print('\nREINFORCES') player.update_ground_reinforces_power_places() reinforces = Player.get_ground_reinforces(player_territories) self.log( "{} earns {} legionaries (he owns {} territories)".format( player.color, reinforces, territories[player.unique_id])) player.put_reinforces(self, reinforces) # player.sacrifice_trireme(sea_area_from, ground_area_to) # use card combination # displace ground, naval and/or power places on the ground tris = player.get_best_tris(self) if tris: reinforces = player.play_tris(self, tris) self.log("{} play tris {}".format( player.color, self.get_tris_name(tris))) player.put_reinforces(self, reinforces) # TODO: log where reinforces are put # 3) Movimento navale # player.naval_movement(sea_area_from, sea_area_to, n_trireme) # 4) Combattimento navale print('\nNAVAL COMBACT!!') # Get all sea_areas that the current player can attack attackable_sea_areas = [] for sea_area in self.get_territories_by_player( player, ground_type='sea'): # Choose the adversary that has the lower probability of winning the combact min_trireme = min(sea_area.trireme) if min_trireme > 0: adv_min_trireme = sea_area.trireme.index(min_trireme) # Check if the atta_wins_combact probabilities matrix needs to be recomputed # self.update_atta_wins_combact_matrix(sea_area.trireme[player.unique_id], sea_area.trireme[adv_min_trireme]) row = sea_area.trireme[player.unique_id] col = sea_area.trireme[adv_min_trireme] m = max(row, col) ratio = 100 / m if ratio < 1: row = min(round(ratio * row), 100) col = min(round(ratio * col), 100) if player.unique_id != adv_min_trireme and \ self.atta_wins_combact[row - 1, col - 1] >= strategies.probs_win[player.strategy]: attackable_sea_areas.append( [sea_area, adv_min_trireme]) for sea_area, adv in attackable_sea_areas: # Randomly select how many attack and defense trireme attacker_trireme = sea_area.trireme[player.unique_id] # The defender must always use the maximux number of armies to defend itself # n_defense_trireme = sea_area.trireme[adversary.unique_id] if sea_area.trireme[adversary.unique_id] <= 3 else 3 # Let's combact biatch!! print('Start battle!') print('Trireme in ' + sea_area.name + ': ', sea_area.trireme) print('Player ' + str(player.unique_id) + ' attacks Player ' + str(adv) + ' on ' + sea_area.name) player.naval_combact(sea_area, adv, attacker_trireme, strategies.probs_win[player.strategy], self.atta_wins_combact) # 5) Attacchi via mare print('\nCOMBACT BY SEA!!') for ground_area in self.ground_areas: ground_area.already_attacked_by_sea = False attacks = self.get_attackable_ground_areas_by_sea(player) # attacks.sort(key=lambda x: x["prob_win"], reverse=True) while 0 < len(attacks): attack = attacks[0] # if not attack['defender'].already_attacked_by_sea: attack['defender'].already_attacked_by_sea = True attacker_armies = attack["attacker"].armies - attack[ "armies_to_leave"] print( 'Battle: {} (player {}) with {} VS {} (player {}) with {}' .format(attack["attacker"].name, player.unique_id, attacker_armies, attack["defender"].name, attack["defender"].owner.unique_id, attack["defender"].armies)) conquered, min_moveable_armies = player.combact_by_sea( attack["attacker"], attack["defender"], attacker_armies) if conquered: # Move armies from attacker area to conquered max_moveable_armies = attack[ "attacker"].armies - attack["armies_to_leave"] nomads = SPQRisiko.get_movable_armies_by_strategy( player.strategy, min_moveable_armies, max_moveable_armies) attack["attacker"].armies -= nomads attack["defender"].armies = nomads can_draw = True # Remove from possible attacks all of those containing as defender the conquered territory # and update the probability attacks = self.update_attacks_by_sea(player, attacks) # 6) Attacchi terrestri print('\nGROUND COMBACT!!') attacks = [] attacks = self.get_attackable_ground_areas(player) # attacks.sort(key=lambda x: x["prob_win"], reverse=True) while 0 < len(attacks): attack = attacks[0] attacker_armies = attack["attacker"].armies - 1 print( 'Battle: {} (player {}) with {} VS {} (player {}) with {}' .format(attack["attacker"].name, player.unique_id, attacker_armies, attack["defender"].name, attack["defender"].owner.unique_id, attack["defender"].armies)) conquered, min_moveable_armies = player.combact( attack["attacker"], attack["defender"], attacker_armies, strategies.probs_win[player.strategy], self.atta_wins_combact) if conquered: # Move armies from attacker area to conquered max_moveable_armies = attack["attacker"].armies - 1 nomads = SPQRisiko.get_movable_armies_by_strategy( player.strategy, min_moveable_armies, max_moveable_armies) attack["attacker"].armies -= nomads attack["defender"].armies = nomads can_draw = True self.log( "{} conquered {} from {} and it moves {} armies there out of {}" .format(player.color, attack["defender"].name, attack["attacker"].name, nomads, max_moveable_armies)) # Re-sort newly attackable areas with newer probabilities attacks = self.get_attackable_ground_areas(player) # attacks.sort(key=lambda x: x["prob_win"], reverse=True) # Controllo se qualche giocatore è stato eliminato for adv in self.players: if adv.unique_id != player.unique_id and not adv.eliminated: territories = self.get_territories_by_player(adv) if len(territories) == 0: self.log("{} has been eliminated by {}".format( adv.color, player.color)) player.cards.extend(adv.cards) adv.cards = [] adv.eliminated = True for sea_area in self.get_territories_by_player( adv, ground_type="sea"): sea_area.trireme[adv.unique_id] = 0 # 7) Spostamento strategico di fine turno player.move_armies_by_goal(self) # 8) Presa della carta # Il giocatore può dimenticarsi di pescare la carta ahah sarebbe bello fare i giocatori smemorati if can_draw and random.random() <= 1: card = self.draw_a_card() if card: player.cards.append(card) self.schedule.step() return False def update_attacks_by_sea(self, player, future_attacks): attack_num = 0 last_attacker = future_attacks[0]['attacker'] del future_attacks[0] while attack_num < len(future_attacks): attack = future_attacks[attack_num] if attack['defender'].owner.unique_id == player.unique_id: print('Since the defender has been conquered, I delete it') del future_attacks[attack_num] elif attack['defender'].already_attacked_by_sea: print( 'Since the defender has already been attacked by sea, I delete it' ) del future_attacks[attack_num] elif attack['attacker'].unique_id == last_attacker.unique_id: print('The attacker may attack again') # Maybe it could change the armies to leave due to garrisons armies_to_leave = self.get_armies_to_leave(attack['attacker']) if attack['attacker'].armies - armies_to_leave >= min( 3, attack['defender'].armies): prob_win = self.atta_wins_combact_by_sea[ attack['attacker'].armies - armies_to_leave - 1, attack['defender'].armies - 1] if prob_win >= strategies.probs_win[player.strategy]: print('The attacker can attack again') attack['prob_win'] = prob_win attack_num += 1 else: print( 'Since the attacker has a lower prob to win, I delete it' ) del future_attacks[attack_num] else: print( 'Since the attacker hasn\'t the min required armies, I delete it' ) del future_attacks[attack_num] else: attack_num += 1 if player.goal == "PP": future_attacks.sort(key=lambda x: (x['defender'].power_place, x['prob_win']), reverse=True) else: future_attacks.sort(key=lambda x: x['prob_win'], reverse=True) return future_attacks def get_attackable_ground_areas_by_sea(self, player): attacks = [] for ground_area in self.get_territories_by_player(player): for neighbor in self.grid.get_neighbors(ground_area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] # A player can attack a ground area through sea, only if it posesses a number of # trireme greater than the possible adversary. if isinstance( neighbor, SeaArea) and neighbor.trireme[player.unique_id] > min( neighbor.trireme): for sea_area_neighbor in self.grid.get_neighbors( neighbor.unique_id): sea_area_neighbor = self.grid.get_cell_list_contents( [sea_area_neighbor])[0] if isinstance(sea_area_neighbor, GroundArea) and \ ground_area.unique_id != sea_area_neighbor.unique_id and \ sea_area_neighbor.owner.unique_id != player.unique_id and \ (sea_area_neighbor.owner.computer or neighbor.trireme[player.unique_id] > neighbor.trireme[sea_area_neighbor.owner.unique_id]): armies_to_leave = self.get_armies_to_leave( ground_area) if ground_area.armies - armies_to_leave >= min( 3, sea_area_neighbor.armies): # self.update_atta_wins_combact_matrix(ground_area.armies - armies_to_leave, sea_area_neighbor.armies, mat_type='combact_by_sea') row = ground_area.armies - armies_to_leave col = sea_area_neighbor.armies m = max(row, col) ratio = 100 / m if ratio < 1: row = min(round(ratio * row), 100) col = min(round(ratio * col), 100) prob_win = self.atta_wins_combact_by_sea[row - 1, col - 1] if prob_win >= strategies.probs_win[ player.strategy]: attacks.append({ "defender": sea_area_neighbor, "attacker": ground_area, "armies_to_leave": armies_to_leave, "prob_win": prob_win }) if player.goal == "PP": attacks.sort(key=lambda x: (x['defender'].power_place, x['prob_win']), reverse=True) else: attacks.sort(key=lambda x: x['prob_win'], reverse=True) return attacks def get_armies_to_leave(self, ground_area): ground_area_neighbors = self.grid.get_neighbors(ground_area.unique_id) for ground_area_neighbor in ground_area_neighbors: ground_area_neighbor = self.grid.get_cell_list_contents( [ground_area_neighbor])[0] if isinstance(ground_area_neighbor, GroundArea) and \ ground_area_neighbor.owner.unique_id != ground_area.owner.unique_id: return 2 return 1 def get_attackable_ground_areas_from(self, ground_area): attacks = [] if ground_area.armies > 1: for neighbor in self.grid.get_neighbors(ground_area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.type == "ground" and \ neighbor.owner.unique_id != ground_area.owner.unique_id and \ ground_area.armies - 1 >= min(3, neighbor.armies): # self.update_atta_wins_combact_matrix(ground_area.armies - 1, neighbor.armies) row = ground_area.armies - 1 col = neighbor.armies m = max(row, col) ratio = 100 / m if ratio < 1: row = min(round(ratio * row), 100) col = min(round(ratio * col), 100) prob_win = self.atta_wins_combact[row - 1, col - 1] if prob_win >= strategies.probs_win[ ground_area.owner.strategy]: attacks.append({ "defender": neighbor, "attacker": ground_area, "prob_win": prob_win }) return attacks def get_attackable_ground_areas(self, player): attacks = [] for ground_area in self.get_territories_by_player(player): attackables = self.get_attackable_ground_areas_from(ground_area) if attackables != []: for attackable in attackables: attacks.append(attackable) if player.goal == "PP": attacks.sort(key=lambda x: (x['defender'].power_place, x['prob_win']), reverse=True) else: attacks.sort(key=lambda x: x['prob_win'], reverse=True) return attacks # Get non attackable areas wiht at least 2 armies and with an ally neighbor def non_attackable_areas(self, player, territories=None): non_attackables = [] if not territories: territories = self.get_territories_by_player(player) for ground_area in territories: if ground_area.armies > 1: attackable, has_ally_neighbor = False, False for neighbor in self.grid.get_neighbors(ground_area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.type == "ground" and neighbor.owner.unique_id != player.unique_id: attackable = True break has_ally_neighbor = True if not attackable and has_ally_neighbor: non_attackables.append(ground_area) return non_attackables def is_not_attackable(self, area): for neighbor in self.grid.get_neighbors(area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if isinstance( neighbor, GroundArea ) and neighbor.owner.unique_id != area.owner.unique_id: return False return True def get_strongest_ally_neighbor(self, area): strongest = None for neighbor in self.grid.get_neighbors(area.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if isinstance(neighbor, GroundArea) and ( not strongest or strongest.armies < neighbor.armies): strongest = neighbor return strongest def is_neighbor_of(self, area1, area2): for neighbor in self.grid.get_neighbors(area1.unique_id): neighbor = self.grid.get_cell_list_contents([neighbor])[0] if neighbor.owner.unique_id == area2.unique_id: return True return False def log(self, log): self.journal.append("Turn {}: ".format(self.current_turn) + log) def run_model(self, n): for _ in range(n): self.step() # Tris name is the ordered initial letters of cards type def get_tris_name(self, tris): if len(tris) != 3: raise Exception("tris name parameter error") return "-".join( [card[0] for card in sorted(set([card["type"] for card in tris]))]) def get_reinforcements_score(self, reinforces, multipliers): m, r = multipliers, reinforces return m[0] * r["legionaries"] + m[1] * r["triremes"] + m[2] * r[ "centers"] def get_n_armies_by_player(self, player=None): if player is not None: return sum( [t.armies for t in self.get_territories_by_player(player)]) else: sum_a = 0 for player in self.players: sum_a += sum( [t.armies for t in self.get_territories_by_player(player)]) return sum_a / len(self.players)
class Model(Model): initial_RefinedKitten = 100 initial_ImperialKitten = 100 initial_CharmingKitten = 100 initial_HelixKitten = 100 initial_StaticKitten = 100 initial_RemixKitten = 100 def __init__(self, nodes, initial_RefinedKitten = 100, initial_ImperialKitten = 100, initial_CharmingKitten = 100, initial_HelixKitten = 100, initial_StaticKitten = 100, initial_RemixKitten = 100): super().__init__() self.initial_RefinedKitten = initial_RefinedKitten self.initial_ImperialKitten = initial_ImperialKitten self.initial_CharmingKitten = initial_CharmingKitten self.initial_HelixKitten = initial_HelixKitten self.initial_StaticKitten = initial_StaticKitten self.initial_RemixKitten = initial_RemixKitten self.grid = NetworkGrid(G) self.schedule = RandomActivationByOrg(self) for i in range(self.initial_RefinedKitten): refinedkitten = RefinedKitten(self.next_id(), 'RFK', self) self.grid.place_agent(refinedkitten, 'RFK') self.schedule.add(refinedkitten) for i in range(self.initial_ImperialKitten): imperialkitten = ImperialKitten(self.next_id(), 'IK', self) self.grid.place_agent(imperialkitten, 'IK') self.schedule.add(imperialkitten) for i in range(self.initial_CharmingKitten): charmingkitten = CharmingKitten(self.next_id(), 'CK', self) self.grid.place_agent(charmingkitten, 'CK') self.schedule.add(charmingkitten) for i in range(self.initial_HelixKitten): helixkitten = HelixKitten(self.next_id(), 'HK', self) self.grid.place_agent(helixkitten, 'HK') self.schedule.add(helixkitten) for i in range(self.initial_StaticKitten): statickitten = StaticKitten(self.next_id(), 'SK', self) self.grid.place_agent(statickitten, 'SK') self.schedule.add(statickitten) for i in range(self.initial_RemixKitten): remixkitten = RemixKitten(self.next_id(), 'RMK', self) self.grid.place_agent(remixkitten, 'RMK') self.schedule.add(remixkitten) self.running = True self.datacollector = DataCollector( agent_reporters = {"Organization": "org", "Spear Phishing": "phish", "Zero Day": "zeroday", "Tool Sophistication": "tools", "Attribution Obfuscation": "attrib", "Stealth": "stealth", "Information Weaponization": "iwo","DDoS": "ddos", "Data Destruction": "destruct", "Critical Infrastructure Disruption": "infra"}) def step(self): self.schedule.step() self.datacollector.collect(self)
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_fake=2, threshold_real=-2, fake_p=1, real_p=1): self.fake_p = fake_p self.real_p = real_p 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.datacollector = DataCollector({ "Infected_fake": number_infected_fake, "Infected_real": number_infected_real, }) # Create agents for i, node in enumerate(self.G.nodes()): a = User( i, self, 0, #make the state a int threshold_fake, threshold_real) self.schedule.add(a) # Add the agent to the node self.grid.place_agent(a, node) # Infect some nodes, initial infection bug free infected_nodes_fake = self.random.sample(self.G.nodes(), self.initial_outbreak_size) infected_nodes_real = self.random.sample(self.G.nodes(), self.initial_outbreak_size) for a in self.grid.get_cell_list_contents(infected_nodes_fake): a.state = 1 neighbors_nodes = self.grid.get_neighbors(a.pos) for n in self.grid.get_cell_list_contents(neighbors_nodes): n.state = 1 for a in self.grid.get_cell_list_contents(infected_nodes_real): a.state = -1 neighbors_nodes = self.grid.get_neighbors(a.pos) for n in self.grid.get_cell_list_contents(neighbors_nodes): n.state = -1 """ state measures fake score!! the more negative the less likely to spread fake news also this model assumes that 1 one piece of real news can cancel out one piece of fake news This model can be modulated by changing the value of fake and real 2 the inital braeakout size of fake and real news are the same This can be chaged by introducing a different initial breaksize for real and fake news however this score is kepet the same intentionally because too uch complexity is not good for modeling """ self.running = True self.datacollector.collect(self) def proportion_infected_fake(self): try: tot_fake = 0 for i in self.grid.get_cell_list_contents(self.G): if i.state == 1: tot_fake += 1 return tot_fake / self.num_nodes except ZeroDivisionError: return math.inf def proportion_infected_real(self): try: tot_real = 0 for i in self.grid.get_cell_list_contents(self.G): if i.state == -1: tot_real += 1 return tot_real / 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 BankSim(Model): simid = None #Simulation ID for SQLITEDB primary key max_steps = 200 conn = None # Sqlite connector db_cursor = None # Sqlite DB cursor lst_bank_ratio = list() lst_ibloan = list() def __init__(self, **params): super().__init__() config = configparser.ConfigParser() config.read('conf/config.ini') self.sqlite_db = config['SQLITEDB']['file'] self.height = 20 self.width = 20 self.is_init_db = False if params.get( 'write_db') is None else params.get('write_db') self.is_write_db = False if params.get( 'write_db') is None else params.get('write_db') self.max_steps = params['max_steps'] self.initial_saver = params['initial_saver'] self.initial_loan = params['initial_loan'] self.initial_bank = params['initial_bank'] self.rfree = params['rfree'] self.reserve_rates = params[ 'rfree'] / 2.0 # set reserve rates one half of risk free rate self.libor_rate = params['rfree'] self.bankrupt_liquidation = 1 # 1: it is fire sale of assets, 0: bank liquidates loans at face value self.car = params['car'] self.min_reserves_ratio = params['min_reserves_ratio'] self.initial_equity = params['initial_equity'] self.G = nx.empty_graph(self.initial_bank) self.grid = NetworkGrid(self.G) self.schedule = RandomActivation(self) self.datacollector = DataCollector({"BankAsset": get_sum_totasset}) def step(self): if self.schedule.steps == 0: if self.is_init_db: init_database() logger.info('db initialization') if self.is_write_db: self.conn = sqlite3.connect(self.sqlite_db) self.db_cursor = self.conn.cursor() self.simid = int( datetime.now().strftime('%y%m%d%H%M%S%f')[:-3]) title = 'CAR {0:f}, Reserves Ratio {1:f}'.format( self.car, self.min_reserves_ratio) task = (self.simid, title, datetime.now(timezone.utc)) insert_simulation_table(self.db_cursor, task) for i in range(self.initial_bank): bank = Bank({ 'unique_id': self.next_id(), 'model': self, 'equity': 100, 'rfree': self.rfree, 'car': self.car, 'buffer_reserves_ratio': 1.5 }) self.grid.place_agent(bank, i) self.schedule.add(bank) for i in range(self.initial_saver): saver = Saver({ 'unique_id': self.next_id(), 'model': self, 'balance': 1, 'owns_account': False, 'saver_solvent': True, 'saver_exit': False, 'withdraw_upperbound': 0.2, 'exitprob_upperbound': 0.06 }) self.grid.place_agent(saver, random.choice(list(self.G.nodes))) self.schedule.add(saver) for i in range(self.initial_loan): loan = Loan({ "unique_id": self.next_id(), "model": self, "rfree": self.rfree, "amount": 1, "loan_solvent": True, "loan_approved": False, "loan_dumped": False, "loan_liquidated": False, "pdf_upper": 0.1, "rcvry_rate": 0.4, "firesale_upper": 0.1 }) bank_id = random.choice(list(self.G.nodes)) loan.bank_id = bank_id self.grid.place_agent(loan, bank_id) # Evenly distributed self.schedule.add(loan) initialize_deposit_base(self.schedule) initialize_loan_book(self.schedule, self.car, self.min_reserves_ratio) self.running = True self.datacollector.collect(self) if self.schedule.steps == self.max_steps: self.running = False # evaluate solvency of banks after loans experience default main_evaluate_solvency(self.schedule, self.reserve_rates, self.bankrupt_liquidation, self.car) # evaluate second round effects owing to cross_bank linkages # only interbank loans to cover shortages in reserves requirements are included main_second_round_effects(self.schedule, self.bankrupt_liquidation, self.car, self.G) # Undercapitalized banks undertake risk_weight optimization main_risk_weight_optimization(self.schedule, self.car) # banks that are well capitalized pay dividends main_pay_dividends(self.schedule, self.car, self.min_reserves_ratio) # Reset insolvent loans, i.e. rebirth lending opportunity main_reset_insolvent_loans(self.schedule) # Build up loan book with loans available in bank neighborhood main_build_loan_book_locally(self.schedule, self.min_reserves_ratio, self.car) # Build up loan book with loans available in other neighborhoods main_build_loan_book_globally(self.schedule, self.car, self.min_reserves_ratio) # main_raise_deposits_build_loan_book # Evaluate liquidity needs related to reserves requirements main_evaluate_liquidity(self.schedule, self.car, self.min_reserves_ratio, self.bankrupt_liquidation) main_write_bank_ratios(self.schedule, self.lst_bank_ratio, self.car, self.min_reserves_ratio) main_write_interbank_links(self.schedule, self.lst_ibloan) if self.is_write_db: # Insert agent variables of current step into SQLITEDB #insert_agtsaver_table(self.db_cursor, self.simid, self.schedule.steps,[x for x in self.schedule.agents if isinstance(x, Saver)]) #insert_agtloan_table(self.db_cursor, self.simid, self.schedule.steps, [x for x in self.schedule.agents if isinstance(x, Loan)]) # # It needs to log before the 2nd round effect begin because the function initializes insert_agtbank_table( self.db_cursor, self.simid, self.schedule.steps, [x for x in self.schedule.agents if isinstance(x, Bank)]) # insert_agtibloan_table(self.db_cursor, self.simid, self.schedule.steps, # [x for x in self.schedule.agents if isinstance(x, Ibloan)]) self.conn.commit() self.schedule.step() self.datacollector.collect(self) def run_model(self, step_count=20): """ This method is only avail in the command mode :param step_count: :return: """ for i in range(step_count): if i % 10 == 0 or (i + 1) == step_count: logger.info( " STEP: %3d - # of sovent bank: %2d", i, len([ x for x in self.schedule.agents if isinstance(x, Bank) and x.bank_solvent ])) try: self.step() except: error = traceback.format_exc() logger.error(error) if len([ x for x in self.schedule.agents if isinstance(x, Bank) and x.bank_solvent ]) == 0: logger.info("All banks are bankrupt!") break #df_bank, df_ibloan = convert_result2dataframe(self.lst_bank_ratio, self.lst_ibloan) #return df_bank, df_ibloan return True
class VirusModel(Model): """A virus model with some number of agents""" def __init__(self): # self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) # self.G = nx.erdos_renyi_graph(n=3, p=0.5) self.G = nx.Graph() self.G.add_node(0) self.G.add_node(1) self.G.add_node(2) self.G.add_node(3) self.G.add_node(4) self.G.add_node(4) self.G.add_edge(0, 1) self.G.add_edge(0, 2) self.G.add_edge(0, 3) self.G.add_edge(0, 4) self.G.add_edge(0, 5) self.G.add_edge(1, 4) self.G.add_edge(4, 5) self.grid = NetworkGrid(self.G) self.rooms = {} self.rooms[0] = {"name": "Wejście", "rates": {}} self.rooms[1] = {"name": "Czytelnia", "rates": {"Nauka": 2}} self.rooms[2] = {"name": "Chillout", "rates": {"Relaks": 10}} self.rooms[3] = {"name": "Biuro", "rates": {"Praca": 1.5}} self.rooms[4] = {"name": "Toaleta", "rates": {"Toaleta": 30}} self.rooms[5] = { "name": "Kawiarnia", "rates": { "Jedzenie": 12, "Kultura": 0.5 } } collector_dict = {} for i, room in enumerate(self.rooms): collector_dict[self.rooms[i]["name"]] = lambda model, i=i: len( model.grid.get_cell_list_contents([i])) - 1 self.datacollector = DataCollector(collector_dict) self.schedule = RandomActivation(self) # Create agents for i, node in enumerate(self.G.nodes()): r = RoomAgent(i, self, self.rooms[i]["name"], self.rooms[i]["rates"]) self.schedule.add(r) # Add the agent to the node self.grid.place_agent(r, node) self.prob_needs = { "Jedzenie": [4, 0.6], "Toaleta": [2, 0.6], "Relaks": [5, 1] } self.prob_studs = { "Nauka": [2, 1.5], "Praca": [0, 0.5], "Kultura": [0, 1.0] } self.prob_works = { "Nauka": [0, 0.3], "Praca": [6, 1.0], "Kultura": [0, 0.2] } self.prob_tours = { "Nauka": [0, 0.3], "Praca": [0, 0.5], "Kultura": [1, 1.0] } self.prob_local = { "Nauka": [1, 0.7], "Praca": [2, 0.9], "Kultura": [1, 1.0] } # godziny 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 self.rate_studs = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 ] self.rate_works = [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 ] self.rate_tours = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3, 4, 4, 4, 6, 6, 4, 3, 2, 0, 0 ] self.rate_local = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 4, 2, 2, 4, 5, 6, 6, 6, 3, 0, 0, 0 ] self.running = True self.datacollector.collect(self) self.tm = 0 * 60 self.count = 0 def get_sample(self, probs): ret = {} for k, [m, s] in probs.items(): tm = int(np.clip(np.random.normal(m, s) * 60, 15, 600)) ret[k] = tm return ret def step(self): # prepare list for the satisfied agents self.satisfied = [] # add new agents hour = int(self.tm / 60) if (hour > 23): hour = 0 for i in range(self.rate_studs[hour]): a = HumanAgent(100 + self.count, self, self.get_sample(self.prob_needs), self.get_sample(self.prob_studs)) self.schedule.add(a) self.grid.place_agent(a, 0) self.count += 1 for i in range(self.rate_works[hour]): a = HumanAgent(100 + self.count, self, self.get_sample(self.prob_needs), self.get_sample(self.prob_works)) self.schedule.add(a) self.grid.place_agent(a, 0) self.count += 1 # update system self.schedule.step() # collect data self.datacollector.collect(self) # make time step self.tm = self.tm + 1 if (self.tm > 24 * 60): self.datacollector.get_model_vars_dataframe().to_csv("one_day.csv") self.tm = 0 # remove satisfied agents from the system for a in self.satisfied: print(a.unique_id, a.goals, "is satisfied") self.grid.move_agent(a, 0) self.grid._remove_agent(a, 0) self.schedule.remove(a) def run_model(self, n): for i in range(n): self.step() def find_best_room(self, goal): #print("Looking for room for", goal) for i, room in enumerate(self.rooms): #print("Room", room, self.rooms[room]["rates"]) if goal in self.rooms[room]["rates"]: return room return -1
class IdeaSpread(Model): # creates a set number of agents and has them share beliefs def __init__(self, num_nodes=50, initial_anti=.18, initial_pro=.71, first_age=18, last_age=22): self.num_nodes = num_nodes self.color_map = ['gray'] * self.num_nodes # mesa addition, creates knowledge of neighbors self.schedule = SimultaneousActivation(self) self.initial_anti = initial_anti self.initial_pro = initial_pro self.Vis = nx.Graph() self.Vis.add_nodes_from(range(num_nodes)) self.ages = {} self.grid = NetworkGrid(self.Vis) self.population = {} groupsize = self.num_nodes / (last_age - first_age) # def setup_agents(self, age, num_nodes): # creates agents for i, node in enumerate(self.Vis.nodes()): a = Individual(i, self, Thought.NEUTRAL, first_age + i * (last_age - first_age) / num_nodes) self.schedule.add(a) self.grid.place_agent(a, node) self.population[a.unique_id] = a # randomly assigns nodes to be +/-/0 within group if random.random() <= self.initial_anti: a.belief = Thought.ANTI self.color_map[a.unique_id] = ('red') elif random.random() <= self.initial_pro: a.belief = Thought.PRO self.color_map[a.unique_id] = 'green' else: self.color_map[a.unique_id] = 'gray' # adds age to dictionary with unique id for l in self.population: self.ages[l] = self.population[l].age # assigns edges (connections) between people for id1 in self.population: for id2 in self.population: if self.population[id1].prob_ref == 0 and id1 < id2: if self.population[id2].prob_ref == 0 and random.random( ) < (talk[0] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 1 and random.random( ) < (talk[1] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 2 and random.random( ) < (talk[2] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 3 and random.random( ) < (talk[3] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id1].prob_ref == 1 and id1 < id2: if self.population[id2].prob_ref == 0 and random.random( ) < (talk[4] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 1 and random.random( ) < (talk[5] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 2 and random.random( ) < (talk[6] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 3 and random.random( ) < (talk[7] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id1].prob_ref == 2 and id1 < id2: if self.population[id2].prob_ref == 0 and random.random( ) < (talk[8] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 1 and random.random( ) < (talk[9] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 2 and random.random( ) < (talk[10] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 3 and random.random( ) < (talk[11] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id1].prob_ref == 3 and id1 < id2: if self.population[id2].prob_ref == 0 and random.random( ) < (talk[12] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 1 and random.random( ) < (talk[13] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 2 and random.random( ) < (talk[14] / groupsize): self.Vis.add_edge(id1, id2) if self.population[id2].prob_ref == 3 and random.random( ) < (talk[15] / groupsize): self.Vis.add_edge(id1, id2) # TODO: figure out implementation of datacollector self.datacollector = DataCollector({ "Anti Vaxxers": percent_anti, "Pro Vaxxine": percent_pro }) self.running = True self.datacollector.collect(self) def step(self): self.schedule.step(self.population, self.Vis) self.datacollector.collect(self) def run(self, n): for i in range(n): print(percent_anti(self)) portray(self, self.Vis) self.step() # reassigns color of people based on belief for a in self.population: if self.population[a].belief == Thought.ANTI: self.color_map[a] = ('red') elif self.population[a].belief == Thought.PRO: self.color_map[a] = 'green' else: self.color_map[a] = 'gray'
class EvacuationModel(Model): """A Mesa ABM model to simulate evacuation during a flood Args: hazard: Spatial table of flood hazard zones in WGS84 output_path: Path to output files without extension domain: Polygon used to select OSM data, required if the graph, agents or targets are not specified target_types: List of OSM amenity values to use as targets, defaults to school network: Undirected network generated from OSM road network targets: Spatial table of OSM amenities target_capacity: The number of agents that can be evacuated to each target agents: Spatial table of agent starting locations seed: Seed value for random number generation Attributes: output_path (str): Path to output files without extension schedule (RandomActivation): Scheduler which activates each agent once per step, in random order, with the order reshuffled every step hazard (GeoDataFrame): Spatial table of flood hazard zones in WGS84 G (Graph): Undirected network generated from OSM road network nodes (GeoDataFrame): Spatial table of nodes in G edges (GeoDataFrame): Spatial table edges in G grid (NetworkGrid): Network grid for agents to travel around based on G data_collector (DataCollector): Stores the model state at each time step target_nodes (Series): Series of nodes to evacuate to target_capacity (int): The number of agents that can be evacuated to each target igraph: Duplicate of G as an igraph object to speed up routing """ def __init__( self, hazard: GeoDataFrame, output_path: str, domain: Optional[Polygon] = None, target_types: Iterable[str] = tuple(['school']), network: Optional[Graph] = None, targets: Optional[GeoDataFrame] = None, target_capacity: int = 100, agents: Optional[GeoDataFrame] = None, seed: Optional[int] = None): super().__init__() self._seed = seed self.output_path = output_path self.hazard = hazard self.schedule = RandomActivation(self) self.target_capacity = target_capacity if network is None: self.G = osmnx.graph_from_polygon(domain, simplify=False) self.G = self.G.to_undirected() else: self.G = network self.nodes: GeoDataFrame self.edges: GeoDataFrame self.nodes, self.edges = osmnx.save_load.graph_to_gdfs(self.G) if agents is None: agents = GeoDataFrame(geometry=create_footprints_gdf(domain).centroid) if targets is None: targets = osmnx.pois_from_polygon(domain, amenities=list(target_types)) # Query can return polygons as well as points, only using the points targets = targets[targets.geometry.geom_type == 'Point'] output_gpkg = output_path + '.gpkg' driver = 'GPKG' targets.crs, agents.crs = [self.nodes.crs] * 2 nodes_tree = cKDTree(np.transpose([self.nodes.geometry.x, self.nodes.geometry.y])) # Prevents warning about CRS not being the same self.hazard.crs = self.nodes.crs self.hazard.to_file(output_gpkg, layer='hazard', driver=driver) agents_in_hazard_zone: GeoDataFrame = sjoin(agents, self.hazard) agents_in_hazard_zone = agents_in_hazard_zone.loc[~agents_in_hazard_zone.index.duplicated(keep='first')] agents_in_hazard_zone.geometry.to_file(output_gpkg, layer='agents', driver=driver) assert len(agents_in_hazard_zone) > 0, 'There are no agents within the hazard zone' targets_in_hazard_zone: GeoDataFrame = sjoin(targets, self.hazard) targets_in_hazard_zone = targets_in_hazard_zone.loc[~targets_in_hazard_zone.index.duplicated(keep='first')] targets_outside_hazard_zone = targets[~targets.index.isin(targets_in_hazard_zone.index.values)] targets_outside_hazard_zone.to_file(output_gpkg, layer='targets', driver=driver) assert len(targets_outside_hazard_zone) > 0, 'There are no targets outside the hazard zone' _, node_idx = nodes_tree.query( np.transpose([agents_in_hazard_zone.geometry.x, agents_in_hazard_zone.geometry.y])) _, target_node_idx = nodes_tree.query( np.transpose([targets_outside_hazard_zone.geometry.x, targets_outside_hazard_zone.geometry.y])) for (_, row), nearest_node in zip(targets_outside_hazard_zone.iterrows(), self.nodes.index[target_node_idx]): if not self.G.has_node(row.osmid): self.G.add_edge(nearest_node, row.osmid, length=0) self.G.nodes[row.osmid]['osmid'] = row.osmid self.G.nodes[row.osmid]['x'] = row.geometry.x self.G.nodes[row.osmid]['y'] = row.geometry.y self.nodes, self.edges = osmnx.save_load.graph_to_gdfs(self.G) self.nodes[['osmid', 'geometry']].to_file(output_gpkg, layer='nodes', driver=driver) self.edges[['osmid', 'geometry']].to_file(output_gpkg, layer='edges', driver=driver) output_gml = output_path + '.gml' osmnx.nx.write_gml(self.G, path=output_gml) self.igraph = igraph.read(output_gml) self.target_nodes = targets_outside_hazard_zone.osmid self.grid = NetworkGrid(self.G) # Create agents for i, idx in enumerate(node_idx): a = agent.EvacuationAgent(i, self) self.schedule.add(a) self.grid.place_agent(a, self.nodes.index[idx]) a.update_route() a.update_location() self.data_collector = DataCollector( model_reporters={ 'evacuated': evacuated, 'stranded': stranded }, agent_reporters={'position': 'pos', 'reroute_count': 'reroute_count', 'lat': 'lat', 'lon': 'lon', 'highway': 'highway', 'status': status}) def step(self): """Advances the model by one step and then stores the current state in data_collector""" self.schedule.step() self.data_collector.collect(self) def run(self, steps: int): """Runs the model for the given number of steps` Args: steps: number of steps to run the model for Returns: DataFrame: the agent vars dataframe """ self.data_collector.collect(self) for _ in range(steps): self.step() if self.data_collector.model_vars['evacuated'][-1] + self.data_collector.model_vars['stranded'][-1] == len( self.schedule.agents): # Continue for 5 steps after all agents evacuated or stranded for _ in range(5): self.step() break self.data_collector.get_agent_vars_dataframe().astype({'highway': pd.Int64Dtype()}).to_csv( self.output_path + '.agent.csv') self.data_collector.get_model_vars_dataframe().to_csv(self.output_path + '.model.csv') return self.data_collector.get_agent_vars_dataframe()
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() '''