Beispiel #1
0
class TotC(Model):
    """
    Main model for the tragedy of the commons
    """
    def __init__(self,
                 initial_herdsmen=5,
                 initial_sheep_per_herdsmen=0,
                 initial_edges=5,
                 l_coop=0,
                 l_fairself=0,
                 l_fairother=0,
                 l_negrecip=0,
                 l_posrecip=0,
                 l_conf=0):
        super().__init__()
        self.width = GRID_SIZE
        self.height = GRID_SIZE

        self.grass = []
        self.herdsmen = []
        self.initial_herdsmen = initial_herdsmen
        self.initial_sheep_per_herdsmen = initial_sheep_per_herdsmen
        self.sheep_survival_rate = []

        self.l_coop = l_coop
        self.l_fairself = l_fairself
        self.l_fairother = l_fairother
        self.l_negrecip = l_negrecip
        self.l_posrecip = l_posrecip
        self.l_conf = l_conf

        self.G = nx.gnm_random_graph(initial_herdsmen, initial_edges)

        Sheep.sheepdeaths = 0
        Herdsman.i = 0
        Herdsman.x = np.zeros(initial_herdsmen, dtype=np.int8)

        self.schedule_Grass = RandomActivation(self)
        self.schedule_Herdsman = StagedActivation(
            self, stage_list=["advance", "step"], shuffle=True)
        self.schedule_Sheep = RandomActivation(self)
        self.schedule = RandomActivation(self)

        self.grid = MultiGrid(self.width, self.height, torus=True)

        # "Grass" is the number of sheep that the grass can sustain
        self.datacollector = DataCollector({
            "Grass":
            lambda m: self.get_expected_grass_growth() / .5,
            "Sheep":
            lambda m: self.get_sheep_count(),
            "Sheep deaths":
            lambda m: Sheep.sheepdeaths
        })

        self.init_population()

        # required for the datacollector to work
        self.running = True
        self.datacollector.collect(self)

    def add_herdsman(self):
        """
        At a herdsman at a random position on the grid.
        """
        x = random.randrange(self.width)
        y = random.randrange(self.height)
        herdsman = self.new_agent(Herdsman, (x, y))
        self.herdsmen.append(herdsman)

    def init_grass(self):
        """
        Initialise a patch of grass at every square on the grid.
        """
        for agent, x, y in self.grid.coord_iter():
            self.grass.append(self.new_agent(Grass, (x, y)))

    def init_herdsman(self):
        """
        Spawn `initial_herdsmen' herdsmen on the field.
        """
        for i in range(getattr(self, "initial_herdsmen")):
            self.add_herdsman()

    def init_node_attr(self):
        """
        Assign the unique herdsman ID as attribute to graph nodes for the social
        network.
        """
        N = self.get_herdsman_count()
        for i in range(getattr(self, "initial_herdsmen")):
            for j in range(getattr(self, "initial_herdsmen")):
                if i is not j:
                    if nx.has_path(self.G, source=i, target=j) == True:
                        if nx.shortest_path_length(self.G, source=i,
                                                   target=j) == 1:
                            if sum(nx.common_neighbors(self.G, i, j)) > 5:
                                self.herdsmen[i].friendship_weights.append(1)
                            else:
                                self.herdsmen[i].friendship_weights.append(
                                    .75 + .05 *
                                    sum(nx.common_neighbors(self.G, i, j)))
                        elif nx.shortest_path_length(self.G,
                                                     source=i,
                                                     target=j) == 1:
                            if sum(nx.common_neighbors(self.G, i, j)) > 10:
                                self.herdsmen[i].friendship_weights.append(1)
                            else:
                                self.herdsmen[i].friendship_weights.append(
                                    .5 + .05 *
                                    sum(nx.common_neighbors(self.G, i, j)))
                        else:
                            self.herdsmen[i].friendship_weights.append(
                                1 / nx.shortest_path_length(
                                    self.G, source=i, target=j))
                    else:
                        self.herdsmen[i].friendship_weights.append(1 / N)

    def init_population(self):
        """
        Initialise grass, herdsmen, sheep, and the social network
        """
        self.init_grass()
        self.init_herdsman()
        self.init_node_attr()

    def get_expected_grass_growth(self):
        """
        Get an estimate of the expected grass growth for the next timestep. 
        If grass is fully grown it will return 0.0123 (the average grass growth
        over its lifetime.
        """
        return sum([
            grass.next_density() if grass.density < 0.99 else 0.0123
            for grass in self.grass
        ])

    def get_grass_count(self):
        """
        Get a sum of all grass densities.
        """
        return sum([grass.density for grass in self.grass])

    def get_herdsman_count(self):
        return self.schedule_Herdsman.get_agent_count()

    def get_sheep_count(self):
        return self.schedule_Sheep.get_agent_count()

    def new_agent(self, agent_type, pos):
        """
        Create new agent, and add it to the scheduler.
        """
        agent = agent_type(self.next_id(), self, pos)
        self.grid.place_agent(agent, pos)
        getattr(self, f"schedule_{agent_type.__name__}").add(agent)

        return agent

    def remove_agent(self, agent):
        """
        Remove agent from the grid and the scheduler.
        """
        self.grid.remove_agent(agent)
        getattr(self, f"schedule_{type(agent).__name__}").remove(agent)

    def run_model(self, step_count=200):
        """
        Runs the model for a specific amount of steps.
        """
        for i in range(step_count):
            self.step()

    def step(self):
        """
        Calls the step method for grass and sheep.
        """
        self.schedule_Grass.step()
        a = self.get_sheep_count()
        self.schedule_Sheep.step()
        b = self.get_sheep_count()
        c = b / a if a > 0 else 0
        self.sheep_survival_rate.append(c)
        self.schedule_Herdsman.step()
        self.schedule.step()

        # save statistics
        self.datacollector.collect(self)
class World(Model):
    def __init__(self, N, coop, e_prob, width=100, height=100):
        self.num_agents = N
        self.grid = MultiGrid(width, height, False)
        self.schedule = RandomActivation(self)
        self.running = True
        self.population_center_x = np.random.randint(
            POP_MARGIN, self.grid.width - POP_MARGIN)
        self.population_center_y = np.random.randint(
            POP_MARGIN, self.grid.height - POP_MARGIN)
        self.num_energy_resources = RESERVE_SIZE
        self.num_explorers = 0
        self.num_exploiters = 0
        self.datacollector = DataCollector(model_reporters={
            "Num_Explorer": "num_explorers",
            "Num_Exploiter": "num_exploiters"
        })
        self.bases = self.init_base()
        self.ages = []
        self.memoryLens = [
        ]  # This will store average memory length at death for agent
        self.expected_ages = []
        self.member_tracker = []
        self.energy_tracker = []
        self.decay_rates = self.init_decay_rates()

        # add social agents to the world
        for i in range(1, self.num_agents + 1):
            if np.random.uniform() <= EXPLORER_RATIO:
                # create a new explorer
                a = Explorer("explorer_%d" % (i), self, coop)
                self.num_explorers += 1
            else:
                # create a new exploiter
                a = Exploiter("exploiter_%d" % (i), self, coop, e_prob)
                self.num_exploiters += 1

            # keep society members confined at beginning
            x = np.random.randint(self.population_center_x - POP_SPREAD,
                                  self.population_center_x + POP_SPREAD)
            y = np.random.randint(self.population_center_y - POP_SPREAD,
                                  self.population_center_y + POP_SPREAD)
            self.grid.place_agent(a, (x, y))

            # add agent to scheduler
            self.schedule.add(a)
            self.expected_ages.append(a.energy / a.living_cost)

        # add energy reserves to the world
        for i in range(self.num_energy_resources):
            a = EnergyResource("energy_reserve_%d" % (i), self,
                               self.decay_rates[i])

            # decide location of energy reserve
            x = np.random.randint(0, self.grid.width)
            y = np.random.randint(0, self.grid.height)
            self.grid.place_agent(a, (x, y))

            self.schedule.add(a)

    def init_base(self):
        pos = (self.population_center_x, self.population_center_y)
        return self.grid.get_neighborhood(pos, True, radius=POP_SPREAD)

    def init_decay_rates(self):
        decay_rates = list(
            np.random.uniform(-0.1, 0.02, self.num_energy_resources))
        np.random.shuffle(decay_rates)
        return decay_rates

    def step(self):
        self.datacollector.collect(self)
        self.schedule.step()
        self.member_tracker.append((self.num_explorers, self.num_exploiters))

        # keep track of total energy in world
        if self.schedule.time % 50 == 0:
            energies = [
                e.reserve for e in self.schedule.agents
                if isinstance(e, EnergyResource)
            ]
            if len(energies) > 0:
                mean_energy = np.mean(energies)
            else:
                mean_energy = 0
            self.energy_tracker.append(mean_energy)

        # change location of energy resources every 500 steps randomly
        # and change decay_rate to opposite every 100 steps
        if self.schedule.time % 50 == 0:
            for e in self.schedule.agents:
                if isinstance(e, EnergyResource) and np.random.uniform() < 0.1:
                    # change location
                    e.decay_rate *= -1
                    if self.schedule.time % 500 == 0:
                        self.grid.remove_agent(e)
                        x = np.random.randint(0, self.grid.width)
                        y = np.random.randint(0, self.grid.height)
                        self.grid.place_agent(e, (x, y))