示例#1
0
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]
        ]
示例#2
0
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]]
示例#3
0
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()
示例#4
0
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()
示例#5
0
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()
示例#6
0
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()
示例#7
0
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()
示例#8
0
文件: model.py 项目: bangtree/mesa
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()
示例#9
0
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()
示例#10
0
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)
示例#11
0
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()

    '''
示例#12
0
class VirusModel(Model):
    def __init__(self, num_nodes, avg_node_degree, prob_rewire,
                 initial_outbreak_size, alpha, beta, gamma, delta, k, n):

        self.num_nodes = num_nodes
        self.avg_node_degree = avg_node_degree
        self.prob_rewire = prob_rewire
        self.G = nx.watts_strogatz_graph(n=self.num_nodes,
                                         k=avg_node_degree,
                                         p=prob_rewire)
        # 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.alpha = alpha
        self.beta = beta
        self.gamma = gamma
        self.delta = delta

        self.k = k
        self.n = n

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            a = VirusAgent(i, self, State.SUSCEPTIBLE, self.alpha, self.beta,
                           self.gamma, self.delta, 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.datacollector = DataCollector(
            model_reporters={
                "Infected": number_active,
                "Susceptible": number_susceptible,
                "Carrier": number_inactive,
                "Removed": number_removed,
                "Active Clustering": infective_clustering,
                "Exposed Clustering": exposed_clustering,
                "Infective Spread": infective_spread,
                "Exposed Spread": exposed_spread
            })

        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()
示例#13
0
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,
    ):
        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": number_infected,
            "Susceptible": number_susceptible,
        })

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            a = User(i, self, "susceptible", threshold)
            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()
示例#14
0
class VirusModel(Model):
    def __init__(self, num_nodes, avg_node_degree, initial_outbreak_size, alpha, beta, gamma, delta, 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)
        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.delta = delta

        self.k=k
        self.n=n

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            a = VirusAgent(i, self, State.SUSCEPTIBLE, self.alpha, self.beta, self.gamma, self.delta, 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.datacollector = DataCollector(
            model_reporters={
                             "Infected": number_active,
                             "Susceptible": number_susceptible,
                             "Carrier": number_inactive,
                             "Removed": number_removed,
                             "Active Clustering": infective_clustering,
                             "Exposed Clustering": exposed_clustering,
                             "Infective Diffusion": infective_diffusion,
                             "Exposed Diffusion": exposed_diffusion
                             }
        )

        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 tyrant_remove(self):
        if number_active(self) > 0:
            if random.random() < 0.002:
                actives = [a for a in self.grid.get_all_cell_contents() if a.state is State.ACTIVE]
                node_for_removal = random.sample(actives, 1)
                for a in node_for_removal:
                    a.state = State.REMOVED
        # for a in self.grid.get_cell_list_contents(active_nodes):
        #     a.state = State.ACTIVE

    def step(self):
        self.tyrant_remove()
        self.schedule.step()
        self.datacollector.collect(self)

    def run_model(self, n):
        for i in range(n):
            self.step()
示例#15
0
class InfectionModel(Model):
    """Over the last decade, an intensive effort has been undertaken to develop global surveillance networks that combat pandemics of emergent infectious diseases. To better understand the spread of a specific virus, computational models have been created. In contemporary mathematical epidemiology, agent-based modeling (ABM) represents the state-of-the-art for simulating complex epidemic systems. Taking into account transportation infrastructure of the simulated area, the mobility of the population, and evolutionary aspects of the disease, ABMs are by nature a “brute force” method that details a bottom-up stochastic approach to approximating the dynamics of real-world cases, i.e. individual-level behaviors define system-level results. To mirror the 2020 coronavirus outbreak, a non-linear network structure similar to a SIR mathematical model is applied. Then, a step further is taken to connect the outbreak with its economic implications in a medical cost-effectiveness analysis. In this study, the agent-based model is utilized to model the epidemiological and economic implications of viral pandemics, specifically the SARS-cov-2 virus.

""" #ABSTRACT

    def __init__(self, N=100, avg_degree = 3, initial_outbreak = 1, ptrans=0.03, severe_rate=0.3,
                 death_rate=0.01, screening_freq = 0.4, recovery_rate = 0.98, recovery_days=21,
                 recovery_sd=7, screening_cost = 300, hospitalization_cost = 30000):

        self.num_agents = N
        prob = avg_degree / N
        self.G = nx.erdos_renyi_graph(n=self.num_agents, p=prob)
        self.grid = NetworkGrid(self.G)
        
        self.schedule = RandomActivation(self)
        self.initial_outbreak = initial_outbreak
        self.ptrans = ptrans
        
        self.recovery_days = recovery_days
        self.recovery_sd = recovery_sd
        self.recovery_rate = recovery_rate
        self.death_rate = death_rate
        self.screening_freq = screening_freq
        self.screening_cost = screening_cost
        self.hospitalization_cost = hospitalization_cost
        self.cost = 0

        self.datacollector = DataCollector({"Infected": number_infected,
                                            "Susceptible": number_susceptible,
                                            "Removed": number_removed})
        # Create agents
        for i, node in enumerate(self.G.nodes()):
            a = MyAgent(i, self, State.SUSCEPTIBLE, ptrans, severe_rate, screening_freq, recovery_rate, screening_cost, hospitalization_cost)
            self.schedule.add(a)
            # Add the agent to the node
            self.grid.place_agent(a, node)
            
        # Initial infection 
        infected_nodes = self.random.sample(self.G.nodes(), self.initial_outbreak)
        for a in self.grid.get_cell_list_contents(infected_nodes):
            a.state = State.INFECTED
        
        self.running = True
        self.datacollector.collect(self)
    
    def removed_percentage(self):
        try:
            return number_state(self, State.REMOVED) / self.num_agents
        
        except ZeroDivisionError:
            return math.inf
        
    def step(self):
        self.schedule.step()
        self.datacollector.collect(self)
        
    # Costs
    def total_cost(self): #check and start back up here 9/20
        screening = sum_state(self, State.INFECTED, self.screening_freq*self.screening_cost)
        hospitalization = sum_state(self, State.SEVERE, self.hospitalization_cost)
        self.cost += screening + hospitalization
        return (self.cost)

    def run_model(self,n=100):
        for i in range(n):
            self.step()
示例#16
0
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()
示例#17
0
class Change_Res_Network(Model):
    """A virus model with some number of agents"""

    def __init__(
        self,
        num_nodes=1740,
        avg_node_degree=3,
        initial_with_clean=174,
        change_clean_chance=0.03,
        check_frequency=1.0,
        switch_back_chance=0.02,
        gain_resistance_chance=0.0,
    ):

        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_with_clean = (
            initial_with_clean if initial_with_clean <= num_nodes else num_nodes
        )
        self.change_clean_chance = change_clean_chance
        self.check_frequency = check_frequency
        self.switch_back_chance = switch_back_chance
        self.gain_resistance_chance = gain_resistance_chance

        self.datacollector = DataCollector(
            {
                "has_clean": number_has_clean,
                "not_clean": number_not_clean,
                "refuses_clean": number_refuses_clean,
            }
        )

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            a = VirusAgent(
                i,
                self,
                State.not_clean,
                self.change_clean_chance,
                self.check_frequency,
                self.switch_back_chance,
                self.gain_resistance_chance,
            )
            self.schedule.add(a)
            # Add the agent to the node
            self.grid.place_agent(a, node)

        # Infect some nodes
        has_clean_nodes = self.random.sample(self.G.nodes(), self.initial_with_clean)
        for a in self.grid.get_cell_list_contents(has_clean_nodes):
            a.state = State.has_clean

        self.running = True
        self.datacollector.collect(self)

    def refuses_clean_not_clean_ratio(self):
        try:
            return number_state(self, State.refuses_clean) / number_state(
                self, State.not_clean
            )
        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()
示例#18
0
class HostNetwork(Model):
    # id generator to track run number in batch run data
    id_gen = itertools.count(1)
    rate_denominator = 1000000

    def __init__(
        self,
        num_nodes,
        avg_node_degree,
        initial_outbreak_size,
        prob_spread_virus_gamma_shape,
        prob_spread_virus_gamma_scale,
        prob_spread_virus_gamma_loc,
        prob_spread_virus_gamma_magnitude_multiplier,
        prob_recover_gamma_shape,
        prob_recover_gamma_scale,
        prob_recover_gamma_loc,
        prob_recover_gamma_magnitude_multiplier,
        prob_virus_kill_host_gamma_shape,
        prob_virus_kill_host_gamma_scale,
        prob_virus_kill_host_gamma_loc,
        prob_virus_kill_host_gamma_magnitude_multiplier,
        prob_infectious_no_to_mild_symptom_gamma_shape,
        prob_infectious_no_to_mild_symptom_gamma_scale,
        prob_infectious_no_to_mild_symptom_gamma_loc,
        prob_infectious_no_to_mild_symptom_gamma_magnitude_multiplier,
        prob_infectious_no_to_severe_symptom_gamma_shape,
        prob_infectious_no_to_severe_symptom_gamma_scale,
        prob_infectious_no_to_severe_symptom_gamma_loc,
        prob_infectious_no_to_severe_symptom_gamma_magnitude_multiplier,
        prob_infectious_no_to_critical_symptom_gamma_shape,
        prob_infectious_no_to_critical_symptom_gamma_scale,
        prob_infectious_no_to_critical_symptom_gamma_loc,
        prob_infectious_no_to_critical_symptom_gamma_magnitude_multiplier,
        prob_infectious_mild_to_no_symptom_gamma_shape,
        prob_infectious_mild_to_no_symptom_gamma_scale,
        prob_infectious_mild_to_no_symptom_gamma_loc,
        prob_infectious_mild_to_no_symptom_gamma_magnitude_multiplier,
        prob_infectious_mild_to_severe_symptom_gamma_shape,
        prob_infectious_mild_to_severe_symptom_gamma_scale,
        prob_infectious_mild_to_severe_symptom_gamma_loc,
        prob_infectious_mild_to_severe_symptom_gamma_magnitude_multiplier,
        prob_infectious_mild_to_critical_symptom_gamma_shape,
        prob_infectious_mild_to_critical_symptom_gamma_scale,
        prob_infectious_mild_to_critical_symptom_gamma_loc,
        prob_infectious_mild_to_critical_symptom_gamma_magnitude_multiplier,
        prob_infectious_severe_to_no_symptom_gamma_shape,
        prob_infectious_severe_to_no_symptom_gamma_scale,
        prob_infectious_severe_to_no_symptom_gamma_loc,
        prob_infectious_severe_to_no_symptom_gamma_magnitude_multiplier,
        prob_infectious_severe_to_mild_symptom_gamma_shape,
        prob_infectious_severe_to_mild_symptom_gamma_scale,
        prob_infectious_severe_to_mild_symptom_gamma_loc,
        prob_infectious_severe_to_mild_symptom_gamma_magnitude_multiplier,
        prob_infectious_severe_to_critical_symptom_gamma_shape,
        prob_infectious_severe_to_critical_symptom_gamma_scale,
        prob_infectious_severe_to_critical_symptom_gamma_loc,
        prob_infectious_severe_to_critical_symptom_gamma_magnitude_multiplier,
        prob_infectious_critical_to_no_symptom_gamma_shape,
        prob_infectious_critical_to_no_symptom_gamma_scale,
        prob_infectious_critical_to_no_symptom_gamma_loc,
        prob_infectious_critical_to_no_symptom_gamma_magnitude_multiplier,
        prob_infectious_critical_to_mild_symptom_gamma_shape,
        prob_infectious_critical_to_mild_symptom_gamma_scale,
        prob_infectious_critical_to_mild_symptom_gamma_loc,
        prob_infectious_critical_to_mild_symptom_gamma_magnitude_multiplier,
        prob_infectious_critical_to_severe_symptom_gamma_shape,
        prob_infectious_critical_to_severe_symptom_gamma_scale,
        prob_infectious_critical_to_severe_symptom_gamma_loc,
        prob_infectious_critical_to_severe_symptom_gamma_magnitude_multiplier,
        prob_recovered_no_to_mild_complication,
        prob_recovered_no_to_severe_complication,
        prob_recovered_mild_to_no_complication,
        prob_recovered_mild_to_severe_complication,
        prob_recovered_severe_to_no_complication,
        prob_recovered_severe_to_mild_complication,
        prob_gain_immunity,
        hospital_bed_capacity_as_percent_of_population,
        hospital_bed_cost_per_day,
        icu_bed_capacity_as_percent_of_population,
        icu_bed_cost_per_day,
        ventilator_capacity_as_percent_of_population,
        ventilator_cost_per_day,
        drugX_capacity_as_percent_of_population,
        drugX_cost_per_day,
    ):

        self.uid = next(self.id_gen)
        self.set_network_seed = 888  # Setting: Accurately set to None or specific seed
        self.set_initial_infectious_node_seed = 888  # Setting: Accurately set to None or specific seed

        self._current_timer = 0
        self._last_n_time_unit_for_mean_r0 = 10  # SETTING: Smoothing mean R0
        self.num_nodes = num_nodes
        self.avg_node_degree = avg_node_degree
        prob = self.avg_node_degree / self.num_nodes
        self.G = nx.erdos_renyi_graph(n=self.num_nodes,
                                      p=prob,
                                      seed=self.set_network_seed)
        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.all_agents_new_infection_tracker = {}
        self.all_agents_new_tested_as_true_positive = []

        self.cumulative_infectious_cases = self.initial_outbreak_size
        self.cumulative_dead_cases = 0
        self.cumulative_test_done = 0
        self.cumulative_infectious_test_confirmed_cases = 0
        self.cumulative_dead_test_confirmed_cases = 0

        self.cumulative_hospital_bed_use_in_new_host_counts = 0
        self.cumulative_icu_bed_use_in_new_host_counts = 0
        self.cumulative_ventilator_use_in_new_host_counts = 0
        self.cumulative_drugX_use_in_new_host_counts = 0

        self.cumulative_hospital_bed_use_in_days = 0
        self.cumulative_icu_bed_use_in_days = 0
        self.cumulative_ventilator_use_in_days = 0
        self.cumulative_drugX_use_in_days = 0

        self.prob_spread_virus_gamma_shape = prob_spread_virus_gamma_shape
        self.prob_spread_virus_gamma_scale = prob_spread_virus_gamma_scale
        self.prob_spread_virus_gamma_loc = prob_spread_virus_gamma_loc
        self.prob_spread_virus_gamma_magnitude_multiplier = prob_spread_virus_gamma_magnitude_multiplier
        self.prob_recover_gamma_shape = prob_recover_gamma_shape
        self.prob_recover_gamma_scale = prob_recover_gamma_scale
        self.prob_recover_gamma_loc = prob_recover_gamma_loc
        self.prob_recover_gamma_magnitude_multiplier = prob_recover_gamma_magnitude_multiplier
        self.prob_virus_kill_host_gamma_shape = prob_virus_kill_host_gamma_shape
        self.prob_virus_kill_host_gamma_scale = prob_virus_kill_host_gamma_scale
        self.prob_virus_kill_host_gamma_loc = prob_virus_kill_host_gamma_loc
        self.prob_virus_kill_host_gamma_magnitude_multiplier = prob_virus_kill_host_gamma_magnitude_multiplier
        self.prob_infectious_no_to_mild_symptom_gamma_shape = prob_infectious_no_to_mild_symptom_gamma_shape
        self.prob_infectious_no_to_mild_symptom_gamma_scale = prob_infectious_no_to_mild_symptom_gamma_scale
        self.prob_infectious_no_to_mild_symptom_gamma_loc = prob_infectious_no_to_mild_symptom_gamma_loc
        self.prob_infectious_no_to_mild_symptom_gamma_magnitude_multiplier = prob_infectious_no_to_mild_symptom_gamma_magnitude_multiplier
        self.prob_infectious_no_to_severe_symptom_gamma_shape = prob_infectious_no_to_severe_symptom_gamma_shape
        self.prob_infectious_no_to_severe_symptom_gamma_scale = prob_infectious_no_to_severe_symptom_gamma_scale
        self.prob_infectious_no_to_severe_symptom_gamma_loc = prob_infectious_no_to_severe_symptom_gamma_loc
        self.prob_infectious_no_to_severe_symptom_gamma_magnitude_multiplier = prob_infectious_no_to_severe_symptom_gamma_magnitude_multiplier
        self.prob_infectious_no_to_critical_symptom_gamma_shape = prob_infectious_no_to_critical_symptom_gamma_shape
        self.prob_infectious_no_to_critical_symptom_gamma_scale = prob_infectious_no_to_critical_symptom_gamma_scale
        self.prob_infectious_no_to_critical_symptom_gamma_loc = prob_infectious_no_to_critical_symptom_gamma_loc
        self.prob_infectious_no_to_critical_symptom_gamma_magnitude_multiplier = prob_infectious_no_to_critical_symptom_gamma_magnitude_multiplier
        self.prob_infectious_mild_to_no_symptom_gamma_shape = prob_infectious_mild_to_no_symptom_gamma_shape
        self.prob_infectious_mild_to_no_symptom_gamma_scale = prob_infectious_mild_to_no_symptom_gamma_scale
        self.prob_infectious_mild_to_no_symptom_gamma_loc = prob_infectious_mild_to_no_symptom_gamma_loc
        self.prob_infectious_mild_to_no_symptom_gamma_magnitude_multiplier = prob_infectious_mild_to_no_symptom_gamma_magnitude_multiplier
        self.prob_infectious_mild_to_severe_symptom_gamma_shape = prob_infectious_mild_to_severe_symptom_gamma_shape
        self.prob_infectious_mild_to_severe_symptom_gamma_scale = prob_infectious_mild_to_severe_symptom_gamma_scale
        self.prob_infectious_mild_to_severe_symptom_gamma_loc = prob_infectious_mild_to_severe_symptom_gamma_loc
        self.prob_infectious_mild_to_severe_symptom_gamma_magnitude_multiplier = prob_infectious_mild_to_severe_symptom_gamma_magnitude_multiplier
        self.prob_infectious_mild_to_critical_symptom_gamma_shape = prob_infectious_mild_to_critical_symptom_gamma_shape
        self.prob_infectious_mild_to_critical_symptom_gamma_scale = prob_infectious_mild_to_critical_symptom_gamma_scale
        self.prob_infectious_mild_to_critical_symptom_gamma_loc = prob_infectious_mild_to_critical_symptom_gamma_loc
        self.prob_infectious_mild_to_critical_symptom_gamma_magnitude_multiplier = prob_infectious_mild_to_critical_symptom_gamma_magnitude_multiplier
        self.prob_infectious_severe_to_no_symptom_gamma_shape = prob_infectious_severe_to_no_symptom_gamma_shape
        self.prob_infectious_severe_to_no_symptom_gamma_scale = prob_infectious_severe_to_no_symptom_gamma_scale
        self.prob_infectious_severe_to_no_symptom_gamma_loc = prob_infectious_severe_to_no_symptom_gamma_loc
        self.prob_infectious_severe_to_no_symptom_gamma_magnitude_multiplier = prob_infectious_severe_to_no_symptom_gamma_magnitude_multiplier
        self.prob_infectious_severe_to_mild_symptom_gamma_shape = prob_infectious_severe_to_mild_symptom_gamma_shape
        self.prob_infectious_severe_to_mild_symptom_gamma_scale = prob_infectious_severe_to_mild_symptom_gamma_scale
        self.prob_infectious_severe_to_mild_symptom_gamma_loc = prob_infectious_severe_to_mild_symptom_gamma_loc
        self.prob_infectious_severe_to_mild_symptom_gamma_magnitude_multiplier = prob_infectious_severe_to_mild_symptom_gamma_magnitude_multiplier
        self.prob_infectious_severe_to_critical_symptom_gamma_shape = prob_infectious_severe_to_critical_symptom_gamma_shape
        self.prob_infectious_severe_to_critical_symptom_gamma_scale = prob_infectious_severe_to_critical_symptom_gamma_scale
        self.prob_infectious_severe_to_critical_symptom_gamma_loc = prob_infectious_severe_to_critical_symptom_gamma_loc
        self.prob_infectious_severe_to_critical_symptom_gamma_magnitude_multiplier = prob_infectious_severe_to_critical_symptom_gamma_magnitude_multiplier
        self.prob_infectious_critical_to_no_symptom_gamma_shape = prob_infectious_critical_to_no_symptom_gamma_shape
        self.prob_infectious_critical_to_no_symptom_gamma_scale = prob_infectious_critical_to_no_symptom_gamma_scale
        self.prob_infectious_critical_to_no_symptom_gamma_loc = prob_infectious_critical_to_no_symptom_gamma_loc
        self.prob_infectious_critical_to_no_symptom_gamma_magnitude_multiplier = prob_infectious_critical_to_no_symptom_gamma_magnitude_multiplier
        self.prob_infectious_critical_to_mild_symptom_gamma_shape = prob_infectious_critical_to_mild_symptom_gamma_shape
        self.prob_infectious_critical_to_mild_symptom_gamma_scale = prob_infectious_critical_to_mild_symptom_gamma_scale
        self.prob_infectious_critical_to_mild_symptom_gamma_loc = prob_infectious_critical_to_mild_symptom_gamma_loc
        self.prob_infectious_critical_to_mild_symptom_gamma_magnitude_multiplier = prob_infectious_critical_to_mild_symptom_gamma_magnitude_multiplier
        self.prob_infectious_critical_to_severe_symptom_gamma_shape = prob_infectious_critical_to_severe_symptom_gamma_shape
        self.prob_infectious_critical_to_severe_symptom_gamma_scale = prob_infectious_critical_to_severe_symptom_gamma_scale
        self.prob_infectious_critical_to_severe_symptom_gamma_loc = prob_infectious_critical_to_severe_symptom_gamma_loc
        self.prob_infectious_critical_to_severe_symptom_gamma_magnitude_multiplier = prob_infectious_critical_to_severe_symptom_gamma_magnitude_multiplier

        self.prob_spread_virus_dist = GammaProbabilityGenerator(
            shape=self.prob_spread_virus_gamma_shape,
            scale=self.prob_spread_virus_gamma_scale,
            loc=self.prob_spread_virus_gamma_loc,
            magnitude_multiplier=self.
            prob_spread_virus_gamma_magnitude_multiplier,
        )

        self.prob_recover_dist = GammaProbabilityGenerator(
            shape=self.prob_recover_gamma_shape,
            scale=self.prob_recover_gamma_scale,
            loc=self.prob_recover_gamma_loc,
            magnitude_multiplier=self.prob_recover_gamma_magnitude_multiplier,
        )

        self.prob_virus_kill_host_dist = GammaProbabilityGenerator(
            shape=self.prob_virus_kill_host_gamma_shape,
            scale=self.prob_virus_kill_host_gamma_scale,
            loc=self.prob_virus_kill_host_gamma_loc,
            magnitude_multiplier=self.
            prob_virus_kill_host_gamma_magnitude_multiplier,
        )

        self.prob_infectious_no_to_mild_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_no_to_mild_symptom_gamma_shape,
            scale=self.prob_infectious_no_to_mild_symptom_gamma_scale,
            loc=self.prob_infectious_no_to_mild_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_no_to_mild_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_no_to_severe_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_no_to_severe_symptom_gamma_shape,
            scale=self.prob_infectious_no_to_severe_symptom_gamma_scale,
            loc=self.prob_infectious_no_to_severe_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_no_to_severe_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_no_to_critical_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_no_to_critical_symptom_gamma_shape,
            scale=self.prob_infectious_no_to_critical_symptom_gamma_scale,
            loc=self.prob_infectious_no_to_critical_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_no_to_critical_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_mild_to_no_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_mild_to_no_symptom_gamma_shape,
            scale=self.prob_infectious_mild_to_no_symptom_gamma_scale,
            loc=self.prob_infectious_mild_to_no_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_mild_to_no_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_mild_to_severe_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_mild_to_severe_symptom_gamma_shape,
            scale=self.prob_infectious_mild_to_severe_symptom_gamma_scale,
            loc=self.prob_infectious_mild_to_severe_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_mild_to_severe_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_mild_to_critical_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_mild_to_critical_symptom_gamma_shape,
            scale=self.prob_infectious_mild_to_critical_symptom_gamma_scale,
            loc=self.prob_infectious_mild_to_critical_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_mild_to_critical_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_severe_to_no_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_severe_to_no_symptom_gamma_shape,
            scale=self.prob_infectious_severe_to_no_symptom_gamma_scale,
            loc=self.prob_infectious_severe_to_no_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_severe_to_no_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_severe_to_mild_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_severe_to_mild_symptom_gamma_shape,
            scale=self.prob_infectious_severe_to_mild_symptom_gamma_scale,
            loc=self.prob_infectious_severe_to_mild_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_severe_to_mild_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_severe_to_critical_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_severe_to_critical_symptom_gamma_shape,
            scale=self.prob_infectious_severe_to_critical_symptom_gamma_scale,
            loc=self.prob_infectious_severe_to_critical_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_severe_to_critical_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_critical_to_no_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_critical_to_no_symptom_gamma_shape,
            scale=self.prob_infectious_critical_to_no_symptom_gamma_scale,
            loc=self.prob_infectious_critical_to_no_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_critical_to_no_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_critical_to_mild_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_critical_to_mild_symptom_gamma_shape,
            scale=self.prob_infectious_critical_to_mild_symptom_gamma_scale,
            loc=self.prob_infectious_critical_to_mild_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_critical_to_mild_symptom_gamma_magnitude_multiplier,
        )

        self.prob_infectious_critical_to_severe_symptom_dist = GammaProbabilityGenerator(
            shape=self.prob_infectious_critical_to_severe_symptom_gamma_shape,
            scale=self.prob_infectious_critical_to_severe_symptom_gamma_scale,
            loc=self.prob_infectious_critical_to_severe_symptom_gamma_loc,
            magnitude_multiplier=self.
            prob_infectious_critical_to_severe_symptom_gamma_magnitude_multiplier,
        )

        self.prob_recovered_no_to_mild_complication = prob_recovered_no_to_mild_complication
        self.prob_recovered_no_to_severe_complication = prob_recovered_no_to_severe_complication
        self.prob_recovered_mild_to_no_complication = prob_recovered_mild_to_no_complication
        self.prob_recovered_mild_to_severe_complication = prob_recovered_mild_to_severe_complication
        self.prob_recovered_severe_to_no_complication = prob_recovered_severe_to_no_complication
        self.prob_recovered_severe_to_mild_complication = prob_recovered_severe_to_mild_complication
        self.prob_gain_immunity = prob_gain_immunity

        self.hospital_bed_capacity_as_percent_of_population = hospital_bed_capacity_as_percent_of_population
        self.hospital_bed_cost_per_day = hospital_bed_cost_per_day
        self.hospital_bed_current_load = 0
        self.hospital_bed_use_day_tracker = 0

        self.icu_bed_capacity_as_percent_of_population = icu_bed_capacity_as_percent_of_population
        self.icu_bed_cost_per_day = icu_bed_cost_per_day
        self.icu_bed_current_load = 0
        self.icu_bed_use_day_tracker = 0

        self.ventilator_capacity_as_percent_of_population = ventilator_capacity_as_percent_of_population
        self.ventilator_cost_per_day = ventilator_cost_per_day
        self.ventilator_current_load = 0
        self.ventilator_use_day_tracker = 0

        self.drugX_capacity_as_percent_of_population = drugX_capacity_as_percent_of_population
        self.drugX_cost_per_day = drugX_cost_per_day
        self.drugX_current_load = 0
        self.drugX_use_day_tracker = 0

        self.clinical_resource = ClinicalResource(
            1,
            self,
            self.hospital_bed_capacity_as_percent_of_population,
            self.hospital_bed_cost_per_day,
            self.hospital_bed_current_load,
            self.hospital_bed_use_day_tracker,
            self.icu_bed_capacity_as_percent_of_population,
            self.icu_bed_cost_per_day,
            self.icu_bed_current_load,
            self.icu_bed_use_day_tracker,
            self.ventilator_capacity_as_percent_of_population,
            self.ventilator_cost_per_day,
            self.ventilator_current_load,
            self.ventilator_use_day_tracker,
            self.drugX_capacity_as_percent_of_population,
            self.drugX_cost_per_day,
            self.drugX_current_load,
            self.drugX_use_day_tracker,
        )

        self.testing = Testing(
            1,
            self,
            agent=None,
            prob_tested_for_no_symptom=[0.005, 0.01, 0.01],
            prob_tested_for_mild_symptom=[0.005, 0.01, 0.01],
            prob_tested_for_severe_symptom=[0.01, 0.03, 0.05],
            prob_tested_for_critical_symptom=[0.01, 0.03, 0.05],
            test_sensitivity=[0.89, 0.95, 0.95],
            test_specificity=[0.95, 0.99, 0.99],
            time_period=[(0, 25), (26, 60), (60, 999)],
            current_time=None,
            on_switch=True)

        self.social_distancing = SocialDistancing(1,
                                                  self,
                                                  edge_threshold=[0.25],
                                                  time_period=[(50, 999)],
                                                  current_time=None,
                                                  on_switch=False)

        self.vaccine = Vaccine(1,
                               self,
                               agent=None,
                               prob_vaccinated=[0.10],
                               vaccine_success_rate=[0.80],
                               time_period=[(50, 999)],
                               current_time=None,
                               on_switch=False)

        self.model_reporters_dict = {
            'Time': return_time,
            'Total N': return_total_n,
            'Test done': number_test_done,
            'Susceptible': number_susceptible,
            'Recovered': number_recovered,
            'Infectious': number_infectious,
            'Dead': number_dead,
            'Test-confirmed infectious': number_infectious_test_confirmed,
            'Test-confirmed dead': number_dead_test_confirmed,
            'Cumulative test done': cumulative_total_test_done,
            'Cumulative infectious': cumulative_total_infectious,
            'Cumulative dead': cumulative_total_dead,
            'Cumulative test-confirmed infectious':
            cumulative_total_infectious_test_confirmed,
            'Cumulative test-confirmed dead':
            cumulative_total_dead_test_confirmed,
            'Rate per 1M cumulative test done': rate_cumulative_test_done,
            'Rate per 1M cumulative infectious': rate_cumulative_infectious,
            'Rate per 1M cumulative dead': rate_cumulative_dead,
            'Rate per 1M cumulative test-confirmed infectious':
            rate_cumulative_infectious_test_confirmed,
            'Rate per 1M cumulative test-confirmed dead':
            rate_cumulative_dead_test_confirmed,
            'Infectious-no symptom': number_infectious_no_symptom,
            'Infectious-mild symptom': number_infectious_mild_symptom,
            'Infectious-severe symptom': number_infectious_severe_symptom,
            'Infectious-critical symptom': number_infectious_critical_symptom,
            'Infectious using non-ICU hospital bed':
            number_infectious_using_hospital_bed,
            'Infectious using ICU hospital bed':
            number_infectious_using_icu_bed,
            'Infectious using ventilator': number_infectious_using_ventilator,
            'Recovered-no complication': number_recovered_no_complication,
            'Recovered-mild complication': number_recovered_mild_complication,
            'Recovered-severe complication':
            number_recovered_severe_complication,
            'Recovered using DrugX': number_recovered_using_drugX,
            'Mean R0': mean_r0,
        }
        self.datacollector = DataCollector(
            model_reporters=self.model_reporters_dict)

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            agent = HostAgent(
                i,
                self,
                DiseaseHealthState.SUSCEPTIBLE,
                RecoveredImmunityState.TBD,
                self.prob_recovered_no_to_mild_complication,
                self.prob_recovered_no_to_severe_complication,
                self.prob_recovered_mild_to_no_complication,
                self.prob_recovered_mild_to_severe_complication,
                self.prob_recovered_severe_to_no_complication,
                self.prob_recovered_severe_to_mild_complication,
                self.prob_gain_immunity,
                self.clinical_resource,
                self.social_distancing,
                self.vaccine,
                self.testing,
            )
            self.schedule.add(agent)
            # Add the agent to the node
            self.grid.place_agent(agent, node)

        # Assign random weights (float: 0 to 1) to each connection
        for u, v in self.G.edges():
            self.G[u][v]['weight'] = random.random()

        # Infect some nodes
        if self.set_initial_infectious_node_seed:
            self.random.seed(self.set_initial_infectious_node_seed)
        infectious_nodes = self.random.sample(self.G.nodes(),
                                              self.initial_outbreak_size)

        for agent in self.grid.get_cell_list_contents(infectious_nodes):
            agent.disease_health_state = DiseaseHealthState.INFECTIOUS
            agent._timer_since_beginning_of_last_infection = 0

            if agent.disease_health_state is DiseaseHealthState.INFECTIOUS:
                agent.infectious_symptom_state = InfectiousSymptomState.NO_SYMPTOM

        self.running = True
        self.datacollector.collect(self)

    def ratio_infectious_susceptible(self):
        try:
            return number_disease_health_state(
                self,
                DiseaseHealthState.INFECTIOUS) / number_disease_health_state(
                    self, DiseaseHealthState.SUSCEPTIBLE)
        except ZeroDivisionError:
            return math.inf

    def ratio_recovered_susceptible(self):
        try:
            return number_disease_health_state(
                self,
                DiseaseHealthState.RECOVERED) / number_disease_health_state(
                    self, DiseaseHealthState.SUSCEPTIBLE)
        except ZeroDivisionError:
            return math.inf

    def ratio_dead_susceptible(self):
        try:
            return number_disease_health_state(
                self, DiseaseHealthState.DEAD) / number_disease_health_state(
                    self, DiseaseHealthState.SUSCEPTIBLE)
        except ZeroDivisionError:
            return math.inf

    def count_total_host(self):
        return number_disease_health_state(self, DiseaseHealthState.SUSCEPTIBLE) + number_disease_health_state(
            self, DiseaseHealthState.INFECTIOUS) + \
            number_disease_health_state(self, DiseaseHealthState.DEAD) + number_disease_health_state(
            self, DiseaseHealthState.RECOVERED)

    def count_total_living_host(self):
        return number_disease_health_state(
            self,
            DiseaseHealthState.SUSCEPTIBLE) + number_disease_health_state(
                self,
                DiseaseHealthState.INFECTIOUS) + number_disease_health_state(
                    self, DiseaseHealthState.RECOVERED)

    def rate_infectious(self):
        return (
            number_disease_health_state(self, DiseaseHealthState.INFECTIOUS) /
            self.count_total_host()) * self.rate_denominator

    def rate_recovered(self):
        return (number_disease_health_state(self, DiseaseHealthState.RECOVERED)
                / self.count_total_host()) * self.rate_denominator

    def rate_dead(self):
        return (number_disease_health_state(self, DiseaseHealthState.DEAD) /
                self.count_total_host()) * self.rate_denominator

    def rate_infectious_test_confirmed(self):
        return (number_disease_health_state_test_confirmed(
            self, DiseaseHealthState.INFECTIOUS) /
                self.count_total_host()) * self.rate_denominator

    def rate_dead_test_confirmed(self):
        return (number_disease_health_state_test_confirmed(
            self, DiseaseHealthState.DEAD) /
                self.count_total_host()) * self.rate_denominator

    def cumulative_total_test_done(self):
        return self.cumulative_test_done

    def cumulative_total_infectious(self):
        return self.cumulative_infectious_cases

    def cumulative_total_dead(self):
        return self.cumulative_dead_cases

    def cumulative_total_infectious_test_confirmed(self):
        return self.cumulative_infectious_test_confirmed_cases

    def cumulative_total_dead_test_confirmed(self):
        return self.cumulative_dead_test_confirmed_cases

    def cumulative_total_new_hosts_of_hospital_bed_use(self):
        return self.cumulative_hospital_bed_use_in_new_host_counts

    def cumulative_total_new_hosts_of_icu_bed_use(self):
        return self.cumulative_icu_bed_use_in_new_host_counts

    def cumulative_total_new_hosts_of_ventilator_use(self):
        return self.cumulative_ventilator_use_in_new_host_counts

    def cumulative_total_new_hosts_of_drugX_use(self):
        return self.cumulative_drugX_use_in_new_host_counts

    def cumulative_total_days_of_hospital_bed_use(self):
        return self.cumulative_hospital_bed_use_in_days

    def cumulative_total_days_of_icu_bed_use(self):
        return self.cumulative_icu_bed_use_in_days

    def cumulative_total_days_of_ventilator_use(self):
        return self.cumulative_ventilator_use_in_days

    def cumulative_total_days_of_drugX_use(self):
        return self.cumulative_drugX_use_in_days

    def cumulative_total_costs_in_hospital_bed_use(self):
        return self.cumulative_hospital_bed_use_in_days * self.hospital_bed_cost_per_day

    def cumulative_total_costs_in_icu_bed_use(self):
        return self.cumulative_icu_bed_use_in_days * self.icu_bed_cost_per_day

    def cumulative_total_costs_in_ventilator_use(self):
        return self.cumulative_ventilator_use_in_days * self.ventilator_cost_per_day

    def cumulative_total_costs_in_drugX_use(self):
        return self.cumulative_drugX_use_in_days * self.drugX_cost_per_day

    def rate_cumulative_test_done(self):
        return (self.cumulative_total_test_done() /
                self.count_total_host()) * self.rate_denominator

    def rate_cumulative_infectious(self):
        return (self.cumulative_total_infectious() /
                self.count_total_host()) * self.rate_denominator

    def rate_cumulative_dead(self):
        return (self.cumulative_total_dead() /
                self.count_total_host()) * self.rate_denominator

    def rate_cumulative_infectious_test_confirmed(self):
        return (self.cumulative_total_infectious_test_confirmed() /
                self.count_total_host()) * self.rate_denominator

    def rate_cumulative_dead_test_confirmed(self):
        return (self.cumulative_total_dead_test_confirmed() /
                self.count_total_host()) * self.rate_denominator

    def rate_infectious_any_symptom(self):
        return ((number_infectious_mild_symptom(self) +
                 number_infectious_severe_symptom(self) +
                 number_infectious_critical_symptom(self)) /
                self.count_total_living_host()) * self.rate_denominator

    def rate_recovered_any_complication(self):
        return ((number_recovered_mild_complication(self) +
                 number_recovered_severe_complication(self)) /
                self.count_total_living_host()) * self.rate_denominator

    def rate_infectious_using_hospital_bed(self):
        try:
            return (number_infectious_using_hospital_bed(self) /
                    number_infectious(self) * 1000)
        except ZeroDivisionError:
            return math.inf

    def rate_infectious_using_icu_bed(self):
        try:
            return (number_infectious_using_icu_bed(self) /
                    number_infectious(self) * 1000)
        except ZeroDivisionError:
            return math.inf

    def rate_infectious_using_ventilator(self):
        try:
            return (number_infectious_using_ventilator(self) /
                    number_infectious(self) * 1000)
        except ZeroDivisionError:
            return math.inf

    def rate_recovered_using_drugX(self):
        try:
            return (number_recovered_using_drugX(self) /
                    number_recovered(self) * 1000)
        except ZeroDivisionError:
            return math.inf

    def mean_age(self):
        count = 0
        total_age = 0
        for agent in self.grid.get_cell_list_contents(self.G.nodes()):
            if agent.disease_health_state is not DiseaseHealthState.DEAD:
                count += 1
                total_age += agent.age
        try:
            return total_age / count
        except ZeroDivisionError:
            return math.inf

    def proportion_sex(self):
        count = 0
        total_male = 0
        total_female = 0
        for agent in self.grid.get_cell_list_contents(self.G.nodes()):
            if agent.disease_health_state is not DiseaseHealthState.DEAD:
                count += 1
                if agent.sex is 'M':
                    total_male += 1
                elif agent.sex is 'F':
                    total_female += 1
        try:
            return {'M': total_male / count, 'F': total_female / count}
        except ZeroDivisionError:
            return {'M': math.inf, 'F': math.inf}

    def mean_r0(self):
        number_infectious_active_in_last_n_time_units = 0
        number_new_infection_in_last_n_time_units = 0
        last_n_time_unit = self._last_n_time_unit_for_mean_r0

        for agent, content in self.all_agents_new_infection_tracker.items():
            for time_of_new_infection, number_of_new_infection in content.items(
            ):
                initial_time = self._current_timer - last_n_time_unit
                if initial_time >= 0:
                    if int(time_of_new_infection) in range(
                            initial_time, self._current_timer):
                        number_infectious_active_in_last_n_time_units += 1
                        number_new_infection_in_last_n_time_units += number_of_new_infection
                else:
                    number_infectious_active_in_last_n_time_units += 1
                    number_new_infection_in_last_n_time_units += number_of_new_infection

        try:
            return number_new_infection_in_last_n_time_units / number_infectious_active_in_last_n_time_units
        except ZeroDivisionError:
            return 0

    def step(self):
        self._current_timer += 1
        self.schedule.step()
        self.datacollector.collect(self)

    def run_model(self, n):
        for i in range(n):
            self.step()
示例#19
0
class KalickHamilton(Model):
    """A model following Andre Grow's Netlogo tutorial of Kalick Hamilton 1986
    replicated using Mesa"""
    def __init__(self,
                 seed=None,
                 num_nodes=50,
                 preference='attractiveness',
                 mean_male=5,
                 sd_male=1,
                 mean_female=5,
                 sd_female=1,
                 corr_results=pd.DataFrame()):

        self.num_nodes = num_nodes
        self.preference = preference
        self.mean_male = mean_male
        self.sd_male = sd_male
        self.mean_female = mean_female
        self.sd_female = sd_female
        self.corr_results = corr_results
        self.step_count = 0
        self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0)
        self.grid = NetworkGrid(self.G)
        self.schedule = RandomActivation(self)
        self.datacollector = DataCollector(model_reporters={
            "number_single":
            number_single,
            "number_female":
            number_female,
            "number_union":
            number_union,
            "mean_attractiveness":
            mean_attractiveness,
            "corr_results":
            calculate_correlations
        },
                                           agent_reporters={
                                               "attractiveness": lambda x: x.A,
                                               "sex": lambda x: x.S,
                                               "relationship": lambda x: x.R,
                                           })

        # Create agents
        for i, node in enumerate(self.G.nodes()):
            person = Human(
                i,
                self,
                "MALE",
                0,
                "SINGLE",
            )
            self.schedule.add(person)
            # Add the agent to the node
            self.grid.place_agent(person, node)
        # convert half of agents to "FEMALE"
        # this need number of agents to always be dividable by 2
        # as set in the user-settable slider
        female_nodes = self.random.sample(self.G.nodes(),
                                          (int(self.num_nodes / 2)))
        for a in self.grid.get_cell_list_contents(female_nodes):
            a.S = "FEMALE"
        # here assign attractiveness based on normal distributions
        for a in self.schedule.agents:
            if a.S == 'MALE':
                A2use = np.random.normal(self.mean_male, self.sd_male, 1)[0]
                while A2use < 1 or A2use > 10:
                    A2use = np.random.normal(self.mean_male, self.sd_male,
                                             1)[0]
                a.A = A2use
            else:
                A2use = np.random.normal(self.mean_female, self.sd_female,
                                         1)[0]
                while A2use < 1 or A2use > 10:
                    A2use = np.random.normal(self.mean_female, self.sd_female,
                                             1)[0]
                a.A = A2use

        self.running = True
        self.datacollector.collect(self)

    def single_union_ratio(self):
        try:
            return number_state(self, "SINGLE") / number_state(self, "UNION")
        except ZeroDivisionError:
            return math.inf

    def do_match_singles(self):
        for a in self.schedule.agents:
            if a.S == 'MALE' and a.R == 'SINGLE':
                a.date_someone()

    def do_calculate_decision_probabilities(self):
        for a in self.schedule.agents:
            if a.R == 'SINGLE':
                a.calculate_decision_probabilities()

    def do_union_decisions(self):
        for a in self.schedule.agents:
            if a.S == 'MALE' and a.R == 'SINGLE':
                a.take_union_decision()

    def step(self):
        # add to step counter
        self.step_count += 1
        self.schedule.step()
        self.do_match_singles()
        self.do_calculate_decision_probabilities()
        self.do_union_decisions()
        # collect data
        self.datacollector.collect(self)
        # if all agents in union or 51 steps past, stop.
        if number_single(self) == 0 or self.step_count == 51:
            self.running = False

    def run_model(self, n):
        for i in range(n):
            self.step()
class SegregationModelModel(Model):

    def __init__(self, datacollector=None):
        super().__init__()
        # work from directory this file is in
        os.chdir(os.path.dirname(os.path.realpath(__file__)))
        self.schedule = SimultaneousActivation(self)
        self.G = nx.Graph()
        self.time = 0  # simple iteration counter
        self._generate_sites()
        self.grid = NetworkGrid(self.G)
        # make a dictionary of {hash: site} values for easy relation lookups in agent generation
        self.site_hashes = {h: s for s, h in dict(
            self.G.nodes.data('hash')).items()}
        self._generate_agents()
        self.vita_groups = []
        self.datacollector = datacollector

    def step(self):
        if self.datacollector:
            self.datacollector.collect(self)
        else:
            warnings.warn('This Model has no DataCollector! You may want to add one in the `datacollector` attribute '
                          'before running the model')

        self.schedule.step()

        while self.vita_groups:
            a = self.vita_groups.pop()
            a.unique_id = self.next_id()
            a.model = self
            self.schedule.add(a)

        for a in self.schedule.agents:
            if a.get('__void__', False):
                self.grid._remove_agent(a, a.pos)
                self.schedule.remove(a)

        self.time += 1

    # ------------------------- INITIALIZATION HELPERS -------------------------

    def _generate_agents(self):
        """
        Called once during __init__ to create appropriate groups from the original simulation's model and add them to
        the model grid.
        Loads group data from the JSON file created during translation.
        """
        with open("SegregationModelGroups.json", 'r') as file:
            j = json.load(file)
            for group in j:
                for _ in range(group['m']):
                    a = SegregationModelAgent(
                        self.next_id(), self, group['attr'], group['rel'])
                    self.schedule.add(a)

    def _generate_sites(self):
        """
        Called once during __init__ to load the original simulation's sites into the networkx graph.
        Loads site data from a JSON file created during translation.
        """
        with open("SegregationModelSites.json", 'r') as file:
            j = json.load(file)
            for site in j:
                self.G.add_node(
                    str(site['name']), hash=site['hash'], rel_name=site['rel_name'])
                for k, v in site['attr'].items():
                    self.G.nodes[str(site['name'])][k] = v

    # ------------------------- RUNTIME FUNCTIONS -------------------------

    def get_attr(self, agent_or_node, name=None):
        """
        Retrieves an attribute of a Mesa Agent or NetworkGrid node.
        :param name: A string containing the attribute to retrieve, or None
        :param agent_or_node: A Mesa Agent or a string corresponding to a node in the NetworkGrid
        :return: If agent_or_node is a string, returns the named attribute represented by it, or the node's entire
                     attribute dictionary if name is None (note: this includes the special 'agent' attribute)
                 If agent_or_node is an Agent, returns the named attribute of that Agent
        """
        name = mpi(name) if name is not None else name
        if isinstance(agent_or_node, str):
            node_dict = self.grid.G.nodes[agent_or_node]
            return node_dict.get(name) if name is not None else node_dict
        elif isinstance(agent_or_node, Agent):
            # return getattr(agent_or_node, name, agent_or_node.namespace[name])
            return agent_or_node.get(name)
        else:
            raise TypeError(
                f"get_attr expected a str or Agent for agent_or_node, but received {type(agent_or_node)}")

    def get_groups(self, node_or_model, qry=None):
        """
        Returns a list of agents at the node or the entire model that satisfy the qry. 
        :param node_or_model: A string corresponding to a node in the NetworkGrid, or a Mesa Model
        :param qry: a GroupQry namedtuple
        :return: a list of agents at the node satisfying the qry. 
        """
        if isinstance(node_or_model, Model):
            agents = node_or_model.schedule.agents
        elif isinstance(node_or_model, str):
            agents = self.grid.get_cell_list_contents([node_or_model])
        else:
            raise TypeError(
                f"get_groups expects a str or Model for node_or_model, but received {type(node_or_model)}")

        return [a for a in agents if a.matches_qry(qry)]
        # if not qry:
        #     return agents
        # # the code below is REALLY PAINFUL... replacing it with 'return agents` makes the code run like 20x faster
        # elif qry.full:
        #     return [a for a in agents
        #             if qry.attr.items() == {k: getattr(a, k) for k in a._attr}.items()
        #             and qry.rel.items() == {k: getattr(a, k) for k in a._rel}.items()
        #             and all([fn(a) for fn in qry.cond])]
        # else:
        #     return [a for a in agents
        #             if qry.attr.items() <= {k: getattr(a, k) for k in a._attr}.items()
        #             and qry.rel.items() <= {k: getattr(a, k) for k in a._rel}.items()
        #             and all([fn(a) for fn in qry.cond])]

    def get_mass(self, agent_node_model, qry=None):
        """
        If agent_node_model is an agent, returns the number of agents with the same attributes as it, including itself.
        This ignores unique_id (and source_name).
        This is probably very unoptimized.
        If agent_node_model is a string corresponding to a node in the NetworkGrid, returns the number of agents at that
        node with the attributes specified in qry, or all agents at that node if qry is None.
        If agent_node_model is a Model, returns the total number of agents in the model.
        """
        if isinstance(agent_node_model, str):
            return len(self.get_groups(agent_node_model, qry))
        elif isinstance(agent_node_model, Agent):
            mod_dict = {k: v for k, v in agent_node_model.__dict__.items()
                        if k not in ('unique_id', 'source_name')}  # toss unique identifiers
            return sum([mod_dict == {k: v for k, v in a.__dict__.items() if k not in ('unique_id', 'source_name')}
                        for a in self.schedule.agents])
        elif isinstance(agent_node_model, Model):
            return len(agent_node_model.schedule.agents)
        else:
            raise TypeError(f"get_mass expects a str, Agent, or Model for agent_node_model, but received "
                            f"{type(agent_node_model)}")
示例#21
0
class SEmodel(Model):
    # manual model has parameters set by user interface
    def __init__(
            self,
            num_nodes=100,
            avg_node_degree=3,
            # taipei : 1.92
            # telaviv : 2.16
            # tallinn : 2.20,
            engagement=0.49,
            trustability=0.21,
            influenceability=0.53,
            recovery=0.63,
            experience=1,
            initial_opinion=0,
            opinion=0,
            public_sector_opinion=1,
            corpo_opinion=1,
            startup_opinion=1,
            academic_opinion=-1,
            civil_opinion=-1,
            media_opinion=-1):
        # set network layout
        self.num_nodes = num_nodes
        prob = avg_node_degree / self.num_nodes
        self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
        # set space and time of the model
        self.grid = NetworkGrid(self.G)
        self.schedule = RandomActivation(self)
        # set model parameters
        self.engagement = engagement
        self.trustability = trustability
        self.influenceability = influenceability
        self.recovery = recovery
        self.experience = experience
        self.initial_opinion = initial_opinion
        self.opinion = initial_opinion
        self.public_sector_opinion = public_sector_opinion
        self.corpo_opinion = corpo_opinion
        self.startup_opinion = startup_opinion
        self.academic_opinion = academic_opinion
        self.civil_opinion = civil_opinion
        self.media_opinion = media_opinion
        # set data collection
        self.datacollector = DataCollector({
            "Negative":
            num_negative,
            "Neutral":
            num_neutral,
            "Positive":
            num_positive,
            "Total Engagement":
            total_engagement,
            "Total Trustability":
            total_trustability,
            "Total Recovery":
            total_recovery,
            "Total Experience":
            total_experience,
        })

        # create agents with average parameters taken on #city tweets
        for i, node in enumerate(self.G.nodes()):
            a = agent.Agent(
                i,
                self,
                self.engagement,
                self.trustability,
                self.influenceability,
                self.recovery,
                self.experience,
                self.initial_opinion,  # fixed by interface
                self.opinion)
            self.schedule.add(a)
            # add the undetermined agents to the network
            self.grid.place_agent(a, node)

        # create 1 representative of each stakeholder category
        public_sector = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(public_sector):
            a.engagement = 0.57
            a.trustability = 0.53
            a.influenceability = 0.59
            a.recovery = 0.70
            a.experience = 1
            a.initial_opinion = public_sector_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        corporate = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(corporate):
            a.engagement = 0.75
            a.trustability = 0.49
            a.influenceability = 0.68
            a.recovery = 0.73
            a.experience = 1
            a.initial_opinion = corpo_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        startup = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(startup):
            a.engagement = 0.69
            a.trustability = 0.29
            a.influenceability = 0.68
            a.recovery = 0.97
            a.experience = 1
            a.initial_opinion = startup_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        academic = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(academic):
            a.engagement = 0.49
            a.trustability = 0.20
            a.influenceability = 0.65
            a.recovery = 0.75
            a.experience = 1
            a.initial_opinion = academic_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        civil = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(civil):
            a.engagement = 0.43
            a.trustability = 0.21
            a.influenceability = 0.69
            a.recovery = 0.72
            a.experience = 1
            a.initial_opinion = civil_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        media = self.random.sample(self.G.nodes(), 1)
        for a in self.grid.get_cell_list_contents(media):
            a.engagement = 0.50
            a.trustability = 0.23
            a.influenceability = 0.65
            a.recovery = 0.71
            a.experience = 1
            a.initial_opinion = media_opinion  # fixed by interface
            a.opinion = a.initial_opinion

        self.running = True
        self.datacollector.collect(self)
        print('Finished initialising model, network has %s nodes' %
              self.G.nodes)
        nx.draw_networkx(self.G)
        #plt.show()

    def positive_negative_ratio(self):
        try:
            return num_positive(self) / num_negative(self)
        except ZeroDivisionError:
            return 0.00

    def step(self):
        # advance the model by one step and collect data
        self.schedule.step()
        # collect data
        self.datacollector.collect(self)

    def run_model(self, n):
        for i in range(n):
            self.step()
示例#22
0
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)
示例#23
0
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()